Schreiben Sie Ihren eigenen einfachen ERC20-Token für Dividenden

Token bieten Wert für ihre Inhaber. Dies ergab sich aus der Fähigkeit von Smart Contracts, die Eigentumsrechte an Vermögenswerten auf der Blockchain auf vertrauenslose Weise zu übertragen. Ein Szenario besteht darin, dass Token einen zugrunde liegenden Vermögenswert darstellen, der proportional entsprechend dem Saldo dieser Token geteilt wird, die ein Inhaber (dh ein Investor) hält. Dieser Artikel zeigt, wie man einen soliden ERC20-Token-Smart-Contract schreibt, der einen zugrunde liegenden Vermögenswert (z. B. Ether) in Form von Dividenden unter den Inhabern aufteilt, nennen wir es den div Zeichen.

Sich nähern

Ein einfacher Ansatz besteht darin, den Vermögenswert aufzuteilen und für jeden der Inhaber des Tokens aufzuzeichnen. Dies geschieht, indem der Betrag des erhaltenen Vermögens sofort durch die Gesamtmenge des Tokens dividiert und dann mit der Höhe der Dividenden multipliziert wird, die jeder Inhaber hat, was durch sein Token-Guthaben dargestellt wird. So sehr dieser Ansatz logisch klingen mag und die Goto-Lösung für unseren Anwendungsfall ist, ist er in der Welt der Solidity Smart Contracts, in der wir mit den folgenden zwei Komplikationen konfrontiert sind, eigentlich unsinnig:

  • Fehlende Unterstützung für Gleitkommaoperationen in der Sprache selbst.
  • Entwickler berücksichtigen die Berechnung, da die Ausführung auf der Blockchain Geld kostet.

Der erste Punkt besagt, dass diese Einschränkung zu Geldverlusten aufgrund der Teilung von nicht zerlegbaren ganzzahligen Werten führen wird. Andererseits zwingt der zweite, der der Hauptgrund für die Verwendung des in diesem Artikel vorgeschlagenen Ansatzes ist, Entwickler dazu, den Ansatz zu finden, der teure unnötige Berechnungen vermeidet.

Vor diesem Hintergrund schreibe ich den Code, um nur den Betrag zu verfolgen, der pro Token erhalten werden soll dividendPerToken und ein Mapping, um die Dividende pro Token-Betrag zu verfolgen, der bereits von einem Investor abgehoben wurde xDividendPerToken.

uint256 dividendPerToken;
mapping (address => uint256) xDividendPerToken;
  • DividendeProToken: ist die Menge des Basiswerts (z. B. Ether) pro Token. Dieser Betrag wird aktualisiert, wenn der Kontrakt Ether erhält (d. h. Basiswert unserer divs). Angenommen, der Vertrag erhält 10 ether und das Gesamtangebot an Token ist 10 divsalso die Menge dividendPerToken gleich 1.
  • xDividendeproToken: ist der Betrag pro div bereits vom Inhaber dieser zurückgezogen div Token. Basierend auf dem letzten Beispiel, einer der Inhaberinnen, Alice, Besitz 5 divsSie entzieht ihren Anteil 10/2 = 5 divs. Die Quantität xDividendPerToken für Alice ist in diesem Fall gleich 1.

Kaution

Der zugrunde liegende Vermögenswert des Vertrags, der unter den Inhabern geteilt werden soll, ist Ether. Dieser zugrunde liegende Vermögenswert repräsentiert die native Münze der Kette (dh ether im Ethereum-Mainnet, matic im Polygon Mainnet und so weiter, …). Dies macht es in Bezug auf die Solidität zu etwas Besonderem, da das Senden einer nativen Münze an die Adresse eines Vertrags die auslöst receive() Funktion dieses Vertrags, die eine spezielle Funktion mit implementierter Logik ist, wenn wir etwas tun müssen, wenn wir diese einheimische Münze erhalten.

receive() external payable {
    require(totalSupply() != 0, "No tokens minted");
    dividendPerToken += msg.value / totalSupply();    
    emit FundsReceived(msg.value, dividendPerToken);
}

Es ist kein Geheimnis, dass wir den Wert von aktualisieren möchten dividendPerToken bei Neuanlieferung ether. In erster Linie bei der Implementierung stellen wir sicher, dass wir eine Versorgung ungleich Null haben divs das ist die Gesamtmenge der geprägten Token, sodass wir am Ende nicht durch Null dividieren, falls niemand welche geprägt hat divs was bedeutet, dass es sowieso keine Inhaber gibt, unter denen der Vermögenswert verteilt werden kann. Dann die Menge ether erhalten, die gespeichert ist msg.value wird durch die aktuelle Gesamtversorgung der geteilt divs und auf den letzten Wert von akkumuliert dividendPerToken. Schließlich gibt der Vertrag ein Ereignis für die Welt außerhalb der Blockchain aus, um zu erfahren, dass der Vertrag eine gewisse Menge an Vermögenswerten erhalten hat und dividendPerToken hatte seinen Wert aktualisiert.

Es ist erwähnenswert, dass dividendPerToken multipliziert mit dem Guthaben des Inhabers gibt den vom Inhaber abzuhebenden Betrag zurück xDividendPerToken ist Null. Für eine Nicht-Null xDividendPerToken die Entnahmegleichung ist

