Search code examples
blockchainethereumsoliditysmartcontractsweb3js

I have multiple token holders for a smart contract but it is still considering the first one


Considering the below code for communicating between web3js and ethereum smart contract in ropsten test net.

web3.eth.getTransactionCount(contractAddress).then(function(lastCountOfTransaction){
                    var rawTransaction = {
                        "from": contractAddress,
                        "nonce": "0x" + lastCountOfTransaction.toString(16),
                        "gasPrice": web3.utils.toHex(1 * 1e9), //1 can be changed to n gwei
                        "gasLimit": web3.utils.toHex(1000000), // 1000000 can be to set to any n number
                        "to": userAddress,
                        "value": "0x0",
                        "data": ContractObject.methods.transfer(userAddress, noOfTokens).encodeABI(),
                        "chainId": chainId
                    };

                    var tx = new Tx(rawTransaction);
                    tx.sign(privKey);
                    var serializedTx = tx.serialize();
                    web3.eth.sendSignedTransaction('0x' + serializedTx.toString('hex'),function(err,hash){
                        if (!err){
                            console.log(hash);
                            resolve(hash);
                        }
                        else{
                            console.log(err);
                            resolve(err);
                        }
                    });    
                }); 

I have multiple token holders like one is contract address which has initial value of tokens and one is token owner which has total number of supply. I want to give tokens from contract address and not from contracts owner account. Token transfer from owner account is working if we change like below

web3.eth.getTransactionCount(myAddress).then(function(lastCountOfTransaction){
                var rawTransaction = {
                    "from": myAddress,
                    "nonce": "0x" + lastCountOfTransaction.toString(16),
                    "gasPrice": web3.utils.toHex(1 * 1e9), //1 can be changed to n gwei
                    "gasLimit": web3.utils.toHex(1000000), // 1000000 can be to set to any n number
                    "to": contractAddress,
                    "value": "0x0",
                    "data": ContractObject.methods.transfer(userAddress, noOfTokens).encodeABI(),
                    "chainId": chainId
                };

But the above code is not working as expected. It do provide me transaction hash but in that tokens are not distributed.


Solution

  • Lets assume ERC20 token contract and check out different use cases.

    First thing you need to note is that all the transfers can be done by calling the contract only, not by communicating with each other directly. Now to the use cases...

    Token Holder A -> Token Holder B : In this scenario, Holder A wants to transfer some tokens to Holder B. If you sign a transaction using private key of holder A, then you can simply call the transfer function on contract at contractAddress. And this is what you are doing by sending transaction in below code (which will work if myAddress has a balance of tokens above noOfTokens in contract at contractAddress)

                var rawTransaction = {
                    "from": myAddress,
                    "nonce": "0x" + lastCountOfTransaction.toString(16),
                    "gasPrice": web3.utils.toHex(1 * 1e9), //1 can be changed to n gwei
                    "gasLimit": web3.utils.toHex(1000000), // 1000000 can be to set to any n number
                    "to": contractAddress,
                    "value": "0x0",
                    "data": ContractObject.methods.transfer(userAddress, noOfTokens).encodeABI(),
                    "chainId": chainId
                };
    

    Token Holder A -> Token Holder B via Address C : In this scenario, Address C wants to do a transaction on behalf of Holder A. As such the standard function to call is transferFrom. Assuming that some sort of approval has been given from Holder A to Address C, and that myAddress refers to Address C, you can use the below transaction

                var rawTransaction = {
                    "from": myAddress, // refers to Address C
                    "nonce": "0x" + lastCountOfTransaction.toString(16),
                    "gasPrice": web3.utils.toHex(1 * 1e9), //1 can be changed to n gwei
                    "gasLimit": web3.utils.toHex(1000000), // 1000000 can be to set to any n number
                    "to": contractAddress,
                    "value": "0x0",
                    "data": ContractObject.methods.transferFrom(userAddressA, userAddressB, noOfTokens).encodeABI(),
                    "chainId": chainId
                };
    

    This is as far as ERC20 goes. Refer https://theethereum.wiki/w/index.php/ERC20_Token_Standard for more info. What you are doing feels like minting, and ERC20 doesn't standardize that. What you can do here is that setup an owner of the contract and use a modifier like onlyOwner for a mint function in the contract. Such a mint function can look something like

    function mint(address _to, uint256 _amount) onlyOwner canMint returns (bool) {
        totalSupply = totalSupply.add(_amount);
        balances[_to] = balances[_to].add(_amount);
        Mint(_to, _amount);
        return true;
    }
    

    This way you can call the mint function from myAddress (if that address is the owner) and pass the userAddress who should receive the tokens. You can also choose to keep an upper limit and make the tokens capped by introducing check in the mint function or by adding an additional modifier (like canMint) to it. You can check out https://solidity.readthedocs.io/en/develop/contracts.html#function-modifiers for more info on modifiers.