ETH Price: $2,472.00 (-20.75%)

Contract Diff Checker

Contract Name:
ERC20Token

Contract Source Code:

// SPDX-License-Identifier: MIT

// Copyright 2023 Energi Core

pragma solidity 0.5.16;

import { StorageBase } from './StorageBase.sol';
import { NonReentrant } from './NonReentrant.sol';
import { ERC20TokenAutoProxy } from './ERC20TokenAutoProxy.sol';

import { LibClaimAirdrop } from './libraries/LibClaimAirdrop.sol';

import { IERC20Token } from './interfaces/IERC20Token.sol';
import { IGovernedProxy } from './interfaces/IGovernedProxy.sol';
import { IERC20TokenStorage } from './interfaces/IERC20TokenStorage.sol';
import { IGovernedContract } from './interfaces/IGovernedContract.sol';
import { IERC20TokenGovernedProxy } from './interfaces/IERC20TokenGovernedProxy.sol';

contract ERC20TokenStorage is StorageBase, IERC20TokenStorage {
    string private name;

    string private symbol;

    uint8 private decimals;

    address private airdropService; // Signs ERC20 airdrop rewards claims

    address private eRC721ManagerProxy; // Can burn tokens

    // ERC20 airdrops lastClaimNonce mappings are stored by airdropId
    mapping(bytes4 => mapping(address => uint256)) private airdropLastClaimNonce;

    constructor(
        address _airdropService,
        address _eRC721ManagerProxy,
        string memory _name,
        string memory _symbol,
        uint8 _decimals
    ) public {
        airdropService = _airdropService;
        eRC721ManagerProxy = _eRC721ManagerProxy;
        name = _name;
        symbol = _symbol;
        decimals = _decimals;
    }

    function getName() external view returns (string memory _name) {
        _name = name;
    }

    function getSymbol() external view returns (string memory _symbol) {
        _symbol = symbol;
    }

    function getDecimals() external view returns (uint8 _decimals) {
        _decimals = decimals;
    }

    function getAirdropService() external view returns (address _airdropService) {
        _airdropService = airdropService;
    }

    function getERC721ManagerProxy() external view returns (address _eRC721ManagerProxy) {
        _eRC721ManagerProxy = eRC721ManagerProxy;
    }

    function getAirdropLastClaimNonce(bytes4 airdropId, address _user)
        external
        view
        returns (uint256 _lastClaimNonce)
    {
        _lastClaimNonce = airdropLastClaimNonce[airdropId][_user];
    }

    function setName(string calldata _name) external requireOwner {
        name = _name;
    }

    function setSymbol(string calldata _symbol) external requireOwner {
        symbol = _symbol;
    }

    function setDecimals(uint8 _decimals) external requireOwner {
        decimals = _decimals;
    }

    function setAirdropService(address _airdropService) external requireOwner {
        airdropService = _airdropService;
    }

    function setERC721ManagerProxy(address _eRC721ManagerProxy) external requireOwner {
        eRC721ManagerProxy = _eRC721ManagerProxy;
    }

    function setAirdropLastClaimNonce(
        bytes4 airdropId,
        address _user,
        uint256 _lastClaimNonce
    ) external requireOwner {
        airdropLastClaimNonce[airdropId][_user] = _lastClaimNonce;
    }
}

