NEAR Live Contract Review – Teil 4: Berry Farm

(0 nL)
16 min read
To Share and +4 nLEARNs

Einleitung

Mein Name ist Eugene, und heute werden wir über den Berry Farm Contract sprechen, der die Ergänzung des Berry Clubs ist, die es Ihnen ermöglicht, Deine Bananen in Gurken umzuwandeln und NEAR zu farmen. Lasst mich euch zunächst einen kurzen Überblick über die Berry Farm geben. Das Repository kann auf meinem Github gefunden werden. Hier ist das ursprüngliche Video, auf dem dieser Leitfaden basiert:

UI

Berry Farm ist eine sehr gut gestaltete App mit großem Text und vielen Emojis. Die App zeigt den Kontostand nach dem Login an und bezieht diesen dabei aus verschiedenen Quellen. Avocados und Bananen, eine Zwischenwährung in der App, stammen aus dem Berry Club Contract, Gurken aus dem lokalen Contract und NEAR aus deinem Kontostand. Die Grundlage des Spiels ist, dass man einen Raum auf dem Screen reservieren kann, und auf Basis dieser Nutzung eine wiederkehrende Belohnung ausgeteilt wird. Wenn ein Spieler einen Raum für sich vereinnahmt hat und dabei nicht von einem anderen Spieler verdrängt wurde, wird ein Teil der gesamten Belohnung an diesen Spieler ausgegeben. Es scheint, dass jemand in letzter Zeit viele Avocados gekauft hat da die Belohnungen zum Zeitpunkt der Erstellung diesen Artikels relativ zur Vergangenheit gestiegen sind. Es gibt auch globale Statistiken, die besagen, dass 2500 NEAR von Berry Club zu Berry Farm übertragen wurden. Ich habe 116,9 NEAR verdient und meine Belohnung wird 0,014332 NEAR betragen, basierend auf der Anzahl der Gurken, die ich im Vergleich zur Gesamtzahl der Gurken habe.

Die Gesamtzahl der Gurken beträgt 54116,4. Ich bekomme jedes Mal, wenn es eine Belohnung gibt, einen Anteil von 3 % von dieser Belohnung. Wenn ich also auf Aktualisieren klicke, aktualisiere ich im Grunde die Statistiken von mehreren Konten, und ich kann NEAR beanspruchen, das vom Contract auf mein Konto übertragen wird. Ich kann auch Bananen gegen Gurken tauschen oder den Berry Club Contract aufrufen.

Ich werde transfer_with_vault zur Übertragung von Assets nutzen. Dabei wird ein neuer, sichererer Standard verwendet. Als Sicherheitsmaßnahme gegen automatische Abbuchungen muss das Abheben von Assets manuell bestätigt werden wodurch verhindert wird, dass ein Zugangsschlüssel verwendet wird. Dieser Prozess kostet 1 yocto NEAR. Wenn du z.B. Berry Club von einer anderen App autorisieren würdest, die versucht, alle deine Bananen abzuziehen, würde das scheitern, weil sie nicht in der Lage ist, transfer_raw oder transfer_with_vault aufzurufen. Zur Klarstellung: transfer_raw ist der neue Name für Überweisungen ohne Contract, bei dem man einfach auf ein bestehendes Konto einzahlen kann, und transfer_with_vault nennt den Empfänger, um Token abzuheben.

Um mehr Gurken zu bekommen habe ich 10 Bananen ausgegeben. Bei der Aktualisierung des Kontostands kann ich sehen, ob jemand etwas abgehoben hat. So funktioniert die Benutzeroberfläche, aber es gibt mehr Funktionen als nur diese. Der Contract unterstützt Gurken und Bananen als fungible Token. Man kann in Zukunft möglicherweise eine weitere App entwickeln, die Gurken überträgt, und es gibt noch weitere Möglichkeiten, wie z.B. Banana Swap, die Vlad Garchina entwickelt hat.

Es ist ein vereinfachter Uniswap mit einem einzigen Pool, in dem man Bananen kaufen und verkaufen kann. Ich habe es tatsächlich noch nicht ausprobiert. Ich möchte Bananen im Wert von 1 NEAR verkaufen, was zum Zeitpunkt des Erstellens diesen Artikels etwa 6 Bananen entspricht. Mal sehen, ob es klappt.

