Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
8313d62
test: add MockERC20 and MockVerifier
natalya-bbr Dec 3, 2025
92bfa40
test: SafeBaseEscrowV1 initialization and escrow creation
natalya-bbr Dec 3, 2025
95b908c
test: add comprehensive tests for RulesEngine, Registry, Executor, an…
natalya-bbr Dec 3, 2025
520d832
ci: improve GitHub Actions workflows
natalya-bbr Dec 3, 2025
8f5934e
fix: replace transfer() with call() for UUPS proxy compatibility
natalya-bbr Dec 3, 2025
52c6f04
fix: add executeWithdrawal calls in release and refund functions
natalya-bbr Dec 3, 2025
2ad93e8
ci: fix gas snapshot step for first run
natalya-bbr Dec 3, 2025
732da59
feat: add UUPS upgrade workflow and script
natalya-bbr Dec 4, 2025
53f47c1
chore: update SafeBaseEscrow implementation addresses after upgrade
natalya-bbr Dec 4, 2025
6013f40
fix: correct __Ownable_init for OpenZeppelin v4.9.6 compatibility
natalya-bbr Dec 11, 2025
ad64fd4
test: add Disputed → Released transition tests
natalya-bbr Dec 11, 2025
7da0046
test: add RulesEngine integration test suite
natalya-bbr Dec 11, 2025
f64b600
docs: add escrow lifecycle specification
natalya-bbr Dec 11, 2025
728162c
fix: use correct __Ownable_init signature for OpenZeppelin v5.5
natalya-bbr Dec 11, 2025
ac44c63
fix: correct test order in testDisputedToReleasedRequiresMediator
natalya-bbr Dec 11, 2025
0c123f0
fix: enforce mediator-only actions in Disputed state
natalya-bbr Dec 11, 2025
aeece92
chore: update deployment addresses after contract upgrades
natalya-bbr Dec 11, 2025
647130c
Merge branch
natalya-bbr Dec 11, 2025
43b3209
docs: update specs
natalya-bbr Dec 13, 2025
8cced45
escrow: add erc20 funding and partial releases
natalya-bbr Dec 14, 2025
b0333d7
config: enable via_ir for coverage
natalya-bbr Dec 14, 2025
7b32083
ci: run coverage with coverage profile
natalya-bbr Dec 14, 2025
1088e3c
ci: run coverage with ir-minimum
natalya-bbr Dec 14, 2025
93733af
rules: add updateable rule sets and tests
natalya-bbr Dec 14, 2025
413ff4b
rules: validate default rule set; ignore ds_store
natalya-bbr Dec 14, 2025
40c7cc1
rules: enforce default rule requires approvals
natalya-bbr Dec 14, 2025
b0cf794
registry: emit index/update from escrow
natalya-bbr Dec 14, 2025
f5d99b5
registry: fix mock naming; update escrow lifecycle doc
natalya-bbr Dec 14, 2025
33f238d
test: fix registry getter destructuring
natalya-bbr Dec 14, 2025
0c2347d
executor: use escrow struct and ruleSetId
natalya-bbr Dec 14, 2025
78d0bb7
test: import escrow interface for executor
natalya-bbr Dec 14, 2025
763c098
test: include ruleSetId in mock escrow data
natalya-bbr Dec 14, 2025
84feedb
test: update executor calls to new signatures
natalya-bbr Dec 14, 2025
a7d5813
executor: add batch auto releases; adjust mocks
natalya-bbr Dec 14, 2025
b78dc21
deploy: allow force_new and update executor impl (84532)
natalya-bbr Dec 14, 2025
2e2a889
deploy: add force_new option and log
natalya-bbr Dec 14, 2025
ed4b304
deploy: record new proxies for escrow and rules engine
natalya-bbr Dec 14, 2025
7d25824
docs: update mainnet executor impl; deployments synced
natalya-bbr Dec 14, 2025
7691f5e
Merge branch 'main' into develop
natalya-bbr Dec 14, 2025
84eb9dd
Revert "Merge branch 'main' into develop"
natalya-bbr Dec 14, 2025
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
6 changes: 4 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@ jobs:
fi

- name: Coverage Report
env:
FOUNDRY_PROFILE: coverage
run: |
forge coverage --report summary
forge coverage --report lcov
forge coverage --report summary --ir-minimum
forge coverage --report lcov --ir-minimum

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ on:
- Registry
- SafeBaseEscrow
- Executor
force_new:
description: 'Force new proxy (ignore existing addresses)'
required: false
type: boolean
default: false

