Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
02a47d5
feat: implement multi-vault architecture
nikitavorobie Dec 2, 2025
87da243
test: add vault management test coverage
nikitavorobie Dec 2, 2025
a7713dc
ci: add upgrade workflows and deployment tracking
nikitavorobie Dec 2, 2025
9c42210
docs: update README with vault architecture examples
nikitavorobie Dec 2, 2025
3f71047
chore: cleanup repository structure
nikitavorobie Dec 2, 2025
c242d87
chore: remove deployments documentation
nikitavorobie Dec 2, 2025
5196f7d
refactor: remove legacy code for fresh deploy
nikitavorobie Dec 2, 2025
55208c9
ci: add automatic deployment address tracking
nikitavorobie Dec 2, 2025
e3b5003
feat: enhance vault model with archival and weight updates
nikitavorobie Dec 7, 2025
251d632
test: add comprehensive vault lifecycle and configuration tests
nikitavorobie Dec 7, 2025
9eabfc1
docs: add comprehensive vault model specification
nikitavorobie Dec 7, 2025
12e45ba
chore: add foundry.lock for dependency tracking
nikitavorobie Dec 7, 2025
57636a1
chore: update dependency submodule versions
nikitavorobie Dec 7, 2025
6164887
ci: fix upgrade workflows to use correct OpenZeppelin versions
nikitavorobie Dec 7, 2025
a04d66f
chore: update Base Sepolia deployment after upgrade
nikitavorobie Dec 7, 2025
1cd249e
chore: record mainnet upgrade
nikitavorobie Dec 7, 2025
d7fd1d2
chore: update Base Mainnet deployment after upgrade
nikitavorobie Dec 7, 2025
5d2deca
feat: implement role-based access control
nikitavorobie Dec 12, 2025
e34ac15
test: add comprehensive role system and emergency pause tests
nikitavorobie Dec 12, 2025
0210113
docs: add role system and governance patterns documentation
nikitavorobie Dec 12, 2025
88a44a5
feat: add upgrade script for role system deployment
nikitavorobie Dec 12, 2025
5822015
docs: update README with role system features
nikitavorobie Dec 12, 2025
a7cb86e
fix: add initializeV2() for role system upgrade migration
nikitavorobie Dec 12, 2025
b766b89
feat: auto-read proxy addresses from deployment files
nikitavorobie Dec 12, 2025
178ec18
chore: add owner verification utility scripts
nikitavorobie Dec 12, 2025
137e8af
fix: use UUPS interface to avoid ABI conflicts during upgrade
nikitavorobie Dec 12, 2025
9719d04
feat: add contract state diagnostic before upgrade
nikitavorobie Dec 12, 2025
c27b60a
chore: prepare for fresh deployment with role system
nikitavorobie Dec 12, 2025
218f458
feat: update deployment addresses and clean repository
nikitavorobie Dec 12, 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
63 changes: 46 additions & 17 deletions .github/workflows/deploy-base-mainnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,8 @@ jobs:
with:
version: nightly

- name: Install dependencies
run: |
forge install foundry-rs/forge-std --no-git
git clone --branch v5.0.2 --recurse-submodules https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable.git lib/openzeppelin-contracts-upgradeable
git clone --branch v5.0.2 --recurse-submodules https://github.com/OpenZeppelin/openzeppelin-contracts.git lib/openzeppelin-contracts
- name: Build contracts
run: forge build

- name: Deploy contracts
env:
Expand All @@ -45,21 +42,53 @@ jobs:
--rpc-url $BASE_MAINNET_RPC_URL \
--broadcast \
--verify \
-vvvv
-vvvv 2>&1 | tee deploy.log

- name: Save deployment artifacts
uses: actions/upload-artifact@v4
with:
name: mainnet-deployment
path: broadcast/
- name: Extract deployment addresses
id: extract
run: |
VAULT_PROXY=$(grep "MultiVault proxy deployed at:" deploy.log | awk '{print $NF}')
VAULT_IMPL=$(grep "MultiVault implementation deployed at:" deploy.log | awk '{print $NF}')
PAYOUT_PROXY=$(grep "PayoutExecutor proxy deployed at:" deploy.log | awk '{print $NF}')
PAYOUT_IMPL=$(grep "PayoutExecutor implementation deployed at:" deploy.log | awk '{print $NF}')

- name: Create deployment record
echo "vault_proxy=$VAULT_PROXY" >> $GITHUB_OUTPUT
echo "vault_impl=$VAULT_IMPL" >> $GITHUB_OUTPUT
echo "payout_proxy=$PAYOUT_PROXY" >> $GITHUB_OUTPUT
echo "payout_impl=$PAYOUT_IMPL" >> $GITHUB_OUTPUT

