slogan3

Ontwerp een Solidity Smart Contract met modulair upgradebaar patroon

description

Met deze prompt kan de gebruiker een geavanceerd, modulair en upgradebaar Solidity contract ontwerpen dat makkelijk aanpasbaar blijft na deploy. Dit voorkomt hoge kosten en risico's van herdeploys en vergroot de onderhoudbaarheid en schaalbaarheid van blockchain projecten.

prompt

Help me een Solidity smart contract te ontwerpen dat gebruikmaakt van een modulair upgradebaar patroon. Beschrijf en implementeer de structuur, inclusief de basis contracten, proxy mechanismen en upgrade functies. Geef ook voorbeelden van hoe ik functies kan toevoegen of wijzigen ...

try_prompt

generate_helper
disclaimerOnPageApi

ai_answers

provider: openai
model: gpt-4.1-nano
Hier is een overzichtelijke en modulaire Solidity-architectuur voor een upgradebaar tokensysteem, inclusief basiscontracten, proxy-mechanismen en upgrade functies. Dit ontwerp maakt het mogelijk om functies toe te voegen of te wijzigen zonder het hele contract opnieuw te moeten deployen. ### 1. Basisstructuur - **Logic Contracten**: Bevatten de daadwerkelijke functionaliteit (mint, transfer, balance). - **Proxy Contract**: Verwijst naar de huidige logic contract en stuurt calls door. - **Proxy Admin Contract**: Beheert upgrades en toegangsrechten. --- ### 2. Contracten #### a. Het `ERC20` basiscontract (logic) ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract TokenV1 { mapping(address => uint256) private _balances; uint256 private _totalSupply; address public owner; event Transfer(address indexed from, address indexed to, uint256 value); event Mint(address indexed to, uint256 value); constructor() { owner = msg.sender; } function mint(address to, uint256 amount) public { require(msg.sender == owner, "Alleen eigenaar kan minten"); _totalSupply += amount; _balances[to] += amount; emit Mint(to, amount); emit Transfer(address(0), to, amount); } function transfer(address to, uint256 amount) public returns (bool) { require(_balances[msg.sender] >= amount, "Onvoldoende saldo"); _balances[msg.sender] -= amount; _balances[to] += amount; emit Transfer(msg.sender, to, amount); return true; } function balanceOf(address account) public view returns (uint256) { return _balances[account]; } } ``` --- #### b. Upgradebaar patroon: Proxy en ProxyAdmin ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract Proxy { address public implementation; address public admin; constructor(address _implementation) { implementation = _implementation; admin = msg.sender; } fallback() external payable { address impl = implementation; assembly { calldatacopy(0, 0, calldatasize()) let result := delegatecall(gas(), impl, 0, calldatasize(), 0, 0) returndatacopy(0, 0, returndatasize()) switch result case 0 { revert(0, returndatasize()) } default { return(0, returndatasize()) } } } function upgrade(address newImplementation) external { require(msg.sender == admin, "Alleen admin"); implementation = newImplementation; } } ``` --- ### 3. Upgraden en uitbreiden van functionaliteit **Voorbeeld van een nieuwe versie met pausfunctie en blacklist:** ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract TokenV2 { mapping(address => uint256) private _balances; uint256 private _totalSupply; address public owner; bool public paused = false; mapping(address => bool) public blacklist; event Transfer(address indexed from, address indexed to, uint256 value); event Mint(address indexed to, uint256 value); event Paused(); event Unpaused(); event Blacklisted(address indexed account); event RemovedFromBlacklist(address indexed account); constructor() { owner = msg.sender; } modifier whenNotPaused() { require(!paused, "Contract gepauzeerd"); _; } modifier notBlacklisted(address account) { require(!blacklist[account], "Geblokkeerd"); _; } function pause() public { require(msg.sender == owner, "Alleen eigenaar"); paused = true; emit Paused(); } function unpause() public { require(msg.sender == owner, "Alleen eigenaar"); paused = false; emit Unpaused(); } function blacklistAddress(address account) public { require(msg.sender == owner, "Alleen eigenaar"); blacklist[account] = true; emit Blacklisted(account); } function removeFromBlacklist(address account) public { require(msg.sender == owner, "Alleen eigenaar"); blacklist[account] = false; emit RemovedFromBlacklist(account); } function mint(address to, uint256 amount) public whenNotPaused notBlacklisted(to) { require(msg.sender == owner, "Alleen eigenaar"); _totalSupply += amount; _balances[to] += amount; emit Mint(to, amount); emit Transfer(address(0), to, amount); } function transfer(address to, uint256 amount) public whenNotPaused notBlacklisted(msg.sender) notBlacklisted(to) returns (bool) { require(_balances[msg.sender] >= amount, "Onvoldoende saldo"); _balances[msg.sender] -= amount; _balances[to] += amount; emit Transfer(msg.sender, to, amount); return true; } function balanceOf(address account) public view returns (uint256) { return _balances[account]; } } ``` --- ### 4. Gebruik voorbeeld: upgraden en toevoegen van functies - **Stap 1:** Deploy `TokenV1`. - **Stap 2:** Deploy `Proxy` met adres van `TokenV1`. - **Stap 3:** Bij upgrade, deploy `TokenV2`. - **Stap 4:** Roep `proxy.upgrade(address_of_TokenV2)` om de logic te veranderen. ### 5. Functie toevoegen of wijzigen zonder opnieuw te deployen - Voeg nieuwe functies toe in `TokenV2`. - Roep `upgrade()` in de proxy met het adres van de nieuwe logic contract. - Calls vanuit de proxy blijven hetzelfde, maar de functionaliteit wordt uitgebreid (bijvoorbeeld pauseren, blacklist). --- ### Samenvatting Door een proxy pattern te gebruiken, kun je je contract upgraden door simpelweg de implementatie te wijzigen zonder het proxy contract te herdeployen. Voeg nieuwe functionaliteiten toe door nieuwe logic contracten te deployen en de proxy te upgraden. Dit biedt maximale modulariteit en flexibiliteit.
provider: deepseek
model: deepseek-chat
Ik help je graag met het ontwerpen van een upgradebaar Solidity smart contract. Hier is een complete implementatie met het Transparent Proxy Pattern: ## Basis Structuur ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.19; // 1. Storage Contract (behoudt staat bij upgrades) contract TokenStorage { mapping(address => uint256) public balances; address public admin; address public implementation; } // 2. Implementatie Contract (logica) contract TokenV1 is TokenStorage { event Transfer(address indexed from, address indexed to, uint256 value); event Mint(address indexed to, uint256 value); modifier onlyAdmin() { require(msg.sender == admin, "Alleen admin"); _; } function initialize(address _admin) external { require(admin == address(0), "Al geinitialiseerd"); admin = _admin; } function mint(address to, uint256 amount) external onlyAdmin { balances[to] += amount; emit Mint(to, amount); emit Transfer(address(0), to, amount); } function transfer(address to, uint256 amount) external { require(balances[msg.sender] >= amount, "Onvoldoende saldo"); balances[msg.sender] -= amount; balances[to] += amount; emit Transfer(msg.sender, to, amount); } function balanceOf(address account) external view returns (uint256) { return balances[account]; } } // 3. Proxy Contract contract TokenProxy is TokenStorage { constructor(address _implementation, address _admin) { implementation = _implementation; admin = _admin; } fallback() external payable { address _impl = implementation; require(_impl != address(0)); assembly { let ptr := mload(0x40) calldatacopy(ptr, 0, calldatasize()) let result := delegatecall(gas(), _impl, ptr, calldatasize(), 0, 0) let size := returndatasize() returndatacopy(ptr, 0, size) switch result case 0 { revert(ptr, size) } default { return(ptr, size) } } } function upgradeTo(address newImplementation) external { require(msg.sender == admin, "Alleen admin"); implementation = newImplementation; } } ``` ## Upgrade naar V2 met Pausfunctie en Blacklist ```solidity // TokenV2.sol - Upgrade met nieuwe functionaliteit contract TokenV2 is TokenStorage { // Nieuwe state variables (volg storage layout!) bool public paused; mapping(address => bool) public blacklisted; event Paused(bool paused); event Blacklisted(address indexed account, bool blacklisted); modifier onlyAdmin() { require(msg.sender == admin, "Alleen admin"); _; } modifier whenNotPaused() { require(!paused, "Contract gepauzeerd"); _; } modifier notBlacklisted() { require(!blacklisted[msg.sender], "Geblacklist"); _; } // Bestaande functies (moetenzelfde signature houden) function mint(address to, uint256 amount) external onlyAdmin whenNotPaused { require(!blacklisted[to], "Ontvanger geblacklist"); balances[to] += amount; emit Mint(to, amount); emit Transfer(address(0), to, amount); } function transfer(address to, uint256 amount) external whenNotPaused notBlacklisted { require(!blacklisted[to], "Ontvanger geblacklist"); require(balances[msg.sender] >= amount, "Onvoldoende saldo"); balances[msg.sender] -= amount; balances[to] += amount; emit Transfer(msg.sender, to, amount); } function balanceOf(address account) external view returns (uint256) { return balances[account]; } // Nieuwe functies (toegevoegd in upgrade) function pause() external onlyAdmin { paused = true; emit Paused(true); } function unpause() external onlyAdmin { paused = false; emit Paused(false); } function addToBlacklist(address account) external onlyAdmin { blacklisted[account] = true; emit Blacklisted(account, true); } function removeFromBlacklist(address account) external onlyAdmin { blacklisted[account] = false; emit Blacklisted(account, false); } } ``` ## Deployment en Upgrade Proces ```solidity // Deployment Script (voorbeeld) contract Deployer { function deployTokenSystem() external returns (address proxy) { // 1. Deploy V1 implementatie TokenV1 v1 = new TokenV1(); // 2. Deploy proxy TokenProxy proxy = new TokenProxy(address(v1), msg.sender); // 3. Initialiseer via proxy (bool success, ) = address(proxy).call( abi.encodeWithSignature("initialize(address)", msg.sender) ); require(success, "Initialisatie mislukt"); return address(proxy); } function upgradeToV2(address proxyAddress) external { // 1. Deploy nieuwe implementatie TokenV2 v2 = new TokenV2(); // 2. Upgrade proxy (bool success, ) = proxyAddress.call( abi.encodeWithSignature("upgradeTo(address)", address(v2)) ); require(success, "Upgrade mislukt"); } } ``` ## Gebruiksvoorbeelden ```javascript // Interactie met proxy (gebruik altijd proxy adres) const interactWithToken = async () => { // Mint tokens (via proxy) await tokenContract.mint(userAddress, 1000); // Transfer (werkt zowel V1 als V2) await tokenContract.transfer(recipient, 100); // Nieuwe V2 functies await tokenContract.pause(); // Alleen in V2 await tokenContract.addToBlacklist(badActor); // Alleen in V2 }; ``` ## Belangrijke Veiligheidsmaatregelen 1. **Storage Layout**: Behoud altijd dezelfde variabele volgorde 2. **Initializer Pattern**: Gebruik i.p.v. constructor 3. **Admin Rechten**: Beperk upgrade rechten tot vertrouwde addresses 4. **Test upgrades**: Altijd testen op testnet voor mainnet deployment Dit patroon laat je toe om functionaliteit toe te voegen zonder bestaande staat te verliezen, terwijl je volledige controle behoudt over het upgrade proces.