jobs:
deploy:
Expand All @@ -51,6 +56,7 @@ jobs:
OWNER_ADDRESS: ${{ secrets.OWNER_ADDRESS }}
BASESCAN_API_KEY: ${{ secrets.BASESCAN_API_KEY }}
DEPLOY_CONTRACT: ${{ inputs.contract }}
FORCE_NEW: ${{ inputs.force_new }}

- name: Save deployment artifacts
uses: actions/upload-artifact@v4
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ node_modules/
.env
broadcast/
lib/
.DS_Store
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,9 @@ forge script script/DeployAndInteract.s.sol \
| Verifier | `0x1B079e9519CF110b491a231d7AA67c9a597F13B2` | ✅ |
| PaymentTracker | `0xdBa335d18751944b46f205F32F03Fa4F1BEf1a94` | ✅ |
| BasePay | `0x062d3a45862a32BF5D1e35404aaA55e7027c4F4B` | ✅ |
| RulesEngine | `0xDb1855c6C8ADd51eE4B7e132173cA9833B1DAf07` | ✅ |
| RulesEngine | `0xFA194dd94Fb7E8253fb6717eF4C6C23D4b2Cc7A2` | ✅ |
| Registry | `0x57741EE5bAc991D43Cf71207781fCB0eE4b9e9a8` | ✅ |
| SafeBaseEscrow | `0xA1e13a0E7E54bC71ee4173D74773b455A86816aB` | ✅ |
| SafeBaseEscrow | `0x2a441dD6a9B81013D49C1d553e8c42Cb32679652` | ✅ |
| Executor | `0xB49e7b4cCB76B3aE9439798eb980434CBCF8c428` | ✅ |

### Base Mainnet
Expand All @@ -86,10 +86,10 @@ forge script script/DeployAndInteract.s.sol \
| Verifier | `0xb06d4414B479eb425f6E7d38226d0194C595c7CF` | ✅ |
| PaymentTracker | `0xAA1be2099208db011dFbEa7174114D69982cFcef` | ✅ |
| BasePay | `0xD47991043dA73bdfcF6c399e5Ed26e5C8D6c3D27` | ✅ |
| RulesEngine | `0x7bFA481f050AC09d676A7Ba61397b3f4dac6E558` | ✅ |
| RulesEngine | `0x02267434995220548CCc3171263229a2aa54e1a4` | ✅ |
| Registry | `0x273930106653461A2F4f33Ea2821652283dcAE11` | ✅ |
| SafeBaseEscrow | `0x1B079e9519CF110b491a231d7AA67c9a597F13B2` | ✅ |
| Executor | `0xdBa335d18751944b46f205F32F03Fa4F1BEf1a94` | ✅ |
| SafeBaseEscrow | `0xec0c6F43b9064cE1C33E9343671c0e67cB19594c` | ✅ |
| Executor | `0xdBa335d18751944b46f205F32F03Fa4F1BEf1a94` | ✅ | Impl: `0xBb744584644c5956353bC8E382EC8E1dAA4286BF` |

**Network Details:**
- **Base Sepolia RPC**: `https://sepolia.base.org`
Expand Down
10 changes: 5 additions & 5 deletions deployments/8453.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,20 @@
"implementation": "0x546bD44cE5576A6e90cC7150aD93aAD1B06291BE"
},
"RulesEngine": {
"proxy": "0x7bFA481f050AC09d676A7Ba61397b3f4dac6E558",
"implementation": "0xB49e7b4cCB76B3aE9439798eb980434CBCF8c428"
"proxy": "0x02267434995220548CCc3171263229a2aa54e1a4",
"implementation": "0xF2a7fbffD5760721C99104A7C1f500797F3D1314"
},
"Registry": {
"proxy": "0x273930106653461A2F4f33Ea2821652283dcAE11",
"implementation": "0xC1E06BfBBe9b812BFc5feFBe4efDFb7B9A4E64cB"
},
"SafeBaseEscrow": {
"proxy": "0x1B079e9519CF110b491a231d7AA67c9a597F13B2",
"implementation": "0xA1e13a0E7E54bC71ee4173D74773b455A86816aB"
"proxy": "0xec0c6F43b9064cE1C33E9343671c0e67cB19594c",
"implementation": "0x9e2B522F357711CF4Cd24D781ED2a553E08cCC59"
},
"Executor": {
"proxy": "0xdBa335d18751944b46f205F32F03Fa4F1BEf1a94",
"implementation": "0xD7fd8D4026123B1c4135cbF6ba91b7A0b3a5C748"
"implementation": "0xBb744584644c5956353bC8E382EC8E1dAA4286BF"
}
}
}
10 changes: 5 additions & 5 deletions deployments/84532.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,20 @@
"implementation": "0xCB66CBF0A09c3Bf336f1290DFB6e6CfB213132Ff"
},
"RulesEngine": {
"proxy": "0xDb1855c6C8ADd51eE4B7e132173cA9833B1DAf07",
"implementation": "0xFA439194fe9B624AD51f7ecccf9FbcdB4350Bc70"
"proxy": "0xFA194dd94Fb7E8253fb6717eF4C6C23D4b2Cc7A2",
"implementation": "0x1d4036bbc2960a6b572D07eab33a3744FbF354D6"
},
"Registry": {
"proxy": "0x57741EE5bAc991D43Cf71207781fCB0eE4b9e9a8",
"implementation": "0x509014Ac3d57ee08B2A47639aCDeEaE1B700960f"
},
"SafeBaseEscrow": {
"proxy": "0xA1e13a0E7E54bC71ee4173D74773b455A86816aB",
"implementation": "0x682026827839A367252Ec80a0bbaaA47AFA3d870"
"proxy": "0x2a441dD6a9B81013D49C1d553e8c42Cb32679652",
"implementation": "0x675c3F20aC7E6C7BF64D07F079EC84785610c12e"
},
"Executor": {
"proxy": "0xB49e7b4cCB76B3aE9439798eb980434CBCF8c428",
"implementation": "0x6112146dea7A81D2F3F189084608a99BCAA2F388"
"implementation": "0x9Bc334daCcB224cac61BDbeB97669BB1C67dBe1E"
}
}
}
37 changes: 35 additions & 2 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,36 @@
# docs: add architecture docs
# SafeBase Architecture

