How to Send Ethereum to Message Sender in Solidity?

Send Ethereum To Msg Sender Featured Image

In this Solidity tutorial, I will teach you how to send Ethereum to msg sender. You landed here today maybe because you were working on your Solidity smart contract and got probably got stuck at the point where you had to send out your contract’s remaining balance in Ether to the msg sender.

Well, there is a simple solution I have brought out for you.

Let us get started with the guide on how to send Ethereum to msg sender in Solidity right away!

What is msg.sender in Solidity?

The msg.sender is the address that has called or initiated a function or created a transaction. Now, this address could be of a contract or even a person like you and me.

To identify the initiator of a transaction or current call, we can make use of the globally available msg object. This msg object has a couple of properties tide to it. For example, we have a value, a sender, gas, and a data property on it.

The one that is of our interest today is the msg.sender property in Solidity.

Creating a Sample Lottery Smart Contract

To help you understand how to send Ethereum to msg sender, I will need a small smart contract for demonstration purposes. I will create a lottery smart contract. You can find the code to it in the below section.

Getting started with the steps to create our smart contract

  • To begin with, let us create a contracts directory in our main project directory
  • Next, we will create a file named Lottery.sol to house our Solidity code of our contract

This is what my Lottery.sol looks like this:

pragma solidity ^0.4.17;

contract Lottery {
    address public manager;
    address[] public players;

    function Lottery() public {
        manager = msg.sender;
    }

    function enter() public payable {
        require(msg.value > .01 ether);
        players.push(msg.sender);
    }

    function random() private view returns (uint256) {
        return uint256(keccak256(block.difficulty, now, players));
    }

    function pickWinner() public {
        require(manager == msg.sender);
        uint256 index = random() % players.length;
        players[index].transfer(this.balance);
        players = new address[](0);
    }

    function getPlayers() public view returns (address[]) {
        return players;
    }
}

Explaining the code:

The Solidity part of our smart contract is complete. It is time for a short explanation of the code so you understand better what is going on here.

  • The contract starts with declaring 2 variables – one is the address of the manager of the lottery and the other is an array of addresses of the players of the lottery.
  • We then immediately set the manager variable’s value as msg.sender indicating the person that has created the contract (the manager).
  • We then create an enter() function that called by anyone who wishes to enter. Even the manager himself can enter. This function requires the participant to pay at least 0.01 ether to be able to enter and get his/her address added in the array of players.
  • We then create the random() function which you must have noticed is a private function because we do not really want anyone outside the contract to call it. This random number generator creates a random number to be able to select a winner of the lottery.

It returns an unsigned integer value. It takes the block difficulty score, the current time and date, and the players to generate a random integer. A random number is then returned.

  • Next, we create a pickWinner() function that can be only by the manager. We do a small authentication here using the require() function again. The random number is then taken and passed into a mod operation against the length of the players array.

This result is stored in a variable named index. The index is now the index number of the player that is the winner. We then immediately transfer the remaining balance of the contract to the winner. We use the transfer(this.balance) function for this.

We also immediately reset the state of our contract without having to redeploy it over and over again by setting the player’s arrays length to 0.

  • We also have a getPlayer() function to retrieve the array of players.

Creating the Compile Script for the Contract

As you might have known by now, we create a compile script using Solc to compile Solidity code to retrieve the ABI and bytecode of the smart contract. These two are eventually deployed on the blockchain and can be read by the browser.

This is what my compile.js looks like. Please create the compile script’s file directly in your main project directory.

const path = require("path");
const fs = require("fs");
const solc = require("solc");

const lotteryPath = path.resolve(__dirname, "contracts", "Lottery.sol");
const source = fs.readFileSync(lotteryPath, "utf8");

module.exports = solc.compile(source, 1).contracts[":Lottery"];

Creating the Deploy Script for the Smart Contract

We shall now write the deploy script to be able to deploy our contract on the Rinkeby test network. Similar to the compile script, I will create the deploy script in the main project directory.

This is what my deploy.js file looks like:

const HDWalletProvider = require('@truffle/hdwallet-provider');
const Web3 = require('web3');
const { interface, bytecode } = require('./compile.js');

const provider = new HDWalletProvider(
    'key shrimp shell dump about fog death unit hard find doomy surprise,
    'https://rinkeby.infura.io/v3/900629bb1516987e9a08e762ef085a1a'
);

const web3 = new Web3(provider);

const deploy = async () => {
  const accounts = await web3.eth.getAccounts();
  console.log(`Attempting to deploy from account: ${accounts[0]}`);
  const result = await new web3.eth.Contract(JSON.parse(interface))
    .deploy({ data: bytecode })
    .send({ gas: '1000000', from: accounts[0], gasLimit: '10000000' });

  console.log(interface);
  console.log('--------------------------------------------------------------------------------------------------------');
  console.log(`The contract was successfully deployed to: ${result.options.address}`);
  provider.engine.stop();
};
deploy();

