mirror of
https://github.com/adambard/learnxinyminutes-docs.git
synced 2024-12-28 17:59:05 +03:00
914 lines
29 KiB
Markdown
914 lines
29 KiB
Markdown
|
---
|
|||
|
language: Solidity
|
|||
|
filename: learnSolidity.sol
|
|||
|
contribuidors:
|
|||
|
- ["Nemil Dalal", "https://www.nemil.com"]
|
|||
|
- ["Joseph Chow", ""]
|
|||
|
translators:
|
|||
|
- ["João Farias", "http://thatsabug.com/"]
|
|||
|
---
|
|||
|
|
|||
|
Solidity permite você programar para a [Ethereum]
|
|||
|
(https://www.ethereum.org/), uma máquina virtual baseada na tecnologia blockhain
|
|||
|
para criação e execução de contratos inteligentes, sem necessidade de partes
|
|||
|
centralizadas ou de confiança.
|
|||
|
|
|||
|
Solidity é uma linguagem de contratos estaticamente tipaada com similaridade com
|
|||
|
Javascript e C. Como objetos em programação orientada a objetos, cada contrato
|
|||
|
possue variáveis de estado, funções e tipos de dados comuns. Funcionalidades
|
|||
|
particulares de contratados incluem cláusuras modificadoras (guarda), notifica
|
|||
|
dores de eventos para listerners e variáveis globais customizadas.
|
|||
|
|
|||
|
|
|||
|
Exemplos de contratos Ethereum incluem crowdfunding, votações e audições cegas.
|
|||
|
|
|||
|
Erros em código Solidity causam grandes riscos e custos; portanto, você
|
|||
|
deve ser muito cuidado com teste e divulgação. DEVIDO ÀS CONSTANTES MUDANÇAS
|
|||
|
NO ETHEREUM, ESTE DOCUMENTOS PROVAVELMENTE NÃO ESTARÁ ATUALIZADO, VOCÊ DEVE
|
|||
|
ACOMPANHAR A CHATROOM DO SOLIDITY E O BLOG DO ETHEREUM PARA NOTÍCIAS ATUALIZADAS.
|
|||
|
TODO CÓDIGO AQUI É ENTREGUE COMO ESTÁ, COM SUBSTANCIAL RISCO DE ERRROS E PADRÕES
|
|||
|
DE CÓDIGO DEPRECADOS.
|
|||
|
|
|||
|
Diferentemente de outros tipo de código, você também deve adicionar padrões
|
|||
|
como pausa, deprecação e retração para reduzir riscos. Este documento discute
|
|||
|
sintaxe, então, muito padrões populares são excluídos.
|
|||
|
|
|||
|
Como Solidity e Ethereum ainda estão sob desenvolvimento, funcionalidades beta
|
|||
|
e experimentais são tipicamente marcadas e sujeitas à mudanças. Pull requests
|
|||
|
são bem-vindos.
|
|||
|
|
|||
|
```javascript
|
|||
|
// Primeiramente, um contrato de um Banco simples
|
|||
|
// Permite depósitos, retiradas e checagens de saldo
|
|||
|
|
|||
|
// banco_simples.sol (note a extensão .sol)
|
|||
|
|
|||
|
/* **** INCICIO DO EXEMPLO **** */
|
|||
|
|
|||
|
// Declare a versão do compilador.
|
|||
|
pragma solidity ^0.4.2;
|
|||
|
|
|||
|
// Inicie com comentários Natspec (as três barras)
|
|||
|
// usados para documentação - e como dados descritivos para elementos/ação de UI
|
|||
|
|
|||
|
/// @title BancoSimples
|
|||
|
/// @author nemild
|
|||
|
|
|||
|
/* 'contrato' tem similadirades com 'classes' em outras linguagens (variáveis de
|
|||
|
class, herança, etc.) */
|
|||
|
|
|||
|
contract BancoSimples { // CamelCase
|
|||
|
// Declare variáveis de estado fora da função, para persistí-la durante a
|
|||
|
// duração do contrato
|
|||
|
|
|||
|
// dicionário que mapeia endereços para saldos
|
|||
|
// tenha cuidado sobre ataques de overflow com números
|
|||
|
|
|||
|
mapping (address => uint) private saldos;
|
|||
|
|
|||
|
// "private" significa que outros contratos não podem acessar saldos
|
|||
|
// diretamente, mas o dado ainda é visível para outras partes da blockchain
|
|||
|
|
|||
|
address public dono;
|
|||
|
|
|||
|
// ´public´ é legível (mas sem acesso de escrita) por usuários e contratos
|
|||
|
|
|||
|
// Eventos - ações públicas para ouvintes externo
|
|||
|
event LogRealizacaoDeDeposito(address numeroDaConta, uint quantidade);
|
|||
|
|
|||
|
// Construtor, pode receber uma ou várias variáveis; apenas uma opção é
|
|||
|
// permitidas
|
|||
|
|
|||
|
function BancoSimples() {
|
|||
|
// msg dá detalhes sobre a mensagem mandada pelo contrato
|
|||
|
// msg.sender é um chamador do contrato (endereço do criador do
|
|||
|
// contrato)
|
|||
|
|
|||
|
dono = msg.sender;
|
|||
|
}
|
|||
|
|
|||
|
/// @notice Deposita ether no banco
|
|||
|
/// @return O saldo do usuário após o depósito
|
|||
|
|
|||
|
function deposito() public returns (uint) {
|
|||
|
saldos[msg.sender] += msg.value;
|
|||
|
|
|||
|
// Sem necessidade de "this." ou "self." para variáveis de estado
|
|||
|
// todos as variáveis são inciadas com seu valor default
|
|||
|
|
|||
|
LogRealizacaoDeDeposito(msg.sender, msg.value); // dispara evento
|
|||
|
|
|||
|
return saldos[msg.sender];
|
|||
|
}
|
|||
|
|
|||
|
/// @notice Retira ether do banco
|
|||
|
/// @dev Isto não retorna nenhum ether excendente
|
|||
|
/// @param quantidadeDeRetirada quantidade que você quer retirar
|
|||
|
/// @return O saldo restante do usuário
|
|||
|
function retirada(uint quantidadeDeRetirada) public returns (uint saldoRestate) {
|
|||
|
if(saldos[msg.sender] >= quantidadeDeRetirada) {
|
|||
|
|
|||
|
// Observe como deduzimos o saldo imediatamente, antes de enviar -
|
|||
|
// devido ao risco de uma chamada recursiva que permite o chamador
|
|||
|
// pedir um valor maior que seu saldo
|
|||
|
|
|||
|
saldos[msg.sender] -= quantidadeDeRetirada;
|
|||
|
|
|||
|
if (!msg.sender.send(quantidadeDeRetirada)) {
|
|||
|
// incremente de volta só se falhar, como pode estar enviando
|
|||
|
// para o contrato que substituiu 'enviar' no final
|
|||
|
// do recebimento
|
|||
|
saldos[msg.sender] += quantidadeDeRetirada;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return saldos[msg.sender];
|
|||
|
}
|
|||
|
|
|||
|
/// @notice Retorna o saldo
|
|||
|
/// @return O saldo do usuário
|
|||
|
// 'constant' evita que a função edite variáveis de estado
|
|||
|
// permite a função executar localmente/fora da blockchain
|
|||
|
function saldo() constant returns (uint) {
|
|||
|
return saldos[msg.sender];
|
|||
|
}
|
|||
|
|
|||
|
// Função de fallback - Chamada se outras funções não forem chamadas ou
|
|||
|
// se ether sem dados forem enviados
|
|||
|
// Tipicamente chamada quando dados inválidos são enviados
|
|||
|
// Adicionada para que ether enviado neste contrato seja revertido se o
|
|||
|
// contrato falhar. Se não existisse, o dinheiro do enviante é transferido
|
|||
|
// para o contrato
|
|||
|
function () {
|
|||
|
throw; // 'throw' reverte o estao para antes da chamada
|
|||
|
}
|
|||
|
}
|
|||
|
// ** FIM DO EXEMPLO **
|
|||
|
|
|||
|
// Agora, o básico de Solidity
|
|||
|
|
|||
|
|
|||
|
//1 TIPO DE DADOS E MÉTODOS ASSOCIADOS
|
|||
|
// uint é usado para quantidade de moeda (não existem doubles ou floats)
|
|||
|
// e para datas (no sistema de tempo Unix)
|
|||
|
|
|||
|
uint x;
|
|||
|
|
|||
|
// int de 256 bits, não pode ser mudado após sua instanciação
|
|||
|
int constant a = 8;
|
|||
|
int256 constant a = 8; // mesmo efeito, mas aqui os 256 bits são explícitos
|
|||
|
uint constant VERSÃO_ID = 0x123A1; // uma constante hexadecimal
|
|||
|
|
|||
|
// com 'constant', o compilador substitui cada ocorrência com o valor
|
|||
|
|
|||
|
// Para int e uint, é possível determinar o espaço explicitamente, em intervalos
|
|||
|
// de 8 a 256, e.g., int8, int16, int24
|
|||
|
uint8 b;
|
|||
|
int64 c;
|
|||
|
uint248 e;
|
|||
|
|
|||
|
// Cuidado contra overflows, e proteja-se contra esse tipo de ataque
|
|||
|
|
|||
|
// Não há funções randômicas padrão, use outros contratos para este objetivo
|
|||
|
|
|||
|
// Casting de tipo
|
|||
|
int x = int(b);
|
|||
|
|
|||
|
bool b = true; // ou então 'var b = true;' para inferição de tipo
|
|||
|
|
|||
|
// Endereços - comportam 20 bytes/160 bits endereços Ethereum
|
|||
|
// Não permite operações aritiméticas
|
|||
|
address public dono;
|
|||
|
|
|||
|
// Tipos de contas:
|
|||
|
// Conta de contrato: endereço criado ao criar (função do endereço do criador,
|
|||
|
// número da transação)
|
|||
|
// Conta externa: (pessoa/entidade externa): endereç criado a partir de chave
|
|||
|
// pública
|
|||
|
|
|||
|
// Adicione o campo 'public' para indicar visibilidade pública/externa
|
|||
|
// um getter é automaticamente criado, mas NÃO um setter
|
|||
|
|
|||
|
// Todos os endereços podem enviar ether
|
|||
|
dono.send(ALGUM_SALDO); // returna falso caso falhe
|
|||
|
if (dono.send) {} // LEMBRE-SE: encapsule num 'if', dado que endereços de
|
|||
|
// contrato tem funções executadas no envio e estas podem falhar
|
|||
|
//Também certifique-se que os saldos deduzidos ANTES de tentar enviar, dado que
|
|||
|
// há um risco de chamada recursiva que pode drenar um contrato
|
|||
|
|
|||
|
// pode sobrescrever seu próprio
|
|||
|
|
|||
|
// Pode verificar o saldo
|
|||
|
dona.balance; // o saldo do dono (usuário ou contrato)
|
|||
|
|
|||
|
// Bytes permitidos de 1 a 32
|
|||
|
byte a; // byte é o mesmo que bytes1
|
|||
|
bytes2 b;
|
|||
|
bytes32 c;
|
|||
|
|
|||
|
// Bytes dinamicamente criados
|
|||
|
|
|||
|
bytes m; // Um array especial, mesmo que byte[] (mas mais comprimido)
|
|||
|
|
|||
|
// Mais custoso que byte1-byte32, então, prefira estes quando possível
|
|||
|
|
|||
|
// mesmo que bytes, mas não permite verificar tamanho ou acesso por indíce (por
|
|||
|
// enquanto)
|
|||
|
|
|||
|
string n = "oi"; // guardado em UTF8, note as aspas duplas, não simples
|
|||
|
|
|||
|
// funções de string serão adicionadas no futuro
|
|||
|
// prefira bytes32/bytes, dado que UTF8 usa mais espaço
|
|||
|
|
|||
|
// Inferência de tipo
|
|||
|
// var não infere tipos baseados na primeira atribuição,
|
|||
|
// não pode ser usado em paramêtros de funções
|
|||
|
|
|||
|
var a = true;
|
|||
|
|
|||
|
// use com cuidado, inferência pode resultar em tipos errados
|
|||
|
// e.g., um int8, quando um contador precisa de int16
|
|||
|
|
|||
|
// var pode ser usado para assinalar uma função a uma variável
|
|||
|
function a(uint x) returns (uint) {
|
|||
|
return x * 2;
|
|||
|
}
|
|||
|
var f = a;
|
|||
|
f(22); // chamada
|
|||
|
|
|||
|
// por padrão, todos os valores são inicializados com 0
|
|||
|
|
|||
|
// Delete pode ser chamada na maioria dos tipos
|
|||
|
// (NÃO destroi o valor, mas retorna para o valor 0, o incial)
|
|||
|
|
|||
|
uint x = 5;
|
|||
|
|
|||
|
// Desestruturação/Tuplas
|
|||
|
(x, y) = (2, 7); // assinada/troca múltiplos valores
|
|||
|
|
|||
|
// 2. ESTRUTURAS DE DADOS
|
|||
|
// Arrays
|
|||
|
|
|||
|
bytes32[5] apelidos; // array estático
|
|||
|
bytes32[] nomes; // array dinâmico
|
|||
|
uint novoTamanho = nomes.push("João"); // adicionando retorna o novo tamanho do
|
|||
|
|
|||
|
// Tamanho
|
|||
|
nomes.length; // pega o tamanho
|
|||
|
nomes.length = 1; // tamanhos pode ser alterados (para arrays dinâmicos)
|
|||
|
|
|||
|
// arrays multidimensionais
|
|||
|
uint x[][5]; // array com 5 arrays dinâmicos como elementos (ordem da maioria
|
|||
|
// das linguagens)
|
|||
|
|
|||
|
// Dicionários (qualquer tipo para qualquer tipo)
|
|||
|
mapping (string => uint) public saldos;
|
|||
|
saldos["charles"] = 1;
|
|||
|
console.log(saldos["ada"]); // é 0, toda chave não assinalada retorna zero
|
|||
|
// 'public' permite o seguinte contrato
|
|||
|
nomeDoContrato.saldos("charles"); // retorna 1
|
|||
|
// 'public' cria um getter (mas não um setter) como o seguinte
|
|||
|
function saldos(string _conta) returns (uint saldo) {
|
|||
|
return saldos[_conta];
|
|||
|
}
|
|||
|
|
|||
|
// Mapeamentos aninhados
|
|||
|
mapping (endereco => mapping (endereco => uint)) public guardioes;
|
|||
|
|
|||
|
// Para deletar
|
|||
|
delete saldos["John"];
|
|||
|
delete saldos; // assinala zero para todas as chaves
|
|||
|
|
|||
|
// Diferentemente de outras linguages, NÃO É POSSÍVEL iterar sobre todos os
|
|||
|
// elementos de um mapeamento, sem saber previamente as chaves - é possível
|
|||
|
// construir estruturas de dados personalizadas para fazer isso
|
|||
|
|
|||
|
// Structs e enums
|
|||
|
struct Banco {
|
|||
|
address dono;
|
|||
|
uint saldo;
|
|||
|
}
|
|||
|
Banco b = Banco({
|
|||
|
dono: msg.sender,
|
|||
|
saldo: 5
|
|||
|
});
|
|||
|
// ou
|
|||
|
Banco c = Banco(msg.sender, 5);
|
|||
|
|
|||
|
c.quantidade = 5; // cria novo valor
|
|||
|
delete b;
|
|||
|
// assinala todos os valores do enum para zero, exceto mapeamentos
|
|||
|
|
|||
|
// Enums
|
|||
|
enum Estado { Criado, Travado, Inativo }; // geralmente usado para máquina de
|
|||
|
// estados
|
|||
|
Estado public estado; // Declara variável para enum
|
|||
|
estado = Estado.Criado;
|
|||
|
// enums podem ser explicitamente convertidas em ints
|
|||
|
uint estadoCriado = uint(Estado.Criado); // 0
|
|||
|
|
|||
|
// Localização de dados: Memória vs. disco vs. pilha - todos os tipos complexos
|
|||
|
// (arrays, structs) tem uma localização de dados
|
|||
|
// 'memória' não é persistida, 'disco' é
|
|||
|
// Padrão é 'disco' para variáveis locais e de estado; 'memória' para paramêtros
|
|||
|
// de função. Pilha guarda pequena variáveis locais
|
|||
|
|
|||
|
// a maioria dos tipos podem ter sua localização de dados explicitamente assinalos
|
|||
|
|
|||
|
// 3. Operações simples
|
|||
|
// Comparações, operadores binários e aritimétricos são providos
|
|||
|
// exponenciação: **
|
|||
|
// ou exclusivo: ^
|
|||
|
// negação binária: ~
|
|||
|
|
|||
|
// 4. Variáveis Globais de nota
|
|||
|
// ** this **
|
|||
|
this; // endereço do contrato
|
|||
|
// geralmente usado no final do contrato para enviar o saldo restante às partes
|
|||
|
this.balance;
|
|||
|
this.algumFuncao(); // chamada de função externa via call, não via jump interno
|
|||
|
|
|||
|
// ** msg - Mensagem corrente recebida pelo contrato ** **
|
|||
|
msg.sender; // endereço do enviador
|
|||
|
msg.value; // quantidade de ether provida para este contrato em wei
|
|||
|
msg.data; // bytes, todos os dados da chamada
|
|||
|
msg.gas; // gas restante
|
|||
|
|
|||
|
// ** tx - Esta transação **
|
|||
|
tx.origin; // endereço do enviador da transação
|
|||
|
tx.gasprice; // valor do gas da transação
|
|||
|
|
|||
|
// ** block - Informação do bloco corrente **
|
|||
|
now; // tempo corrente (aproxiamdo), substituto para block.timestamp
|
|||
|
//(usa tempo do Unix)
|
|||
|
block.number; // número do bloco corrente
|
|||
|
block.difficulty; // dificuldade do bloco corrente
|
|||
|
block.blockhash(1); // retorna bytes32, só funciona para os 256 blocos mais
|
|||
|
//recentes
|
|||
|
block.gasLimit();
|
|||
|
|
|||
|
// ** storage - Hash de disco persistente **
|
|||
|
storage['abc'] = 'def'; // mapea palavras de 256 bits em palavras de 256 bits
|
|||
|
|
|||
|
|
|||
|
// 4. FUNÇÕES E MAIS
|
|||
|
// A. Funções
|
|||
|
// Funções simples
|
|||
|
function incremento(uint x) returns (uint) {
|
|||
|
x += 1;
|
|||
|
return x;
|
|||
|
}
|
|||
|
|
|||
|
// Funções podem retornar muito argumentos, e podem especificar argumentos
|
|||
|
// retornados sem necessidade de explicitamente usar return
|
|||
|
function incremento(uint x, uint y) returns (uint x, uint y) {
|
|||
|
x += 1;
|
|||
|
y += 1;
|
|||
|
}
|
|||
|
// Chamando a função anterior
|
|||
|
uint (a,b) = incremento(1,1);
|
|||
|
|
|||
|
// 'constant' indica que uam função não altera variáveis persistidas
|
|||
|
// Funções constantes são executadas localmente, não na blockchain
|
|||
|
uint y;
|
|||
|
|
|||
|
function incremento(uint x) constant returns (uint x) {
|
|||
|
x += 1;
|
|||
|
y += 1; // Esta linha deve falhar
|
|||
|
// y é uma variável de estado e não pode ser alterada por uma função local
|
|||
|
}
|
|||
|
|
|||
|
// 'Especificadores de visibilidade de funções'
|
|||
|
// Estes podem substituitir 'constante', incluíndo:
|
|||
|
// public - visbilidade externa e interna (padrão)
|
|||
|
// private - apenas visível no contrato corrente
|
|||
|
// internal - apenas visível no contrato corrente e seus derivados
|
|||
|
|
|||
|
// Functions hosteada - e pode ser assinalada para variável
|
|||
|
function a() {
|
|||
|
var z = b;
|
|||
|
b();
|
|||
|
}
|
|||
|
|
|||
|
function b() {
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
// Prefira loops sobre recursões (pilha de chamada é no máximo 1024)
|
|||
|
|
|||
|
// B. Eventos
|
|||
|
// Eventos podem notificar partes externas; facilmente buscáveis e acessáveis
|
|||
|
// de fora da blockchain (com clientes leves)
|
|||
|
// tipicamente declarados após os parâmetros do contrato
|
|||
|
|
|||
|
// Tipicamente, com letra maiúscula - e adicione Log na frente para
|
|||
|
// ser explicito e previnir confusão na chamada da função
|
|||
|
|
|||
|
// Declaração
|
|||
|
event LogEnvio(address indexed de, address indexed para, uint quantidade);
|
|||
|
// Observe a letra maíscula no início do nome
|
|||
|
|
|||
|
// Chamada
|
|||
|
Envio(de, para, quantidade);
|
|||
|
|
|||
|
// Para partes externas (um contrato ou entidade externo), observe:
|
|||
|
Coin.Envio().watch({}, '', function(erro, resultado) {
|
|||
|
if (!erro) {
|
|||
|
console.log("Moeda transferida: " + resultado.args.quantidade +
|
|||
|
" moedas enviadas de " + resultado.args.de +
|
|||
|
" para " + resultado.args.para + ".");
|
|||
|
console.log("Saldo atual:\n" +
|
|||
|
"Enviador: " + Coin.balances.call(resultado.args.de) +
|
|||
|
"Recebedor: " + Coin.balances.call(resultado.args.para));
|
|||
|
}
|
|||
|
}
|
|||
|
// Paradigma comum para um contrato depender de outro (e.g., um contrato que
|
|||
|
// depende da taxa de troca provida por outro)
|
|||
|
|
|||
|
// C. ModifiCadores
|
|||
|
// MOdificadores validam entradas de funções, como saldo mínimo e autorização
|
|||
|
// do usuário; semelhantes a guardas em outras linguagens
|
|||
|
|
|||
|
// '_' (subtraço) geralmente incluído como última linha do corpo, indica que a
|
|||
|
// função sendo chamada deve ser colocada ali
|
|||
|
modifier apenasDepois(uint _tempo) { if (agora <= _tempo) throw; _ }
|
|||
|
modifier apenasDono { if (msg.sender == dono) _ }
|
|||
|
// geralmente usado para máquina de estado
|
|||
|
modifier apenasSeEmEstado (Estado estadoCorrente)
|
|||
|
{ if (estadoCorrente != Estado.A) _ }
|
|||
|
|
|||
|
// Concatenado logo após a chamada da função
|
|||
|
function mudeDona(novoDono)
|
|||
|
apenasDepois(algumTempo)
|
|||
|
apenasDono()
|
|||
|
apenasSeEmEstado(Estado.A)
|
|||
|
{
|
|||
|
dono = novoDono;
|
|||
|
}
|
|||
|
|
|||
|
// subtração pode ser incluído antes do final do corpo, mas retorno explícitos
|
|||
|
// pode ser ignorado, então, tome cuidado
|
|||
|
modifier chequeValor(uint quantidade) {
|
|||
|
_
|
|||
|
if (msg.value > quantidade) {
|
|||
|
uint quantidadeASerDevolvida = quantidade - msg.value;
|
|||
|
if (!msg.sender.send(quantidadeASerDevolvida)) {
|
|||
|
throw;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 6. BRANCHES E LOOPS
|
|||
|
|
|||
|
// Todas as lógicas básicas de bloco funcionam - incluindo if/else,
|
|||
|
// while, break, continue, return - mas não há switch
|
|||
|
|
|||
|
// A sintaxe é semelhante a Javascript, mas sem conversão de tipos
|
|||
|
// de não-booleanos para booleanos (operadores de comparação precisam
|
|||
|
// utilizar valores booleanos)
|
|||
|
|
|||
|
// Loops que dependem o comportamento do usuário exigem cuidado - dado
|
|||
|
// que contratos tem uma quantidade máxima de gas para cada bloco de
|
|||
|
// de código - falhas acontecerão caso ele seja excedido
|
|||
|
// Por exemplo:
|
|||
|
for(uint x = 0; x < listaDeEnderecoDeRefundo.length; x++) {
|
|||
|
if (!listaDeEnderecoDeRefundo[x].send(ALGUMA_QUANTIDADE)) {
|
|||
|
throw;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Dois erros acima:
|
|||
|
// 1. Uma falha no enviar para o loop completamente, travando dinheiro
|
|||
|
// 2. Este loop pode ser abitrariamente longo (basado na quando que
|
|||
|
// o usuário precisa de refundo), portanto, pode falhar quando exceder
|
|||
|
// a quantidade máxima de gas do bloco
|
|||
|
// Ao invés disso, você deve deixar as pessoas retirarem
|
|||
|
// individualmente de suas subcontas e marcarem a retirada
|
|||
|
|
|||
|
|
|||
|
// 7. OBJETOS/CONTRATOS
|
|||
|
|
|||
|
// A. Chamando um contrato externo
|
|||
|
contract FonteDeInformacoes {
|
|||
|
function info() returns (uint ret) { return 42; }
|
|||
|
}
|
|||
|
|
|||
|
contract Consumidor {
|
|||
|
FonteDeInformacoes fonte; // aponta para um contrato na blockchain
|
|||
|
|
|||
|
// Assinala variável para uma instância do contrato
|
|||
|
function setFonte(address endereco) {
|
|||
|
// Cast automático, cuidado; construtor não é chamado
|
|||
|
fonte = FonteDeInformacoes(endereco);
|
|||
|
}
|
|||
|
|
|||
|
// Assinala variável para uma nova instância do contrato
|
|||
|
function createNewFeed() {
|
|||
|
fonte = new FonteDeInformacoes(); // nova instância criada
|
|||
|
// construtor é chamado
|
|||
|
}
|
|||
|
|
|||
|
function chameFonte() {
|
|||
|
// último parenteses chama o contrato, podendo adicionar
|
|||
|
// opcionalmente valores ou gas
|
|||
|
fonte.info.value(10).gas(800)();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// B. Herança
|
|||
|
|
|||
|
// Ordem importa, último contrato herdado (i.e., 'def') pode
|
|||
|
// sobrescrever partes de contratos previamente herdados
|
|||
|
contract MeuContratdo is abc, def("um argumento personalizado def") {
|
|||
|
|
|||
|
// sobrescrevendo função
|
|||
|
function z() {
|
|||
|
if (msg.sender == dono) {
|
|||
|
def.z(); // chama função sobrescrita de def
|
|||
|
super.z(); // chama função do pai imeadiato
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// função abstrata
|
|||
|
function umaFuncaoAbstrata(uint x);
|
|||
|
// não pode ser compilada, usada em contratos base/abstratos que
|
|||
|
// então, a implementam
|
|||
|
|
|||
|
// C. Import
|
|||
|
|
|||
|
import "filename";
|
|||
|
import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol";
|
|||
|
|
|||
|
// 'Import' está sobre desenvolvimento
|
|||
|
// Atualmente não pode ser usada na linha de comando
|
|||
|
|
|||
|
|
|||
|
// 8.OUTRAS PALAVRAS-CHAVE
|
|||
|
|
|||
|
// A. Throwing
|
|||
|
// Throwing
|
|||
|
throw; // reverte estado e dinheiro NÃO-USADO é devolvido ao usuários
|
|||
|
// Atualmente não pode ser capturado
|
|||
|
|
|||
|
// Um padrão de design comum é:
|
|||
|
if (!endereco.send(123)) {
|
|||
|
throw;
|
|||
|
}
|
|||
|
|
|||
|
// B. Selfdestruct
|
|||
|
// auto-destroe o contrato corrente, enviando fundos para o endereço
|
|||
|
// (geralmente o criador)
|
|||
|
selfdestruct(ALGUM_ENDERECO);
|
|||
|
|
|||
|
// remove disco/código dos blocos corrente e futuros
|
|||
|
// ajuda clientes leves, mas dados persistidos continuam no blockchain
|
|||
|
|
|||
|
// Padrão comum, permite ao dono finalizar o contrato e receber fundos
|
|||
|
// restantes
|
|||
|
function remover() {
|
|||
|
if(msg.sender == criador) { // Apenas o criador do contrato pode
|
|||
|
// fazer isso
|
|||
|
selfdestruct(criador); // Inativa o contrato e retorna os fundos
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Talvez queria desativar o contrato manualmente, ao invés de usar
|
|||
|
// selfdestruct (ether enviado para contratos selfdestructed é perdido)
|
|||
|
|
|||
|
|
|||
|
// 9. NOTAS SOBRE DESIGN DE CONTRATOS
|
|||
|
|
|||
|
// A. Obfuscação
|
|||
|
// Todas as variáveis são publicamente visíveis na blockchain, então
|
|||
|
// qualquer coisa privada precisa ser obfuscada (e.g., hash com segredo)
|
|||
|
|
|||
|
// Passo-a-pass: 1. Comprometa-se com algo, 2. Revele compromisso
|
|||
|
sha3("quantidade_de_lance", "algum segredo"); // compromisso
|
|||
|
|
|||
|
// chame a função reveal (revelar) do contrato no futuros
|
|||
|
// mostrando o lance mais o segredo para foi hasheado com SHA3
|
|||
|
reveal(100, "meuSegredo");
|
|||
|
|
|||
|
// B. Otimização de disco
|
|||
|
// Escrever na blockchain pode ser caro, dado que os dados são guardados
|
|||
|
// para sempre. É encorajado que contratos inteligentes usem memória (
|
|||
|
// enventualmente, compilação será melhor, mas por enquanto é benéfico
|
|||
|
// usar estruturas de dados simples - armazenando minimamente na
|
|||
|
// blockchain)
|
|||
|
|
|||
|
// Custo pode ser alto para item como arrays multidimensionais
|
|||
|
// (custo para guardar os dados - não declarar variáveis)
|
|||
|
|
|||
|
// C. Acesso de dados da blockchain
|
|||
|
|
|||
|
// Não pode restringir humanos ou computadores de ler os conteúdos
|
|||
|
// de transações ou estado de transações
|
|||
|
|
|||
|
// Enquanto 'private' previne outros *contratos* de ler dados ]
|
|||
|
// diretamente - qualquer outra parte ainda pode ler dados da blockchain
|
|||
|
|
|||
|
// Todos os dados são armazedos na blockchain, para que qualquer um
|
|||
|
// possa observar dados antigos e mudanças
|
|||
|
|
|||
|
// D. Jobs Cron
|
|||
|
// Contratos deve ser manualmente chamados para lidar com agendamentos
|
|||
|
// baseados em tempo; podendo criar código externo para pingar
|
|||
|
// regularmente ou prover incentivos (ether) para outros fazê-lo
|
|||
|
|
|||
|
// E. Padrão Observador
|
|||
|
// O Padrão Observador permite que você registre um inscritor e
|
|||
|
// registre uma função a ser chamada pelo Oráculo (nota, Oráculos pagam
|
|||
|
// pela ação executada). Similarmente à subscrição em Pub/sub
|
|||
|
|
|||
|
// Este é um contrato abstrato, tanto as classes cliente como a
|
|||
|
// servidor importam o cliente que deve ser implementado
|
|||
|
contract AlgumOraculoCallback {
|
|||
|
function OraculoCallback(int _valor, uint _tempo, bytes32 info) external;
|
|||
|
}
|
|||
|
|
|||
|
contract AlgumOráculo {
|
|||
|
AlgumOraculoCallback[] callbacks; // array com todos os inscritos
|
|||
|
|
|||
|
// Registra inscrito
|
|||
|
function addInscrito(AlgumOraculoCallback a) {
|
|||
|
callbacks.push(a);
|
|||
|
}
|
|||
|
|
|||
|
function notificar(valor, tempo, info) private {
|
|||
|
for(uint i = 0;i < callbacks.length; i++) {
|
|||
|
// todos os inscritos precisam implementar AlgumOraculoCallback
|
|||
|
callbacks[i].OraculoCallback(valor, tempo, info);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function facaAlgo() public {
|
|||
|
// Código para fazer algo
|
|||
|
|
|||
|
// Notifica todos os inscrito
|
|||
|
notificar(_valor, _tempo, _info);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Agora, seu contrato cliente pode addInscrito importando
|
|||
|
// AlgumOraculoCallback e registrando algum Oráculo
|
|||
|
|
|||
|
// F. Máquinas de estado
|
|||
|
// veja o exemplo abaixo para o enum Estado e o modificador noEstado
|
|||
|
|
|||
|
// *** EXEMPLO: Um exemplo de crowdfunding example (similar ao
|
|||
|
// Kickstarter) ***
|
|||
|
// ** INCIO DO EXEMPLO **
|
|||
|
|
|||
|
// FundadorDoCrowdFunding.sol
|
|||
|
|
|||
|
/// @title FundadorDoCrowdFunding
|
|||
|
/// @author nemild
|
|||
|
contract FundadorDoCrowdFunding {
|
|||
|
// Variáveis assinaladas na crição pelo criador
|
|||
|
address public criador;
|
|||
|
address public recipiente; // criador pode ser diferente do Recipiente
|
|||
|
uint public minALevantar; // requisito para pagar, pelo contrário
|
|||
|
// os doadores recebem o dinheiro de volta
|
|||
|
string urlDaCampanha;
|
|||
|
byte constant versao = 1;
|
|||
|
|
|||
|
// Estruturas de dados
|
|||
|
enum Estado {
|
|||
|
LevantandoFundos,
|
|||
|
RefundoExpirado,
|
|||
|
Sucesso
|
|||
|
}
|
|||
|
struct Contribuicao {
|
|||
|
uint quantidade;
|
|||
|
address contribuidor;
|
|||
|
}
|
|||
|
|
|||
|
// Variáveis de Estado
|
|||
|
State public estado = Estado.LevantandoFundos; // incializado na criação
|
|||
|
uint public totalLevantado;
|
|||
|
uint public levantadoPor;
|
|||
|
uint public completadoEm;
|
|||
|
Contribution[] contribuidores;
|
|||
|
|
|||
|
event LogRecebimentoDeFundos(address endereco,
|
|||
|
uint quantidade,
|
|||
|
uint totalAtual);
|
|||
|
event LogFundosPagos(address enderecoDoRecebedor);
|
|||
|
|
|||
|
modifier noEstado(Estado _estado) {
|
|||
|
if (estado != _estado) throw;
|
|||
|
_
|
|||
|
}
|
|||
|
|
|||
|
modifier eOCriador() {
|
|||
|
if (msg.sender != criador) throw;
|
|||
|
_
|
|||
|
}
|
|||
|
|
|||
|
// Aguarda 6 meses após o final do contrato para destruí-lo
|
|||
|
modifier noFimDoContrato() {
|
|||
|
if(!((estado == Estado.RefundoExpirado || estado == Estado.Sucesso) &&
|
|||
|
completadoEm + 6 months < now)) {
|
|||
|
throw;
|
|||
|
}
|
|||
|
_
|
|||
|
}
|
|||
|
|
|||
|
function FundadorDoCrowdFunding(
|
|||
|
uint tempoEmHorasParaFundraising,
|
|||
|
string _urlDaCampanha,
|
|||
|
address _recipiente,
|
|||
|
uint _minALevantar)
|
|||
|
{
|
|||
|
criador = msg.sender;
|
|||
|
recipiente = _recipiente;
|
|||
|
urlDaCampanha = _urlDaCampanha;
|
|||
|
minALevantar = _minALevantar;
|
|||
|
levantadoPor = now + (tempoEmHorasParaFundraising * 1 hours);
|
|||
|
}
|
|||
|
|
|||
|
function contribuir()
|
|||
|
public
|
|||
|
noEstado(Estado.LevantandoFundos)
|
|||
|
{
|
|||
|
contribuidores.push(
|
|||
|
Contribuicao({
|
|||
|
quantidade: msg.value,
|
|||
|
contribuidor: msg.sender
|
|||
|
}) // use array, para podermos iterar
|
|||
|
);
|
|||
|
totalLevantado += msg.value;
|
|||
|
|
|||
|
LogRecebimentoDeFundos(msg.sender, msg.value, totalRaised);
|
|||
|
|
|||
|
verifiqueSeLevantamentoFoiCompletadoOuExpirado();
|
|||
|
return contribuicoes.length - 1; // retorna id
|
|||
|
}
|
|||
|
|
|||
|
function verifiqueSeLevantamentoFoiCompletadoOuExpirado() {
|
|||
|
if (totalLevantado > minALevantar) {
|
|||
|
estado = Estado.Sucesso;
|
|||
|
pagar();
|
|||
|
|
|||
|
// pode incentivar enviador que iniciou a mudanção de estado
|
|||
|
} else if ( now > levantadoPor ) {
|
|||
|
estado = Estado.RefundoExpirado; // backers podem coletar
|
|||
|
// o fundo chamando receberRefundo(id)
|
|||
|
}
|
|||
|
completadoEm = now;
|
|||
|
}
|
|||
|
|
|||
|
function pagar()
|
|||
|
public
|
|||
|
emEstado(Estado.Sucesso)
|
|||
|
{
|
|||
|
if(!recipiente.send(this.balance)) {
|
|||
|
throw;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
LogFundosPagos(fundRecipient);
|
|||
|
}
|
|||
|
|
|||
|
function receberRefundo(id)
|
|||
|
public
|
|||
|
emEstado(Estado.RefundoExpirado)
|
|||
|
{
|
|||
|
if (contribuicoes.length <= id || id < 0 || contribuicoes[id].amount == 0 ) {
|
|||
|
throw;
|
|||
|
}
|
|||
|
|
|||
|
uint quantidadeDeRefundo = contribuicoes[id].amount;
|
|||
|
contribuicoes[id].amount = 0;
|
|||
|
|
|||
|
if(!contribuicoes[id].contribuidor.send(quantidadeParaEnviar)) {
|
|||
|
contribuicoes[id].amount = quantidadeParaEnviar;
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
function removerContrato()
|
|||
|
public
|
|||
|
eOCriador()
|
|||
|
noFimDoContrato()
|
|||
|
{
|
|||
|
selfdestruct(msg.sender);
|
|||
|
// criador recebe todo o dinheiro restante{
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
function () { throw; }
|
|||
|
}
|
|||
|
// ** FIM DO EXEMPLO **
|
|||
|
|
|||
|
// 10. OUTRAS FUNÇÕES NATIVAS
|
|||
|
|
|||
|
// Unidades monetárias
|
|||
|
// Moeda é definida usando wei, menor quantidade de ether
|
|||
|
uint quantidadeMin = 1 wei;
|
|||
|
uint a = 1 finney; // 1 ether == 1000 finney
|
|||
|
// Para outras unidades, veja: http://ether.fund/tool/converter
|
|||
|
|
|||
|
// Unidades temporais
|
|||
|
1 == 1 second // segundos
|
|||
|
1 minutes == 60 seconds // Minutos
|
|||
|
|
|||
|
// Pode multiplicar uma variável de tempo, dado que unidades não são guardadas
|
|||
|
// na variável
|
|||
|
uint x = 5;
|
|||
|
(x * 1 days); // 5 dias
|
|||
|
|
|||
|
// Cuidado com o salto de segundos / anos com declarações de igualdade para o tempo
|
|||
|
// (em vez disso, prefira maior que / menor que)
|
|||
|
|
|||
|
// Criptografia
|
|||
|
// Todas as string passadas são concatenadas antes de realizar hashing
|
|||
|
sha3("ab", "cd");
|
|||
|
ripemd160("abc");
|
|||
|
sha256("def");
|
|||
|
|
|||
|
// 11. Segurança
|
|||
|
|
|||
|
// Bugs são desastrosos para contratos Ethereum - até padrões Solidity populares
|
|||
|
// podem ser considerados anti-padrões
|
|||
|
|
|||
|
// Veja links para segurança no final deste documento
|
|||
|
|
|||
|
// 12. FUNÇÕES DE BAIXO NÍVELS
|
|||
|
// call - baixo nível, geralmente não usada, não tem segurança de tipos
|
|||
|
booleanSucesso = algumEnderecoDeContrato.call('nome_da_funcao', 'arg1', 'arg2');
|
|||
|
|
|||
|
// callcode - Código no endereço alvo executado no *contexto* do contrato
|
|||
|
// de chamada. Fornece funcionalidade de biblioteca
|
|||
|
algumEnderecoDeContrato.callcode('nome_da_funcao');
|
|||
|
|
|||
|
|
|||
|
// 13. NOTAS DE ESTILO
|
|||
|
// Baseado no guia de estilo PEP8 do Python
|
|||
|
|
|||
|
// Resumo rápido:
|
|||
|
// 4 espaços para identação
|
|||
|
// Duas linhas separam declaração de contratos (e outras declarações de alto nível)
|
|||
|
// Evite espaços estranhos entre parênteses
|
|||
|
// Pode omitir chaves curly para uma declaração de linha(if, for, etc)
|
|||
|
// else deve ser colocado na própria linha
|
|||
|
|
|||
|
|
|||
|
// 14. COMENTÁRIOS NATSPEC
|
|||
|
// usado para documentação, comentários e UIs externos
|
|||
|
|
|||
|
// Contrato natspec - sempre acima da definição do contrato
|
|||
|
/// @title Título do Contrato
|
|||
|
/// @author Nome do autor
|
|||
|
|
|||
|
// Função natspec
|
|||
|
/// @notice informações sobre o que funciona; mostrado quando a função é executada
|
|||
|
/// @dev Documentação de função para desenvolvedor
|
|||
|
|
|||
|
// Parâmetro de função / valor de retorno natspec
|
|||
|
/// @param algumParametro Alguma descrição do que o parametro faz
|
|||
|
/// @return Descrição do valor de retorno
|
|||
|
```
|
|||
|
|
|||
|
## Recursos adicionais
|
|||
|
- [Documetanção Solidity](https://solidity.readthedocs.org/en/latest/)
|
|||
|
- [Guia de Estilo do Solidity](https://ethereum.github.io/solidity//docs/style-guide/):
|
|||
|
O guia de estilo Ethereum é derivado do guia de estilo do Python [pep8](https://www.python.org/dev/peps/pep-0008/).
|
|||
|
- [Editor de Browser Solidity](http://chriseth.github.io/browser-solidity/)
|
|||
|
- [Gitter Solidity Chat room](https://gitter.im/ethereum/solidity)
|
|||
|
- [Estratégias de projeto modular para contratos Ethereum](https://docs.erisindustries.com/tutorials/solidity/)
|
|||
|
|
|||
|
## Contratos de Exemplo
|
|||
|
- [Dapp Bin](https://github.com/ethereum/dapp-bin)
|
|||
|
- [Solidity Baby Step Contracts](https://github.com/fivedogit/solidity-baby-steps/tree/master/contracts)
|
|||
|
- [ConsenSys Contracts](https://github.com/ConsenSys/dapp-store-contracts)
|
|||
|
- [State of Dapps](http://dapps.ethercasts.com/)
|
|||
|
|
|||
|
## Segurança
|
|||
|
- [Thinking About Smart Contract Security](https://blog.ethereum.org/2016/06/19/thinking-smart-contract-security/)
|
|||
|
- [Smart Contract Security](https://blog.ethereum.org/2016/06/10/smart-contract-security/)
|
|||
|
- [Hacking Distributed Blog](http://hackingdistributed.com/)
|
|||
|
|
|||
|
## Informação excluída intencionalmente
|
|||
|
- Libraries
|
|||
|
|
|||
|
## Estilo
|
|||
|
- [PEP8](https://www.python.org/dev/peps/pep-0008/) é usado como guia de estilo,
|
|||
|
incluindo sua filosofia geral
|
|||
|
|
|||
|
## Editores
|
|||
|
- [Vim Solidity](https://github.com/tomlion/vim-solidity)
|
|||
|
- Snippets de Editores ([Ultisnips format](https://gist.github.com/nemild/98343ce6b16b747788bc))
|
|||
|
|
|||
|
## Trabalhos Futuros
|
|||
|
- Novas palavras-chave: protected, inheritable
|
|||
|
- Lista de padrões de design comuns (throttling, RNG, atualização de versão)
|
|||
|
- Padrões anti-segurança comuns
|
|||
|
|
|||
|
|
|||
|
Sinta-se a vontade para enviar um pull request com quaisquer edições - ou email
|
|||
|
para nemild - / at- / gmail
|