Documentation placeholder.
## Modules
- **SafeBaseEscrowV1** - 6-state FSM, stores deal params, routes calls to Treasury, RulesEngine, Registry.
- **RulesEngineV1** - evaluates release/refund (approvals, deadlines, external verifier, auto-release/auto-refund).
- **RegistryV1** - escrow index (id -> metadata), emits events for frontend/indexer.
- **ExecutorV1** - automated execution (auto-refund/auto-release) based on rules/deadlines.
- **Treasury / TreasuryV2** - custody, multi-step approvals, Base Pay transaction idempotency.
- **BasePay / PaymentTracker / Verifier** - off-chain payment bridge, paymentId -> escrow mapping, verification.
- **Wallet layer (SmartWallet, SubAccountManager, BatchCaller, WalletFactory, NameService)** - B2B roles/limits, batching.
- **AccessController** - roles/admins, used by Treasury/Registry/Executor.
- **Webhook / onchain Utils** - helper calls and notifications.

## Flows (high-level)
1) **Create**: buyer calls `createEscrow` (seller, mediator?, token, amount, deadline, ruleSetId) -> write to Registry.
2) **Funding**:
- `fundEscrow` (ETH) -> Treasury, state Funded.
- `fundEscrowWithBasePay(paymentId)` for off-chain payment -> PaymentTracker/Verifier confirm -> Funded.
3) **Approvals**: buyer/seller may approve; mediator can override when enabled.
4) **Release/Refund**:
- `releaseToSeller` or `refundToBuyer` check RulesEngine (approvals/deadline/verifier/mediator override) -> Treasury transfers.
- In Disputed state mediator decides.
5) **Automation**: Executor polls rules/deadlines and triggers release/refund without manual clicks.
6) **Upgrades**: UUPS, proxy owner = admin; verify storage layout before upgrade.

## Roles
- Buyer: create/fund, approve, dispute/cancel, may trigger release when fully approved.
- Seller: receives funds, may dispute.
- Mediator: override rules, release/refund from Funded/Disputed.
- Admin/Owner: manages addresses for RulesEngine/Registry/Treasury/Executor, pause, upgrades.
- Executor bot: service that calls Executor for automated actions.

## Integrations
- **Base Pay**: off-chain payment -> Verifier -> `fundEscrowWithBasePay`, `paymentIdToEscrow` in Registry/PaymentTracker.
- **Indexer**: consume Escrow/Treasury/Rules/Executor/BasePay events; build timelines and SLA metrics.
- **Frontend (OnchainKit + Base SDK)**: wallet connect, tx launch, status rendering from indexer.
39 changes: 37 additions & 2 deletions docs/BASEPAY.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,38 @@
# docs: add Base Pay guide
# Base Pay - flow and integration

Documentation placeholder.
## Goals
- Map off-chain payment to on-chain escrow.
- Idempotency: each `paymentId` processed once (`paymentIdToEscrow`, `TreasuryV2.basePayTransactions`).
- Transparency: events for indexer and dashboards.