contract ERC20Token is NonReentrant, ERC20TokenAutoProxy, IERC20Token {
    // Data for migration
    //---------------------------------
    ERC20TokenStorage public eRC20TokenStorage;
    //---------------------------------

    modifier onlyERC20TokenOwner() {
        require(_callerAddress() == owner, 'ERC20Token: FORBIDDEN');
        _;
    }

    modifier onlyERC20TokenOwnerOrERC721Manager() {
        require(
            _callerAddress() == owner ||
                msg.sender ==
                address(
                    IGovernedProxy(address(uint160(eRC20TokenStorage.getERC721ManagerProxy())))
                        .impl()
                ),
            'ERC20Token: FORBIDDEN'
        );
        _;
    }

    constructor(
        address _proxy, // If set to address(0), ERC20TokenGovernedProxy will be deployed by ERC20TokenAutoProxy
        address _airdropService,
        address _eRC721ManagerProxy,
        address _owner,
        string memory _name,
        string memory _symbol,
        uint8 _decimals
    ) public ERC20TokenAutoProxy(_proxy, this, _owner) {
        // Deploy ERC20 Token storage
        eRC20TokenStorage = new ERC20TokenStorage(
            _airdropService,
            _eRC721ManagerProxy,
            _name,
            _symbol,
            _decimals
        );
    }

    // Governance functions
    //
    // This function allows to set sporkProxy address after deployment in order to enable upgrades
    function setSporkProxy(address payable _sporkProxy) public onlyERC20TokenOwner {
        IERC20TokenGovernedProxy(proxy).setSporkProxy(_sporkProxy);
    }

    // This function is called in order to upgrade to a new implementation
    function destroy(IGovernedContract _newImpl) external requireProxy {
        eRC20TokenStorage.setOwner(_newImpl);
        _destroyERC20(_newImpl);
        _destroy(_newImpl);
    }

    // This function would be called on the new implementation if necessary for the upgrade
    function migrate(IGovernedContract _oldImpl) external requireProxy {
        _migrateERC20(address(_oldImpl));
        _migrate(_oldImpl);
    }

    // Getter functions
    //
    function name() external view returns (string memory _name) {
        _name = eRC20TokenStorage.getName();
    }

    function symbol() external view returns (string memory _symbol) {
        _symbol = eRC20TokenStorage.getSymbol();
    }

    function decimals() external view returns (uint8 _decimals) {
        _decimals = eRC20TokenStorage.getDecimals();
    }

    function getAirdropService() external view returns (address _airdropService) {
        _airdropService = eRC20TokenStorage.getAirdropService();
    }

    function getAirdropLastClaimNonce(bytes4 airdropId, address _user)
        external
        view
        returns (uint256 _lastClaimNonce)
    {
        _lastClaimNonce = eRC20TokenStorage.getAirdropLastClaimNonce(airdropId, _user);
    }

    // Setter functions
    //
    function setName(string calldata _name) external onlyERC20TokenOwner {
        eRC20TokenStorage.setName(_name);
    }

    function setSymbol(string calldata _symbol) external onlyERC20TokenOwner {
        eRC20TokenStorage.setSymbol(_symbol);
    }

    function setDecimals(uint8 _decimals) external onlyERC20TokenOwner {
        eRC20TokenStorage.setDecimals(_decimals);
    }

    function setAirdropService(address _airdropService) external onlyERC20TokenOwner {
        eRC20TokenStorage.setAirdropService(_airdropService);
    }

    function setERC721ManagerProxy(address _eRC721ManagerProxy) external onlyERC20TokenOwner {
        eRC20TokenStorage.setERC721ManagerProxy(_eRC721ManagerProxy);
    }

    // Mint/burn functions
    //
    function mint(address recipient, uint256 amount) external onlyERC20TokenOwner {
        _mint(recipient, amount);
        IERC20TokenGovernedProxy(proxy).emitTransfer(address(0x0), recipient, amount);
    }

    function burn(address account, uint256 amount) external onlyERC20TokenOwnerOrERC721Manager {
        _burn(account, amount);
        IERC20TokenGovernedProxy(proxy).emitTransfer(account, address(0x0), amount);
    }

    // ERC20 airdrop and airdrop referral rewards claim function
    function claimAirdrop(
        uint256 claimAmountAirdrop,
        uint256 claimAmountReferral1,
        uint256 claimAmountReferral2,
        uint256 claimAmountReferral3,
        bytes4 airdropId,
        uint256 lastClaimNonce,
        uint256 claimNonce,
        bytes calldata airdropServiceSignature
    ) external noReentry {
        // Get rewards recipient address
        address recipient = _callerAddress();
        // Make sure claim has not been processed yet
        require(
            lastClaimNonce == eRC20TokenStorage.getAirdropLastClaimNonce(airdropId, recipient),
            'ERC20Token: invalid lastClaimNonce value'
        );
        // Check that claimNonce > lastClaimNonce
        require(
            lastClaimNonce < claimNonce,
            'ERC20Token: claimNonce must be larger than lastClaimNonce'
        );
        // Validate airdrop claim
        LibClaimAirdrop.validateClaim(
            recipient, // Referral rewards claim recipient address
            claimAmountAirdrop, // Claim amount corresponding to ERC20 airdrop
            claimAmountReferral1, // Claim amount corresponding to first level of ERC20 airdrop referral rewards
            claimAmountReferral2, // Claim amount corresponding to second level of ERC20 airdrop referral rewards
            claimAmountReferral3, // Claim amount corresponding to third level of ERC20 airdrop referral rewards
            airdropId, // Airdrop campaign Id
            lastClaimNonce, // Recipient's last claim nonce
            claimNonce, // Recipient's current claim nonce
            airdropServiceSignature, // Claim signature from ERC20 airdrop service
            proxy, // Verifying contract address
            eRC20TokenStorage.getAirdropService() // Airdrop service address
        );
        // Update recipient's last claim nonce to current claim nonce
        eRC20TokenStorage.setAirdropLastClaimNonce(airdropId, recipient, claimNonce);
        // Mint total claim amount to recipient
        mintAirdropClaim(
            recipient,
            claimAmountAirdrop,
            claimAmountReferral1,
            claimAmountReferral2,
            claimAmountReferral3
        );
        // Emit AirdropRewardsClaimed event
        IERC20TokenGovernedProxy(proxy).emitAirdropRewardsClaimed(
            recipient,
            claimAmountAirdrop,
            claimAmountReferral1,
            claimAmountReferral2,
            claimAmountReferral3,
            airdropId,
            lastClaimNonce,
            claimNonce,
            airdropServiceSignature
        );
    }

    function mintAirdropClaim(
        address recipient,
        uint256 claimAmountAirdrop,
        uint256 claimAmountReferral1,
        uint256 claimAmountReferral2,
        uint256 claimAmountReferral3
    ) private {
        // Calculate total claim amount
        uint256 totalClaimAmount = claimAmountAirdrop
            .add(claimAmountReferral1)
            .add(claimAmountReferral2)
            .add(claimAmountReferral3);
        // Mint total claim amount to recipient
        _mint(recipient, totalClaimAmount);
        // Emit Transfer event
        IERC20TokenGovernedProxy(proxy).emitTransfer(address(0x0), recipient, totalClaimAmount);
    }
}

// SPDX-License-Identifier: MIT

// Copyright 2023 Energi Core

pragma solidity 0.5.16;

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, 'SafeMath: addition overflow');

        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return sub(a, b, 'SafeMath: subtraction overflow');
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     * - Subtraction cannot overflow.
     *
     * _Available since v2.4.0._
     */
    function sub(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        uint256 c = a - b;

        return c;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) {
            return 0;
        }

        uint256 c = a * b;
        require(c / a == b, 'SafeMath: multiplication overflow');

        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return div(a, b, 'SafeMath: division by zero');
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     *
     * _Available since v2.4.0._
     */
    function div(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        // Solidity only automatically asserts when dividing by 0
        require(b > 0, errorMessage);
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold

        return c;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        return mod(a, b, 'SafeMath: modulo by zero');
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts with custom message when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     *
     * _Available since v2.4.0._
     */
    function mod(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        require(b != 0, errorMessage);
        return a % b;
    }
}

// SPDX-License-Identifier: MIT

// Copyright 2023 Energi Core

pragma solidity 0.5.16;

