<aside> ⏱️
Status: Spec · Not deployed · Supersedes EulersIdentitySynthesis.sol
Drift resolved: Title/body mismatch closed May 12, 2026 by Igor Holt. Body now hosts the Clock Spring contract referenced by EOC, IWA, JCS, Booster v2, and the UCP×GC decision matrix.
Owner: Igor Holt · Profit sink (immutable): 0x9545e2439c5c75d3aA723AcaC1AA6B0fa1DB6956
</aside>
<aside> 🔗
Canonical cluster anchors (previewable). Yennefer cluster · TAEX · Retraining loop · §6.17 · Integrations registry · Integrative Flow.
‣ · ‣ · ‣ · ‣ · ‣ · TAEX Intent Router — Agent-Based Routing with Dissonance Memory MCP · ‣ · ‣ · ‣
</aside>
This page hosts the canonical Clock Spring redesign of the on-chain convergence kernel. It retires the prior EulersIdentitySynthesis.sol reasoning-engine-as-contract pattern (preserved at the bottom of this page for provenance) and replaces it with a fail-closed cadence kernel whose reasoning lives off-chain (HAVIS DAG, /maru, Greg architect review, Self-Evolving Agent Retraining Worker) while the on-chain layer only ratchets one admissible cycle per tick with a verifiable receipts hash.
The redesign closes the five gaps identified in ‣:
| # | Gap (predecessor) | Fix (Clock Spring) |
|---|---|---|
| 1 | agent.addr == address(0) passthrough in _invokeAgent; every convergence resolves to zero residual trivially. |
Constructor takes five concrete IAgent addresses and reverts if any is zero. _invokeAgent reverts on UnboundAgent instead of returning zero. |
| 2 | No executable-action output — convergence emits events only. | IExecutableAgent extension with Action{target,data,value}. executeWinningAction() calls into target and sweeps ETH to treasurySink in the same tx. |
| 3 | No treasury sink; ETH flows are unbound. | address public immutable treasurySink set in constructor and verified equal to 0x9545…6956. Every executeWinningAction() sweeps the contract balance to the sink before returning. |
| 4 | setRiskThreshold and escalate have no access control. |
OpenZeppelin Ownable • renounceOwnership() blocked for 90 days via OWNERSHIP_TIMELOCK_SECONDS. After day 90, contract is fully autonomous. |
| 5 | Unbounded traces.push; no ReentrancyGuard; gas bomb in getEnsemble. |
Fixed-size ring buffer (TRACE_RING_SIZE = 1024); nonReentrant on converge() and executeWinningAction(); paginated getTracePage(offset, limit) and getEnsemblePage(offset, limit). |
settleCycle.attestor.bindAgent(id, addr) in IWA-050; v1 retired in favor of EOC vertex routing.// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
/**
* @title EulerCycleAttestor (Clock Spring Implementation v1.0)
* @notice Audit substrate for the Genesis Conductor convergence engine.
* Retires the EulersIdentitySynthesis reasoning-engine-as-contract
* pattern. Reasoning lives off-chain (HAVIS DAG, /maru, Greg, the
* Self-Evolving Retraining Worker); the on-chain layer ratchets one
* admissible cycle per tick with a verifiable receipts hash.
*
* Gap fixes vs predecessor:
* 1. Bound IAgent ensemble at construction (no address(0) passthrough).
* 2. Synthesizer returns Action{target,data,value}; executed and ETH
* proceeds routed through the immutable treasurySink in the same tx.
* 3. treasurySink is immutable and pinned to 0x9545…6956.
* 4. Ownable + 90-day renounceOwnership() timelock.
* 5. Ring-buffer traces (mod 1024); nonReentrant on mutating entrypoints;
* paginated ensemble + trace views.
*/
interface IAgent {
function constantValue() external pure returns (uint256);
function role() external pure returns (string memory);
function process(bytes calldata input)
external
returns (bytes memory output, uint256 residual);
}
interface IExecutableAgent is IAgent {
struct Action { address target; bytes data; uint256 value; }
function proposeAction(bytes calldata input)
external
returns (Action memory);
}
enum Phase { Expansion, AdversarialPrune, LateralInject, MergeLock, Converged }
uint256 constant DEFAULT_RISK_THRESHOLD = 0.4e18;
uint8 constant MAX_CYCLES_STANDARD = 3;
uint8 constant MAX_CYCLES_ESCALATED = 5;
uint256 constant TRACE_RING_SIZE = 1024;
uint256 constant OWNERSHIP_TIMELOCK_SECONDS = 90 days;
address constant CANONICAL_TREASURY_SINK =
0x9545e2439c5c75d3aA723AcaC1AA6B0fa1DB6956;
struct ConvergenceTrace {
uint256 cycleNumber;
Phase phase;
bytes32 winnerCandidateId;
uint256 residual;
uint256 timestamp;
bytes32 inputHash;
}
struct AgentDescriptor {
address addr;
string name;
string role;
uint256 constantValue;
}
contract EulerCycleAttestor is ReentrancyGuard, Ownable {
// ─── Immutable Treasury (Gap 3) ──────────────────────────────
address public immutable treasurySink;
uint256 public immutable deployTimestamp;
// ─── Euler Constants (scaled 1e18) ───────────────────────────
uint256 public constant E = 2_718_281_828_459_045_235;
uint256 public constant PI = 3_141_592_653_589_793_238;
uint256 public constant ONE = 1_000_000_000_000_000_000;
// ─── Agent Registry ─────────────────────────────────────────
bytes32 public constant ARCHITECT_ID = keccak256("growthAgentE");
bytes32 public constant GUARDIAN_ID = keccak256("cycleAgentPi");
bytes32 public constant INNOVATOR_ID = keccak256("imaginaryAgentI");
bytes32 public constant SYNTHESIZER_ID = keccak256("convergenceTarget0");
bytes32 public constant AUDITOR_ID = keccak256("unitAgent1");
mapping(bytes32 => AgentDescriptor) public agents;
bytes32[5] public agentIds;
// ─── State ──────────────────────────────────────────────────
uint256 public riskThreshold;
uint8 public maxCycles;
uint256 public currentResidual;
Phase public currentPhase;
uint8 public cycleCount;
bool public escalated;
// ─── Trace Ring Buffer (Gap 5) ──────────────────────────────
ConvergenceTrace[TRACE_RING_SIZE] private _traceRing;
uint256 public traceHead;
uint256 public traceWritten;
// ─── Events ─────────────────────────────────────────────────
event AgentBound(bytes32 indexed id, address indexed addr, uint256 constantValue);
event CycleStarted(uint8 indexed cycle, Phase phase);
event Converged(uint8 totalCycles, uint256 residual, bytes32 winnerId);
event ReEntry(uint8 cycle, uint256 drift);
event Escalated(uint8 newMaxCycles);
event EmittedWithUncertainty(uint256 residual, bytes32 bestCandidateId);
event ActionExecuted(
address indexed target,
uint256 value,
bytes32 traceHash,
uint256 sweptToSink
);
// ─── Errors ─────────────────────────────────────────────────
error UnboundAgent(bytes32 agentId);
error InvalidTreasurySink(address provided);
error EscalationLocked();
error TimelockNotExpired(uint256 unlocksAt);
error ActionFailed(bytes returndata);
error SinkSweepFailed();
// ─── Constructor (Gap 1) ────────────────────────────────────
/// @notice All five agent implementations MUST be deployed and passed
/// at construction. Zero-address passthrough is prohibited and
/// treasurySink is pinned to the canonical address.
constructor(
address architectAgent,
address guardianAgent,
address innovatorAgent,
address synthesizerAgent,
address auditorAgent,
address _treasurySink
) Ownable(msg.sender) {
if (_treasurySink != CANONICAL_TREASURY_SINK)
revert InvalidTreasurySink(_treasurySink);
if (architectAgent == address(0)) revert UnboundAgent(ARCHITECT_ID);
if (guardianAgent == address(0)) revert UnboundAgent(GUARDIAN_ID);
if (innovatorAgent == address(0)) revert UnboundAgent(INNOVATOR_ID);
if (synthesizerAgent == address(0)) revert UnboundAgent(SYNTHESIZER_ID);
if (auditorAgent == address(0)) revert UnboundAgent(AUDITOR_ID);
treasurySink = _treasurySink;
deployTimestamp = block.timestamp;
riskThreshold = DEFAULT_RISK_THRESHOLD;
maxCycles = MAX_CYCLES_STANDARD;
currentPhase = Phase.Expansion;
_bind(0, ARCHITECT_ID, architectAgent, "Architect", "Exponential Engine of Becoming", E);
_bind(1, GUARDIAN_ID, guardianAgent, "Guardian", "Circular Rhythm Constant", PI);
_bind(2, INNOVATOR_ID, innovatorAgent, "Innovator", "Dimensional Bridge", 0);
_bind(3, SYNTHESIZER_ID, synthesizerAgent, "Synthesizer", "Zero-Residual Convergence Target", 0);
_bind(4, AUDITOR_ID, auditorAgent, "Auditor", "Anchor / Reference Point", ONE);
}
function _bind(
uint8 slot,
bytes32 id,
address addr,
string memory name,
string memory roleDesc,
uint256 constantValue
) internal {
agents[id] = AgentDescriptor({
addr: addr,
name: name,
role: roleDesc,
constantValue: constantValue
});
agentIds[slot] = id;
emit AgentBound(id, addr, constantValue);
}
// ─── Core: Convergence Engine (Gap 5: nonReentrant) ─────────
function converge(bytes calldata inputConstraints)
external
nonReentrant
returns (
bytes memory result,
ConvergenceTrace memory trace,
uint256 residual
)
{
bytes32 inputHash = keccak256(inputConstraints);
cycleCount = 0;
currentResidual = type(uint256).max;
bytes memory candidates = inputConstraints;
bytes32 winnerId;
while (cycleCount < maxCycles && currentResidual > 0) {
cycleCount++;
currentPhase = Phase.Expansion;
emit CycleStarted(cycleCount, currentPhase);
(candidates, ) = _invokeAgent(ARCHITECT_ID, candidates);
currentPhase = Phase.AdversarialPrune;
emit CycleStarted(cycleCount, currentPhase);
(candidates, ) = _invokeAgent(GUARDIAN_ID, candidates);
currentPhase = Phase.LateralInject;
emit CycleStarted(cycleCount, currentPhase);
(candidates, ) = _invokeAgent(INNOVATOR_ID, candidates);
currentPhase = Phase.MergeLock;
emit CycleStarted(cycleCount, currentPhase);
uint256 synthResidual;
(candidates, synthResidual) = _invokeAgent(SYNTHESIZER_ID, candidates);
uint256 auditResidual;
(candidates, auditResidual) = _invokeAgent(AUDITOR_ID, candidates);
currentResidual = synthResidual + auditResidual;
winnerId = keccak256(candidates);
_pushTrace(ConvergenceTrace({
cycleNumber: cycleCount,
phase: currentPhase,
winnerCandidateId: winnerId,
residual: currentResidual,
timestamp: block.timestamp,
inputHash: inputHash
}));
if (currentResidual > 0 && cycleCount < maxCycles) {
emit ReEntry(cycleCount, currentResidual);
}
}
if (currentResidual == 0) {
currentPhase = Phase.Converged;
emit Converged(cycleCount, 0, winnerId);
} else if (cycleCount >= maxCycles) {
emit EmittedWithUncertainty(currentResidual, winnerId);
}
trace = _traceRing[(traceHead + TRACE_RING_SIZE - 1) % TRACE_RING_SIZE];
residual = currentResidual;
result = candidates;
}
// ─── Gap 2: Executable Action Routing ───────────────────────
/// @notice After converge() yields a winner, the Synthesizer's
/// IExecutableAgent.proposeAction is executed and any ETH
/// balance is swept to the immutable treasurySink in the
/// same call frame.
function executeWinningAction(bytes calldata input)
external
payable
nonReentrant
returns (bytes memory returndata, uint256 sweptToSink)
{
address synth = agents[SYNTHESIZER_ID].addr;
IExecutableAgent.Action memory action =
IExecutableAgent(synth).proposeAction(input);
(bool ok, bytes memory rd) =
action.target.call{value: action.value}(action.data);
if (!ok) revert ActionFailed(rd);
sweptToSink = address(this).balance;
if (sweptToSink > 0) {
(bool sent, ) = treasurySink.call{value: sweptToSink}("");
if (!sent) revert SinkSweepFailed();
}
emit ActionExecuted(action.target, action.value, keccak256(rd), sweptToSink);
return (rd, sweptToSink);
}
// ─── Gap 4: Access-Controlled Risk Gate ─────────────────────
function setRiskThreshold(uint256 newThreshold) external onlyOwner {
require(newThreshold <= 1e18, "threshold > 1.0");
riskThreshold = newThreshold;
}
function escalate() external onlyOwner {
if (escalated) revert EscalationLocked();
escalated = true;
maxCycles = MAX_CYCLES_ESCALATED;
emit Escalated(MAX_CYCLES_ESCALATED);
}
/// @notice Owner may renounce only after 90 days. After renouncement
/// the contract becomes fully autonomous — no further
/// parameter changes possible.
function renounceOwnership() public override onlyOwner {
uint256 unlocksAt = deployTimestamp + OWNERSHIP_TIMELOCK_SECONDS;
if (block.timestamp < unlocksAt) revert TimelockNotExpired(unlocksAt);
super.renounceOwnership();
}
// ─── Gap 5: Ring Buffer + Paginated Views ───────────────────
function _pushTrace(ConvergenceTrace memory t) internal {
_traceRing[traceHead] = t;
traceHead = (traceHead + 1) % TRACE_RING_SIZE;
if (traceWritten < TRACE_RING_SIZE) traceWritten++;
}
function getTracePage(uint256 offset, uint256 limit)
external
view
returns (ConvergenceTrace[] memory page)
{
uint256 available = traceWritten;
if (offset >= available) return new ConvergenceTrace[](0);
uint256 n = available - offset;
if (n > limit) n = limit;
page = new ConvergenceTrace[](n);
uint256 firstSlot =
(traceHead + TRACE_RING_SIZE - available + offset) % TRACE_RING_SIZE;
for (uint256 i = 0; i < n; i++) {
page[i] = _traceRing[(firstSlot + i) % TRACE_RING_SIZE];
}
}
function getEnsemblePage(uint8 offset, uint8 limit)
external
view
returns (AgentDescriptor[] memory page)
{
if (offset >= 5) return new AgentDescriptor[](0);
uint8 n = 5 - offset;
if (n > limit) n = limit;
page = new AgentDescriptor[](n);
for (uint8 i = 0; i < n; i++) {
page[i] = agents[agentIds[offset + i]];
}
}
// ─── Internals ──────────────────────────────────────────────
function _invokeAgent(bytes32 agentId, bytes memory input)
internal
returns (bytes memory output, uint256 residual)
{
AgentDescriptor storage agent = agents[agentId];
if (agent.addr == address(0)) revert UnboundAgent(agentId);
return IAgent(agent.addr).process(input);
}
function isConverged() external view returns (bool) {
return currentPhase == Phase.Converged && currentResidual == 0;
}
/// @notice Symbolic verification of the Euler identity structure.
function verifyIdentity()
external
pure
returns (string memory equation, string memory interpretation)
{
equation = "e^(i*pi) + 1 = 0";
interpretation =
"Expansion(e) rotated through Imagination(i), constrained by "
"Cycle(pi), grounded by Unit(1) => Zero residual. Convergence "
"is perfect cancellation of opposing forces.";
}
}
IAgent / IExecutableAgent implementations exist (ArchitectAgent, GuardianAgent, InnovatorAgent, SynthesizerAgent, AuditorAgent) — see ‣ for required logic per role.dispute_dryrun.s.sol fixture asserts residual > 0 for a known-malformed attestation (per IWA-050 binding sequence).