## Flow
1) User pays via Base Pay off-chain.
2) Backend listens to Base Pay notification -> validates -> triggers Verifier/PaymentTracker.
3) On-chain call `fundEscrowWithBasePay(escrowId, paymentId)`:
- Sets state to Funded.
- Stores `paymentIdToEscrow[paymentId] = escrowId`.
- Emits `BasePayFundingReceived`.
4) (Optional) `TreasuryV2.processBasePayTransaction(txId, token, to, amount)`:
- Marks `basePayTransactions[txId] = true` (idempotent).
- Emits `BasePayTransactionProcessed`.

## Backend requirements
- Verify `paymentId` authenticity (Base Pay signature/webhook).
- Guarantee once-only processing per `paymentId` (retry with same id must be no-op).
- Log escrowId/paymentId/tx hash; retry on network errors.
- Respect proxy settings from environment.

## Errors / edge cases
- Duplicate `paymentId`: should safely no-op/fail.
- Escrow not in Created: `InvalidState`.
- Invalid `escrowId`: `EscrowNotFound`.
- Verifier unavailable: backend must not call contract until verified.

## Metrics/events for indexer
- `BasePayFundingReceived(escrowId, paymentId)`
- `EscrowFunded(escrowId, amount)`
- `BasePayTransactionProcessed(txId)`

## Next steps
- Add full ERC20 handling in `fundEscrowWithBasePay` (currently only marks funding).
- Add worker service for reliable retries and dead-letter handling.
31 changes: 29 additions & 2 deletions docs/SECURITY_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,30 @@
# docs: add security notes
# Security Notes

Documentation placeholder.
## Invariants
- Escrow: terminal states are final; exactly one payout (release or refund) per escrow.
- Deadline: refund after deadline is open to anyone if no mediator; dispute requires mediator.
- Treasury: funds balance >= sum of active Funded/Disputed escrows; approvals before withdrawal.
- Rules: external verifier required when enabled; missing address blocks release.

## Risks and mitigations
- **Mediator collusion**: mediator can release/refund from Funded/Disputed. Mitigate with trusted selection, log review, rule templates/limits.
- **Reentrancy / external calls**: withdrawals via call; keep reentrancy guard, ensure safe tokens when ERC20 path is added.
- **Deadline DoS**: many escrows with short deadlines stress Executor. Mitigate with batching, rate-limits in off-chain worker.
- **Verifier downtime**: blocks release; backend should retry and avoid calling contract until verified.
- **Upgrade risk**: UUPS - check storage layout, proxy owner, test on testnet before prod.
- **Key/role compromise**: AccessController/Treasury admins in multisig, enforce limits/approvals.

## Upgrades
- Ensure proxy owner is nonzero; never renounce on upgradeable infra.
- Review storage layout diff before release; adjust gap carefully.
- Upgrade scripts must use `upgradeTo`/`upgradeToAndCall` and log tx.

## Tests / QA (minimum)
- Invariant tests: single payout, terminal states irreversible, deadline rules, no release without buyerApproved when required by RuleSet.
- Fuzz: dispute/approve racing deadline; duplicate `paymentId`; mass fund/refund.
- Negative: missing verifier, zero mediator dispute, release from Created, double refund.

## Operations
- Monitor events: `EscrowFunded/Released/Refunded/Disputed`, `BasePayFundingReceived`, `BasePayTransactionProcessed`, Treasury approvals/executions.
- Runbooks: manual withdrawal if Executor fails; actions when Verifier/Base Pay is down.
- RPC: prefer reliable RPC (mainnet.base.org/Alchemy/Infura/Ankr); avoid unstable endpoints.
31 changes: 29 additions & 2 deletions docs/SMARTWALLET.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,30 @@
# docs: add smart wallet guide
# Smart Wallet layer (B2B)

Documentation placeholder.
## Components
- **SmartWallet** - base smart account with roles.
- **SubAccountManager** - hierarchy of org subaccounts/budgets.
- **BatchCaller** - batch operations (approve, release, refund, registry calls).
- **WalletFactory** - issues wallets for orgs.
- **NameService** - readable names/aliases.

## Roles / limits (example)
- Org Admin - creates subaccounts, sets limits, assigns operators.
- Operator - performs payments/escrow actions within limits.
- Viewer/Auditor - read-only/off-chain sign-offs.
- Limits: per-tx, daily, by op type (fund, release/refund, treasury withdraw).

## Flows
1) Org deploys SmartWallet via Factory, registers in NameService.
2) Creates subaccounts with limits (e.g., procurement with daily cap).
3) Operators interact with Escrow/Treasury via BatchCaller (gas and UX reduction).
4) Events include org/subaccount/actor/opType/amount for audit and indexing.

