Maak een Solidity Smart Contract voor Geautomatiseerd NFT Royaltybeheer
description
Maakt het NFT-makers mogelijk royaltybetalingen automatisch, transparant en veilig te laten verlopen, waardoor handmatige afhandeling en fouten worden verminderd. Zorgt voor naleving van gangbare NFT-royaltystandaarden en beschermt tegen kwetsbaarheden bij betalingsverdeling, wat een professionele en betrouwbare oplossing biedt voor NFT-marktplaatsen en makers.
prompt
Help me met het ontwikkelen van een Solidity smart contract dat geautomatiseerde royaltybetalingen voor NFT's beheert. Voeg functies toe zoals het instellen van royaltypercentages: <voer het royaltypercentage in>, het volgen va ...
try_prompt
generate_helper
disclaimerOnPageApi
tags
ai_answers
provider: openai
model: gpt-4.1-nano
Natuurlijk! Hieronder vind je een overzicht en een voorbeeld van een Solidity smart contract dat geautomatiseerde royaltybetalingen voor NFT's beheert, inclusief het instellen van royaltypercentages, het volgen van doorverkoop en het veilig uitbetalen aan de makers. Daarnaast leg ik uit hoe je naleving van EIP-2981 implementeert en hoe je kwetsbaarheden minimaliseert.
---
### 1. Basisprincipes en standaarden
- **EIP-2981**: Een standaard voor royalty-informatie die door marketplaces en andere contracten kan worden uitgelezen om royalty-betalingen te automatiseren.
- **Royaltypercentage instellen**: Bijvoorbeeld 5% (oftewel 500 basispunten).
- **Doorverkoop volgen**: Het contract moet weten wie de maker is en waar royalties naartoe moeten gaan.
- **Veilig uitbetalen**: Gebruik van `call` in plaats van `transfer`, en checks voor succesvol uitbetalen.
---
### 2. Voorbeeldcontract
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
interface IERC2981 {
function royaltyInfo(uint256 tokenId, uint256 salePrice) external view returns (address receiver, uint256 royaltyAmount);
}
contract NFTMetRoyalties is ERC721, IERC2981, Ownable {
// Mapping van tokenId naar royalty percentage (basispunten, bv 500 = 5%)
mapping(uint256 => uint16) private _royaltyPercentages;
// Mapping van tokenId naar maker adres
mapping(uint256 => address) private _makers;
// Default royalty percentage (bijvoorbeeld 5%)
uint16 public defaultRoyaltyPercentage = 500;
constructor() ERC721("MyNFT", "MNFT") {}
// Mint functie met maker en royalty percentage
function mint(address to, uint256 tokenId, uint16 royaltyPercentage) external onlyOwner {
require(royaltyPercentage <= 10000, "Royalty te hoog"); // max 100%
_mint(to, tokenId);
_makers[tokenId] = to;
_royaltyPercentages[tokenId] = royaltyPercentage;
}
// Set royalty percentage voor een token
function setRoyaltyPercentage(uint256 tokenId, uint16 royaltyPercentage) external {
require(_exists(tokenId), "Token bestaat niet");
require(msg.sender == _makers[tokenId], "Niet de maker");
require(royaltyPercentage <= 10000, "Royalty te hoog");
_royaltyPercentages[tokenId] = royaltyPercentage;
}
// Implementatie van royaltyInfo volgens EIP-2981
function royaltyInfo(uint256 tokenId, uint256 salePrice) external view override returns (address receiver, uint256 royaltyAmount) {
address receiverAddress = _makers[tokenId];
uint16 royaltyBP = _royaltyPercentages[tokenId] != 0 ? _royaltyPercentages[tokenId] : defaultRoyaltyPercentage;
uint256 amount = (salePrice * royaltyBP) / 10000;
return (receiverAddress, amount);
}
// Voor marketplaces die EIP-2981 gebruiken
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721, IERC2981) returns (bool) {
return interfaceId == type(IERC2981).interfaceId || super.supportsInterface(interfaceId);
}
// Functie om de maker te bekijken
function getMaker(uint256 tokenId) external view returns (address) {
return _makers[tokenId];
}
}
```
---
### 3. Uitleg en beveiligingsmaatregelen
- **Naleving EIP-2981**: Implementatie van `royaltyInfo()` maakt het contract compatibel met marketplaces die royalty-informatie uitlezen.
- **Royaltypercentages instellen**: Alleen de maker (of eigenaar) kan het percentage aanpassen via `setRoyaltyPercentage()`.
- **Veilige uitbetaling**:
- Gebruik van `call` bij uitbetalingen wordt aanbevolen in plaats van `transfer`, om veiligheidsredenen.
- Bijvoorbeeld: `payable(receiver).call{value: amount}("")`.
- **Kwetsbaarheden voorkomen**:
- **Reentrancy**: Gebruik `ReentrancyGuard` of volg best practices bij uitbetalingen.
- **Overflows/Underflows**: Solidity ^0.8.x heeft ingebouwde overflow bescherming.
- **Access control**: Alleen de maker kan royaltypercentages aanpassen.
- **Token existence check**: `require(_exists(tokenId))` voorkomt fouten.
---
### 4. Aanvullende tips
- Overweeg het gebruik van OpenZeppelin’s `PaymentSplitter` voor geavanceerde verdelingen.
- Voeg functies toe voor het opvragen van royalty-informatie van meerdere tokens.
- Test het contract grondig in een testnet voordat je het in productie gebruikt.
---
Als je verdere hulp nodig hebt bij het uitbreiden van dit contract of specifieke functionaliteiten, laat het gerust weten!
provider: deepseek
model: deepseek-chat
Zeker! Ik help je bij het ontwikkelen van een Solidity smart contract voor NFT-royaltybetalingen. Hier is een complete uitleg met codevoorbeelden.
### Basis Smart Contract met Royalty Functionaliteit
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/interfaces/IERC2981.sol";
contract NFTWithRoyalties is ERC721, Ownable, IERC2981 {
struct RoyaltyInfo {
address recipient;
uint24 percentage; // Basis punten (bijv. 500 = 5%)
}
mapping(uint256 => RoyaltyInfo) private _royalties;
uint256 private _tokenCounter;
event RoyaltySet(uint256 tokenId, address recipient, uint256 percentage);
event RoyaltyPaid(uint256 tokenId, address recipient, uint256 amount);
constructor() ERC721("RoyaltyNFT", "RNFT") {}
// EIP-2981 implementatie
function royaltyInfo(uint256 tokenId, uint256 salePrice)
external
view
override
returns (address receiver, uint256 royaltyAmount)
{
RoyaltyInfo memory royalty = _royalties[tokenId];
require(royalty.recipient != address(0), "Royalty niet ingesteld");
royaltyAmount = (salePrice * royalty.percentage) / 10000;
return (royalty.recipient, royaltyAmount);
}
// NFT minten met royalty
function mintWithRoyalty(address to, address royaltyRecipient, uint24 royaltyPercentage)
external
onlyOwner
returns (uint256)
{
require(royaltyPercentage <= 1000, "Maximaal 10% royalty"); // Beveiliging tegen te hoge percentages
_tokenCounter++;
uint256 newTokenId = _tokenCounter;
_safeMint(to, newTokenId);
_setRoyalty(newTokenId, royaltyRecipient, royaltyPercentage);
return newTokenId;
}
// Royalty instellen
function _setRoyalty(uint256 tokenId, address recipient, uint24 percentage) internal {
require(_exists(tokenId), "Token bestaat niet");
require(recipient != address(0), "Ongeldig recipient adres");
require(percentage <= 1000, "Percentage te hoog");
_royalties[tokenId] = RoyaltyInfo(recipient, percentage);
emit RoyaltySet(tokenId, recipient, percentage);
}
// Ondersteuning voor EIP-2981 controleren
function supportsInterface(bytes4 interfaceId)
public
view
virtual
override(ERC721, IERC165)
returns (bool)
{
return interfaceId == type(IERC2981).interfaceId || super.supportsInterface(interfaceId);
}
}
```
### Uitgebreide Betalingsdistributie Contract
```solidity
contract RoyaltyPaymentDistributor {
address public immutable nftContract;
struct SaleInfo {
address seller;
address buyer;
uint256 price;
uint256 timestamp;
}
mapping(uint256 => SaleInfo[]) public saleHistory;
mapping(address => uint256) public pendingWithdrawals;
event SaleRecorded(uint256 tokenId, address seller, address buyer, uint256 price);
event RoyaltyDistributed(uint256 tokenId, address recipient, uint256 amount);
event Withdrawal(address recipient, uint256 amount);
constructor(address _nftContract) {
nftContract = _nftContract;
}
// Doorverkoop registreren en royalty betalen
function recordSale(uint256 tokenId, address seller, address buyer, uint256 price) external {
require(msg.sender == nftContract, "Alleen NFT contract");
// Royalty informatie ophalen
(address royaltyRecipient, uint256 royaltyAmount) = IERC2981(nftContract).royaltyInfo(tokenId, price);
// Betalingen verdelen
uint256 sellerAmount = price - royaltyAmount;
// Bijhouden van bedragen (in een echte implementatie zou je hier ETH transfers doen)
pendingWithdrawals[seller] += sellerAmount;
pendingWithdrawals[royaltyRecipient] += royaltyAmount;
// Sale geschiedenis bijhouden
saleHistory[tokenId].push(SaleInfo(seller, buyer, price, block.timestamp));
emit SaleRecorded(tokenId, seller, buyer, price);
emit RoyaltyDistributed(tokenId, royaltyRecipient, royaltyAmount);
}
// Opname functie (pull betaling voor veiligheid)
function withdraw() external {
uint256 amount = pendingWithdrawals[msg.sender];
require(amount > 0, "Geen pending withdrawals");
pendingWithdrawals[msg.sender] = 0;
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer mislukt");
emit Withdrawal(msg.sender, amount);
}
}
```
### Belangrijke Beveiligingsmaatregelen
1. **Pull i.p.v. Push Betalingen**:
```solidity
// GOED - Veilige opname
function withdraw() external {
uint256 amount = pendingWithdrawals[msg.sender];
require(amount > 0, "Geen funds");
pendingWithdrawals[msg.sender] = 0;
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer mislukt");
}
// SLECHT - Onveilige directe transfer
function unsafeTransfer(address recipient, uint256 amount) external {
payable(recipient).transfer(amount); // Kan falen door gas limits
}
```
2. **Re-entrancy Bescherming**:
```solidity
// Gebruik Checks-Effects-Interacties pattern
function secureFunction() external {
// CHECK
require(conditions, "Niet voldaan");
// EFFECTS
balances[msg.sender] = 0;
// INTERACTIE (laatste stap)
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer mislukt");
}
```
3. **Access Control**:
```solidity
modifier onlyAuthorized() {
require(msg.sender == owner() || msg.sender == authorizedOperator, "Niet geautoriseerd");
_;
}
```
### EIP-2981 Naleving
**Waarom EIP-2981 belangrijk is**:
- Standaard interface voor marktplaatsen zoals OpenSea
- Compatibiliteit met alle EIP-2981 ondersteunende platforms
- Toekomstbestendige implementatie
**Implementatie vereisten**:
```solidity
function royaltyInfo(uint256 tokenId, uint256 salePrice)
external
view
returns (address receiver, uint256 royaltyAmount);
```
### Aanbevolen Praktijken
1. **Percentage Limieten**:
```solidity
uint24 public constant MAX_ROYALTY = 1000; // 10%
require(royaltyPercentage <= MAX_ROYALTY, "Royalty te hoog");
```
2. **Event Logging**:
```solidity
event RoyaltyPayment(
uint256 indexed tokenId,
address indexed recipient,
uint256 amount,
uint256 timestamp
);
```
3. **Unit Tests**:
```solidity
// Test royalty berekening
function testRoyaltyCalculation() public {
uint256 salePrice = 1 ether;
uint256 expectedRoyalty = (salePrice * 500) / 10000; // 5%
assert(royaltyAmount == expectedRoyalty);
}
```
### Deployment Stappen
1. **Installeren dependencies**:
```bash
npm install @openzeppelin/contracts
```
2. **Compileren**:
```bash
npx hardhat compile
```
3. **Testen**:
```bash
npx hardhat test
```
### Conclusie
Dit contract implementeert een veilige, EIP-2981 compatibele royalty-oplossing met:
- Automatische betalingsdistributie
- Beveiliging tegen common vulnerabilities
- Uitgebreide event logging
- Flexibele royalty-instellingen
Zorg altijd voor uitgebreide testing en security audits voordat je naar mainnet deployt!