Compartilhe esta página

Aprenda X em Y Minutos

Onde X=Solidity

Solidity permite você programar para a Ethereum, 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.

// 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[][5] x; // 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

Contratos de Exemplo

Segurança

Estilo

Editores


Sugestões ou correções? Abra uma issue no repositório do GitHub, ou faça um pull request você mesmo!

Originalmente contribuído por Nemil Dalal e atualizado por 5 colaboradores.