## UX / integrations
- OnchainKit + Base SDK: select subaccount, view limits, submit batches.
- Multisig/role approvals on wallet level (extra layer beyond escrow approvals).
- AA/4337 compatibility priority: avoid breaking standard smart wallet interfaces.

## Next steps
- Implement limits/roles fully if placeholders remain.
- Emit detailed audit events (org, subaccount, actor, opType, amount).
- E2E tests: batches with limits, limit violations, operator without admin rights.
46 changes: 33 additions & 13 deletions docs/escrow-lifecycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ SafeBase escrow system is a production-grade, modular escrow and conditional pay
### States (6 total)

1. **Created** - Initial state after escrow creation
2. **Funded** - Funds deposited (via direct funding or Base Pay)
3. **Released** - Funds released to seller (terminal state)
4. **Refunded** - Funds refunded to buyer (terminal state)
2. **Funded** - Funds deposited (via direct funding or Base Pay; ETH or ERC20)
3. **Released** - Funds released to seller (terminal state; may reach via partial releases)
4. **Refunded** - Funds refunded to buyer (terminal state; remaining amount after partial releases)
5. **Disputed** - Dispute raised, requires mediator intervention
6. **Cancelled** - Escrow cancelled before funding (terminal state)

Expand All @@ -39,9 +39,9 @@ Created ──fundEscrow()──────────> Funded ──releaseTo
| Created | Funded | fundEscrow() | Buyer only |
| Created | Funded | fundEscrowWithBasePay() | Anyone (off-chain verified) |
| Created | Cancelled | cancelEscrow() | Buyer only |
| Funded | Released | releaseToSeller() | Buyer (with approval) OR Mediator |
| Funded | Refunded | refundToBuyer() | Mediator OR Anyone after deadline |
| Funded | Disputed | disputeEscrow() | Buyer OR Seller (requires mediator set) |
| Funded | Released | releaseToSeller() | Buyer (with approval) OR Mediator |
| Funded | Refunded | refundToBuyer() | Mediator OR Anyone after deadline |
| Funded | Disputed | disputeEscrow() | Buyer OR Seller (requires mediator set) |
| Disputed | Released | releaseToSeller() | Mediator only |
| Disputed | Refunded | refundToBuyer() | Mediator only |

Expand Down Expand Up @@ -145,7 +145,7 @@ struct RuleSet {

## Integration Patterns

### Standard Escrow Flow
### Standard Escrow Flow (ETH)
```solidity
// 1. Create escrow
uint256 escrowId = escrow.createEscrow(
Expand Down Expand Up @@ -175,6 +175,28 @@ escrow.fundEscrowWithBasePay(escrowId, paymentId);
```

### Dispute Resolution

### ERC20 Funding
```solidity
escrow.createEscrow(
seller,
mediator,
address(token), // ERC20
100 ether,
block.timestamp + 7 days,
ruleSetId
);

token.approve(address(escrow), 100 ether);
escrow.fundEscrow(escrowId); // msg.value must be 0
```

### Partial Releases
```solidity
// After approvals / rules allow
escrow.releasePartialToSeller(escrowId, 40 ether); // updates state, Registry, Treasury withdrawal
// Remaining amount stays in escrow until released or refunded
```
```solidity
// Buyer raises dispute
escrow.disputeEscrow(escrowId);
Expand Down Expand Up @@ -231,12 +253,10 @@ SafeBaseEscrowV1 uses UUPS proxy pattern:

## Future Enhancements (Beyond Block 1)

1. **Partial releases**: Split payments for milestone-based escrows
2. **Multi-token support**: ERC20 token escrows (currently ETH only)
3. **Time-locked releases**: Automatic release after deadline + approval
4. **Appeal mechanism**: Secondary mediator for disputed cases
5. **Escrow templates**: Pre-configured rule sets for common use cases
6. **Event-driven automation**: Executor integration for auto-release/refund
1. **Time-locked releases**: Automatic release after deadline + approval
2. **Appeal mechanism**: Secondary mediator for disputed cases
3. **Escrow templates**: Pre-configured rule sets for common use cases
4. **Event-driven automation**: Executor integration for auto-release/refund

---

Expand Down
5 changes: 5 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ optimizer_runs = 200
fs_permissions = [{ access = "read", path = "./deployments" }]
gas_reports = ["SafeBaseEscrowV1", "RulesEngineV1", "RegistryV1", "ExecutorV1"]

[profile.coverage]
via_ir = true
optimizer = true
optimizer_runs = 200

[rpc_endpoints]
base = "https://mainnet.base.org"
base-sepolia = "https://sepolia.base.org"
Expand Down
Loading