Berry Club Transfers wird aufgerufen, der wieder einmal beteiligt ist, aber dieses mal mit einem anderen Contract. Wir zeigen derzeit keine Argumente an, aber bald werden wir das tun.

Es wird angezeigt dass die Summe überwiesen wurde, und ich habe 1 NEAR bekommen, für das ich 6 Bananen ausgegeben habe. Ziemlich cool.

Die Liquidität kann nicht aufgestockt werden, und sie wird um 10 % gekürzt. Das ist also das User Interface.

Überprüfung des Contracts

Berry Club

Lasst mich nun den Contract vorstellen. Wir beginnen mit dem Banana Farm Contract, aber Du musst Dir wahrscheinlich auch den Token Contract von pixelboard, den Contract von Berry Club und den Contract, der Token verschickt, ansehen.

Berry Club hat einen hard-coded Contract, der Farm Contract id genannt wird.

Wenn du zeichnest, wird überprüft, dass du keine leeren Pixel zeichnest, sonst könntest du einfach eine Belohnung auslösen, ohne Avocados auszugeben.

Dann wird maybe_send_reward aufgerufen, das die aktuelle Zeit aus der Blockchain abruft und überprüft, ob der next_reward_timestamp kleiner als die aktuelle Zeit ist.

Die nächste Belohnung wird entweder am Beginn der Farming-Zeit ausgelöst, die ein global timestamp ist, welcher ursprünglich einen 26-Stunden-Countdown ermöglichte, oder beim Zeitstempel last_reward_timestamp, als wir ihn zuletzt ausgelöst haben, plus 60 Sekunden.

Wenn es also an der Zeit ist, den nächsten Reward zu senden, wird sich die aktuelle Zeit gemerkt und der Reward auf der Grundlage dieser Logik berechnet. D.h. der aktuelle Kontostand von Berry Club und der aktuelle Speicherplatz wird genommen, es wird der Speicherplatz berechnet um die Speicherkosten plus eine Sicherheitsspanne, die 50 NEAR beträgt, zu decken.

Es werden immer 15 NEAR für die zukünftigen Guthaben gehalten. Wenn unser Guthaben höher ist als der Betrag, den wir für die Lagerung plus Sicherheit benötigen, dann berechnen wir das verfügbare Guthaben und teilen es durch den Anteil der Belohnung, den wir jedes Mal auszahlen werden, was 24 mal 68 ist. Wenn man es jede Minute aufrufen würde, dann würde man den größten Teil des Guthabens auf dem Konto aufbrauchen. Aber die Belohnungen nehmen von selbst ab, weil das Guthaben an sich von selbst abnimmt. Aber es ergibt meistens alles Guthaben innerhalb eines Tages, wenn man es jede Minute aufrufen würde. Erstens kann man keinen Block produzieren, der einen früheren Zeitstempel hat und zweitens kann man keinen Block produzieren, der über den eigenen Slot hinausgeht. Man hat einen bestimmten Slot, wenn man einen Chunk produzieren muss. Man kann also höchstens einen Teil des letzten Blocks in der letzten Millisekunde des Slots produzieren, also eine Sekunde früher. Man kann es nicht wirklich weit nach vorne verschieben, da sonst alle anderen Nodes das Protokoll ebenfalls brechen würden. Das Protokoll ist also dahingehend begrenzt, dass die Entscheidung ob ein Block angenommen wird oder nicht begrenzt ist. Das würde bedeuten, dass die Prüfer im Grunde aufhören, das Protokoll zu befolgen. Sie müssen ihre Zeit koordinieren.

Bei Ablauf der Zeit ist also die auszuschüttende Belohnung ermittelt und der Farm Contract ruft take_my_near auf. Dabei übergibt er keine Argumente sondern nur die auszuzahlende Belohnung und GAS für die Netzwerkgebühren. Es wird nur eine minimale Menge an GAS benötigt, um die Guthaben zu addieren. So wurde der Berry Club aktualisiert, um nach jeder Zeichnung potenziell Belohnungen zu verteilen. Aber es braucht etwas mehr. Es wird GAS verwendet, das für die Zeichnung ausgegeben wurde, um die Belohnung von Zeit zu Zeit zu verteilen, wofür etwa 10 TERA GAS benötigt werden.

Berry Farm

