Hoe Solidity-programmering te gebruiken
Solidity wordt gebruikt voor het maken van complexe slimme contracten die zijn ontworpen om te werken op de Ethereum-blockchain. Deze taal biedt unieke strategieën die hem onderscheiden van andere programmeertalen.
Wat is Solidity?
Solidity is een programmeertaal op hoog niveau voor het maken van slimme contracten op de Ethereum-blockchain. Slimme contracten zijn zelfuitvoerende contracten die de uitwisseling van activa tussen partijen automatiseren. Het bijzondere aan deze contracten is dat er geen tussenpersonen nodig zijn om naleving van het slimme contract te waarborgen.
Solidity-broncode wordt gecompileerd tot bytecode en geïmplementeerd op de Ethereum-blockchain als een slim contract. Zodra dit is gebeurd, kan het slimme contract worden uitgevoerd door elk knooppunt op het netwerk en wordt de status opgeslagen op de blockchain. We tonen een voorbeeld van een eenvoudig contract dat een NFT-automaat modelleert:
pragma Solidity 0.8.7;
contract NFTVendingMachine {
// Declare state variables
address public owner;
mapping (address => uint) public nftBalance;
// Run on deployment
constructor() {
owner = msg.sender;
nftBalance[address(this)] = 100;
}
// Allow the owner to restock the NFT balance
function restock(uint amount) public {
require(msg.sender == owner, "Only the owner can restock.");
nftBalance[address(this)] += amount;
}
// Allow anyone to purchase NFTs
function purchase(uint amount) public payable {
require(msg.value >= amount * 1 ether, "You must pay at least 1 ETH per NFT");
require(nftBalance[address(this)] >= amount, "Not enough NFTs in stock to complete this purchase");
nftBalance[address(this)] -= amount;
nftBalance[msg.sender] += amount;
}
}solidityVoor welke toepassingen is Solidity geschikt?
Solidity is speciaal ontworpen voor het maken van gedistribueerde applicaties of DApps die draaien op de Ethereum Virtual Machine (EVM). Slimme contracten zijn onder andere geschikt voor het beheren van digitale activa, het creëren van gedecentraliseerde uitwisselingen en het implementeren van stemsystemen.
Gedecentraliseerde financiën (DeFi)
Solidity wordt gebruikt om DeFi-toepassingen te ontwikkelen, zoals gedecentraliseerde beurzen, krediet- en leenplatforms, voorspellingsmarkten en cryptovaluta’s. DeFi is een van de populairste toepassingen van blockchaintechnologie geworden. Daarbij is Solidity een onmisbaar hulpmiddel geworden voor het bouwen van DeFi-toepassingen op het Ethereum-netwerk.
Niet-vervangbare tokens
De non-fungible token (NFT) is sinds de jaren 2020 enorm populair. NFT’s zijn unieke digitale activa die op de blockchain worden opgeslagen. Het kan gaan om digitale kunstwerken, sportmemorabilia of artefacten uit de gamingindustrie. Solidity wordt gebruikt om de slimme contracten te creëren die NFT’s mogelijk maken.
Beheer van de leveringsketen
Solidity kan worden gebruikt om slimme contracten te maken voor het monitoren en beheren van toeleveringsketens. De contracten worden gebruikt om verschillende processen in de toeleveringsketen te automatiseren. Deze omvatten het volgen van de bewegingen van goederen, het verifiëren van de authenticiteit van producten en het verwerken van betalingen tussen partijen.
Verzoeningssystemen
Solidity kan worden gebruikt om slimme contracten te maken die veilige en transparante stemsystemen op de blockchain implementeren. De contracten kunnen worden gebruikt om ervoor te zorgen dat stemmen correct worden geteld en dat het stemproces eerlijk en transparant verloopt.
Wat zijn de voor- en nadelen van Solidity?
Hoewel Solidity een krachtige taal is voor het bouwen van slimme contracten op de Ethereum-blockchain, heeft het specifieke voor- en nadelen waarmee ontwikkelaars rekening moeten houden bij het ontwikkelen van slimme contracten. Het opstellen van veilige slimme contracten vereist echter een bepaald niveau van vaardigheid en voorzichtigheid.
Ter illustratie volgt hieronder een slim contract dat functioneert als een zwart gat. Alle Ether die naar het contract wordt gestuurd, wordt permanent verbruikt, zonder mogelijkheid om de Ether terug te halen of uit te betalen:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >= 0.9.0;
// This contract swallows all Ether sent to it
contract Blackhole {
event Received(address, uint);
receive() external payable {
emit Received(msg.sender, msg.value);
}
}solidityVoordelen van Solidity
- Flexibiliteit: Solidity is een veelzijdige taal. Het kan worden gebruikt om verschillende slimme contracten te ontwikkelen met een verscheidenheid aan gebruiksscenario’s.
- Beveiliging: Solidity is ontwikkeld met de nadruk op beveiliging en bevat functies zoals toegangscontrole, uitzonderingsafhandeling en storingsmechanismen om ontwikkelaars te helpen bij het opstellen van veilige contracten.
- Compatibiliteit met Ethereum: Momenteel is Solidity de voorkeurstaal voor het produceren van slimme contracten op de Ethereum-blockchain.
- Onderscheidende community: Een grote community van blockchain-ontwikkelaars werkt met Solidity, wat resulteert in een overvloed aan bronnen voor leren en het oplossen van problemen.
Nadelen van Solidity
- Leercurve: Voor ontwikkelaars die nog niet bekend zijn met blockchain en de ontwikkeling van slimme contracten, heeft Solidity een relatief steile leercurve.
- Onveranderlijkheid: Zodra een smart contract op de blockchain is geïmplementeerd, kan het niet meer worden gewijzigd. Dit betekent dat ontwikkelaars uiterst voorzichtig moeten zijn bij het schrijven en testen.
- Gebrek aan formele verificatie: Solidity mist ingebouwde tools voor formele codereview. Dit maakt het noodzakelijk dat ontwikkelaars externe tools gebruiken om de nauwkeurigheid van hun contracten te garanderen.
- Beperkte tooling: Het tooling-ecosysteem van Solidity bevindt zich nog in een pril stadium, wat kan leiden tot problemen met geïntegreerde ontwikkelomgevingen (IDE’s), testframeworks en andere ontwikkeltools.
Wat is de basissyntaxis van Solidity?
Solidity is een objectgeoriënteerde programmeertaal die is ontworpen voor slimme contracten. De taal is geïnspireerd op JavaScript, Python en C++. De syntaxis van de taal lijkt op die van JavaScript, maar heeft een aantal interessante eigenaardigheden.
Variabelen in Solidity
Op het eerste gezicht lijkt de variabeleverwerking van Solidity misschien vergelijkbaar met die van andere programmeertalen. Er is echter een belangrijk verschil: de Ethereum Virtual Machine (EVM) fungeert als uitvoeringsomgeving. Alle bewerkingen op de EVM, inclusief gegevensopslag, brengen bepaalde ‘gas’-kosten met zich mee. Bij het programmeren moet daarom de efficiëntie van een bewerking worden afgewogen en moet worden bepaald hoe deze zo efficiënt mogelijk kan worden geïmplementeerd.
Naast reguliere variabelen heeft Solidity ook constanten, die tijdens het compileren moeten worden gedefinieerd. Constanten vereisen minder gas voor opslag:
// Regular variable can be declared without defining
int a;
// Constant needs to be defined at declaration
int constant b = 51;solidityHetzelfde geldt voor immutable variabelen, omdat ze minder gas vereisen en niet kunnen worden gewijzigd na toewijzing. In tegenstelling tot constant variabelen kan de toewijzing van onveranderlijke variabelen tijdens runtime worden uitgevoerd.
Control statements in Solidity
Als imperatieve programmeertaal ondersteunt Solidity bekende controlestatements, zoals branches en loops. We tonen de code voor het selecteren van het grootste van twee getallen, a en b:
int largerNumber = 0;
// If-else statement
if (a > b) {
largerNumber = a;
} else {
largerNumber = b;
}solidityDe for -lus in Solidity komt overeen met de syntaxis die bekend is uit JavaScript of C++:
// Loop 10 times
for (int i = 0; i < 10; i++) {
// …
}solidityDe while 3-lus werkt ook zoals gewoonlijk. We combineren een beëindigingsvoorwaarde met een numerieke tellervariabele:
bool continueLoop = true;
int counter = 0;
// Loop at most 10 times
while (continueLoop && counter < 10) {
// …
counter++;
}solidityEenvoudige typen in Solidity
Solidity is een statisch getypeerde taal en ondersteunt de typen die vaak voorkomen in programmeertalen. Eenvoudige typen die enkele waarden vertegenwoordigen zijn onder andere booleaanse waarden, getallen en tekenreeksen.
Booleaanse waarden in Solidity komen overeen met de waarden true en false. Ze kunnen worden gekoppeld met behulp van de bekende Booleaanse operatoren en worden gebruikt in if:
bool paymentReceived = true;
bool itemsStocked = true;
bool continueTransaction = paymentReceived && itemsStocked;
if (continueTransaction) {
// ...
}soliditySolidity ondersteunt een breed scala aan numerieke typen. Gehele getallen kunnen worden onderscheiden tussen getallen met teken (int) en getallen zonder teken (uint), waarbij de laatste alleen positief mogen zijn. Bovendien kan het bereik van een getal worden gespecificeerd in stappen van 8 bits, van int8 via int16 tot int265:
uint8 smallNumber = 120;
int8 negativeNumber = -125;
int8 result = smallNumber + negativeNumber;
assert(result == -5)solidityStrings worden in Solidity voornamelijk gebruikt voor het genereren van statusberichten. De taal ondersteunt enkele en dubbele aanhalingstekens, evenals Unicode-tekens:
string message = 'Hello World';
string success = unicode"Transfer sent";SolidityFuncties in Solidity
Functies zijn een fundamenteel aspect van Solidity, net als in de meeste programmeertalen. De definitie van een functie is vergelijkbaar met JavaScript, waarbij de argumenttypes expliciet moeten worden gespecificeerd. Daarnaast wordt een returns-sleutelwoord gebruikt om de return waardetypes aan te geven.
// Define a function
function addNumbers(int a, int b) returns (int) {
return a + b;
}solidityHet aanroepen van een functie gebeurt zoals gewoonlijk:
// Call the function
int result = addNumbers(2, 3);solidityInteressant is dat, net als bij benoemde argumenten, ook retourwaarden in Solidity een naam kunnen krijgen. In dit geval volstaat het om de bijbehorende variabelen in de functielichaam toe te wijzen, en is een expliciete retour via return niet nodig:
function divideNumbers(int dividend, int divisor) returns (int quotient) {
quotient = dividend / divisor;
// No `return` necessary
}solidityNet als bij constant of immutable variabelen kunnen functies in Solidity worden gemarkeerd als niet-statuswijzigend. Hiervoor worden de sleutelwoorden view en pure gebruikt. Een view wijzigt de status niet, terwijl een pure bovendien garandeert dat er geen statusvariabelen worden gelezen.
Slimme contracten in Solidity
Naast standaardtypen kent Solidity een aantal speciale typen voor slimme contracten. Het basistype is address en koppelt Ethereum-adressen. Adressen die payable zijn, kunnen overboekingen in Ether ontvangen. Voor dit doel bieden payable balance() en transfer().
// Get address of this contract
address mine = address(this);
// Get payable external address
address payable other = payable(0x123);
// Transfer if balances fulfill conditions
if (other.balance < 10 && mine.balance >= 100) {
other.transfer(10);
}solidityVoortbouwend op het type address bestaat het type contract als een centrale taalconstructie. Contracten komen grofweg overeen met klassen in objectgeoriënteerde programmeertalen. Contracten bundelen dus statusgegevens en functies en schermen deze af van de buitenwereld. Contracten ondersteunen meervoudige overerving, zoals bekend uit Python of C++.
Contracten beginnen meestal met een regel pragma waarin de toegestane Solidity-versie wordt gespecificeerd, gevolgd door de feitelijke definitie:
// Make sure Solidity version matches
pragma Solidity >=0.7.1 <0.9.0;
// Contract definition
contract Purchase {
// Public state variables
address seller;
address buyer;
// View-function
function getSeller() external view returns (address) {
return seller;
}
}soliditySlimme contracten kunnen statusgegevens en functies definiëren. Zoals bekend uit C++ en Java, kan in elk geval een van de drie toegangsniveaus worden gedefinieerd:
public: De variabele is toegankelijk door te lezen en te schrijven vanuit het contract. Bovendien wordt automatisch eenviewgegenereerd als een getter voor externe leestoegang.internal: De variabele wordt afgeschermd tegen externe toegang. Lezen en schrijven is mogelijk vanuit het contract zelf en vanuit overgenomen contracten.private: zoalsinternal, maar er is geen toegang vanuit overgenomen contracten
Functies kunnen verder worden gekarakteriseerd als external. Een external fungeert als onderdeel van de contractinterface en wordt gebruikt voor externe toegang. De receive voor het ontvangen van ether is een bekend voorbeeld:
// Define without `function` keyword
receive() external payable {
// Handle Ether
}solidityModificatoren in Solidity
Solidity heeft een intrigerende taalconstructie in de vorm van modifiers, die lijken op de decorators van Python. Net als in Python worden modifiers in Solidity gebruikt om de aanroep van een functie te wijzigen. Ze worden vaak gebruikt om ervoor te zorgen dat aan een bepaalde voorwaarde is voldaan voordat een functie wordt uitgevoerd:
contract Sale {
uint price;
address payable owner;
modifier onlyOwner {
// Will throw error if called by anyone other than the owner
require(
msg.sender == owner,
"Only owner can call this function."
);
// The wrapped function's body is inserted here
_;
}
// `onlyOwner` wraps `changePrice`
function changePrice(uint newPrice) public onlyOwner {
// We'll only get here if the owner called this function
price = newPrice;
}
}solidityTransactiebeheer met Solidity
Solidity heeft een ingebouwd transactiebeheer. Dit kan worden gebruikt om ervoor te zorgen dat een etheroverdracht volledig wordt afgewikkeld of helemaal niet wordt afgewikkeld. De taal begrijpt het revert -sleutelwoord, dat een ‘roll-back’ van een transactie activeert. Met het error sleutelwoord kunt u uw eigen foutcodes definiëren:
// Custom error definition
error InsufficientPayment(uint256 paid, uint256 required);
// Contract representing a sale
contract Sale {
uint price;
// Purchase if enough ether transferred
function purchase() public payable {
if (msg.value < price) {
revert InsufficientPayment(msg.value, price);
}
// Complete purchase
}
}solidityEen ander veelvoorkomend patroon is het gebruik van de functie require(). Deze kan op dezelfde manier worden gebruikt als revert:
// Using `require()` function
if (!condition) revert("Error message");
// Equivalent to
require(condition, "Error message");solidity