Baixar o código: learnSolidity.sol
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
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.