├── .github └── workflows │ ├── CI.yml │ └── publish.yml ├── .gitignore ├── .gitmodules ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── foundry.toml ├── package-lock.json ├── package.json ├── src └── CREATE3UUPSProxy.sol ├── test ├── CREATE3UUPSProxyTest.sol └── TestImplementation.sol └── yarn.lock /.github/workflows/CI.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | check: 6 | name: Foundry project 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | with: 11 | submodules: recursive 12 | 13 | - name: Install Foundry 14 | uses: onbjerg/foundry-toolchain@v1 15 | with: 16 | version: nightly 17 | 18 | - name: Install dependencies 19 | run: forge install 20 | - name: Run tests 21 | run: forge test -vvv 22 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish Package to npmjs 2 | on: 3 | release: 4 | types: [published] 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | permissions: 9 | contents: read 10 | id-token: write 11 | steps: 12 | - uses: actions/checkout@v4 13 | with: 14 | submodules: recursive 15 | # Setup .npmrc file to publish to npm 16 | - uses: actions/setup-node@v4 17 | with: 18 | node-version: '20.x' 19 | registry-url: 'https://registry.npmjs.org' 20 | - run: npm install 21 | - run: npm ci 22 | - run: npm publish --provenance --access public 23 | env: 24 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | cache 3 | node_modules 4 | artifacts 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/openzeppelin-contracts"] 2 | path = lib/openzeppelin-contracts 3 | url = https://github.com/OpenZeppelin/openzeppelin-contracts 4 | [submodule "lib/solmate"] 5 | path = lib/solmate 6 | url = https://github.com/transmissions11/solmate 7 | [submodule "lib/forge-std"] 8 | path = lib/forge-std 9 | url = https://github.com/foundry-rs/forge-std 10 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | - Using welcoming and inclusive language 12 | - Being respectful of differing viewpoints and experiences 13 | - Gracefully accepting constructive criticism 14 | - Focusing on what is best for the community 15 | - Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | - The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | - Trolling, insulting/derogatory comments, and personal or political attacks 21 | - Public or private harassment 22 | - Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | - Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned with this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at [INSERT EMAIL ADDRESS]. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1, available at https://www.contributor-covenant.org/version/2/1/code_of_conduct.html. 44 | 45 | For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq. 46 | 47 | [homepage]: https://www.contributor-covenant.org 48 | 49 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributing to Bando OS 3 | 4 | 🎉 Thank you for your interest in contributing to Bando OS! 🎉 5 | 6 | We welcome contributions from everyone. By participating in this project, you agree to abide by our [Code of Conduct](CODE_OF_CONDUCT.md). 7 | 8 | ## How to Contribute 9 | 10 | ### Reporting Bugs 11 | 12 | If you encounter a bug or issue with the project, please follow these steps: 13 | 14 | 1. Check the [existing issues](https://github.com/bandohq/create3-proxy/issues) to see if the issue has already been reported. 15 | 2. If it hasn't, [create a new issue](https://github.com/bandohq/create3-proxy/issues/new) with a descriptive title and detailed description of the problem, including steps to reproduce if possible. 16 | 17 | ### Suggesting Enhancements 18 | 19 | We welcome suggestions for how we can improve React Bando. If you have an idea for an enhancement, follow these steps: 20 | 21 | 1. Check the [existing issues](https://github.com/bandohq/create3-proxy/issues) to see if the enhancement has already been suggested. 22 | 2. If it hasn't, [create a new issue](https://github.com/bandohq/create3-proxy/issues/new) with a clear title and description of your enhancement idea. 23 | 24 | ### Pull Requests 25 | 26 | We actively welcome your pull requests! To contribute code to React Bando, follow these steps: 27 | 28 | 1. Fork the repository and create your own branch off the `main` branch. 29 | 2. Make your changes and ensure the code is well-documented and tested. 30 | 3. Open a pull request (PR) to the `main` branch of the original repository, describing the changes you've made. 31 | 32 | ### Code Style 33 | 34 | - Follow the coding style and conventions used throughout the project. 35 | - Ensure any new code adheres to existing patterns and practices. 36 | - Write clear commit messages and PR descriptions. 37 | 38 | ### Code of Conduct 39 | 40 | This project and everyone participating in it is governed by our [Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to [maintainer's email]. 41 | 42 | ### License 43 | 44 | By contributing to React Bando, you agree that your contributions will be licensed under the [LICENSE](LICENSE) file of the project. 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Bando 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CREATE3 for Upgradeable Proxies 2 | 3 | [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/bandohq/create3-proxy/blob/main/LICENSE) 4 | [![Build](https://github.com/bandohq/create3-proxy/actions/workflows/CI.yml/badge.svg?branch=main)](https://github.com/bandohq/create3-proxy/actions/workflows/CI.yml) 5 | [![npm version](https://img.shields.io/npm/v/create3-proxy.svg)](https://www.npmjs.com/package/create3-proxy) 6 | 7 | 8 | A Solidity library for deploying upgradeable proxies using the CREATE3 pattern, enabling deterministic addresses across different chains without relying on nonces or code. 9 | 10 | ## Overview 11 | 12 | CREATE3 Proxy combines the power of CREATE3 for deterministic contract deployment with the upgradeability of UUPS (Universal Upgradeable Proxy Standard) proxies. This library allows developers to deploy upgradeable contracts to the same address across multiple networks, simplifying cross-chain development and management. 13 | 14 | ## Features 15 | 16 | - Deterministic addresses across different chains 17 | - UUPS (ERC1967) proxy pattern for upgradeability 18 | - Gas-efficient deployment using CREATE3 19 | - Easy-to-use interface for deploying proxies 20 | 21 | ## Installation 22 | 23 | ### Install with Foundry: 24 | 25 | ```bash 26 | forge install bandohq/create3-proxy 27 | ``` 28 | 29 | ### Install with npm: 30 | 31 | ```bash 32 | npm i create3-proxy 33 | ``` 34 | ### Install with yarn: 35 | 36 | ```bash 37 | yarn add create3-proxy 38 | ``` 39 | 40 | ## Usage 41 | 42 | To use the library in your project, follow these steps: 43 | 44 | ### Deploy a UUPS (ERC1967) Proxy 45 | 46 | 1. Import the library into your contract: 47 | 48 | ```solidity 49 | import "create3-proxy/src/CREATE3UUPSProxy.sol"; 50 | ``` 51 | 52 | 2. Deploy using your implementation contract: 53 | 54 | ```solidity 55 | bytes32 salt = keccak256("my_proxy"); 56 | bytes memory creationCode = type(MyUUPSContract).creationCode; 57 | bytes memory initializerData = abi.encodeWithSignature("myInitialize(uint256)", initialValue); 58 | address proxy = CREATE3UUPSProxy.deploy(salt, creationCode, initializerData); 59 | ``` 60 | 61 | ## Contributing 62 | 63 | We welcome contributions to this library. Please open an issue or submit a pull request. 64 | Read more on our [contributing guidelines](./CONTRIBUTING.md). 65 | 66 | ### Code of Conduct 67 | 68 | We have a [code of conduct](./CODE_OF_CONDUCT.md) that we expect all contributors to adhere to. 69 | 70 | ### Building from source 71 | 72 | ```bash 73 | forge build 74 | ``` 75 | 76 | ### Test 77 | 78 | ```bash 79 | forge test 80 | ``` 81 | -------------------------------------------------------------------------------- /foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | optimizer_runs = 1000000 3 | solc = "0.8.20" 4 | bytecode_hash = "none" 5 | libs = ["node_modules", "lib"] 6 | 7 | remappings = [ 8 | "forge-std/=lib/forge-std/src/", 9 | "@openzeppelin/=lib/openzeppelin-contracts/", 10 | "solmate/=lib/solmate/src/", 11 | ] 12 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "create3-proxy", 3 | "version": "0.1.3", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "create3-proxy", 9 | "version": "0.1.3", 10 | "license": "MIT", 11 | "dependencies": { 12 | "@openzeppelin/contracts": "^5.0.0", 13 | "solmate": "^6.2.0" 14 | } 15 | }, 16 | "node_modules/@openzeppelin/contracts": { 17 | "version": "5.0.2", 18 | "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-5.0.2.tgz", 19 | "integrity": "sha512-ytPc6eLGcHHnapAZ9S+5qsdomhjo6QBHTDRRBFfTxXIpsicMhVPouPgmUPebZZZGX7vt9USA+Z+0M0dSVtSUEA==" 20 | }, 21 | "node_modules/solmate": { 22 | "version": "6.2.0", 23 | "resolved": "https://registry.npmjs.org/solmate/-/solmate-6.2.0.tgz", 24 | "integrity": "sha512-AM38ioQ2P8zRsA42zenb9or6OybRjOLXIu3lhIT8rhddUuduCt76pUEuLxOIg9GByGojGz+EbpFdCB6B+QZVVA==", 25 | "license": "AGPL-3.0-only" 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "create3-proxy", 3 | "version": "0.1.4", 4 | "description": "Create3 deterministic addresses for Upgradeable Proxies", 5 | "author": "g6s", 6 | "license": "MIT", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/bandohq/create3-proxy.git" 10 | }, 11 | "dependencies": { 12 | "solmate": "^6.2.0", 13 | "@openzeppelin/contracts": "^5.0.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/CREATE3UUPSProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0; 3 | 4 | import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; 5 | import "@openzeppelin/contracts/utils/Create2.sol"; 6 | import "solmate/utils/CREATE3.sol"; 7 | 8 | /// @title CREATE3UUPSProxy 9 | /// @author g6s 10 | /// @notice A library for deploying ERC1967 proxies using the CREATE3 pattern 11 | library CREATE3UUPSProxy { 12 | error ImplementationDeploymentFailed(); 13 | error ProxyDeploymentFailed(); 14 | 15 | /// @notice Deploys an ERC1967 proxy using the CREATE3 pattern 16 | /// @param salt A unique value to determine the contract's address 17 | /// @param creationCode The bytecode of the implementation contract 18 | /// @param initializerData The data to be passed to the initializer function 19 | /// @return proxy The address of the deployed proxy contract 20 | function deploy( 21 | bytes32 salt, 22 | bytes memory creationCode, 23 | bytes memory initializerData 24 | ) public returns (address proxy) { 25 | //deploy the implementation 26 | //create a salt that is unique to the proxy deployment 27 | //to avoid collission 28 | bytes32 implSalt = keccak256(abi.encodePacked(salt, "impl")); 29 | address implementation = _deployImplementation(implSalt, creationCode); 30 | if (implementation == address(0)) revert ImplementationDeploymentFailed(); 31 | 32 | // Generate the initialization code for the ERC1967Proxy 33 | bytes memory initCode = abi.encodePacked( 34 | type(ERC1967Proxy).creationCode, 35 | abi.encode(implementation, initializerData) 36 | ); 37 | 38 | proxy = CREATE3.deploy(salt, initCode, 0); 39 | if (proxy == address(0)) revert ProxyDeploymentFailed(); 40 | 41 | } 42 | 43 | /// @notice Deploys the implementation contract using CREATE3 44 | /// @dev This is an internal function used by deployProxy 45 | /// @param salt A unique value to determine the contract's address 46 | /// @param creationCode The bytecode of the implementation contract 47 | /// @return impl The address of the deployed implementation contract 48 | function _deployImplementation(bytes32 salt, bytes memory creationCode) internal returns (address impl) { 49 | impl = CREATE3.deploy(salt, creationCode, 0); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /test/CREATE3UUPSProxyTest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import "forge-std/Test.sol"; 5 | import "solmate/utils/CREATE3.sol"; 6 | import "../src/CREATE3UUPSProxy.sol"; 7 | import "./TestImplementation.sol"; 8 | import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; 9 | 10 | contract CREATE3UUPSProxyTest is Test { 11 | function testDeployImplementation() public { 12 | bytes32 salt = keccak256("test_implementation"); 13 | bytes memory creationCode = type(TestImplementation).creationCode; 14 | 15 | address impl = CREATE3UUPSProxy._deployImplementation(salt, creationCode); 16 | 17 | assertTrue(impl != address(0), "Implementation should be deployed"); 18 | assertTrue(impl.code.length > 0, "Implementation should have code"); 19 | } 20 | 21 | function testDeployProxy() public { 22 | bytes32 salt = keccak256("test_proxy"); 23 | bytes memory creationCode = type(TestImplementation).creationCode; 24 | uint256 initialValue = 42; 25 | bytes memory initializerData = abi.encodeWithSignature("initialize(uint256)", initialValue); 26 | 27 | try CREATE3UUPSProxy.deploy(salt, creationCode, initializerData) returns (address proxy) { 28 | assertTrue(proxy != address(0), "Proxy should be deployed"); 29 | assertTrue(proxy.code.length > 0, "Proxy should have code"); 30 | 31 | TestImplementation impl = TestImplementation(proxy); 32 | assertEq(impl.value(), initialValue, "Proxy should be initialized with correct value"); 33 | } catch Error(string memory) { 34 | fail(); 35 | } catch (bytes memory) { 36 | fail(); 37 | } 38 | } 39 | 40 | function testProxyFunctionality() public { 41 | bytes32 salt = keccak256("test_proxy_functionality"); 42 | bytes memory creationCode = type(TestImplementation).creationCode; 43 | uint256 initialValue = 42; 44 | bytes memory initializerData = abi.encodeWithSignature("initialize(uint256)", initialValue); 45 | 46 | address proxy = CREATE3UUPSProxy.deploy(salt, creationCode, initializerData); 47 | TestImplementation impl = TestImplementation(proxy); 48 | 49 | uint256 newValue = 100; 50 | impl.setValue(newValue); 51 | assertEq(impl.value(), newValue, "Proxy should update value correctly"); 52 | } 53 | 54 | function testDeterministicAddresses() public { 55 | bytes32 salt = keccak256("test_deterministic"); 56 | bytes memory creationCode = type(TestImplementation).creationCode; 57 | bytes memory initializerData = abi.encodeWithSignature("initialize(uint256)", 42); 58 | address predictedProxy1 = CREATE3.getDeployed(salt); 59 | address proxy1 = CREATE3UUPSProxy.deploy(salt, creationCode, initializerData); 60 | assertEq(proxy1, predictedProxy1, "Proxy should have deterministic address"); 61 | } 62 | 63 | function testRevertOnRedeployment() public { 64 | bytes32 salt = keccak256("test_revert_redeploy"); 65 | bytes memory creationCode = type(TestImplementation).creationCode; 66 | bytes memory initializerData = abi.encodeWithSignature("initialize(uint256)", 42); 67 | 68 | CREATE3UUPSProxy.deploy(salt, creationCode, initializerData); 69 | 70 | vm.expectRevert(); 71 | CREATE3UUPSProxy.deploy(salt, creationCode, initializerData); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /test/TestImplementation.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | contract TestImplementation { 5 | uint256 public value; 6 | 7 | function initialize(uint256 _value) public { 8 | value = _value; 9 | } 10 | 11 | function setValue(uint256 _value) public { 12 | value = _value; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@openzeppelin/contracts@^5.0.0": 6 | version "5.0.2" 7 | resolved "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-5.0.2.tgz" 8 | integrity sha512-ytPc6eLGcHHnapAZ9S+5qsdomhjo6QBHTDRRBFfTxXIpsicMhVPouPgmUPebZZZGX7vt9USA+Z+0M0dSVtSUEA== 9 | 10 | solmate@^6.2.0: 11 | version "6.2.0" 12 | resolved "https://registry.npmjs.org/solmate/-/solmate-6.2.0.tgz" 13 | integrity sha512-AM38ioQ2P8zRsA42zenb9or6OybRjOLXIu3lhIT8rhddUuduCt76pUEuLxOIg9GByGojGz+EbpFdCB6B+QZVVA== 14 | --------------------------------------------------------------------------------