Gehen wir zur Berry Farm und beginnen wir mit diesem Contract.  So wie die Berry Farm funktioniert, muss sie in der Lage sein, in konstanten Zeitabständen Belohnungen zu verteilen, die auf der Gesamtzahl der Gurken basieren, was keine triviale Aufgabe ist. Sie ähnelt ein wenig dem Staking Pool, und wie ein typischer Staking Pool Belohnungen verteilt. Der Staking-Pool erstellt eine Reihe von Anteilen für jede durchgeführte Einzahlung. Dabei hat der Anteil einen aktuellen globalen Preis, der sagt: “Hey, ich habe mehr Anteile gekauft oder geprägt, und jetzt können meine Anteile gegen eine bestimmte Anzahl von NEAR Token eingelöst werden.” Der Aktienpreis beginnt bei 1 yocto NEAR pro Aktie, und je mehr Rewards man ansammelt, desto mehr steigt der Preis der Anteile. Wenn Du unstake verwendest, verkaufst Du Anteile zum aktuellen Preis, und Du erhältst NEAR zurück, welches unstaked und abgerundet ist. Das Problem dabei ist, dass der Preis einer Gurke gleich Null ist, wenn man sie zum ersten Mal stakest, und dass man nicht wirklich Anteile kaufen und prägen kann, weil der Preis von Anteilen gleich Null ist. Die Logik von Anteilen funktioniert in diesem Fall nicht sehr gut, also musste ich für diese Sache etwas anderes entwickeln, um die Buchführung zu erledigen. Kommen wir zu den Beispielen. Nehmen wir an, Mike ist die erste Person, die 10 Gurken in den Contract eingezahlt hat, und dann werden 100 NEAR verteilt. Mike bekommt alle Belohnungen, weil er der Einzige ist, der jetzt 100% der Anteile an der Berry Farm kontrolliert. Mike war z.B. offline und hat sie nicht beansprucht, also können wir sein Konto nicht wirklich aktualisieren, da dies in konstanten Zeitabständen geschehen muss. Wir können nicht durch alle Konten gehen, aber wir müssen uns irgendwie merken, dass Mike alle NEAR für seine Gurken bekommen hat. Nehmen wir nun an, Josh kommt hinzu und setzt weitere 10 Gurken ein. Jetzt beträgt Mikes Anteil 50 % und Joshs Anteil 50 %, aber Josh hat die Gurken eingesetzt, nachdem die ursprünglichen 100 NEAR bereits verteilt wurden. Er hat kein Anrecht auf die 100 NEAR, die Mike jetzt bekommt, weil er 50% Anteil hat und Josh 50 NEAR bekommt. Wenn Mike wieder online geht, müssen wir in der Lage sein, seinen Kontostand mit 150 NEAR anzuzeigen, die er in dem konstanten Zeitabstand verdient hat. Das ist also die Herausforderung, die wir mit diesem Contract angehen mussten, und die Art und Weise, wie wir sie gelöst haben, ist, dass wir ein globales Größenverhältnis haben, das near per cucumber genannt wird und das angibt, wie viel NEAR ein bestimmtes Gurkenguthaben erhält, wenn ein Konto erstellt oder in irgendeiner Weise berührt wird. Wir merken uns den letzten Wert dieses Bruchteils.

Dann haben wir einen NEAR Guthaben, d.h. wenn der letzte Bruchteil niedriger war als der aktuelle Bruchteil, können wir die Gurken des betreffenden Kontos mit der Differenz zwischen zwei Brüchen multiplizieren und berechnen, wie viel NEAR diesem Konto zusteht. Als Mikes Konto angelegt wurde, war der Bruchteil 0 geteilt durch eins, und als 100 NEAR eingezahlt wurden und die Gesamtzahl der Gurken 10 betrug, wurde dieser Bruchteil gleich 10. Als dann Josh hinzu kam und weitere 10 Gurken zur Gesamtzahl hinzufügte und weitere 100 NEAR verteilt wurden, wurde dieser Bruch um 5 erhöht, weil es insgesamt 20 Gurken und 100 NEAR gab. 100 NEAR geteilt durch 20 bedeutet also 5 NEAR pro Gurke, also wurden es 15. Wenn Mike nun zurückkommt, sieht er sich seinen Gurken Guthaben an, der 10 Near pro Gurke beträgt, bei einem globalen Contract von 15. Sein zuletzt erinnerter Wert ist Null, also nimmt er einfach die Differenz, die 15 multipliziert mit 10 beträgt, und setzt diesen Bestand in NEAR um, und diese 1 wird wieder auf 15 gesetzt. Wenn Josh zurückkommt, erhält er diesen Wert, der bei 10 lag, und er hat 10 Gurken und der aktuelle Wert ist 15. Die Differenz ist 5 multipliziert mit dem Gurken Guthaben von 10, also bekommt er 50 NEAR. Auf diese Weise verwalten wir das NEAR-Guthaben und eignen uns die Belohnungen an, die wir von Leuten erhalten und verwenden dabei konstante Zeitabstände für die Neuberechnung. Anstatt also einen ganzen Bruchteil im Speicher zu halten, verwenden wir einen globalen Nenner, der feststeht, und ich habe ihn auf 10 hoch 18 gesetzt, was der Genauigkeit von Gurken, Bananen und Avocados entspricht. Das ist der Hintergrund, wie das alles berechnet wird.

