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

Informação excluída intencionalmente

Estilo

Editores

Trabalhos Futuros

Sinta-se a vontade para enviar um pull request com quaisquer edições - ou email para nemild - / at- / gmail


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 4 colaborador(es).