- name: Update deployment file
run: |
echo "Deployment completed at $(date)" >> deployment-log.txt
echo "Network: Base Mainnet" >> deployment-log.txt
cat > deployments/base-mainnet.json << EOF
{
"network": "base-mainnet",
"chainId": 8453,
"rpcUrl": "https://mainnet.base.org",
"explorer": "https://basescan.org",
"deployedAt": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")",
"contracts": {
"MultiVault": {
"proxy": "${{ steps.extract.outputs.vault_proxy }}",
"implementation": "${{ steps.extract.outputs.vault_impl }}",
"verified": true
},
"PayoutExecutor": {
"proxy": "${{ steps.extract.outputs.payout_proxy }}",
"implementation": "${{ steps.extract.outputs.payout_impl }}",
"verified": true
}
}
}
EOF

- name: Commit deployment log
- name: Commit deployment addresses
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: "chore: update mainnet deployment log"
file_pattern: deployment-log.txt
commit_message: "chore: update Base Mainnet deployment addresses"
file_pattern: deployments/base-mainnet.json

- name: Save deployment artifacts
uses: actions/upload-artifact@v4
with:
name: mainnet-deployment
path: broadcast/
54 changes: 47 additions & 7 deletions .github/workflows/deploy-base-sepolia.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,15 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
submodules: false
submodules: recursive

- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly

- name: Install dependencies
run: |
forge install foundry-rs/forge-std --no-git
git clone --branch v5.0.2 --recurse-submodules https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable.git lib/openzeppelin-contracts-upgradeable
git clone --branch v5.0.2 --recurse-submodules https://github.com/OpenZeppelin/openzeppelin-contracts.git lib/openzeppelin-contracts
- name: Build contracts
run: forge build

- name: Deploy contracts
env:
Expand All @@ -35,7 +32,50 @@ jobs:
--rpc-url $BASE_SEPOLIA_RPC_URL \
--broadcast \
--verify \
-vvvv
-vvvv 2>&1 | tee deploy.log

- name: Extract deployment addresses
id: extract
run: |
VAULT_PROXY=$(grep "MultiVault proxy deployed at:" deploy.log | awk '{print $NF}')
VAULT_IMPL=$(grep "MultiVault implementation deployed at:" deploy.log | awk '{print $NF}')
PAYOUT_PROXY=$(grep "PayoutExecutor proxy deployed at:" deploy.log | awk '{print $NF}')
PAYOUT_IMPL=$(grep "PayoutExecutor implementation deployed at:" deploy.log | awk '{print $NF}')

echo "vault_proxy=$VAULT_PROXY" >> $GITHUB_OUTPUT
echo "vault_impl=$VAULT_IMPL" >> $GITHUB_OUTPUT
echo "payout_proxy=$PAYOUT_PROXY" >> $GITHUB_OUTPUT
echo "payout_impl=$PAYOUT_IMPL" >> $GITHUB_OUTPUT

- name: Update deployment file
run: |
cat > deployments/base-sepolia.json << EOF
{
"network": "base-sepolia",
"chainId": 84532,
"rpcUrl": "https://sepolia.base.org",
"explorer": "https://sepolia.basescan.org",
"deployedAt": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")",
"contracts": {
"MultiVault": {
"proxy": "${{ steps.extract.outputs.vault_proxy }}",
"implementation": "${{ steps.extract.outputs.vault_impl }}",
"verified": true
},
"PayoutExecutor": {
"proxy": "${{ steps.extract.outputs.payout_proxy }}",
"implementation": "${{ steps.extract.outputs.payout_impl }}",
"verified": true
}
}
}
EOF

- name: Commit deployment addresses
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: "chore: update Base Sepolia deployment addresses"
file_pattern: deployments/base-sepolia.json

- name: Save deployment artifacts
uses: actions/upload-artifact@v4
Expand Down
11 changes: 4 additions & 7 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: Test

on:
push:
branches: [main, develop]
branches: [main, develop, developing]
pull_request:
branches: [main]
branches: [main, developing]

jobs:
test:
Expand All @@ -20,11 +20,8 @@ jobs:
with:
version: nightly

- name: Install dependencies
run: |
forge install foundry-rs/forge-std --no-git
git clone --branch v5.0.2 --recurse-submodules https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable.git lib/openzeppelin-contracts-upgradeable
git clone --branch v5.0.2 --recurse-submodules https://github.com/OpenZeppelin/openzeppelin-contracts.git lib/openzeppelin-contracts
- name: Build contracts
run: forge build --sizes

- name: Run tests
run: forge test -vvv
Expand Down
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ package-lock.json
yarn.lock
pnpm-lock.yaml
typechain/
deployments/
*.env.local
*.bak
*.orig
Expand Down
9 changes: 9 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[submodule "lib/forge-std"]
path = lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "lib/openzeppelin-contracts"]
path = lib/openzeppelin-contracts
url = https://github.com/openzeppelin/openzeppelin-contracts
[submodule "lib/openzeppelin-contracts-upgradeable"]
path = lib/openzeppelin-contracts-upgradeable
url = https://github.com/openzeppelin/openzeppelin-contracts-upgradeable
1 change: 0 additions & 1 deletion CODE_OF_CONDUCT.md

This file was deleted.

1 change: 0 additions & 1 deletion CONTRIBUTING.md

This file was deleted.

48 changes: 0 additions & 48 deletions DEPLOYMENTS.md

This file was deleted.