Schauen wir uns die Hauptstruktur des Contracts an, einen Contract, der Farm genannt wird. Er hat eine Lookup-Map von Konten und verwendet Short Hashes, ähnlich wie fungible Token mit Vaults. Er enthält die Account-ID des Bananen-Tokens, die auch hardcodiert werden kann, aber wir haben sie aus dem Contract übernommen, weil wir sie tatsächlich weitergeben können, denn dieser Account wurde erst nach dem Deployment eingerichtet und ich wollte den Zustand des Banana Berry Club Contracts nicht aktualisieren. Ich wollte den Zustand nicht noch einmal durch die Aktualisierbarkeit ändern, also habe ich stattdessen den Wert im vorherigen Contract hartcodiert. NEAR pro Gurke, der Bruch, den ich vorhin erklärt habe, ist der Zähler und das Gesamt Gurken Guthaben wird als Nenner benötigt. Vaults und Next Vote werden für Transfers verwendet; sie wurden wahrscheinlich nie dazu benutzt, Gurken zu transferieren, außer vielleicht einmal für einen Test.

Dies ist das, was wir zuvor gesehen haben. Auch hier handelt es sich um einen Hash der Account-ID.

Die Account-Struktur ist der letzte Wert, bevor der Account mit diesem Teilwert in Berührung gekommen ist. Die near_balance ist das nicht beanspruchte Guthaben. Jedes Mal, wenn du ein Konto veränderst, indem du entweder mehr Gurken einzahlst oder NEAR beanspruchst, wird der letzte Wert aktualisiert und alle nicht beanspruchten NEAR aus der Differenz in dein lokales Guthaben verschoben, und cucumber balance ist dein Guthaben in Gurken. Der Wert near_claimed ist die Statistik, die nur angibt, wie viel Belohnung du bereits beansprucht hast. Es wird nicht für die eigentliche Contract-Logik benötigt.

Es gibt zwei Hilfskonstruktionen nur für das Frontend. HumanAccount gibt Werte in einer besser lesbaren Form zurück und HumanStats ist eine globale Statistik für den Verbrauch. Schauen wir uns die Funktion take_my_near an, um zu sehen, wie sie funktioniert.

Als erstes überprüfen wir, ob genügend Gurken vorhanden sind, da wir sie als Nenner verwenden. Wir wollen nur eine Gurke, um dies tatsächlich auszulösen, was nur eine Vorsichtsmaßnahme ist. Man möchte diese Art von Dingen nicht auslösen. Lasst uns diesen Wert untersuchen. Was passiert, ist, dass man den Betrag von NEAR und yocto NEAR erhält und ihn mit dem Nenner global multipliziert. Man löst also einen Bruch, der NEAR pro Gurke geteilt durch den Nenner plus angehängte Bilanz geteilt durch die Gesamtbilanz der Gurken ist. Hier ist die Formel: near_per_cucumber / Nenner + beigefügtes Guthaben / Gesamt Gurken Guthaben. Also ist new_near_per_cucumber eigentlich der Zähler mit attached_deposit und dem Nenner, der dann durch den Nenner geteilt wird. Hier ist die Formel: new_near_per_cucumber = (near_per-cucumber_numer + denom + attached deposit) / denom. Dies ist die Formel, mit der wir die new_near_per_cucumber berechnen. Immer wenn Du einen Kontostand veränderst, wird Dein aktuelles Guthaben in eine feste Zahl umgewandelt und Du kannst dann jeden der Werte ändern, ohne die Logik zu unterbrechen. Diese Touch-Methode wird every Getter und every Change-Method genannt. Bevor Du das Konto änderst, wird immer die Touch Funktion für das Konto angewandt.

