HODL ETC HODL Contract
Featured: Register HENS (.etc) Domain Name Click to register
Overview [ERC-20]
Max Total Supply:
25.666
Holders:
25,506
Transfers:
26,312
Profile Summary
Decimals:
18
Official Site:
FILTERED BY TOKEN HOLDER
BALANCE
0 HODL
VALUE
$0 (~0 ETC)
Contract name:
HODL
Optimization enabled :
false
Compiler version:
v0.8.5+commit.a4f2e591
EVM Version:
default
Contract source code
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.5;

// ETC HODL Contract
// To mint HODL: Send ETC to this contract
// To redeem HODL for ETC: Call the withdraw function

// 20% withdrawal fee
// 20% transfer fee

// Simple web UI deployed at https://ipfs.io/ipfs/QmStRUTXRagCvcnYwTrqsGJZqaB6F1XYAjZsbtzsZQG7Ba
// This contract and the web UI are provided under the MIT License.

contract HODL {
    string public name     = "ETC HODL Contract";
    string public symbol   = "HODL";
    uint8  public decimals = 18;

    event  Approval(address indexed owner, address indexed spender, uint amount);
    event  Transfer(address indexed from, address indexed to, uint amount);
    event  Deposit(address indexed owner, uint amount);
    event  Withdrawal(address indexed owner, uint amount);

    uint                                            public  totalSupply;
    mapping (address => uint)                       public  balanceOf;
    mapping (address => mapping (address => uint))  public  allowance;

    receive() external payable {
        // tokenAmount is calculated so that withdraw(tokenAmount) returns
        // 80% of msg.value to sender if called immediately after deposit
        // ==> max loss is 20% (in ETC terms)
        uint tokenAmount;
        if (msg.value == address(this).balance) {
            // first deposit ever, or first deposit after all ETC was withdrawn
            tokenAmount = msg.value;
        } else {
            // tokenAmount/(totalSupply + tokenAmount) = msg.value/address(this).balance
            // ==> solve for tokenAmount
            tokenAmount = (totalSupply * msg.value)/(address(this).balance - msg.value);
        }

        balanceOf[msg.sender] += tokenAmount;
        totalSupply += tokenAmount;

        emit Deposit(msg.sender, msg.value);
    }

    function withdraw(uint tokenAmount) external {
        require(balanceOf[msg.sender] >= tokenAmount);
        balanceOf[msg.sender] -= tokenAmount;

        uint etcAmount;
        if (tokenAmount == totalSupply) {
            // last holder pays no withdrawal fee
            etcAmount = address(this).balance;
        } else {
            // 20% withdrawal fee
            // contract ETC balance * share of total supply * 4/5
            etcAmount = address(this).balance * (tokenAmount * 4)/(totalSupply * 5);
        }
        totalSupply -= tokenAmount;

        (bool success, ) = msg.sender.call{value: etcAmount}("");
        require(success, "ETC transfer failed");

        emit Withdrawal(msg.sender, etcAmount);
    }

    function approve(address spender, uint amount) external returns (bool) {
        allowance[msg.sender][spender] = amount;
        emit Approval(msg.sender, spender, amount);
        return true;
    }

    function transfer(address to, uint amount) external returns (bool) {
        return transferFrom(msg.sender, to, amount);
    }

    function transferFrom(address from, address to, uint amount)
        public
        returns (bool)
    {
        require(balanceOf[from] >= amount);

        if (from != msg.sender && allowance[from][msg.sender] != type(uint).max) {
            require(allowance[from][msg.sender] >= amount);
            allowance[from][msg.sender] -= amount;
        }

        balanceOf[from] -= amount;

        // 20% transfer fee (inclusive)
        uint fee = amount/5;
        require(fee > 0);
        totalSupply -= fee;

        balanceOf[to] += amount - fee;

        emit Transfer(from, to, amount);

        return true;
    }
}

