ERC20 totalSupply no devuelve el valor correcto

Estoy probando un contrato ERC20 basado en la plantilla de solidez de Openzeppelin. Pero estoy observando un comportamiento curioso al leer una variable global. Aquí está el contrato:

pragma solidity ^0.4.24;

import "../node_modules/openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";


contract FunnyToken is ERC20 {
    string public constant name = "FunnyToken";
    string public constant symbol = "FYT";
    uint8 public constant decimals = 18;
    uint256 private _totalSupply = 10000 * (10 ** uint256(decimals));
    address private owner;

    constructor() public {
        owner = msg.sender;
    }
}

luego truffle migratelo conecto a ganache-cli y ejecuto los siguientes comandos en la consola de truffle:

FunnyToken.deployed().then(inst=> tok=inst);
tok.totalSupply();

el valor devuelto es: BigNumber { s: 1, e: 0, c: [ 0 ] }. Entonces concluyo que la variable global _totalSupply no se está inicializando correctamente, porque la función totalSupplyse implementa así:

function totalSupply() public view returns (uint256) {
    return _totalSupply;
  }

¿Alguien puede explicarme qué está pasando?

¿Estás seguro de que el contrato está implementado cuando llamas totalSupply?

Respuestas (4)

El problema es que está creando una nueva variable de almacenamiento en su FunnyTokencontrato. El contrato base seguirá accediendo a la antigua variable de almacenamiento.

Una solución es asignar el valor en el constructor.

pragma solidity ^0.4.0;

contract TokenBase {
    uint _totalSupply;
    function totalSupply() public view returns (uint) {
        return _totalSupply;
    }
}

contract Token is TokenBase {
    // uint _totalSupply = 4321; // <-- this creates a new storage variable
    constructor() public {
        _totalSupply = 1234; // It is better to initialize the old variable
    }
}

Otra solución posible es anular la función totalSupply()de su contrato, pero desperdiciará un espacio adicional de almacenamiento.

Creo que también intenté asignar el valor de _totalSupply en el constructor, pero no resolvió el problema. Terminé renunciando a la herencia, por ahora.
Creo que esto se debe a que en la nueva versión de OpenZeppelin esa variable es privada y no tiene acceso desde contratos secundarios. Pero su ejemplo github.com/OpenZeppelin/openzeppelin-solidity/blob/master/… muestra cómo lo hace llamando a la función interna _mint.

Todo lo que necesita hacer es convertir el número grande en un número real. Además, teniendo en cuenta cuántas personas usan zepelín abierto, parece bastante inusual que esto suceda. Intente mostrar el gran número como un número.

const number = tok.totalSupply()
number.toNumber()

Consulte ¿Cómo convertir BigNumber a Number en el marco de Truffle?

Eso da un mensaje de error de que toNumber no es una función. si intenta acceder a él como un atributo, devuelve indefinido.
Realmente nunca usé la consola de trufas, actualizaré el código.
En todo caso el error devuelto en tipo BigNumber, es incorrecto, es el equivalente a 0

Encontré la respuesta a mi propia pregunta: tras la herencia, los contratos derivados no pueden acceder a las variables privadas del contrato base (gracioso, pero cierto), por lo tanto, cuando llamo totalSupply(definido el contrato base) accederá _totalSupplydesde el contrato base, en lugar de el que definí en el contrato derivado, devolviendo así 0.

Supongo que para solucionar esto tendría que sobrecargar el totalSupplymétodo en el contrato derivado. Eso sí no lo he probado.

Usted declaró _totalSupply como

uint256 private _totalSupply = 10000 * (10 ** uint256(decimals));

Dado que es una variable privada dentro de su contrato base, no podrá llamarla desde ninguno de sus contratos derivados ( Motivo ).

Ahora, es aún más sencillo omitir lo mismo en Solidity ^0.5.0:

pragma solidity ^0.5.0;

import 'openzeppelin-solidity/contracts/token/ERC721/ERC721Full.sol';
import 'openzeppelin-solidity/contracts/token/ERC721/ERC721Mintable.sol';

contract FunnyToken is ERC20 {
    string public constant name = "FunnyToken";
    string public constant symbol = "FYT";
    uint8 public constant decimals = 18;
    uint256 public _totalSupply = 10000;
    address private owner;

    constructor() public {
        owner = msg.sender;
    }
}

Espero tener claro lo anterior. Gracias