In get_near_balance wird beispielsweise das interne Konto abgefragt. Wenn das Konto existiert, ändern wir es lokal. Dann sollten wir den near_balance erhalten.

Wenn Du das Konto erhältst, das wir vorher verändert haben, erhältst Du die near_balance, cucumber_balance und claim balance. Das sind Funktionen zur Veranschaulichung, die etwas verändern, aber es wird nicht gespeichert, so dass der Kontowert nur vorübergehend verändert wird und nicht zurückgespeichert wird. Es wird lediglich eine Veränderung vorgenommen, die eine Art Neuberechnung mit dem neuesten Wert darstellt.

Es wird gespeichert, wenn Du einen Änderungsaufruf tätigst, also zum Beispiel claim_near aufrufst. Wir erhalten Dein Konto und get_mut_account verändert das Konto tatsächlich. Du erhältst das Konto, das bereits aktualisiert wurde und dein Guthaben wird dir als near_balance angezeigt. Es setzt die near_balance auf Null und sagt, dass du das alles beansprucht hast, und es speichert diese Informationen in deinemKonto. Wenn Du dann einen positiven Wert beanspruchst, wird dieser von Deinem Konto an Dich überwiesen. Die Funktion gibt dabei zurück, wie viel NEAR Du beansprucht hast. Das Schöne an dieser Touch Method ist, dass sie an jedem beliebigen Punkt aufgerufen werden kann. Sie muss nicht an einem bestimmten Punkt aufgerufen werden, so dass sie immer das gleiche Ergebnis liefert, egal ob Du sie zweimal in der Mitte oder einmal mittendrin aufgerufen hast. Dies ist nur dazu da, dem Benutzer mitzuteilen, wie viel Guthaben er hat. Es sagt dir nur, wie viel NEAR du verdient hast, indem es den aktuellen Stand berechnet. Im Berry Club machen wir das Gleiche wie bei der Touch Funktion, und das ist auch nötig, denn sagen wir mal, du willst wissen, wie viele Bananen du gezüchtet hast. Da wir wissen, wann du dein Konto das letzte Mal verändert hast, beziehungsweise wann du zuletzt etwas gezüchtet hast, wissen wir, wie viele Pixel du hast. Wir können von einem bestimmten Zeitpunkt bis zum vorherigen Zeitpunkt multipliziert mit der Anzahl der Pixel, die Du hattest, berechnen, wie viele Bananen Du jetzt haben solltest, und das Frontend macht genau das Gleiche. Das Frontend simuliert es, es erhält dieselben Informationen, außer dem letzten Zeitpunkt, und erstellt dann einfach einen Timer, der vorwärts läuft und dies berechnet. Theoretisch muss die get_near_balance-Methode nicht den aktuellen NEAR-Wert zurückgeben, sondern kann einfach sagen: Hier sind die gesamten NEAR pro Gurke und die letzten gesamten NEAR pro Gurke und auch das letzte NEAR-Guthaben, aber stattdessen wird es auf der Ebene des Contracts gemacht. Angenommen, wir hätten keine Ansichtsmethoden, für die man nicht zahlt und nur einen Status zurückgibt und abruft, dann müsste man diese Logik am Frontend ausführen, anstatt sie hier im Speicher zu machen.

Token.rs

Die letzte Logik, die wir brauchen, ist die Art und Weise, wie Du deine Gurken aus dem Berry Club erhalten kannst. Dies geschieht durch die Verwendung von transfer_with_vault aus dem Berry Club. Wir haben bereits besprochen, wie die Map 122 funktioniert.

