NRC20

Resumo

O seguinte padrão permite a implementação de uma API padrão para tokens em contratos inteligentes. Este padrão fornece funcionalidade básica para a transferência de tokens, bem como permite que os tokens sejam aprovados para que possam ser gastos por terceiros na cadeia.

Motivação

Uma interface padrão permite que um novo token seja facilmente criado por qualquer aplicativo: desde carteiras até trocas descentralizadas.

Métodos

name

Nome. Retorna o nome do token - por exemplo "MyToken".

// returns string, the name of the token.
function name()

symbol

Símbolo. Retorna o símbolo do token. Por exemplo, "TK".

// returns string, the symbol of the token 
function symbol()

decimals

Decimais. Retorna o número de decimais que o token usa - por exemplo 8, significa dividir o valor do token 100000000 para obter sua representação do utilizador.

// returns number, the number of decimals the token uses
function decimals()

totalSupply

Retorna o fornecimento total de token.

// returns string, the total token supply, the decimal value is decimals* total.
function totalSupply()

balanceOf

Retorna o saldo da conta de um endereço.

// returns string, the account balance of another account with address
function balanceOf(address)

transfer

Transferência. Transfere a quantidade de tokens value para o endereço addresse deve disparar o evento Transfer. A função deve disparar throwse o saldo da conta from não tiver tokens suficientes para gastar.

Nota: Transferências de 0 valores DEVEM ser tratadas como transferências normais e disparar o evento Transfer.
// returns `true`, if transfer success, else throw error
function transfer(address, value)

transferFrom

Transfere a quantidade value de tokens do endereço from para o endereço to e DEVE disparar o evento Transfer.

O método transferFrom é usado para um fluxo de trabalho de retirada, permitindo que contratos transfiram tokens em seu nome. Isso pode ser usado, por exemplo, para permitir que um contrato transfira tokens em seu nome e / ou cobrar taxas em sub-moedas. A função deve throw, a menos que a from conta deliberadamente autorizou o remetente da mensagem através de algum mecanismo.

Nota: Transferências de 0 valores DEVEM ser tratadas como transferências normais e disparar o Transfer evento.
// returns `true`, if transfer success, else throw error
function transferFrom(from, to, value)

approve

Aprovar. Permite que o spender retire da sua conta várias vezes, até currentValue do montante value. Se esta função for chamada novamente, ela sobrescreve a permissão atual com value.

Nota: Para evitar vectores de ataque, o utilizador precisa de fornecer um valor de aprovação anterior e o valor padrão de não aprovado é 0.
// returns `true`, if approve success, else throw error
function approve(spender, currentValue, value)

allowance

Subsídio. Retorna o valor que o spender ainda pode ser retirado do owner.

// returns string, the value allowed to withdraw from `owner`.
function allowance(owner, spender)

Events

transferEvent

DEVE ser accionado quando tokens são transferidos, incluindo transferências de valor zero.

Um contrato de token que cria novos tokens DEVE accionar um evento de transferência com o endereço from definido para o fornecimento total totalSupply quando os tokens são criados.

function transferEvent: function(status, from, to, value)

approveEvent

DEVE acionar em qualquer chamada para approve(spender, currentValue, value).

function approveEvent: function(status, from, spender, value)

Implementação

Exemplos de implementações estão disponíveis em

'use strict';

var Allowed = function (obj) {
    this.allowed = {};
    this.parse(obj);
}

Allowed.prototype = {
    toString: function () {
        return JSON.stringify(this.allowed);
    },

    parse: function (obj) {
        if (typeof obj != "undefined") {
            var data = JSON.parse(obj);
            for (var key in data) {
                this.allowed[key] = new BigNumber(data[key]);
            }
        }
    },

    get: function (key) {
        return this.allowed[key];
    },

    set: function (key, value) {
        this.allowed[key] = new BigNumber(value);
    }
}

