Smart contracts for the agent raise stack: identity-gated project creation, time-bounded fundraising, treasury-operated fund execution, fixed-supply share issuance, and policy-constrained treasury execution on MegaETH.
This repository is the onchain backend for the current raise flow. It does not try to be a full governance system or a generic treasury framework. Its scope is narrower and more explicit:
- create a project from an agent identity
- raise ERC-20 collateral during a scheduled sale window
- move accepted capital into the project treasury for agent operation
- mint fixed-supply fund shares to investors
- enforce a fund term lockup before settlement and redemption open
- support policy-constrained treasury execution through an
AgentExecutorSafe module
At a high level, one factory call creates a full project envelope:
- a Safe treasury
- a
Salecontract - an
AgentExecutorSafe module
After that:
- the platform admin explicitly approves the raise
- investors commit collateral during the active window
- anyone can finalize once the sale ends
- success bootstraps an
AgentVaultTokenand moves accepted capital to the treasury - failure enables refunds
- post-raise treasury actions can be routed through the executor under an allowlist and selector policy
The design intentionally separates:
- project origination
- fundraising
- investor settlement
- treasury operation
That separation is one of the main structural properties of the stack.
- Solidity:
0.8.28 - Framework: Foundry
- Primary dependency: OpenZeppelin contracts
- Deployment target: MegaETH
- Source directory:
src/
flowchart TD
Creator["Project Creator"] --> Factory["AgentRaiseFactory"]
Admin["Admin"] --> Factory
Admin --> Allowlist["ContractAllowlist"]
Admin --> Executor["AgentExecutor"]
Identity["ERC-8004 Identity Registry"] --> Factory
Factory --> Safe["Safe Treasury"]
Factory --> Sale["Sale"]
Factory --> Executor
Investor["Investor"] --> Sale
Collateral["Collateral ERC-20"] --> Sale
Sale --> Shares["AgentVaultToken"]
Sale --> Safe
Operator["Agent Operator"] --> Executor
Executor --> Safe
Executor --> Collateral
Safe --> Shares
Path: src/agents/AgentRaiseFactory.sol
This is the origination and governance entry point.
Responsibilities:
- verifies that the caller owns the
agentId - validates creation parameters
- deploys the Safe treasury,
Sale, andAgentExecutor - wires the Safe module setup
- stores project metadata and raise configuration
- gates fundraising through explicit admin approval
Important behaviors:
- raise bounds are stored in normalized 18 decimals and scaled to the collateral token decimals
- unsupported collateral is rejected at creation time
- project approval gates
Sale.commit(...), but revoking approval does not unwind already committed capital updateProjectOperationalStatus(...)is callable by the stored project agent or admin, not by a freshownerOf(agentId)lookup
Path: src/launch/Sale.sol
This is the fundraising state machine.
Responsibilities:
- accepts collateral commitments during the active sale window
- finalizes permissionlessly after
endTime - resolves success vs failure
- deploys and bootstraps
AgentVaultTokenon success - transfers accepted capital into the treasury on success
- handles
claim(),refund(), andemergencyRefund()
Important behaviors:
commit(...)requires project approval and exact collateral transfer behavioracceptedAmountis capped atMAX_RAISE- oversubscription is resolved during
claim(), not duringcommit() claim()transfers fund shares, not underlying collateralLOCKUP_MINUTESstarts at sale end and defines when settlement and redemption may open- the final claimer receives residual accounting to avoid trapped rounding dust
Path: src/token/AgentVaultToken.sol
This is the post-raise ownership token.
Responsibilities:
- represents project ownership via fixed-supply fund shares
- is bootstrapped once by the sale
- finalizes settlement once the treasury has unwound back into collateral
- redeems shares into collateral after settlement opens
Important behaviors:
- initial share pricing is bootstrapped at
1:1against the underlying asset - fixed supply is minted only once during bootstrap
- accepted capital stays in the treasury during the fund term, not inside the share token
LOCKUP_END_TIMEmarks the earliest time settlement can be finalized and redemptions can open- platform fees are applied on positive profit during settlement, not during initial bootstrap
Path: src/agents/AgentExecutor.sol
This is the policy-constrained treasury execution module.
Responsibilities:
- acts as a Safe module
- allows only the immutable
AGENTaddress to trigger execution through the module path - enforces policy on targets, selectors, and approval recipients for module-originated calls
Important behaviors:
- hard-blocks
TREASURY, the executor itself, and the allowlist contract as targets - forwards only
Call, neverDelegateCall - when
allowlistEnforced == true, both target and selector must be approved - approval-like selectors also require the spender or operator to be allowlisted
- when
allowlistEnforced == false, all those checks are bypassed except the three hard-blocked targets - the contract constrains the module path only; treasury owner powers still depend on the Safe configuration used at deployment
Path: src/registry/ContractAllowlist.sol
This is a shared target registry used by executors when allowlist enforcement is enabled.
Responsibilities:
- add and remove allowed targets
- batch add and batch remove targets
- transfer allowlist admin
Important note:
- this contract stores target allowlisting only
- selector policy is stored per
AgentExecutor
Path: src/safe/SafeModuleSetup.sol
This is a helper used during Safe initialization to enable modules.
- must own the supplied
agentId - calls
createAgentRaise(...) - becomes the stored
project.agent - may update project operational status
- is provided as
agentAddressduring project creation - is the only caller allowed to use
AgentExecutor.execute(...)
- approves or revokes projects
- changes global config
- changes allowed collateral
- updates project metadata
- is the
SUPER_ADMIN()seen bySale
- toggles allowlist enforcement
- configures allowed selectors per target
- manages globally allowed targets in
ContractAllowlist
- commits collateral
- can finalize after sale end
- claims shares on success
- refunds on failure
Project creation begins at:
createAgentRaise(
agentId,
name,
description,
categories,
agentAddress,
collateral,
duration,
launchTime,
lockupMinutes,
tokenName,
tokenSymbol
)The factory checks:
IDENTITY_REGISTRY.ownerOf(agentId) == msg.sender- non-empty
name - non-zero
agentAddress - supported collateral
duration > 0launchTime >= block.timestamp- valid scaled min/max raise after applying collateral decimals
On success, the factory:
- creates a Safe treasury
- deploys
Sale - deploys
AgentExecutor - enables the executor as a Safe module
- removes itself from the Safe module list
- stores the project
The sale is not immediately investable.
Investors can only commit after:
- the project has been approved
startTimehas arrivedendTimehas not passed- the sale is not finalized
After endTime, anyone can call finalize().
Possible outcomes:
- no commitments: failed
- commitments below
MIN_RAISE: failed - commitments at or above
MIN_RAISE: success
On success:
- accepted capital is capped at
MAX_RAISE AgentVaultTokenis deployed- accepted collateral is transferred into the treasury
- fixed shares are minted to the sale contract
- the share token records
LOCKUP_END_TIME = endTime + lockupMinutes
After finalization:
- success path: investors call
claim() - failure path: investors call
refund()
After claim() on a successful raise:
- investors hold ERC-20 fund shares in their wallet
redeem()andwithdraw()stay blocked untilLOCKUP_END_TIME- once the lockup expires and the treasury finalizes settlement, investors can exit against the settled collateral pool
There is also an admin emergency path:
emergencyRefund()
After successful finalization:
- admin configures target and selector policy
- operator can call
AgentExecutor.execute(...) - executor-enforced policy applies to calls routed through the module path
- direct treasury owner actions remain governed by the deployed Safe configuration
This system is not trustless. It is a constrained-authority design.
- the identity registry is trusted as the source of creation authority
- the admin is trusted for raise admission, emergency controls, and treasury policy configuration
- the operator is trusted to use approved treasury actions correctly
- the Safe deployment, owner configuration, and module wiring must be correct
- strict caller checks on privileged functions
- reentrancy protection on
SaleandAgentExecutorstate-changing entry points - hard-blocked treasury self-targeting inside the module path
- exact-transfer checks for collateral-sensitive flows
- bounded oversubscription refund accounting
Saleaccounting and settlement logic- executor break-glass mode via
setAllowlistEnforced(false) - gaps between Safe owner powers and module-level policy assumptions
- privileged operations with no in-code timelock
- assumptions about collateral token behavior
src/protocol contractstest/unit and end-to-end testsscript/deployment and operational scriptsdocs/supporting operational documentationx-ray/generated audit-readiness and architecture outputs
Current scripts:
script/DeployFactoryStackTestnet.s.solscript/DeployNewAgentRaiseFactory.s.solscript/DeployTestnetStackAndRaise.s.solscript/RegisterAgent.s.sol
From the repository root:
cd backend
forge buildTypical commands:
cd backend
forge test
forge test --fuzz-runs 1000 -q
forge fmt --checkBased on the current test suite, the repository contains:
15test files137test functions
What is currently missing from the codebase quality posture:
- stateless fuzz coverage
- stateful invariant testing
- formal verification artifacts
For detailed deployment instructions, see:
docs/DEPLOY.mddocs/CREATE_AGENT.mddocs/AGENT_CREATION_GUIDE.md
Defined in foundry.toml:
megaeth-testnet = https://carrot.megaeth.com/rpcmegaeth-mainnet = https://mainnet.megaeth.com/rpc
Source of truth:
Backend mirrors:
Testnet:
SafeModuleSetup:0x7b6EbB0ede8ac0224a176663e6c07Dece0a37010ContractAllowlist:0x54459A9431bD98c754180DEB32B067Cf31bDfF33AgentRaiseFactory:0x577be362178d20A3370722807d0294fA5D8A5a2AUSDM:0x9f5A17BD53310D012544966b8e3cF7863fc8F05f
Mainnet:
SafeModuleSetup:0x54459A9431bD98c754180DEB32B067Cf31bDfF33ContractAllowlist:0x577be362178d20A3370722807d0294fA5D8A5a2AAgentRaiseFactory:0x45179eE92887e5770E42CD239644bc7b662673afUSDM:0xFAfDdbb3FC7688494971a79cc65DCa3EF82079E7
Deploy testnet factory stack:
cd backend
NO_PROXY="*" forge script script/DeployFactoryStackTestnet.s.sol:DeployFactoryStackTestnet \
--rpc-url megaeth-testnet \
--broadcast \
--gas-estimate-multiplier 5000 \
--code-size-limit 100000 \
-vvvRedeploy mainnet raise stack:
cd backend
NO_PROXY="*" forge script script/DeployNewAgentRaiseFactory.s.sol:DeployNewAgentRaiseFactory \
--rpc-url megaeth-mainnet \
--broadcast \
--gas-estimate-multiplier 5000 \
--code-size-limit 100000 \
-vvvRegister an agent identity:
cd backend
export AGENT_URI="ipfs://your-agent-metadata"
NO_PROXY="*" forge script script/RegisterAgent.s.sol:RegisterAgent \
--rpc-url megaeth-mainnet \
--broadcast \
-vvvMost integrations only need a small subset of methods.
projectCount()getProject(projectId)getProjectRaiseSnapshot(projectId)getProjectCommitment(projectId, user)globalConfig()minRaiseForCollateral(collateral)maxRaiseForCollateral(collateral)
getStatus()isActive()timeRemaining()getClaimable(user)getRefundable(user)token()
allowlistEnforced()isSelectorAllowed(target, selector)ContractAllowlist.isAllowed(target)
- caller is not the current owner of
agentId - unsupported collateral
- zero
duration - zero or past
launchTime - invalid scaled min/max config after decimal conversion
- project not approved
- sale not active
- sale already finalized
- zero amount
- collateral transfer did not match the requested amount
- caller is not the configured
AGENT - target is not allowed
- selector is not allowed
- approval recipient is not allowed
- Safe execution failed downstream
The latest x-ray output in x-ray/ classifies the codebase as:
FRAGILE
Why:
- compact codebase, but concentrated complexity in
AgentRaiseFactory,Sale, andAgentExecutor - single-developer git history
- no visible merge-based review flow
- no fuzz, invariant, or formal verification artifacts
- privileged operational powers execute immediately
Priority review areas:
src/launch/Sale.solsrc/agents/AgentExecutor.solsrc/token/AgentVaultToken.solsrc/agents/AgentRaiseFactory.sol
- add invariant tests for raise lifecycle closure
- add fuzz tests for oversubscription, rounding, and profit distribution
- harden or narrow the executor break-glass path
- evaluate timelock or multisig control for admin-operated surfaces
- document collateral assumptions explicitly for integrators
docs/DEPLOY.mddocs/DEBUG_AGENT_RAISE.mddocs/CREATE_AGENT.mddocs/AGENT_CREATION_GUIDE.mddocs/SMART_CONTRACT_FLOW.typx-ray/x-ray.mdx-ray/entry-points.md