Du übergibst die receiver_id, den Betrag und die Payload. Es gibt einen assert_paid Check, der nur überprüft, ob die Einzahlung positiv ist. Es sagt, oh, du brauchst mindestens 1 yocto NEAR, um Aufrufe mit einem Funktionsschlüssel zu verhindern, damit eine bösartige Berry Club UI nicht all deine Bananen abzapft. Es erhält GAS und initiiert dann einen Aufruf, der Bananen in einen Vault legt. Es wird auch ein Anruf an den Empfänger getätigt. In diesem Fall nennen wir es einen On-Farm Contract und übergeben der Absender-ID eine Menge Bananen, eine Vault-ID, die nur eine u64 ohne JSON ist, und einen Payload, der ein String ist. Im vorangegangenen Beispiel, als wir über map 122 sprachen, war die Payload eine Binärdatei von vec q8 in base64, weil es eine borsh serialisierte Payload war. Während der Diskussion habe ich bemerkt, dass, wenn Du dieses Argument in einer Wallet überprüfen würdest, Du nicht in der Lage sein würdest, die Payload zu sehen, die Du staken willst, so dass wir in dieser Contract Implementierung tatsächlich JSON für den Payload verwenden, so dass wir die JSON aus der String-Payload einfügen.  Es ist ein Enum mit einer einzigen Option namens deposit_and_stake.

Wenn man nun eine Funktion von dem Farm User Interface aus aufruft, die an die Wallet in den Argumenten geht, wird die Nutzlast wie ein angrenzender String innerhalb eines Strings sein, der Einzahlung und Einsatz angibt, anstatt eine Null zu sein, die als Basis kodiert ist, anstatt eine borsh serialisierte Version zu sein, ist es eine menschlich lesbare. Dieser Contract hat andere Payload-Typen als Uniswap, z.B. hat er einen Payload, der angibt, wie viel NEAR man maximal oder mindestens erhalten möchte. Die bösartige Uniswap-Benutzeroberfläche kann diesen Wert einfach ersetzen, und wenn Du ihn nicht auf der Wallet-Seite verifiziert hast, kann es sein, dass deine Transaktion tatsächlich unter das beste Angebot fällt. Wir haben das Konto mithilfe von get_mut_account erhalten. Diesen kann entweder ein bestehendes Konto erhalten oder ein neues Konto erstellen, bei dem alle Guthaben auf 0 gesetzt werden, wobei der letzte Zähler für den Preis von NEAR zu Gurken der aktuelle globale ist. Das bedeutet, dass du nichts gefarmt hast und dein Guthaben für Gurken gleich Null ist. Es verändert sich trotzdem, auch wenn es nicht nötig ist, wir hätten es vermeiden können, wenn wir die Map gemacht hätten, und dann gibt es den Hash und das Konto zurück. Das Konto ist bereits auf dem neuesten Stand. Was wir tun, ist, dass wir zuerst das gesamte Gurken Guthaben erhöhen und es dann in den Zustand des Kontos zurückspeichern, und wir erhöhen auch das globale Gurken Guthaben um die Menge der eingezahlten Gurken. Dann überprüfen wir nicht wirklich, ob der Vault Gurken enthält, also vertrauen wir darauf, dass der vorherige Aufrufer, der ein Bananen-Contract ist, die Bananen tatsächlich in den Vault gelegt hat, und wir geben das Versprechen aus dieser Methode zurück. Es ist eine seltsame Übereinstimmung mit einem einzigen Eintrag, weil die Payload im Moment nur einen Typ haben kann. Wir rufen withdraw_from_vault auf und wir geben den Betrag an, den wir abheben wollen, nämlich den vollen Betrag, und dann rufen wir die Account-ID des Bananen-Tokens zurück, die ein Berry Club Contract ist. Dieser Contract hat zwar noch keine Bananen erhalten, aber das ist ihm egal, weil er sowieso keine Bananen braucht. Er braucht keine Bananen, um fortzufahren, also ist es egal, wie lange er dort Gurken staken wird, und der Vault ist bereits verschlossen. Es erhöht das Guthaben der Gurken insgesamt, indem es die Anteile verteilt. Wenn take_my_near nun durchgeführt wird, kann es das neue gesamte Gurken Guthaben verwenden. Jetzt werde ich die Anzahl der Anteile auf alle Teilnehmer aufteilen.

Fazit

Das ist der Überblick über die Verträge von Berry Club, Berry Farm und token.rs. Vielen Dank für das Durchlesen diesen Artikels und viel Glück bei Deinen zukünftigen Unternehmungen mit NEAR.

Generate comment with AI 2 nL
17

Kommentar verfassen


To leave a comment you should to:


Scroll to Top
Report a bug👀