116 changes: 90 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,44 +1,108 @@
# MultiVault

A DAO-oriented multi-signature payout system built on Base with upgradeable smart contracts.
Multi-signature treasury protocol for DAOs on Base. Manage multiple independent vaults with weighted approvals and programmable payouts.

## Features
## Overview

- **Multi-Signer Architecture**: Role-based weights for flexible governance
- **Threshold Approvals**: M-of-N signature requirements
- **Proposal Lifecycle**: Create → Approve → Execute → Cancel
MultiVault enables DAOs and teams to create isolated treasury vaults with customizable governance. Each vault maintains its own signers, voting weights, and approval thresholds.

## Documentation

- **[Vault Model Specification](docs/VAULT_MODEL.md)** - Comprehensive technical documentation covering vault lifecycle, data model, configuration rules, and integration guidelines for developers and auditors
- **[Role System Design](docs/ROLE_SYSTEM.md)** - Complete role hierarchy, access control matrix, and privilege escalation protection mechanisms
- **[Governance Patterns](docs/GOVERNANCE_PATTERNS.md)** - Recommended governance configurations for DAOs, companies, and hybrid organizations

## Key Features

- **Multiple Vaults**: Create unlimited independent treasuries
- **Weighted Voting**: Assign different voting power to signers
- **Flexible Thresholds**: Set custom approval requirements per vault
- **Programmable Payouts**: One-time, vesting, and streaming payments
- **Base Pay Integration**: Native USDC transfers on Base
- **Upgradeable Contracts**: UUPS proxy pattern for seamless updates
- **USDC Transfers**: Native Base Pay integration
- **Role-Based Access Control**: Granular permissions with GLOBAL_ADMIN, VAULT_ADMIN, OPERATOR, and PAUSER roles
- **Emergency Pause**: Circuit breaker for security incidents
- **Fully Upgradeable**: UUPS proxy pattern with safe storage layout

## Use Cases

### DAO Treasury Management
```solidity
// Create DAO treasury vault
uint256 daoVault = multiVault.createVault(
"DAO Treasury",
"ipfs://QmMetadata..."
);

// Add council members with weights
multiVault.addSigner(daoVault, member1, 100); // 100 votes
multiVault.addSigner(daoVault, member2, 150); // 150 votes
multiVault.addSigner(daoVault, member3, 200); // 200 votes

// Set threshold (need 300+ votes to execute)
multiVault.setThreshold(daoVault, 300);
```

### Multi-Department Budgets
```solidity
// Separate vaults for different teams
uint256 devFund = multiVault.createVault("Development", "ipfs://...");
uint256 marketingFund = multiVault.createVault("Marketing", "ipfs://...");
uint256 grantsFund = multiVault.createVault("Grants", "ipfs://...");

## Contracts
// Each vault has independent signers and rules
```

- `MultiVault.sol`: Core multi-sig vault with proposal management
- `PayoutExecutor.sol`: Handles programmable payment schedules
- `BasePayIntegration.sol`: Base Pay USDC transfer integration
### Contributor Payroll
```solidity
// Create proposal for payment
uint256 proposalId = multiVault.createProposal(
contributor,
5000 * 1e6, // 5000 USDC
USDC_ADDRESS,
""
);

## Deployment
// Signers approve
multiVault.approveProposal(proposalId);

Deploy to Base Sepolia:
```bash
forge script script/Deploy.s.sol:DeployScript \
--rpc-url $BASE_SEPOLIA_RPC_URL \
--broadcast \
--verify
// Execute when threshold met
multiVault.executeProposal(proposalId);
```

## Testing
## Deployed Contracts

**Base Mainnet:**
- MultiVault: `0xB4FD2402c97c0F2E38B3Be91596aDe8927A36439`
- PayoutExecutor: `0x5828179353Ad884B10f40Be9122747e1415d7Ea1`

```bash
forge test -vvv
**Base Sepolia:**
- MultiVault: `0xB4FD2402c97c0F2E38B3Be91596aDe8927A36439`
- PayoutExecutor: `0x5828179353Ad884B10f40Be9122747e1415d7Ea1`

## Core Functions

### Vault Management
```solidity
createVault(string name, string metadataRef) returns (uint256 vaultId)
addSigner(uint256 vaultId, address signer, uint256 weight)
removeSigner(uint256 vaultId, address signer)
setThreshold(uint256 vaultId, uint256 threshold)
```

## Security
### Proposals
```solidity
createProposal(address recipient, uint256 amount, address token, bytes data) returns (uint256)
approveProposal(uint256 proposalId)
executeProposal(uint256 proposalId)
cancelProposal(uint256 proposalId)
```

All contracts use OpenZeppelin upgradeable patterns and include:
- Reentrancy protection
- Access control
- SafeERC20 for token transfers
### Payouts
```solidity
createOneTimePayout(address recipient, address token, uint256 amount)
createVestingPayout(address recipient, address token, uint256 amount, uint256 duration, uint256 cliff)
createStreamingPayout(address recipient, address token, uint256 amount, uint256 duration)
```

## License

Expand Down
Loading