library LibSignature {
    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        // Check the signature length
        if (signature.length != 65) {
            revert('LibSignature: invalid ECDSA signature length');
        }

        // Divide the signature in r, s and v variables
        bytes32 r;
        bytes32 s;
        uint8 v;

        // ecrecover takes the signature parameters, and the only way to get them
        // currently is to use assembly.
        // solhint-disable-next-line no-inline-assembly
        assembly {
            r := mload(add(signature, 0x20))
            s := mload(add(signature, 0x40))
            v := byte(0, mload(add(signature, 0x60)))
        }

        return recover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover-bytes32-bytes-} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        require(
            uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0,
            'LibSignature: invalid ECDSA signature `s` value'
        );

        // If the signature is valid (and not malleable), return the signer address
        // v > 30 is a special case, we need to adjust hash with '\x19Ethereum Signed Message:\n32'
        // and v = v - 4
        address signer;
        if (v > 30) {
            require(v - 4 == 27 || v - 4 == 28, 'LibSignature: invalid ECDSA signature `v` value');
            signer = ecrecover(toEthSignedMessageHash(hash), v - 4, r, s);
        } else {
            require(v == 27 || v == 28, 'LibSignature: invalid ECDSA signature `v` value');
            signer = ecrecover(hash, v, r, s);
        }

        require(signer != address(0), 'LibSignature: invalid ECDSA signature');

        return signer;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * replicates the behavior of the
     * https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`]
     * JSON-RPC method.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        return keccak256(abi.encodePacked('\x19Ethereum Signed Message:\n32', hash));
    }
}

// SPDX-License-Identifier: MIT

// Copyright 2023 Energi Core

pragma solidity 0.5.16;

library LibEIP712 {
    // Calculates EIP712 encoding for a hash struct in this EIP712 Domain.
    // Note that we use the verifying contract's proxy address here instead of the verifying contract's address,
    // so that users signatures remain valid when we upgrade the ERC20Token contract
    function hashEIP712Message(bytes32 hashStruct, address verifyingContractProxy)
        internal
        pure
        returns (bytes32 result)
    {
        uint256 chainId;
        assembly {
            chainId := chainid()
        }

        bytes32 eip712DomainHash = keccak256(
            abi.encode(
                keccak256(
                    'EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'
                ),
                keccak256(bytes('Energi')),
                keccak256(bytes('1')),
                chainId,
                verifyingContractProxy
            )
        );

        result = keccak256(abi.encodePacked('\x19\x01', eip712DomainHash, hashStruct));
    }
}

// SPDX-License-Identifier: MIT

// Copyright 2023 Energi Core

pragma solidity 0.5.16;

import { LibEIP712 } from './LibEIP712.sol';
import { LibSignature } from './LibSignature.sol';

library LibClaimAirdrop {
    bytes32 constant AIRDROP_CLAIM_TYPEHASH =
        keccak256(
            'ERC20Claim(address recipient,uint256 claimAmountAirdrop,uint256 claimAmountReferral1,uint256 claimAmountReferral2,uint256 claimAmountReferral3,bytes4 airdropId,uint256 lastClaimNonce,uint256 claimNonce)'
        );

    function hashClaim(
        address recipient,
        uint256 claimAmountAirdrop,
        uint256 claimAmountReferral1,
        uint256 claimAmountReferral2,
        uint256 claimAmountReferral3,
        bytes4 airdropId,
        uint256 lastClaimNonce,
        uint256 claimNonce
    ) internal pure returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    AIRDROP_CLAIM_TYPEHASH,
                    recipient,
                    claimAmountAirdrop,
                    claimAmountReferral1,
                    claimAmountReferral2,
                    claimAmountReferral3,
                    airdropId,
                    lastClaimNonce,
                    claimNonce
                )
            );
    }

    function validateClaim(
        address recipient,
        uint256 claimAmountAirdrop,
        uint256 claimAmountReferral1,
        uint256 claimAmountReferral2,
        uint256 claimAmountReferral3,
        bytes4 airdropId,
        uint256 lastClaimNonce,
        uint256 claimNonce,
        bytes memory airdropServiceSignature,
        address verifyingContractProxy,
        address airdropService
    ) internal pure {
        // Generate EIP712 hashStruct of airdropClaim
        bytes32 hashStruct = hashClaim(
            recipient,
            claimAmountAirdrop,
            claimAmountReferral1,
            claimAmountReferral2,
            claimAmountReferral3,
            airdropId,
            lastClaimNonce,
            claimNonce
        );
        // Verify claim EIP712 hashStruct signature
        if (
            LibSignature.recover(
                LibEIP712.hashEIP712Message(hashStruct, verifyingContractProxy),
                airdropServiceSignature
            ) != airdropService
        ) {
            revert('LibClaimAirdrop: EIP-712 airdrop service signature verification error');
        }
    }
}

// SPDX-License-Identifier: MIT

// Copyright 2023 Energi Core

// Energi Governance system is the fundamental part of Energi Core.

// NOTE: It's not allowed to change the compiler due to byte-to-byte
// match requirement.

pragma solidity 0.5.16;

import { IProposal } from './IProposal.sol';
import { IGovernedContract } from './IGovernedContract.sol';

contract IUpgradeProposal is IProposal {
    function impl() external view returns (IGovernedContract);
}

// SPDX-License-Identifier: MIT

// Copyright 2023 Energi Core

// Energi Governance system is the fundamental part of Energi Core.

// NOTE: It's not allowed to change the compiler due to byte-to-byte
// match requirement.

pragma solidity 0.5.16;

import { IGovernedContract } from './IGovernedContract.sol';
import { IUpgradeProposal } from './IUpgradeProposal.sol';

interface ISporkRegistry {
    function createUpgradeProposal(
        IGovernedContract _impl,
        uint256 _period,
        address payable _fee_payer
    ) external payable returns (IUpgradeProposal);

    function consensusGasLimits() external view returns (uint256 callGas, uint256 xferGas);
}

// SPDX-License-Identifier: MIT

// Copyright 2023 Energi Core

// Energi Governance system is the fundamental part of Energi Core.

// NOTE: It's not allowed to change the compiler due to byte-to-byte
// match requirement.

pragma solidity 0.5.16;

interface IProposal {
    function parent() external view returns (address);

    function created_block() external view returns (uint256);

    function deadline() external view returns (uint256);

    function fee_payer() external view returns (address payable);

    function fee_amount() external view returns (uint256);

    function accepted_weight() external view returns (uint256);

    function rejected_weight() external view returns (uint256);

    function total_weight() external view returns (uint256);

    function quorum_weight() external view returns (uint256);

    function isFinished() external view returns (bool);

    function isAccepted() external view returns (bool);

    function withdraw() external;

    function destroy() external;

    function collect() external;

    function voteAccept() external;

    function voteReject() external;

    function setFee() external payable;

    function canVote(address owner) external view returns (bool);
}

// SPDX-License-Identifier: MIT

// Copyright 2023 Energi Core

pragma solidity 0.5.16;

interface IOwnedERC20 {
    function owner() external view returns (address _owner);

    function setOwner(address _owner) external;

    function mint(address recipient, uint256 amount) external;

    function burn(address recipient, uint256 amount) external;
}

// SPDX-License-Identifier: MIT

// Copyright 2023 Energi Core

// Energi Governance system is the fundamental part of Energi Core.

// NOTE: It's not allowed to change the compiler due to byte-to-byte
// match requirement.

pragma solidity 0.5.16;

import { IGovernedContract } from './IGovernedContract.sol';
import { IUpgradeProposal } from './IUpgradeProposal.sol';

interface IGovernedProxy {
    event UpgradeProposal(IGovernedContract indexed impl, IUpgradeProposal proposal);

    event Upgraded(IGovernedContract indexed impl, IUpgradeProposal proposal);

    function impl() external view returns (IGovernedContract);

    function proposeUpgrade(IGovernedContract _newImpl, uint256 _period)
        external
        payable
        returns (IUpgradeProposal);

    function upgrade(IUpgradeProposal _proposal) external;

    function upgradeProposalImpl(IUpgradeProposal _proposal)
        external
        view
        returns (IGovernedContract new_impl);

    function listUpgradeProposals() external view returns (IUpgradeProposal[] memory proposals);

    function collectUpgradeProposal(IUpgradeProposal _proposal) external;

    function() external payable;
}

// SPDX-License-Identifier: MIT

// Copyright 2023 Energi Core

pragma solidity 0.5.16;

interface IGovernedERC20Storage {
    function setBalance(address _owner, uint256 _amount) external;

    function setAllowance(
        address _owner,
        address _spender,
        uint256 _amount
    ) external;

    function setTotalSupply(uint256 _amount) external;

    function getBalance(address _account) external view returns (uint256 balance);

    function getAllowance(address _owner, address _spender)
        external
        view
        returns (uint256 allowance);

    function getTotalSupply() external view returns (uint256 totalSupply);
}

// SPDX-License-Identifier: MIT

// Copyright 2023 Energi Core

pragma solidity 0.5.16;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP. Does not include
 * the optional functions; to access them see {ERC20Detailed}.
 */
interface IGovernedERC20 {
    function erc20Storage() external view returns (address _erc20Storage);

    function totalSupply() external view returns (uint256);

    function balanceOf(address account) external view returns (uint256);

    function allowance(address owner, address spender) external view returns (uint256);

    function transfer(
        address sender,
        address recipient,
        uint256 amount
    ) external returns (bool);

    function approve(
        address owner,
        address spender,
        uint256 amount
    ) external returns (bool);

    function transferFrom(
        address spender,
        address sender,
        address recipient,
        uint256 amount
    ) external returns (bool);

    function increaseAllowance(
        address sender,
        address spender,
        uint256 addedValue
    ) external returns (bool);

    function decreaseAllowance(
        address sender,
        address spender,
        uint256 subtractedValue
    ) external returns (bool);
}

// SPDX-License-Identifier: MIT

// Copyright 2023 Energi Core

// Energi Governance system is the fundamental part of Energi Core.

// NOTE: It's not allowed to change the compiler due to byte-to-byte
// match requirement.

pragma solidity 0.5.16;

/**
 * Genesis version of GovernedContract interface.
 *
 * Base Consensus interface for upgradable contracts.
 * Unlike common approach, the implementation is NOT expected to be
 * called through delegatecall() to minimize risks of shared storage.
 *
 * NOTE: it MUST NOT change after blockchain launch!
 */

interface IGovernedContract {
    // Return actual proxy address for secure validation
    function proxy() external view returns (address);

    // It must check that the caller is the proxy
    // and copy all required data from the old address.
    function migrate(IGovernedContract _oldImpl) external;

    // It must check that the caller is the proxy
    // and self destruct to the new address.
    function destroy(IGovernedContract _newImpl) external;

    // function () external payable; // This line (from original Energi IGovernedContract) is commented because it
    // makes truffle migrations fail
}

// SPDX-License-Identifier: MIT

// Copyright 2023 Energi Core

pragma solidity 0.5.16;

interface IERC20TokenStorage {
    function getName() external view returns (string memory _name);

    function getSymbol() external view returns (string memory _symbol);

    function getDecimals() external view returns (uint8 _decimals);

    function getAirdropService() external view returns (address _airdropService);

    function getAirdropLastClaimNonce(bytes4 airdropId, address _user)
        external
        view
        returns (uint256 _lastClaimNonce);

    function getERC721ManagerProxy() external view returns (address _eRC721ManagerProxy);

    function setName(string calldata _name) external;

    function setSymbol(string calldata _symbol) external;

    function setDecimals(uint8 _decimals) external;

    function setAirdropService(address _airdropService) external;

    function setAirdropLastClaimNonce(
        bytes4 airdropId,
        address _user,
        uint256 _lastClaimNonce
    ) external;

    function setERC721ManagerProxy(address _eRC721ManagerProxy) external;
}

// SPDX-License-Identifier: MIT

// Copyright 2023 Energi Core

pragma solidity 0.5.16;

interface IERC20TokenGovernedProxy {
    event AirdropRewardsClaimed(
        address indexed recipient,
        uint256 claimAmountAirdrop,
        uint256 claimAmountReferral1,
        uint256 claimAmountReferral2,
        uint256 claimAmountReferral3,
        bytes4 airdropId,
        uint256 lastClaimNonce,
        uint256 claimNonce,
        bytes airdropServiceSignature
    );

    // ERC20 events
    event Transfer(address indexed from, address indexed to, uint256 value);

    event Approval(address indexed owner, address indexed spender, uint256 value);

    function setSporkProxy(address payable _sporkProxy) external;

    function emitAirdropRewardsClaimed(
        address recipient,
        uint256 claimAmountAirdrop,
        uint256 claimAmountReferral1,
        uint256 claimAmountReferral2,
        uint256 claimAmountReferral3,
        bytes4 airdropId,
        uint256 lastClaimNonce,
        uint256 claimNonce,
        bytes calldata airdropServiceSignature
    ) external;

    function emitTransfer(
        address from,
        address to,
        uint256 value
    ) external;

    // ERC20 standard interface
    function name() external view returns (string memory _name);

    function symbol() external view returns (string memory _symbol);

    function decimals() external view returns (uint256 _decimals);

    function balanceOf(address account) external view returns (uint256 _balance);

    function allowance(address owner, address spender) external view returns (uint256 _allowance);

    function totalSupply() external view returns (uint256 _totalSupply);

    function approve(address spender, uint256 value) external returns (bool result);

    function transfer(address to, uint256 value) external returns (bool result);

    function transferFrom(
        address from,
        address to,
        uint256 value
    ) external returns (bool result);

    function increaseAllowance(address spender, uint256 addedValue) external returns (bool result);

    function decreaseAllowance(address spender, uint256 subtractedValue)
        external
        returns (bool result);
}

// SPDX-License-Identifier: MIT

// Copyright 2023 Energi Core

pragma solidity 0.5.16;

interface IERC20Token {
    function eRC20TokenStorage() external view returns (address _eRC20TokenStorage);

    function name() external view returns (string memory _name);

    function symbol() external view returns (string memory _symbol);

    function decimals() external view returns (uint8 _decimals);

    function getAirdropService() external view returns (address _airdropService);

    function getAirdropLastClaimNonce(bytes4 airdropId, address _user)
        external
        view
        returns (uint256 _lastClaimNonce);

    // Setter functions
    //
    function setName(string calldata _name) external;

    function setSymbol(string calldata _symbol) external;

    function setDecimals(uint8 _decimals) external;

    function setAirdropService(address _airdropService) external;

    function setERC721ManagerProxy(address _eRC721ManagerProxy) external;

    // Mint/burn functions
    //
    function mint(address recipient, uint256 amount) external;

    function burn(address account, uint256 amount) external;

    // ERC20 airdrop and airdrop referral rewards claim function
    function claimAirdrop(
        uint256 claimAmountAirdrop,
        uint256 claimAmountReferral1,
        uint256 claimAmountReferral2,
        uint256 claimAmountReferral3,
        bytes4 airdropId,
        uint256 lastClaimNonce,
        uint256 claimNonce,
        bytes calldata airdropServiceSignature
    ) external;
}

// SPDX-License-Identifier: MIT

// Copyright 2023 Energi Core

// Energi Governance system is the fundamental part of Energi Core.

// NOTE: It's not allowed to change the compiler due to byte-to-byte
// match requirement.

pragma solidity 0.5.16;

import { IGovernedContract } from './interfaces/IGovernedContract.sol';

/**
 * Base for contract storage (SC-14).
 *
 * NOTE: it MUST NOT change after blockchain launch!
 */

contract StorageBase {
    address payable internal owner;

    modifier requireOwner() {
        require(msg.sender == address(owner), 'Not owner!');
        _;
    }

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

    function setOwner(IGovernedContract _newOwner) external requireOwner {
        owner = address(uint160(address(_newOwner)));
    }

    function kill() external requireOwner {
        selfdestruct(msg.sender);
    }
}

// SPDX-License-Identifier: MIT

// Copyright 2023 Energi Core

pragma solidity 0.5.16;

import { Context } from './Context.sol';
import { Ownable } from './Ownable.sol';

/**
 * @title Pausable
 * @dev Base contract which allows children to implement an emergency stop mechanism.
 */
contract Pausable is Context, Ownable {
    /**
     * @dev Emitted when pause() is called.
     * @param account of contract owner issuing the event.
     * @param unpauseBlock block number when contract will be unpaused.
     */
    event Paused(address account, uint256 unpauseBlock);

    /**
     * @dev Emitted when pause is lifted by unpause() by
     * @param account.
     */
    event Unpaused(address account);

    /**
     * @dev state variable
     */
    uint256 public blockNumberWhenToUnpause = 0;

    constructor(address _owner) public Ownable(_owner) {}

    /**
     * @dev Modifier to make a function callable only when the contract is not
     *      paused. It checks whether the current block number
     *      has already reached blockNumberWhenToUnpause.
     */
    modifier whenNotPaused() {
        require(
            block.number >= blockNumberWhenToUnpause,
            'Pausable: Revert - Code execution is still paused'
        );
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     */
    modifier whenPaused() {
        require(
            block.number < blockNumberWhenToUnpause,
            'Pausable: Revert - Code execution is not paused'
        );
        _;
    }

    /**
     * @dev Triggers or extends pause state.
     *
     * Requirements:
     *
     * - @param blocks needs to be greater than 0.
     */
    function pause(uint256 blocks) external onlyOwner {
        require(
            blocks > 0,
            'Pausable: Revert - Pause did not activate. Please enter a positive integer.'
        );
        blockNumberWhenToUnpause = block.number + blocks;
        emit Paused(_msgSender(), blockNumberWhenToUnpause);
    }

    /**
     * @dev Returns to normal code execution.
     */
    function unpause() external onlyOwner {
        blockNumberWhenToUnpause = block.number;
        emit Unpaused(_msgSender());
    }
}

// SPDX-License-Identifier: MIT

// Copyright 2023 Energi Core

pragma solidity 0.5.16;

/**
 * @title Ownable
 * @dev The Ownable contract has an owner address, and provides basic authorization control
 * functions, this simplifies the implementation of 'user permissions'.
 */
contract Ownable {
    address public owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev The Ownable constructor sets the original `owner` of the contract to the sender
     * account.
     */
    constructor(address _owner) public {
        owner = _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(msg.sender == owner, 'Ownable: Not owner');
        _;
    }

    /**
     * @dev Allows the current owner to transfer control of the contract to a newOwner.
     * @param newOwner The address to transfer ownership to.
     */
    function transferOwnership(address newOwner) public onlyOwner {
        require(newOwner != address(0), 'Ownable: Zero address not allowed');
        emit OwnershipTransferred(owner, newOwner);
        owner = newOwner;
    }
}

// SPDX-License-Identifier: MIT

// Copyright 2023 Energi Core

pragma solidity 0.5.16;

/**
 * A little helper to protect contract from being re-entrant in state
 * modifying functions.
 */

contract NonReentrant {
    uint256 private entry_guard;

    modifier noReentry() {
        require(entry_guard == 0, 'Reentry');
        entry_guard = 1;
        _;
        entry_guard = 0;
    }
}

// SPDX-License-Identifier: MIT

// Copyright 2023 Energi Core

pragma solidity 0.5.16;

import { Pausable } from './Pausable.sol';
import { GovernedContract } from './GovernedContract.sol';
import { StorageBase } from './StorageBase.sol';
import { Context } from './Context.sol';

import { IGovernedERC20 } from './interfaces/IGovernedERC20.sol';
import { IGovernedContract } from './interfaces/IGovernedContract.sol';
import { IGovernedERC20Storage } from './interfaces/IGovernedERC20Storage.sol';
import { SafeMath } from './libraries/SafeMath.sol';

/**
 * Permanent storage of GovernedERC20 data.
 */

contract GovernedERC20Storage is StorageBase, IGovernedERC20Storage {
    mapping(address => uint256) private _balances;

    mapping(address => mapping(address => uint256)) private _allowances;

    uint256 private _totalSupply;

    function setBalance(address _owner, uint256 _amount) external requireOwner {
        _balances[_owner] = _amount;
    }

    function setAllowance(
        address _owner,
        address _spender,
        uint256 _amount
    ) external requireOwner {
        _allowances[_owner][_spender] = _amount;
    }

    function setTotalSupply(uint256 _amount) external requireOwner {
        _totalSupply = _amount;
    }

    function getBalance(address _account) external view returns (uint256 balance) {
        balance = _balances[_account];
    }

    function getAllowance(address _owner, address _spender)
        external
        view
        returns (uint256 allowance)
    {
        allowance = _allowances[_owner][_spender];
    }

    function getTotalSupply() external view returns (uint256 totalSupply) {
        totalSupply = _totalSupply;
    }
}

contract GovernedERC20 is Pausable, GovernedContract, IGovernedERC20 {
    using SafeMath for uint256;

    // Data for migration
    //---------------------------------
    GovernedERC20Storage public erc20Storage;

    //---------------------------------

    constructor(address _proxy, address _owner) public Pausable(_owner) GovernedContract(_proxy) {
        erc20Storage = new GovernedERC20Storage();
    }

    // IGovernedContract
    //---------------------------------
    // This function would be called by GovernedProxy on an old implementation to replace it with a new one
    function _destroyERC20(IGovernedContract _newImpl) internal {
        erc20Storage.setOwner(_newImpl);
    }

    //---------------------------------
    // This function would be called on the new implementation if necessary for the upgrade
    function _migrateERC20(address _oldImpl) internal {
        erc20Storage = GovernedERC20Storage(IGovernedERC20(_oldImpl).erc20Storage());
    }

    // ERC20
    //---------------------------------
    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() external view returns (uint256) {
        return erc20Storage.getTotalSupply();
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) external view returns (uint256) {
        return erc20Storage.getBalance(account);
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) external view returns (uint256) {
        return erc20Storage.getAllowance(owner, spender);
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `recipient` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(
        address sender,
        address recipient,
        uint256 amount
    ) external requireProxy returns (bool) {
        _transfer(sender, recipient, amount);
        return true;
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(
        address owner,
        address spender,
        uint256 amount
    ) external requireProxy returns (bool) {
        _approve(owner, spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20};
     *
     * Requirements:
     * - `sender` and `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     * - the caller must have allowance for `sender`'s tokens of at least
     * `amount`.
     */
    function transferFrom(
        address spender,
        address sender,
        address recipient,
        uint256 amount
    ) external requireProxy returns (bool) {
        _transfer(sender, recipient, amount);
        uint256 approveAmount = erc20Storage.getAllowance(sender, spender).sub(
            amount,
            'ERC20Token ERC20: transfer amount exceeds allowance'
        );
        _approve(sender, spender, approveAmount);
        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(
        address owner,
        address spender,
        uint256 addedValue
    ) external requireProxy returns (bool) {
        uint256 approveAmount = erc20Storage.getAllowance(owner, spender).add(addedValue);
        _approve(owner, spender, approveAmount);
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(
        address owner,
        address spender,
        uint256 subtractedValue
    ) external requireProxy returns (bool) {
        uint256 approveAmount = erc20Storage.getAllowance(owner, spender).sub(
            subtractedValue,
            'ERC20Token ERC20: decreased allowance below zero'
        );
        _approve(owner, spender, approveAmount);
        return true;
    }

    /**
     * @dev Moves tokens `amount` from `sender` to `recipient`.
     *
     * This is internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `sender` cannot be the zero address.
     * - `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     */
    function _transfer(
        address sender,
        address recipient,
        uint256 amount
    ) internal whenNotPaused {
        require(sender != address(0), 'ERC20Token ERC20: transfer from the zero address');
        require(recipient != address(0), 'ERC20Token ERC20: transfer to the zero address');

        erc20Storage.setBalance(
            sender,
            erc20Storage.getBalance(sender).sub(
                amount,
                'ERC20Token ERC20: transfer amount exceeds balance'
            )
        );
        erc20Storage.setBalance(recipient, erc20Storage.getBalance(recipient).add(amount));
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements
     *
     * - `to` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal whenNotPaused {
        require(account != address(0), 'ERC20Token ERC20: mint to the zero address');

        erc20Storage.setTotalSupply(erc20Storage.getTotalSupply().add(amount));
        erc20Storage.setBalance(account, erc20Storage.getBalance(account).add(amount));
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal whenNotPaused {
        require(account != address(0), 'ERC20Token ERC20: burn from the zero address');

        erc20Storage.setBalance(
            account,
            erc20Storage.getBalance(account).sub(
                amount,
                'ERC20Token ERC20: burn amount exceeds balance'
            )
        );
        erc20Storage.setTotalSupply(erc20Storage.getTotalSupply().sub(amount));
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
     *
     * This is internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(
        address owner,
        address spender,
        uint256 amount
    ) internal whenNotPaused {
        require(owner != address(0), 'ERC20Token ERC20: approve from the zero address');
        require(spender != address(0), 'ERC20Token ERC20: approve to the zero address');

        erc20Storage.setAllowance(owner, spender, amount);
    }
}

// SPDX-License-Identifier: MIT

// Copyright 2023 Energi Core

// Energi Governance system is the fundamental part of Energi Core.

// NOTE: It's not allowed to change the compiler due to byte-to-byte
// match requirement.

pragma solidity 0.5.16;

import { IGovernedContract } from './interfaces/IGovernedContract.sol';

/**
 * Genesis version of GovernedContract common base.
 *
 * Base Consensus interface for upgradable contracts.
 * Unlike common approach, the implementation is NOT expected to be
 * called through delegatecall() to minimize risks of shared storage.
 *
 * NOTE: it MUST NOT change after blockchain launch!
 */
contract GovernedContract is IGovernedContract {
    address public proxy;

    constructor(address _proxy) public {
        proxy = _proxy;
    }

    modifier requireProxy() {
        require(msg.sender == proxy, 'Governed Contract: Not proxy');
        _;
    }

    function getProxy() internal view returns (address _proxy) {
        _proxy = proxy;
    }

    // Function overridden in child contract
    function migrate(IGovernedContract _oldImpl) external requireProxy {
        _migrate(_oldImpl);
    }

    // Function overridden in child contract
    function destroy(IGovernedContract _newImpl) external requireProxy {
        _destroy(_newImpl);
    }

    // solium-disable-next-line no-empty-blocks
    function _migrate(IGovernedContract) internal {}

    function _destroy(IGovernedContract _newImpl) internal {
        selfdestruct(address(uint160(address(_newImpl))));
    }

    function _callerAddress() internal view returns (address payable) {
        if (msg.sender == proxy) {
            // This is guarantee of the GovernedProxy
            // solium-disable-next-line security/no-tx-origin
            return tx.origin;
        } else {
            return msg.sender;
        }
    }
}

// SPDX-License-Identifier: MIT

// Copyright 2023 Energi Core

// Energi Governance system is the fundamental part of Energi Core.

// NOTE: It's not allowed to change the compiler due to byte-to-byte
// match requirement.

pragma solidity 0.5.16;

import { NonReentrant } from './NonReentrant.sol';

import { SafeMath } from './libraries/SafeMath.sol';

import { IERC20TokenGovernedProxy } from './interfaces/IERC20TokenGovernedProxy.sol';
import { IGovernedContract } from './interfaces/IGovernedContract.sol';
import { IGovernedProxy } from './interfaces/IGovernedProxy.sol';
import { IUpgradeProposal } from './interfaces/IUpgradeProposal.sol';
import { ISporkRegistry } from './interfaces/ISporkRegistry.sol';
import { IGovernedERC20 } from './interfaces/IGovernedERC20.sol';
import { IOwnedERC20 } from './interfaces/IOwnedERC20.sol';
import { IERC20Token } from './interfaces/IERC20Token.sol';

/**
 * SC-9: This contract has no chance of being updated. It must be stupid simple.
 *
 * If another upgrade logic is required in the future - it can be done as proxy stage II.
 */
contract ERC20TokenGovernedProxy is IERC20TokenGovernedProxy, IGovernedProxy, NonReentrant {
    using SafeMath for uint256;

    IGovernedContract public impl;
    IGovernedContract public implementation; // only used for block explorers to detect contract as a proxy

    IGovernedProxy public spork_proxy;

    mapping(address => IGovernedContract) public upgrade_proposals;

    IUpgradeProposal[] public upgrade_proposal_list;

    modifier senderOrigin() {
        // Internal calls are expected to use impl directly.
        // That's due to use of call() instead of delegatecall() on purpose.
        // solium-disable-next-line security/no-tx-origin
        require(tx.origin == msg.sender, 'Only direct calls are allowed!');
        _;
    }

    modifier onlyImpl() {
        require(msg.sender == address(impl), 'Only calls from impl are allowed!');
        _;
    }

    constructor(IGovernedContract _impl) public {
        impl = _impl;
        implementation = _impl; // to allow block explorers to find the impl contract
    }

    function setSporkProxy(address payable _sporkProxy) external onlyImpl {
        spork_proxy = IGovernedProxy(_sporkProxy);
    }

    function emitAirdropRewardsClaimed(
        address recipient,
        uint256 claimAmountAirdrop,
        uint256 claimAmountReferral1,
        uint256 claimAmountReferral2,
        uint256 claimAmountReferral3,
        bytes4 airdropId,
        uint256 lastClaimNonce,
        uint256 claimNonce,
        bytes calldata airdropServiceSignature
    ) external onlyImpl {
        emit AirdropRewardsClaimed(
            recipient,
            claimAmountAirdrop,
            claimAmountReferral1,
            claimAmountReferral2,
            claimAmountReferral3,
            airdropId,
            lastClaimNonce,
            claimNonce,
            airdropServiceSignature
        );
    }

    function emitTransfer(
        address from,
        address to,
        uint256 value
    ) external onlyImpl {
        emit Transfer(from, to, value);
    }

    function emitApproval(
        address owner,
        address spender,
        uint256 value
    ) external onlyImpl {
        emit Approval(owner, spender, value);
    }

    // ERC20 standard functions
    //
    function name() external view returns (string memory _name) {
        _name = IERC20Token(address(uint160(address(impl)))).name();
    }

    function symbol() external view returns (string memory _symbol) {
        _symbol = IERC20Token(address(uint160(address(impl)))).symbol();
    }

    function decimals() external view returns (uint256 _decimals) {
        _decimals = IERC20Token(address(uint160(address(impl)))).decimals();
    }

    function balanceOf(address account) external view returns (uint256 _balance) {
        _balance = IGovernedERC20(address(uint160(address(impl)))).balanceOf(account);
    }

    function allowance(address owner, address spender) external view returns (uint256 _allowance) {
        _allowance = IGovernedERC20(address(uint160(address(impl)))).allowance(owner, spender);
    }

    function totalSupply() external view returns (uint256 _totalSupply) {
        _totalSupply = IGovernedERC20(address(uint160(address(impl)))).totalSupply();
    }

    function approve(address spender, uint256 value) external returns (bool result) {
        result = IGovernedERC20(address(uint160(address(impl)))).approve(
            msg.sender,
            spender,
            value
        );
        emit Approval(msg.sender, spender, value);
    }

    function transfer(address to, uint256 value) external returns (bool result) {
        result = IGovernedERC20(address(uint160(address(impl)))).transfer(msg.sender, to, value);
        emit Transfer(msg.sender, to, value);
    }

    function transferFrom(
        address from,
        address to,
        uint256 value
    ) external returns (bool result) {
        result = IGovernedERC20(address(uint160(address(impl)))).transferFrom(
            msg.sender,
            from,
            to,
            value
        );
        emit Transfer(from, to, value);
        uint256 newApproveAmount = IGovernedERC20(address(uint160(address(impl)))).allowance(
            from,
            msg.sender
        );
        emit Approval(from, msg.sender, newApproveAmount);
    }

    function increaseAllowance(address spender, uint256 addedValue) external returns (bool result) {
        result = IGovernedERC20(address(uint160(address(impl)))).increaseAllowance(
            msg.sender,
            spender,
            addedValue
        );
        uint256 newApproveAmount = IGovernedERC20(address(uint160(address(impl)))).allowance(
            msg.sender,
            spender
        );
        emit Approval(msg.sender, spender, newApproveAmount);
    }

    function decreaseAllowance(address spender, uint256 subtractedValue)
        external
        returns (bool result)
    {
        result = IGovernedERC20(address(uint160(address(impl)))).decreaseAllowance(
            msg.sender,
            spender,
            subtractedValue
        );
        uint256 newApproveAmount = IGovernedERC20(address(uint160(address(impl)))).allowance(
            msg.sender,
            spender
        );
        emit Approval(msg.sender, spender, newApproveAmount);
    }

    // OwnedERC20 functions
    //
    function mint(address recipient, uint256 amount) external {
        IOwnedERC20(address(uint160(address(impl)))).mint(recipient, amount);
    }

    function burn(address recipient, uint256 amount) external {
        IOwnedERC20(address(uint160(address(impl)))).burn(recipient, amount);
    }

    // Governance functions
    //
    /**
     * Pre-create a new contract first.
     * Then propose upgrade based on that.
     */
    function proposeUpgrade(IGovernedContract _newImpl, uint256 _period)
        external
        payable
        senderOrigin
        noReentry
        returns (IUpgradeProposal)
    {
        require(_newImpl != impl, 'Already active!');
        require(_newImpl.proxy() == address(this), 'Wrong proxy!');

        ISporkRegistry spork_reg = ISporkRegistry(address(spork_proxy.impl()));
        IUpgradeProposal proposal = spork_reg.createUpgradeProposal.value(msg.value)(
            _newImpl,
            _period,
            msg.sender
        );

        upgrade_proposals[address(proposal)] = _newImpl;
        upgrade_proposal_list.push(proposal);

        emit UpgradeProposal(_newImpl, proposal);

        return proposal;
    }

    /**
     * Once proposal is accepted, anyone can activate that.
     */
    function upgrade(IUpgradeProposal _proposal) external noReentry {
        IGovernedContract new_impl = upgrade_proposals[address(_proposal)];
        require(new_impl != impl, 'Already active!'); // in case it changes in the flight
        require(address(new_impl) != address(0), 'Not registered!');
        require(_proposal.isAccepted(), 'Not accepted!');

        IGovernedContract old_impl = impl;

        new_impl.migrate(old_impl);
        impl = new_impl;
        implementation = new_impl;
        old_impl.destroy(new_impl);

        // SECURITY: prevent downgrade attack
        _cleanupProposal(_proposal);

        // Return fee ASAP
        _proposal.destroy();

        emit Upgraded(new_impl, _proposal);
    }

    /**
     * Map proposal to implementation
     */
    function upgradeProposalImpl(IUpgradeProposal _proposal)
        external
        view
        returns (IGovernedContract new_impl)
    {
        new_impl = upgrade_proposals[address(_proposal)];
    }

    /**
     * Lists all available upgrades
     */
    function listUpgradeProposals() external view returns (IUpgradeProposal[] memory proposals) {
        uint256 len = upgrade_proposal_list.length;
        proposals = new IUpgradeProposal[](len);

        for (uint256 i = 0; i < len; ++i) {
            proposals[i] = upgrade_proposal_list[i];
        }

        return proposals;
    }

    /**
     * Once proposal is reject, anyone can start collect procedure.
     */
    function collectUpgradeProposal(IUpgradeProposal _proposal) external noReentry {
        IGovernedContract new_impl = upgrade_proposals[address(_proposal)];
        require(address(new_impl) != address(0), 'Not registered!');
        _proposal.collect();
        delete upgrade_proposals[address(_proposal)];

        _cleanupProposal(_proposal);
    }

    function _cleanupProposal(IUpgradeProposal _proposal) internal {
        delete upgrade_proposals[address(_proposal)];

        uint256 len = upgrade_proposal_list.length;
        for (uint256 i = 0; i < len; ++i) {
            if (upgrade_proposal_list[i] == _proposal) {
                upgrade_proposal_list[i] = upgrade_proposal_list[len - 1];
                upgrade_proposal_list.pop();
                break;
            }
        }
    }

    /**
     * Related to above
     */
    function proxy() external view returns (address) {
        return address(this);
    }

    /**
     * SECURITY: prevent on-behalf-of calls
     */
    function transferFrom(
        address,
        address,
        address,
        uint256
    ) external pure {
        revert('Good try');
    }

    /**
     * SECURITY: prevent on-behalf-of calls
     */
    function increaseAllowance(
        address,
        address,
        uint256
    ) external pure {
        revert('Good try');
    }

    /**
     * SECURITY: prevent on-behalf-of calls
     */
    function decreaseAllowance(
        address,
        address,
        uint256
    ) external pure {
        revert('Good try');
    }

    /**
     * SECURITY: prevent on-behalf-of calls
     */
    function transfer(
        address,
        address,
        uint256
    ) external pure {
        revert('Good try');
    }

    /**
     * SECURITY: prevent on-behalf-of calls
     */
    function approve(
        address,
        address,
        uint256
    ) external pure {
        revert('Good try');
    }

    /**
     * SECURITY: prevent on-behalf-of calls
     */
    function migrate(IGovernedContract) external pure {
        revert('Good try');
    }

    /**
     * SECURITY: prevent on-behalf-of calls
     */
    function destroy(IGovernedContract) external pure {
        revert('Good try');
    }

    /**
     * Proxy all other calls to implementation.
     */
    function() external payable senderOrigin {
        // SECURITY: senderOrigin() modifier is mandatory

        // A dummy delegatecall opcode in the fallback function is necessary for
        // block explorers to pick up the Energi proxy-implementation pattern
        if (false) {
            (bool success, bytes memory data) = address(0).delegatecall(
                abi.encodeWithSignature('')
            );
            require(
                success && !success && data.length == 0 && data.length != 0,
                'ERC20TokenGovernedProxy: delegatecall cannot be used'
            );
        }

        IGovernedContract impl_m = impl;

        // solium-disable-next-line security/no-inline-assembly
        assembly {
            let ptr := mload(0x40)
            calldatacopy(ptr, 0, calldatasize)

            let res := call(sub(gas, 10000), impl_m, callvalue, ptr, calldatasize, 0, 0)
            // NOTE: returndatasize should allow repeatable calls
            //       what should save one opcode.
            returndatacopy(ptr, 0, returndatasize)

            switch res
            case 0 {
                revert(ptr, returndatasize)
            }
            default {
                return(ptr, returndatasize)
            }
        }
    }
}

// SPDX-License-Identifier: MIT

// Copyright 2023 Energi Core

pragma solidity 0.5.16;

import { IGovernedContract } from './interfaces/IGovernedContract.sol';
import { GovernedERC20 } from './GovernedERC20.sol';
import { ERC20TokenGovernedProxy } from './ERC20TokenGovernedProxy.sol';

contract ERC20TokenAutoProxy is GovernedERC20 {
    constructor(
        address _proxy,
        IGovernedContract _impl,
        address _owner
    ) public GovernedERC20(_proxy, _owner) {
        if (_proxy == address(0)) {
            _proxy = address(new ERC20TokenGovernedProxy(_impl));
        }
        proxy = _proxy;
    }
}

// SPDX-License-Identifier: MIT

// Copyright 2023 Energi Core

pragma solidity 0.5.16;

/*
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with GSN meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */

contract Context {
    // Empty internal constructor, to prevent people from mistakenly deploying
    // an instance of this contract, which should be used via inheritance.
    constructor() internal {}

    // solhint-disable-previous-line no-empty-blocks

    function _msgSender() internal view returns (address payable) {
        return msg.sender;
    }

    function _txOrigin() internal view returns (address payable) {
        return tx.origin;
    }

    function _msgData() internal view returns (bytes memory) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        return msg.data;
    }
}

Please enter a contract address above to load the contract details and source code.

Context size (optional):