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
tags
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.