Disclaimer: Bear in mind this is still a project in early development. It still needs further testing and polishing. Feedback and contributions are welcome!
A 100% local web interface for managing multisignature wallets inspired by SafeWallet and EternalSafeWallet. No worrying about the SafeAPI being compromised... Run an instance yourself!
v0.2.0 - Now with IPFS deployment support and enhanced UI!
- Safe Account Dashboard: View Safe details, owners, threshold, nonce, and balance with enhanced UI organization.
- Transaction Workflow: Create, import, export, and execute Safe transactions with improved collaboration tools.
- WalletConnect Integration: Sign messages and transactions from dApps via WalletConnect v2.
- IPFS Deployment: Automated deployment to IPFS via Pinata when creating GitHub releases.
- SafeWallet Data Import/Export: Backup and restore address book, visited accounts, and undeployed safes.
- Calldata Decoding: Decode transaction calldata directly in the UI for better transparency.
- Collaboration Tools: Easy sharing of transactions, signatures, and links with organized dropdown menus.
- Wallet Connection: MetaMask and RainbowKit integration with multiple wallet support.
- Client-Side State: All wallet and Safe logic is handled client-side using wagmi, RainbowKit, and Safe Protocol Kit.
- Hash-Based Routing: Uses React Router with hash-based routing for static IPFS deployment compatibility.
- Node.js v18+
- pnpm
- Anvil (for E2E tests)
-
Install pnpm.
-
Clone the repository and install dependencies:
git clone https://github.com/cyfrin/localsafe.eth
cd localsafe.eth
pnpm install- Create a
.envfile in the root (take.env.exampleas a reference):
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=your_walletconnect_project_id- Start the development server:
pnpm dev- Open your browser and navigate to
http://localhost:3000.
E2E tests use Playwright with @wonderland/walletless for wallet automation. This approach eliminates the need for MetaMask browser extension automation, making tests faster and more reliable.
-
Ensure you have Anvil installed and updated.
-
Install Playwright dependencies:
pnpm exec playwright install --with-deps- Run the E2E tests (starts Anvil automatically):
pnpm run test:e2e- For UI mode (useful for debugging):
# Terminal 1
pnpm run anvil
# Terminal 2
pnpm run anvil:two
# Terminal 3
pnpm run test:e2e:uiNote: UI mode requires Anvil to be running in a separate terminal:
pnpm run anvil
How it works:
- Tests set
E2E_MODE=truein localStorage, which triggers the app to use thee2eConnectorfrom@wonderland/walletless - The e2eConnector provides a virtual wallet that auto-signs transactions using Anvil's first test account
- No MetaMask or browser extension is needed - all wallet interactions are handled programmatically
Utility commands:
pnpm run test:clean- Kill any stray next-server or anvil processes
To run the app with a production build locally and run the optimized version:
pnpm run localsafeLocalSafe can be automatically deployed to IPFS via Pinata when you create a GitHub release or push a version tag. See DEPLOYMENT.md for complete setup instructions.
Quick Deploy:
# Create and push a version tag
git tag v0.2.0
git push origin v0.2.0
# Or create a GitHub Release via the UIThe deployment workflow will:
- Build the static site (
pnpm run build) - Deploy to IPFS via Pinata
- Output the IPFS CID and gateway URLs in the GitHub Actions logs
Access your deployment:
- Pinata Gateway:
https://gateway.pinata.cloud/ipfs/<CID> - IPFS Gateway:
https://ipfs.io/ipfs/<CID> - Cloudflare IPFS Gateway:
https://cloudflare-ipfs.com/ipfs/<CID>
To ensure deterministic E2E tests, we use Anvil's state management to save and restore a chain with pre-deployed Safe contracts.
To create a new state file:
- Start Anvil with the
--dump-stateflag (state will be saved when Anvil exits):anvil --dump-state ./anvil-safe-state.json
- In another terminal, deploy the Safe contracts (see Deploying Safe Contracts Locally)
- Stop Anvil (Ctrl+C) to save the state to the JSON file
To restore the state for testing:
anvil --load-state ./anvil-safe-state.json --block-time 1The test runner script and package.json use this approach:
"anvil": "anvil --load-state ./anvil-safe-state.json --block-time 1",
"test:e2e": "bash tests/scripts/start-anvil-and-test.sh"Warning: Anvil state files may not be compatible across different Foundry versions. If
--load-statefails with your current Anvil version, you'll need to regenerate the state file by deploying the Safe contracts fresh.
- Client-Side Architecture: All wallet and Safe logic is handled client-side for maximum privacy and flexibility.
- Hash-Based Routing: Uses React Router with hash-based routing (
HashRouter) to ensure compatibility with static IPFS deployments where server-side routing is not available. - Modular Structure: The project structure is modular, with reusable components and hooks for maintainability.
- WalletConnect Integration: WalletConnect v2 is integrated for dApp connections and signing requests. Session data is persisted in localStorage.
- E2E Testing: Uses Playwright with
@wonderland/walletlessfor headless wallet automation. No MetaMask extension required. - Process Cleanup: Sometimes
next-serverandanvilprocesses may remain running in the background. Usepnpm run test:cleanto kill them. - Local Contract Deployment: For deploying Safe contracts locally, see the instructions below.
To run your own local Safe contracts for development, follow these steps:
- Clone the Repository
git clone https://github.com/safe-global/safe-smart-account.git cd safe-smart-account - Checkout the Correct Version
git checkout tags/v1.4.1-3
- Install Dependencies and Build
npm install npm run build
- Start a Local Anvil Node
anvil
- Create a
.envFileMNEMONIC="test test test test test test test test test test test junk" NODE_URL="http://127.0.0.1:8545"
- Deploy Contracts
npm run deploy-all custom
- Update Contract Addresses
- After deployment, copy the contract addresses from the output and update them in your project’s
utils/contractNetworks.tsfile.
- After deployment, copy the contract addresses from the output and update them in your project’s
Note: Currently, contract addresses are manually maintained in
utils/contractNetworks.ts. In the future, we may automate this process or use environment variables for better flexibility.
-
Improve devcontainer setup for E2E tests; currently, UI mode has limitations.
-
Ensure smooth DX when switching between local and devcontainer environments and wild processes cleaning (next-server, anvil).
-
Adapt for different SafeWallet contract versions (currently optimized for 1.4.1-3).
-
Automate
versionvalue inDEFAULT_SAFE_WALLET_DATAconstant (app/utils/constants.tshardcoded to3.0.0now). -
Add ENS name resolution for addresses in the UI.
-
Implement transaction history and filtering.
-
Extract out the EIP-712 data to it's own component for reusability.
-
Run linter
- SafeSDK: Protocol Kit
- wagmi
- RainbowKit
- WalletConnect
- React Router
- @wonderland/walletless
- Playwright
- Foundry
- Pinata
- IPFS
- Tailwind CSS
- DaisyUI
Special thanks to all contributors!