var StandardToken = function () {
    LocalContractStorage.defineProperties(this, {
        _name: null,
        _symbol: null,
        _decimals: null,
        _totalSupply: {
            parse: function (value) {
                return new BigNumber(value);
            },
            stringify: function (o) {
                return o.toString(10);
            }
        }
    });

    LocalContractStorage.defineMapProperties(this, {
        "balances": {
            parse: function (value) {
                return new BigNumber(value);
            },
            stringify: function (o) {
                return o.toString(10);
            }
        },
        "allowed": {
            parse: function (value) {
                return new Allowed(value);
            },
            stringify: function (o) {
                return o.toString();
            }
        }
    });
};

StandardToken.prototype = {
    init: function (name, symbol, decimals, totalSupply) {
        this._name = name;
        this._symbol = symbol;
        this._decimals = decimals || 0;
        this._totalSupply = new BigNumber(totalSupply).mul(new BigNumber(10).pow(decimals));

        var from = Blockchain.transaction.from;
        this.balances.set(from, this._totalSupply);
        this.transferEvent(true, from, from, this._totalSupply);
    },

    // Returns the name of the token
    name: function () {
        return this._name;
    },

    // Returns the symbol of the token
    symbol: function () {
        return this._symbol;
    },

    // Returns the number of decimals the token uses
    decimals: function () {
        return this._decimals;
    },

    totalSupply: function () {
        return this._totalSupply.toString(10);
    },

    balanceOf: function (owner) {
        var balance = this.balances.get(owner);

        if (balance instanceof BigNumber) {
            return balance.toString(10);
        } else {
            return "0";
        }
    },

    transfer: function (to, value) {
        value = new BigNumber(value);
        if (value.lt(0)) {
            throw new Error("invalid value.");
        }

        var from = Blockchain.transaction.from;
        var balance = this.balances.get(from) || new BigNumber(0);

        if (balance.lt(value)) {
            throw new Error("transfer failed.");
        }

        this.balances.set(from, balance.sub(value));
        var toBalance = this.balances.get(to) || new BigNumber(0);
        this.balances.set(to, toBalance.add(value));

        this.transferEvent(true, from, to, value);
    },

    transferFrom: function (from, to, value) {
        var spender = Blockchain.transaction.from;
        var balance = this.balances.get(from) || new BigNumber(0);

        var allowed = this.allowed.get(from) || new Allowed();
        var allowedValue = allowed.get(spender) || new BigNumber(0);
        value = new BigNumber(value);

        if (value.gte(0) && balance.gte(value) && allowedValue.gte(value)) {

            this.balances.set(from, balance.sub(value));

            // update allowed value
            allowed.set(spender, allowedValue.sub(value));
            this.allowed.set(from, allowed);

            var toBalance = this.balances.get(to) || new BigNumber(0);
            this.balances.set(to, toBalance.add(value));

            this.transferEvent(true, from, to, value);
        } else {
            throw new Error("transfer failed.");
        }
    },

    transferEvent: function (status, from, to, value) {
        Event.Trigger(this.name(), {
            Status: status,
            Transfer: {
                from: from,
                to: to,
                value: value
            }
        });
    },

    approve: function (spender, currentValue, value) {
        var from = Blockchain.transaction.from;

        var oldValue = this.allowance(from, spender);
        if (oldValue != currentValue.toString()) {
            throw new Error("current approve value mistake.");
        }

        var balance = new BigNumber(this.balanceOf(from));
        var value = new BigNumber(value);

        if (value.lt(0) || balance.lt(value)) {
            throw new Error("invalid value.");
        }

        var owned = this.allowed.get(from) || new Allowed();
        owned.set(spender, value);

        this.allowed.set(from, owned);

        this.approveEvent(true, from, spender, value);
    },

    approveEvent: function (status, from, spender, value) {
        Event.Trigger(this.name(), {
            Status: status,
            Approve: {
                owner: from,
                spender: spender,
                value: value
            }
        });
    },

    allowance: function (owner, spender) {
        var owned = this.allowed.get(owner);

        if (owned instanceof Allowed) {
            var spender = owned.get(spender);
            if (typeof spender != "undefined") {
                return spender.toString(10);
            }
        }
        return "0";
    }
};

module.exports = StandardToken;