Introducción
Soy Eugene, y hoy vamos a hablar sobre el contrato de Berry Farm (Granja de Bayas), que es el complemento de Berry Club que le permite convertir sus bananas en pepinos y cultivar NEAR. Primero hagamos una revisión rápida de Berry Farm. El repositorio se puede encontrar en mi Github. Aquí está el video original en el que se basa esta guía:
UI
Berry farm es una aplicación muy bien diseñada con texto grande y muchos emojis. La forma en que funciona es que muestra sus saldos una vez que haya iniciado sesión. Lo extrae de dos contratos diferentes, así como de su cuenta. Los combina de múltiples fuentes. El aguacate y las bananas provienen del contrato de Berry Club, los pepinos son locales para ese contrato y NEAR proviene del saldo de su cuenta. Si usted formó algo, obtiene recompensas cuando éstas se distribuyen. Parece que alguien hizo muchas compras de aguacate recientemente y las recompensas en realidad aumentaron. También tiene estadísticas globales, lo que significa que se transfirieron 2500 NEAR de Berry Club a Berry Farm. Gané 116,9 NEAR y mi recompensa será 0,014332 NEAR según la cantidad de pepinos que tengo en comparación con la cantidad total de pepinos.
El número total de pepinos es 54116.4. Obtengo una participación del 3% de esta recompensa cada vez que hay una recompensa. Entonces, cuando hago clic en actualizar, básicamente actualizo las estadísticas de varias cuentas, y puedo reclamar NEAR el cual se transfiere desde el contrato a mi cuenta. También puedo cambiar plátanos por pepinos y estoy haciendo una llamada al contrato de Berry Club.
Voy a transfer_with_vault, el cual está usando el nuevo estándar, y requiere una pequeña cantidad de NEAR, y creo que es como 1 yocto NEAR para confirmar que no estoy haciendo esto a través de una clave de acceso. No puedo hacer una transferencia a través de una clave de acceso para evitar los retiros automáticos, y esto es sólo una medida de seguridad. Si, por ejemplo, le gustaría autorizar a Berry Club desde alguna otra aplicación que intente vaciar todos sus bananas, fallaría, porque no podrá llamar a transfer_raw o transfer_with_vault. Para aclarar, transfer_raw es el nuevo nombre para transferir sin contrato, por lo que sólo puedes depositar en la cuenta de alguien que ya existe, y en transfer_with_vault la bóveda llama al receptor para retirar tokens.
Gasté 10 bananas para obtener más pepinos, y puedo actualizar el saldo nuevamente para ver si alguien retiró algo. Así es como funciona la interfaz de usuario (IU), pero hay más funciones para esto y no solo esto. El contrato admite pepinos como token fungible, también bananas. Potencialmente, puede crear otra aplicación que transfiera pepinos y hay una más que es Banana Swap que construyó Vlad Garchina.
Es un uniswap simplificado con un solo pool donde se puede comprar y vender bananas. En realidad no lo he probado antes. Quiero comprar 1 NEAR a través de bananas para lo cual necesito vender 6 bananas. Veamos si funciona.
Este llama a las transferencias en Berry Club que vuelve a estar implicado, pero con otro contrato. Actualmente no mostramos argumentos, pero eventualmente lo haremos.
Dice que me lo transferí y obtuve 1 NEAR por lo que gasté 6 bananas. Está genial.
Usted no puede agregar liquidez y se necesita un recorte del 10%. Así que esta es la interfaz de usuario (IU).
Revisión del contrato
Berry Club (club de bayas)
Permíteme presentarte el contrato. Entonces, comenzaremos con el contrato de Banana Farm, pero probablemente deba echarle un vistazo al contrato del token de pixelboard, el contrato de Berry Club, así como el que envía tokens.
Berry club tiene un contrato codificado llamado farm contract Id (id del contrato de la granja).
Cuando dibuja, éste verifica que no dibuje píxeles vacíos; de lo contrario, puede activar una recompensa sin gastar aguacates.
Luego llama a maybe_send_reward, que obtiene la hora actual de la cadena de bloques y verifica que next_reward_timestamp sea menor que la hora actual.
La próxima recompensa es el comienzo del tiempo de cultivo, que es una marca de tiempo global que originalmente habilitaba una cuenta regresiva de 26 horas o last_reward_timestamp cuando la activamos por última vez más 60 segundos.
Entonces, si es hora de enviar la próxima recompensa, recuerda la hora actual y calcula la recompensa en función de esta lógica, por lo que toma el saldo actual de Berry Club, toma el almacenamiento actual, calcula el almacenamiento por el costo de almacenamiento más alguna barra de margen de seguridad, que es 50 NEAR.
Siempre mantiene 15 NEAR para los saldos futuros. Entonces está diciendo si el saldo está disponible, si nuestro saldo es mayor que la cantidad que necesitamos para el almacenamiento más la seguridad, entonces calculamos el saldo líquido y lo dividimos por la porción de recompensa que daremos cada vez que es 24 veces 68. Si usted lo llama cada minuto, agotará la mayor parte de la cuenta, pero siempre está disminuyendo por sí mismo, porque el saldo disminuye por sí mismo, pero en su mayoría da todo el saldo en un día si lo llama cada minuto. En primer lugar, no puede producir un bloque que tenga una marca de tiempo anterior, y luego no puede producir un bloque más allá de su ranura, tiene una ranura dedicada cuando necesite producir un fragmento. Entonces, lo máximo que puede hacer es producir una parte del bloque final en el último milisegundo de su ranura, que está un segundo por delante. Realmente no puede avanzar demasiado, de lo contrario, todos los demás nodos también romperán el protocolo, por lo que están limitados por su comprensión del tiempo más la barra que usan para decidir si aceptan un bloque o no, por lo que no puede realmente hacer que el tiempo corra más rápido a menos que todos los validadores quieran hacer esto. Esto significaría que tenemos los validadores que esencialmente dejarán de seguir el protocolo. Necesitan coordinar su tiempo.
Así que al final obtuvo la recompensa, dice que voy a distribuir esta recompensa, y lo que hace es llamar al contrato de la granja y llamar a take_my_near y simplemente no pasa ningún argumento y pasa su recompensa y gas. Solo se necesita una cantidad mínima de gas para agregar los saldos. Así fue como se actualizó Berry Club para comenzar a distribuir recompensas potencialmente después de cada sorteo. Se necesita un poco más. Utiliza el gas que se gastó en dibujar para distribuir la recompensa de vez en cuando, lo que necesita alrededor de 10 tera gas para esto.
Berry Farm – Granja de bayas
Vayamos a Berry Farm y comencemos con este contrato. La forma en que funciona la granja de bayas necesita, en tiempo constante, poder distribuir recompensas en función del número total de pepinos, lo cual no es una tarea trivial, es algo similar al grupo de participación y cómo el grupo de participación distribuye las recompensas. El Staking pool (grupo de participación) crea un montón de shares o acciones por cada depósito que realiza y la acción tiene un precio global actual que dice: “Oye, compré o acuñé más shares o acciones, y ahora mis acciones se pueden canjear por una cantidad determinada de tokens NEAR”. El precio de las acciones comienza desde 1 yocto NEAR por acción, y luego, cuantas más recompensas acumule, más sube el precio de las acciones. Cuando usa la opción de no participación (unstake), vende las acciones al precio actual, y obtiene NEAR al cambio, y está unstaked (no apostada, sin participación), y se redondea hacia abajo. El desafío allí es que cuando usted hace staking o apuesta pepinos, el precio de un pepino es cero, y cuando hace staking o apuesta por primera vez, realmente no puede comprar shares o acciones y mint shares, porque el precio de ellas es cero. La lógica de las shares o acciones no funciona muy bien para esto, así que yo necesitaba crear algo diferente para hacer la contabilidad. Vayamos a los ejemplos. Digamos que Mike es la primera persona que depositó 10 pepinos en el contrato, y luego digamos que se distribuyeron 100 NEAR. Mike se llevará todas las recompensas porque es el único, ahora controla el 100% de la participación en Berry Farm. Mike estaba desconectado, por ejemplo, y no los reclamó, por lo que realmente no podemos actualizar su cuenta, porque tiene que hacerse en tiempo constante. No podemos iterar a través de todas las cuentas, pero de alguna manera debemos recordar que Mike obtuvo todo en NEAR por sus pepinos. Ahora digamos que Josh entra y hace staking o apuesta otros 10 pepinos ahora la participación de Mike es del 50 % y la participación de Josh es del 50 %, pero Josh hizo staking o apostó los pepinos después de que ya se distribuyeron estos 100 NEAR iniciales. No tiene derecho a nada de esto ahora, otros 100 NEAR caen y Mike ahora necesita obtener 50 más, porque tiene una participación del 50% y Josh obtiene 50 NEAR. Cuando Mike vuelva a estar en línea, debemos poder mostrar su saldo en 150 NEAR que ganó en tiempo constante. Entonces, este es el desafío que necesitábamos abordar con este contrato, y la forma en que se resolvió es tener una relación global de la llamada NEAR por pepinos que dice cuánto NEAR recibe un saldo por pepino dado cuando se crea una cuenta, o tocado de ninguna manera. Recordamos el último valor de esta fracción.
Entonces tenemos un saldo en NEAR, por lo que si la última fracción fue menor que la fracción actual, significa que podemos multiplicar los pepinos de la cuenta dada por la diferencia entre 2 fracciones, y calcular a cuántos NEAR tengo derecho. Cuando se creó la cuenta de Mike, la fracción era 0 dividida por uno y cuando se depositaron 100 NEAR donde el recuento total de pepinos era 10, esta fracción se volvió igual a 10. Luego, una vez que Josh se unió y dejó caer otros 10 pepinos al total, y otros 100 NEAR se repartieron, esta fracción se incrementó en 5, debido a que había un total de 20 pepinos y 100 NEAR. Entonces, 100 NEAR dividido por 20 significa 5 NEAR por pepino, por lo que se convirtió en 15. Ahora, cuando Mike regresa, mira su saldo de pepino, que es 10 NEAR por pepino en un contrato global, es 15, su último valor recordado es cero, así que solo toma la diferencia que es 15 multiplicada por 10 y reclama este saldo en NEAR, y este 1 vuelve a 15. Cuando Josh regresa, obtiene este valor que era 10, y tiene 10 pepinos y el valor actual es 15. La diferencia es 5 multiplicado por el saldo de pepino de 10 por lo que obtiene 50 NEAR. Así es cómo administramos el saldo NEAR y nos apropiamos de las recompensas recibidas de las personas que usan tiempo constante para recalcularlo. Entonces, en lugar de mantener una fracción completa en la memoria, usamos un denominador global que es fijo y lo usé a 10 elevado a 18, que es la misma precisión que los pepinos, bananas y aguacates. Ese es el trasfondo de cómo se calcula todo.
Veamos la estructura principal del contrato, un contrato llamado farm (granja). Tiene un mapa de búsqueda de cuentas y utiliza hashes cortos similares a tokens fungibles con vaults o bóvedas. Tiene la identificación de la cuenta del token banana que también se puede codificar, pero se usó desde el contrato porque en realidad podemos pasarlo, porque esta cuenta se implementó después y no quería actualizar el estado del contrato de banana Berry Club. No quería volver a modificar el estado a través de la capacidad de actualización, así que codifiqué el valor en el contrato anterior. NEAR por pepino, la fracción que expliqué anteriormente es el numerador y se necesita el saldo total de pepinos como denominador. Las bóvedas y el próximo voto se utilizan para transferencias; en realidad, probablemente nunca terminaron acostumbrándose a transferir pepinos, excepto tal vez una vez para una prueba.
Esto es lo que vimos antes, esto es una vez más un hash de la identificación de la cuenta.
La estructura de la cuenta es el último valor antes de que la cuenta fuera tocada con esta fracción. El near_balance es el saldo no reclamado. Cada vez que usted toca una cuenta, ya sea depositando más pepinos o reclamando NEAR, este actualiza el último valor y mueve todo el NEAR no reclamado a su saldo local fuera de la diferencia, y el saldo de pepinos es su saldo en pepinos. Near_claimed son las estadísticas, es decir, la cantidad de recompensas que ya usted la reclamó. No es necesario para la lógica del contrato.
Hay dos estructuras auxiliares solo para la parte delantera. HumanAccount devuelve valores de una forma más legible y HumanStats son estadísticas globales de consumo. Vayamos a la función take_my_near para ver cómo funciona.
Lo primero que hacemos es verificar que haya suficientes pepinos allí, porque lo usamos como denominador. Solo queremos que 1 pepino desencadene esto, y esto es solo una precaución. A usted no le gustaría desencadenar este tipo de cosas. Exploremos este valor. Qué sucede si usted obtiene la cantidad de NEAR, y yocto NEAR, y la multiplica por el denominador global. Por lo que está resolviendo una fracción que es NEAR por pepino dividida por el denominador más el saldo adjunto dividido por el saldo total de pepino. Esta es la fórmula: near_per_pepino/denom+attached_deposit/total_pepino_balance. Entonces new_near_per_pepino es en realidad el numerador con el attached_deposit y el denominador luego dividido por el denominador. Esta es la fórmula: new_near_per_pepino = (near_per-pepino_numer +denom + attached_deosit) / denom. Esta es la fórmula que usamos para calcular new_near_per_pepino. Cada vez que toca una cuenta, siempre convierte su saldo actual no reclamado en un número fijo, y luego puede modificar cualquiera de los valores sin romper la lógica. Este método táctil es llamado por cada getter y cada método de cambio. Antes de modificar la cuenta, siempre llama touch on the account (toque en la cuenta).
Por ejemplo, en get_near_balance obtenemos la cuenta interna. Si la cuenta existe, la tocamos localmente. Entonces deberíamos obtener el near_balance
Si obtiene la cuenta, la tocamos antes, obtenga su near_balance, cucumber_balance y claim balance o saldo reclamado. Esas son funciones de vista que están cambiando cosas, pero no lo guarda, por lo que solo cambia temporalmente el valor de la cuenta y no lo vuelve a guardar. Simplemente hace un toque que es una especie de cálculo con el valor más reciente.
Lo guarda cuando realmente realiza una llamada de cambio, por ejemplo, llame a Claim_near. Obtenemos su cuenta y get_mut_account realmente toca la cuenta. Obtiene la cuenta que ya está actualizada, y near_balance es el saldo real. Este lleva el near_blance a cero, dice que reclamó todo esto y guarda su cuenta. Luego, si reclama un valor positivo, se lo transferiremos de su cuenta. Devuelve cuánto NEAR reclamó. La belleza de este método tocar o “touch” es que se puede llamar en cualquier momento. No tiene que llamarse en el punto dado, por lo que siempre le dará el mismo resultado sin importar si lo llamó dos veces en el medio o lo llamó una vez. Esto es solo para devolverle al usuario cuánto tiene. Simplemente le dice cuánto NEAR ganó al calcular desde el estado actual. En Berry Club también hacemos las mismas cosas, tenemos “touch”, y esto es necesario, porque digamos que quiere saber cuántas bananas cultivó. Como sabemos la última vez que tocó su cuenta, la última vez que dibujó algo, sabemos cuántos píxeles tiene. Podemos calcular desde un momento dado hasta el momento anterior multiplicado por la cantidad de píxeles que tenía, cuántas bananas debería tener en este momento, y la parte frontal hace lo mismo. El front-end lo simula, obtiene la misma información excepto la más reciente, y luego simplemente crea un temporizador que lo calcula. En teoría, el método get_near_balance no tiene que devolver su valor de NEAR actualizado, solo puede decir aquí están los NEAR totales por pepino y su último NEAR total por pepino y también su último saldo NEAR, pero en su lugar esto se hace en el nivel de contrato. Digamos que no tenemos métodos de visualización por los que no paga y solo devuelve y recupera un estado, entonces tendría que hacer esta lógica en el front-end en lugar de hacerlo aquí en la memoria.
Token.rs
La lógica final que debemos hacer es cómo recibe sus pepinos de Berry Club, y esto se hace usando transfer_with_vault de Berry Club. Anteriormente discutimos cómo funciona el mapa 122.
Pasas el receiver_id, amount y payload. Esto tiene un chequeo assert_paid que solo verifica que el depósito sea positivo. Está diciendo, oh, necesita al menos 1 yocto NEAR para evitar llamadas de teclas de acceso a funciones para evitar que una interfaz de usuario maliciosa de Berry Club agote todas sus bananas. Obtiene el gas y luego inicia una llamada que pone bananas en una vault o bóveda. También inicia una llamada al receptor. En este caso, lo llamamos un on-farm contract o contrato en la granja y pasa la identificación del remitente una cantidad de bananas, una identificación de bóveda que es solo un u64 sin JSON y una carga útil que es una cadena. En el ejemplo anterior, cuando hablábamos del mapa 122, la carga útil era un binario de vec q8 en base64, porque era una carga útil serializada borsh. Durante la discusión, noté que si revisa este argumento en una billetera, no podrá ver la carga útil que deseas hacer, por lo que en la implementación de este contrato, en realidad usamos JSON para la carga útil, de modo que insertamos el JSON desde el carga útil de cadena. Es una enumeración con una sola opción llamada deposit_and_stake.
Ahora, cuando realiza una llamada de función desde la interfaz de usuario de la granja que irá a la billetera en los argumentos, la carga útil será como una cadena adyacente dentro de una cadena que dice depósito y apuestas en lugar de ser un cero codificado como base, en lugar de ser un borsh versión serializada, es legible por humanos. Este contrato tiene diferentes tipos de carga útil que Uniswap, por ejemplo, tendrá una carga útil que indica cuánto NEAR deseas recibir como máximo o al menos. La interfaz de usuario maliciosa de Uniswap puede reemplazar este valor, y si no lo verificaste en el lado de la billetera, es posible que se caiga por debajo de la tendencia principal. Obtuvimos la cuenta usando get_mut_account, que obtiene una cuenta existente o crea una nueva cuenta donde establece todos los saldos en 0, pero el último numerador cercano por pepino es el global actual. Esto significa que no cultivaste nada y tienes un saldo de pepino cero. Se toca de todos modos, incluso si no es necesario, podríamos haberlo evitado si hubiéramos mapeado, y luego devuelve el hash y la cuenta. La cuenta ya está actualizada. Lo que hacemos es primero aumentar el saldo total de pepinos y luego lo guardamos en el estado de la cuenta y también aumentamos el saldo global de pepinos por la cantidad de pepinos depositados. Luego, no verificamos realmente que la bóveda contenga pepinos, por lo que confiamos en que la persona que llamó anteriormente, que es un contrato de bananas, realmente puso las bananas en la bóveda, y devolvemos la promesa con este método. Es una combinación extraña con una sola entrada porque la carga útil sólo puede ser de un tipo en este momento. Llamamos a retirar_de_vault, indicamos la cantidad que queremos retirar, que es la cantidad total, y luego la llamamos de vuelta a la identificación de la cuenta del token de banana, que es un contrato de Berry Club. Aun así, este contrato aún no recibió bananas, no le importa porque de todos modos no usa bananas. Este no necesita bananas antes de que pueda continuar, así que no importa cuánto tiempo vaya a poner pepinos allí, y la bóveda ya está cerrada. Aumenta el saldo total de pepinos al diluir las acciones. Ahora, cuando llegue take_my_near, utilizará el nuevo saldo total de pepino. Ahora dividiré el número de acciones entre todos los participantes.
Conclusión
Esa es la descripción general de los contratos de Berry Club, Berry Farm y token.rs. Gracias por leer y buena suerte en sus futuros proyectos con NEAR.