// Web UI backup:
// <!DOCTYPE html><html lang="en"> <head> <title>ETC HODL Contract</title> <meta name="viewport" content="width=device-width, initial-scale=1"/> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous"/> <script src="https://cdn.jsdelivr.net/npm/web3@latest/dist/web3.min.js"></script> <script>const chain_id="0x3d"; const contract_address="0x3B20DF51FAbc46e8A8BE13C295569D88D12f6868"; const contract_abi=[{anonymous: false, inputs: [{indexed: true, internalType: "address", name: "owner", type: "address"},{indexed: true, internalType: "address", name: "spender", type: "address"},{indexed: false, internalType: "uint256", name: "amount", type: "uint256"},], name: "Approval", type: "event",},{anonymous: false, inputs: [{indexed: true, internalType: "address", name: "owner", type: "address"},{indexed: false, internalType: "uint256", name: "amount", type: "uint256"},], name: "Deposit", type: "event",},{anonymous: false, inputs: [{indexed: true, internalType: "address", name: "from", type: "address"},{indexed: true, internalType: "address", name: "to", type: "address"},{indexed: false, internalType: "uint256", name: "amount", type: "uint256"},], name: "Transfer", type: "event",},{anonymous: false, inputs: [{indexed: true, internalType: "address", name: "owner", type: "address"},{indexed: false, internalType: "uint256", name: "amount", type: "uint256"},], name: "Withdrawal", type: "event",},{inputs: [{internalType: "address", name: "", type: "address"},{internalType: "address", name: "", type: "address"},], name: "allowance", outputs: [{internalType: "uint256", name: "", type: "uint256"}], stateMutability: "view", type: "function",},{inputs: [{internalType: "address", name: "spender", type: "address"},{internalType: "uint256", name: "amount", type: "uint256"},], name: "approve", outputs: [{internalType: "bool", name: "", type: "bool"}], stateMutability: "nonpayable", type: "function",},{inputs: [{internalType: "address", name: "", type: "address"}], name: "balanceOf", outputs: [{internalType: "uint256", name: "", type: "uint256"}], stateMutability: "view", type: "function"},{inputs: [], name: "decimals", outputs: [{internalType: "uint8", name: "", type: "uint8"}], stateMutability: "view", type: "function"},{inputs: [], name: "name", outputs: [{internalType: "string", name: "", type: "string"}], stateMutability: "view", type: "function"},{inputs: [], name: "symbol", outputs: [{internalType: "string", name: "", type: "string"}], stateMutability: "view", type: "function"},{inputs: [], name: "totalSupply", outputs: [{internalType: "uint256", name: "", type: "uint256"}], stateMutability: "view", type: "function"},{inputs: [{internalType: "address", name: "to", type: "address"},{internalType: "uint256", name: "amount", type: "uint256"},], name: "transfer", outputs: [{internalType: "bool", name: "", type: "bool"}], stateMutability: "nonpayable", type: "function",},{inputs: [{internalType: "address", name: "from", type: "address"},{internalType: "address", name: "to", type: "address"},{internalType: "uint256", name: "amount", type: "uint256"},], name: "transferFrom", outputs: [{internalType: "bool", name: "", type: "bool"}], stateMutability: "nonpayable", type: "function",},{inputs: [{internalType: "uint256", name: "tokenAmount", type: "uint256"}], name: "withdraw", outputs: [], stateMutability: "nonpayable", type: "function"},{stateMutability: "payable", type: "receive"},]; function GenericError(message){this.message=message;}function LoadWeb3(){if (window.ethereum){}else{throw new GenericError("Install MetaMask.");}window.web3=new Web3(ethereum); if (window.web3==null){throw new GenericError("Could not initialize Web3");}else{console.log("Web3 initialized");}}let account; function HandleAccounts(accounts){if (accounts.length===0){throw new GenericError("Could not load accounts");}account=accounts[0]; console.log("Selected account: " + account);}function HandleChainId(selectedChainId){if (selectedChainId !=chain_id){throw new GenericError( "Wrong chain (" + selectedChainId + "). Add Ethereum Classic to MetaMask and switch to that network.\n\nNetwork name: ETC\nRPC URL: https://www.ethercluster.com/etc\nChain id: 61\nCurrency symbol: ETC" );}console.log("Selected chain id: " + selectedChainId);}function WeiToEther(wei){return web3.utils.fromWei(wei, "ether");}function EtherToWei(ether){return web3.utils.toWei(ether, "ether");}let contract; function LoadContract(){contract=new web3.eth.Contract(contract_abi, contract_address);}window.addEventListener("load", async ()=>{document.getElementById("contract_address").innerHTML=contract_address; try{LoadWeb3(); await ethereum.request({method: "eth_requestAccounts"}).then(HandleAccounts); await ethereum.request({method: "eth_chainId"}).then(HandleChainId); document.getElementById("account").innerHTML=account; let etcBalance=await ethereum.request({method: "eth_getBalance", params: [account, "latest"]}).then(WeiToEther); document.getElementById("etc_balance").innerHTML=etcBalance; document.getElementById("mint_amount_etc").value=Math.max(etcBalance - 0.1, 0); LoadContract(); let hodlBalance=await contract.methods.balanceOf(account).call().then(WeiToEther); document.getElementById("hodl_balance").innerHTML=hodlBalance; document.getElementById("burn_amount_hodl").value=hodlBalance; let etcBalanceContract=await ethereum.request({method: "eth_getBalance", params: [contract_address, "latest"]}).then(WeiToEther); let hodlTotalSupply=await contract.methods.totalSupply().call().then(WeiToEther); document.getElementById("hodl_etc_rate").innerHTML=(0.8 * etcBalanceContract) / hodlTotalSupply;}catch (error){alert(error.message); return;}}); function mint(){let mintAmountETC=document.getElementById("mint_amount_etc").value; if (mintAmountETC < 0){return;}web3.eth.sendTransaction({from: account, to: contract_address, value: EtherToWei(mintAmountETC),}, function (error, hash){if (error){alert(error.message);}else{alert("Mint transaction processing! Transaction hash: " + hash); console.log("Mint " + hash);}});}async function burn(){let burnAmountHODL=document.getElementById("burn_amount_hodl").value; if (burnAmountHODL < 0){return;}try{await contract.methods .withdraw(EtherToWei(burnAmountHODL)) .send({from: account}) .on("transactionHash", function (hash){alert("Burn transaction processing! Transaction hash: " + hash); console.log("Burn " + hash);});}catch (error){alert(error.message);}}</script> </head> <body> <div class="container"> <div class="row mt-3"> <div class="col"> <h1>ETC HODL Contract</h1> </div></div><div class="row"> <div class="col"> HODL is an ERC20 token on Ethereum Classic. Each token is backed by ETC. Deposit ETC to mint HODL, burn HODL to receive ETC -- minus a 20% withdrawal fee. Each withdrawal increases the amount of ETC that can be redeemed per unit of HODL. How long can you hodl? </div></div><div class="row mt-3"> <div class="col"> Contract address: <span id="contract_address">?</span> <br/> Your account: <span id="account">?</span> <br/> ETC balance: <span id="etc_balance">?</span> <br/> HODL balance: <span id="hodl_balance">?</span> <br/> ETC redeemable per 1 HODL: <span id="hodl_etc_rate">?</span> <br/> <a href="" class="link-primary">Refresh</a> </div></div><div class="row mt-3"> <div class="col"> <h2>Mint HODL (pay ETC)</h2> <form> <div class="mb-3"> <label for="mint_amount_etc" class="form-label">Enter ETC amount:</label> <input type="number" id="mint_amount_etc" class="form-control" min="0" step="any"/> </div><div> <input type="button" value="Mint HODL" onclick="mint()" class="btn btn-primary"/> </div></form> </div></div><div class="row mt-3"> <div class="col"> <h2>Burn HODL (receive ETC)</h2> <form> <div class="mb-3"> <label for="burn_amount_hodl" class="form-label">Enter HODL amount:</label> <input type="number" id="burn_amount_hodl" class="form-control" min="0" step="any"/> <div class="form-text">20% withdrawal fee</div></div><div> <input type="button" value="Burn HODL" onclick="burn()" class="btn btn-primary"/> </div></form> </div></div></div></body></html>

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Contract ABI
[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdrawal","type":"event"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]