withdrawal = balance(holder) * (dividendPerToken - xDividendPerToken(holder))

Minze

Prägung divs sollte so einfach sein wie schreiben _mint(to_, amount_) wo to_ ist die Adresse des Empfängers von minted divs und amount_ ist die Menge an Token, die an den Empfänger geprägt werden sollen.
Aber das Prägen neuer Token-Updates balance(holder) was die Gleichung von beeinflusst withdrawal, daher sollten beim Prägen neuer Token vorher Gelder an den Empfänger abgehoben werden. Ich führe eine neue Zuordnungsvariable ein credit zu diesem Zweck, um den Betrag der zugrunde liegenden Vermögenswerte zu speichern, die zurückgenommen werden an den Empfänger, anstatt das Vermögen tatsächlich abzuheben.

mapping (address => uint256) credit;

Diese Funktion wird vor dem Prägen neu implementiert divs zu diesem Zweck

function _withdrawToCredit(
    address to_
) private
{
    uint256 recipientBalance = balanceOf(to_);
    uint256 amount = (dividendPerToken - xDividendPerToken[to_]) * recipientBalance ;
    credit[to_] += amount;
    xDividendPerToken[to_] = dividendPerToken;
}

Ab der zweiten Zeile der Implementierung von _withdrawToCreditdie Werte werden in die Entnahmegleichung eingesetzt, um die zu erhalten amount dem Inhaber zurückgenommen werden to_. Anstatt die zu senden ether sofort an den Halter, es wird in die gespeichert credit Zuordnung dieses Inhabers. Zuletzt aktualisieren Sie die xDividendPerToken dieses Inhabers anzugeben, dass sie diesen Teil der Dividende abgezogen hat.

Schließlich ist hier die Prägelogik

function mint(address to_, uint256 amount_) public onlyOwner {
    _withdrawToCredit(to_);
    _mint(to_, amount_);
}

Abheben

Hier kommt der interessante Teil, bei dem es um das Abheben der zugrunde liegenden Vermögenswerte für den Inhaber unserer geht div Zeichen.

function withdraw() external {
    uint256 holderBalance = balanceOf(_msgSender());
    require(holderBalance != 0, "DToken: caller possess no shares");

    uint256 amount = ( (dividendPerToken - xDividendPerToken[_msgSender()]) * holderBalance );
    amount += credit[_msgSender()];
    credit[_msgSender()] = 0;
    xDividendPerToken[_msgSender()] = dividendPerToken;

    (bool success, ) = payable(_msgSender()).call{value: amount}("");
    require(success, "DToken: Could not withdraw eth");
}

Der zu erhaltende Betrag ist proportional zum Guthaben des Inhabers gemäß der Auszahlungsgleichung, auf die weiter oben in diesem Artikel Bezug genommen wird. Dieser Betrag wird dem aktuellen Guthaben hinzugefügt, das der Inhaber aufgrund von Prägungen oder Überweisungen hat. Dann die Werte von credit und xDividendPerToken werden für diesen Inhaber aktualisiert. Abschließend sendet der Kontrakt den Betrag des Basiswerts an den Inhaber, der diesen aufgerufen hat withdraw Funktion.

Transfer

Überweisung eines Betrages von divs von einem Inhaber zum anderen ist ein Teil des ERC20-Standardvertrags, den dieser Vertrag erbt. Daher musste in diesem Teil nicht viel getan werden, außer zu bemerken, dass die Übertragung des Token-Betrags zwischen zwei Inhabern die Salden dieser Inhaber ändert. Daher müssen wir dies berücksichtigen, bevor der Abzug des zugrunde liegenden Vermögenswerts durch diese beiden Inhaber stattfindet. Der ERC20-Standard bietet uns eine Hook-Funktion, um dies zu implementieren, bevor Token übertragen werden: _beforeTokenTransfer(address from,address to,uint256 amount).

function _beforeTokenTransfer(
    address from,
    address to,
    uint256 amount
) internal override {
    if(from == address (0) || to == address(0)) return;
    _withdrawToCredit(to);
    _withdrawToCredit(from);
}

from und to sind die Adressen von Absender bzw. Empfänger. Diese Adressen beziehen sich auf die an der Überweisung beteiligten Inhaber, daher wird ihre Gutschrift entsprechend aktualisiert, bevor die Überweisung ausgeführt wird. Eine Nulladresse für den Absender impliziert Prägung, während für den Empfänger impliziert wird, dass der Token verbrannt wird. In diesen beiden Fällen kehrt die Funktion sofort zurück, ohne die Auszahlungen auszuführen.

Fazit

Wir nutzen den ERC20-Standard, um einen Vertrag zu erstellen, der Dividenden unter den Inhabern proportional zu ihren Guthaben ausschüttet. Beachten Sie auch, dass ein guter Smart Contract ohne gründliche Tests, die Szenarien reproduzieren, in denen Inhaber ähnlich wie in der realen Welt handeln, nie gut ist. Beziehen Sie sich darauf Repo auf github, das den Smart Contract und die Tests enthält.

Similar Posts

Leave a Reply

Your email address will not be published.