O governo diz que não estamos em recessão, mas, ao mesmo tempo, ouvimos falar de inflação vertiginosa, aumento das taxas de juros e demissões em quase todos os setores da economia.
Apesar da criptografia e do TradFi serem os mais afetados, muitas empresas ainda estão construindo seus tokens, protocolos e produtos DeFi. És um deles?
Hoje, falarei sobre tipos de dados e aguarde. Tenho algo muito importante a dizer. Você pode me imaginar como um professor de mais de 60 anos do MIT torturando alunos com palestras sobre assuntos que não importam mais. Mas isso não é verdade.
Os tipos de dados ainda são importantes e negligenciá-los leva a sérias consequências. Vou tentar passar brevemente por todos os possíveis problemas e abordá-los para que você não ache os 8 minutos que gasta lendo este artigo desperdiçados.
Linguagens modernas como JavaScript e Python estão usando “digitação de pato” para determinar o tipo de variável. Se atribuirmos esse tipo de fórmula a = 2 + 2
a uma variável, o interpretador da linguagem saberá que está lidando com números e executará operações matemáticas sobre esse literal.
A tipificação do pato pode ser explicada por esta frase: “Se anda como um pato e grasna como um pato, então deve ser um pato”. Quando você olha mais de perto o significado disso - faz todo o sentido. Se literal tiver letras e números, deve ser uma string, e isso é claro. Mas e se tiver números?
É um boolean
, integer
, decimal
, float
ou date
. Na maioria dos casos, seu intérprete de linguagem lidará com os tipos da maneira correta. O problema começa quando seu programa precisa executar equações (mesmo simples) em números grandes.
“Se anda como um pato e grasna como um pato, então deve ser um pato”, certo? Na verdade não é.
Nos próximos parágrafos, estou me referindo às denominações comuns do Ethereum - wei
e gwei
. Deixe-me apresentá-los brevemente para que possamos falar a linguagem comum.
A menor denominação é 1 wei , e 1 éter equivale a 1.000.000.000.000.000.000 Wei (18 zeros) . Repito - 18 zeros. É difícil entender números tão grandes, mas eles fazem a diferença e importam muito.
A próxima denominação comum é 1 gwei . 1 éter é igual a 1.000.000.000 gwei (9 zeros) . Gwei é mais suportável para os humanos - no final, todo mundo quer ser milionário, certo? (pisca, pisca)
Vamos resumir - 1 éter é igual a:
Nota técnica: Ethereum tem duas camadas - a camada de execução e a camada de consenso. A camada de execução está usando wei para representar valores de éter, e a camada de consenso está usando gwei. Se você é um desenvolvedor de blockchain, precisa aprender a interagir com ambos.
Sou engenheiro de software na stakefish . Sou responsável por construir nossa paleta de produtos DeFi, e um dos mais recentes é nosso pool de dicas e MEV para Ethereum.
A partir de 15 de setembro de 2022, todos os validadores são elegíveis para dicas de transação e podem participar de MEVs para ganhar recompensas adicionais. Dicas de transação e MEVs são ganhos quando o validador propõe um novo bloco.
Decidimos criar um contrato inteligente que coleta todas as recompensas em um cofre comum e permite que o usuário reivindique sua parte dele. Não estou tentando anunciar nosso produto, mas preciso definir o contexto deste artigo.
Se você está mais interessado neste produto, pode ler mais aqui . Não estou vendendo nada além da minha experiência.
Como mencionei, temos um contrato inteligente que recebe dicas de transação e recompensas MEV ganhas pelos validadores. Isso significa que nosso contrato inteligente tem um saldo bastante grande. É 963+ ether agora (US$ 1,1 milhão) e temos 8671 validadores contribuindo para isso.
A parte crítica responsável pela sincronização entre a execução do Ethereum e a camada de consenso é o Oracle . É um sistema muito importante que nos permite determinar quais validadores estão contribuindo para o pool.
O oráculo é escrito em Python, mas poderia ser escrito em JavaScript - o problema permanece inalterado, e irei prová-lo em breve.
Vamos nos aprofundar no código!
O saldo do contrato inteligente é igual a 963.135.554.442.603.402.422 wei (963 ether) agora. Esse número não é apenas difícil de entender para humanos, mas também para computadores (intérpretes de linguagem para ser exato). Vamos verificar o JavaScript:
const vault_balance = parseInt("963135554442603402422") console.log(vault_balance) // 963135554442603400000 (lost 2422 wei in total)
Eu apenas converti o saldo de string
para int
, e já estou com 2422 wei
short. Ainda não executamos nenhuma equação.
O saldo do contrato inteligente é tão alto graças à contribuição de muitos validadores. Agora, vamos calcular qual é a participação média do validador no saldo do contrato agora:
const vault_balance = parseInt("963135554442603402422") const validator_count = 8671 const avg_validator_contribution = vault_balance / validator_count // 111075487768723730 (lost 7 wei per validator)
A participação média é de 0,111 éter. Mas esse valor não está correto - na verdade, temos 7 wei a menos. São 60,697 wei
no total (7 wei vezes 8.671 validadores). Vou mostrar o número correto mais tarde.
Continuando na toca do coelho das perdas - vamos calcular a quantidade total de recompensas por determinado validador. Lembre-se de que o usuário precisava depositar 32 ether para iniciar um validador, portanto, deduzirei do saldo do validador.
E vou tomar como exemplo um validador aleatório que contribui para o contrato inteligente que tem um saldo de 32.779 ether.
const vault_balance = parseInt("963135554442603402422") // (lost 2422 wei) const validator_count = 8671 const avg_validator_contribution = vault_balance / validator_count // (lost 7 wei) const initial_deposit = parseInt("32000000000000000000") const validator_balance = parseInt("32779333896000000000") const total_validator_rewards = validator_balance - initial_deposit + avg_validator_contribution // 890409383768723700 (lost 23 wei per validator)
O total de recompensas recebidas por este validador é igual a 0,8904 ether, mas esse valor também não é preciso. Neste momento, contamos incorretamente 199,443 wei
no total (23 wei vezes 8671 validador). Como você pode ver, essa forma de calcular números não é sustentável.
Existem dois problemas com o código acima:
2^53 - 1
apenas. Isso significa que pode lidar com até 9007199254740991 wei
(0,009 éter)
BigInt
, mas teríamos problemas com a divisão. Terminaríamos com valores “flutuantes”. As flutuações são a raiz de todos os males nas finanças porque são aproximadas. Isso significa que eles perdem a precisão. Precisamos usar decimais. (A principal diferença entre decimal e float é que decimal armazena o valor exato e float aproxima).
Se você já fez alguma codificação relacionada ao Ethereum em JavaScript, deve ter ouvido falar sobre ethers.js
. Esta biblioteca contém todos os utilitários necessários para interagir com o blockchain. Para corrigir o problema acima, usaremos uma das ferramentas chamada BigNumber
que suporta números extremamente grandes e lida com decimais da maneira correta.
Vamos fazê-lo!
const vault_balance = BigNumber.from("963135554442603402422") // no loss const validator_count = BigNumber.from(8671) const avg_validator_contribution = vault_balance.div(validator_count) // no loss // 111075487768723723 const initial_deposit = BigNumber.from("32000000000000000000") const validator_balance = BigNumber.from("32779333896000000000") const total_validator_rewards = validator_balance.sub(initial_deposit).add(avg_validator_contribution) // 890409383768723723
Como você pode ver, agora acabamos com o número exato. Como sei que este é realmente o número certo? Vou repetir o mesmo exercício em Python para provar que estou certo.
O Python suporta inteiros longos, portanto, os valores não serão cortados repentinamente, como vimos no JavaScript. Infelizmente, ele ainda determina todos os números flutuantes como float
s por padrão:
vault_balance = int("963135554442603402422") # no loss validator_count = 8671 avg_validator_contribution = vault_balance / validator_count # 111075487768723728 (5 wei too much) initial_deposit = int("32000000000000000000") validator_balance = int("32779333896000000000") total_validator_rewards = validator_balance - initial_deposit + avg_validator_contribution # 890409383768723712 (lost 11 wei)
Quer saber onde exatamente ele perdeu a precisão? A divisão avg_validator_contribution
para float
em vez de decimal
. O trecho correto ficaria assim:
vault_balance = Decimal("963135554442603402422") validator_count = Decimal(8671) avg_validator_contribution = vault_balance / validator_count # 111075487768723723 initial_deposit = Decimal("32000000000000000000") validator_balance = Decimal("32779333896000000000") total_validator_rewards = validator_balance - initial_deposit + avg_validator_contribution # 890409383768723723
Agora, os valores retornados por Python e JavaScript são exatos. Verifique você mesmo!
Esses tipos de perdas são marginais e podem ser facilmente ignorados. Freqüentemente, descobrimos sobre eles quando se acumulam ao longo do tempo e crescem para números significativos.
Situações como essa sempre dão dor de cabeça, não só aos desenvolvedores, mas também a outros departamentos, como financeiro ou jurídico. Você deve sempre testar suas fórmulas e nunca usar números redondos legais para fazer isso!
Significaria o mundo para mim se você me seguisse no Twitter . Estou focando minha atividade em engenharia de software e blockchain. Estou abrindo o código da maior parte do meu trabalho, então você pode querer verificar meu GitHub .