Please make sure you use your own MetaMask private key and Infura API key 😊.

Testing Deployment of the Smart Contract with Mocha

Let us now deploy the lottery smart contract to see if everything’s going well. However, this is not final. We are now only performing a simulation of deployment with Mocha to test our contract and see if everything’s good to go.

  • Start by creating a test directory in your project directory
  • Create your test file named Lottery.test.js
  • Make sure you have configured your package.json to allow Mocha for testing. If not, look for the “scripts” object and set the value for “test” as “mocha”
  • To run the script, pass the command: npm run test

This is what our test script looks like:

const assert = require('assert');
const ganache = require('ganache-cli');
const { beforeEach } = require('mocha');
const Web3 = require('web3');
const web3 = new Web3(ganache.provider());
const { interface, bytecode } = require('../compile.js');

let lottery;
let accounts;

beforeEach(async () => {
    accounts = await web3.eth.getAccounts()
    lottery = await new web3.eth.Contract(JSON.parse(interface))
        .deploy({ data: bytecode })
        .send({ from: accounts[0], gas: '300000' });
});

describe('Lottery', () => {
    it('deploys a contract', () => {
        assert.ok(lottery.options.address);
    });

    it('allows one account to enter', async () => {
        await lottery.methods.enter().send({
            from: accounts[0],
            value: web3.utils.toWei('.02', 'ether')
        })

        const players = await lottery.methods.getPlayers().call({
            from: accounts[0]
        })
        assert.equal(accounts[0], players[0]);
        assert.equal(1, players.length);
    });

    it('allows multiple account to enter', async () => {
        await lottery.methods.enter().send({
            from: accounts[0],
            value: web3.utils.toWei('.02', 'ether')
        });
        await lottery.methods.enter().send({
            from: accounts[1],
            value: web3.utils.toWei('.02', 'ether')
        });
        await lottery.methods.enter().send({
            from: accounts[2],
            value: web3.utils.toWei('.02', 'ether')
        });

        const players = await lottery.methods.getPlayers().call({
            from: accounts[0]
        })
        assert.equal(accounts[0], players[0]);
        assert.equal(accounts[1], players[1]);
        assert.equal(accounts[2], players[2]);
        assert.equal(3, players.length);
    });

    it('requires a min ETH to enter', async () => {
        try {
            await lottery.methods.enter().send({
                from: accounts[0],
                value: 200
            });
            assert(false); // purposefully hrows an error no matter what
        } catch (error) {
            assert(error); // 
        }
    });

    it('checks if only manager can pick winner', async () => {
        try {
            await lottery.methods.pickWinner().send({
                from: accounts[1]
            })
            assert(false);
        } catch (error) {
            assert(error);
        }
    });

    it('sends money to winner and resets lottery', async () => {
        await lottery.methods.enter().send({
            from: accounts[0],
            value: web3.utils.toWei('2', 'ether')
        })

        const initialBalance = await web3.eth.getBalance(accounts[0]);

        await lottery.methods.pickWinner().send({
            from: accounts[0]
        });

        const finalBalance = await web3.eth.getBalance(accounts[0]);
        const difference = finalBalance - initialBalance;
        console.log(difference);
        assert(difference > web3.utils.toWei('1.8', 'ether'));
    });

    it('checks if players array is empty', async () => {
        const players = await lottery.methods.getPlayers().call({
            from: accounts[0]
        })
        assert(players.length == 0);
    });
});

Running the test:

$ npm run test

> lottery@1.0.0 test
> mocha



  Lottery
    √ deploys a contract
    √ allows one account to enter (601ms)
    √ allows multiple account to enter (1184ms)
    √ requires a min ETH to enter (225ms)
    √ checks if only manager can pick winner (178ms)
1999953164000002000
    √ sends money to winner and resets lottery (653ms)
    √ checks if players array is empty (150ms)


  7 passing (6s)

Perfect! We are successfully passing all the tests.

Deploying the Smart Contract on Rinkeby Testnet

$ node deploy.js

Attempting to deploy from account: 0x5bA36E8dc1b0b5236f0a7330e0B34B70258F4De5

[{"constant":true,"inputs":[],"name":"manager","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"pickWinner","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getPlayers","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"enter","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"players","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]

--------------------------------------------------------------------------------------------------------

The contract was successfully deployed to: 0xC1e6c54E1546f7D97Deda849167b91E15674B9bF

Awesome! The contract was successfully deployed and we have received the ABI (interface) in the terminal.

Conclusion

Learn to send Ethereum to msg sender in Solidity.