This contract is useful when you want to save gas to send ETH to multiple recipient. There are 2 ways to execute, one is send based on right in ETH value, another way is send from contract's ETH balance.
Few things worth to take note:
<?Php
$abi = '[{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"constant":true,"inputs":[],"name":"getEthBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address payable[]","name":"recipients","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"multiSendDiffEth","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"internalType":"address payable[]","name":"recipients","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"multiSendDiffEthFromContract","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address payable[]","name":"recipients","type":"address[]"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"multiSendFixedEth","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"internalType":"address payable[]","name":"recipients","type":"address[]"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"multiSendFixedEthFromContract","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]';
$bytecode = "608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610b4d806100606000396000f3fe60806040526004361061007b5760003560e01c8063893d20e81161004e578063893d20e8146102605780638da5cb5b146102b75780639749c04a1461030e578063eaeb15df1461045a5761007b565b806331a933911461008d578063390430361461014f5780633ccfd60b1461021e57806370ed0ada14610235575b6000803690501461008b57600080fd5b005b61014d600480360360408110156100a357600080fd5b81019080803590602001906401000000008111156100c057600080fd5b8201836020820111156100d257600080fd5b803590602001918460208302840111640100000000831117156100f457600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050509192919290803590602001909291905050506105b3565b005b34801561015b57600080fd5b5061021c6004803603604081101561017257600080fd5b810190808035906020019064010000000081111561018f57600080fd5b8201836020820111156101a157600080fd5b803590602001918460208302840111640100000000831117156101c357600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f8201169050808301925050505050505091929192908035906020019092919050505061065e565b005b34801561022a57600080fd5b50610233610763565b005b34801561024157600080fd5b5061024a610805565b6040518082815260200191505060405180910390f35b34801561026c57600080fd5b5061027561080d565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156102c357600080fd5b506102cc610836565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6104586004803603604081101561032457600080fd5b810190808035906020019064010000000081111561034157600080fd5b82018360208201111561035357600080fd5b8035906020019184602083028401116401000000008311171561037557600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050509192919290803590602001906401000000008111156103d557600080fd5b8201836020820111156103e757600080fd5b8035906020019184602083028401116401000000008311171561040957600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f82011690508083019250505050505050919291929050505061085b565b005b34801561046657600080fd5b506105b16004803603604081101561047d57600080fd5b810190808035906020019064010000000081111561049a57600080fd5b8201836020820111156104ac57600080fd5b803590602001918460208302840111640100000000831117156104ce57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f8201169050808301925050505050505091929192908035906020019064010000000081111561052e57600080fd5b82018360208201111561054057600080fd5b8035906020019184602083028401116401000000008311171561056257600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050509192919290505050610946565b005b60008251116105c157600080fd5b600081116105ce57600080fd5b3481835102146105dd57600080fd5b60008251905060008090505b81811015610658578381815181106105fd57fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff166108fc849081150290604051600060405180830381858888f1935050505015801561064a573d6000803e3d6000fd5b5080806001019150506105e9565b50505050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146106b757600080fd5b60008251116106c557600080fd5b600081116106d257600080fd5b478183510211156106e257600080fd5b60008251905060008090505b8181101561075d5783818151811061070257fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff166108fc849081150290604051600060405180830381858888f1935050505015801561074f573d6000803e3d6000fd5b5080806001019150506106ee565b50505050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146107bc57600080fd5b3373ffffffffffffffffffffffffffffffffffffffff166108fc479081150290604051600060405180830381858888f19350505050158015610802573d6000803e3d6000fd5b50565b600047905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600082511161086957600080fd5b805182511461087757600080fd5b600082519050600080905060008090505b8281101561093f57600084828151811061089e57fe5b60200260200101519050600081116108b557600080fd5b6108c88184610a9090919063ffffffff16565b9250348311156108d757600080fd5b8582815181106108e357fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050158015610930573d6000803e3d6000fd5b50508080600101915050610888565b5050505050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461099f57600080fd5b60008251116109ad57600080fd5b80518251146109bb57600080fd5b6000825190506000809050600047905060008090505b83811015610a885760008582815181106109e757fe5b60200260200101519050600081116109fe57600080fd5b610a118185610a9090919063ffffffff16565b935082841115610a2057600080fd5b868281518110610a2c57fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050158015610a79573d6000803e3d6000fd5b505080806001019150506109d1565b505050505050565b600080828401905083811015610b0e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b809150509291505056fea265627a7a72315820ac6172173b7ecde67c3fbf5d0b36985128e0d511b9f1698f1471990324830cd664736f6c63430005110032";
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<meta name="keywords" content="<?php echo $_HTML['meta']['keywords']?>">
<title><?php echo $_HTML['title']?></title>
<script src="https://cdn.jsdelivr.net/gh/ethereum/[email protected]/dist/web3.min.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/[email protected]/dist/ethjs.min.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/[email protected]/dist/ethjs-contract.min.js"></script>
<!-- Bootstrap core CSS -->
<link href="../media/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<link href="../media/vendor/enlighter/css/enlighterjs.min.css?<?php echo time()?>" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="../media/css/simple-sidebar.css?<?php echo time()?>" rel="stylesheet">
<!-- Bootstrap core JavaScript -->
<script src="../media/vendor/jquery/jquery.min.js"></script>
</head>
<body>
<div class="d-flex" id="wrapper">
<!-- Page Content -->
<div id="page-content-wrapper" >
<p>
<h6 class="mt-3">Experiment</h6>
<ul>
<li>Contract deployment <a target="_blank" href="https://ropsten.etherscan.io/address/0x4e58bd0521716a2da492165d58d83580190b9725"><img style='width:12px;height:12px;' src='../media/images/external_link.png'/></a>
</li>
<li>
✔ Call multiSendFixedEth with input of 2 <b>addresses with ETH balance</b> <a target="_blank" href="https://ropsten.etherscan.io/tx/0x6c7952906daa4212c4b4dcf0ac3df661471ceb66f52bdd8f6aa068336ae48f8c"><img style='width:12px;height:12px;' src='../media/images/external_link.png'/></a>. Gas consumed 39,481, with 19,740.5 gas per ETH transfer.
</li>
<li>
✖ Call multiSendFixedEth with input of 2 <b>zero ETH balance address</b> <a target="_blank" href="https://ropsten.etherscan.io/tx/0x1e6085f4cd6681b68e7789f81ef011e8cb322db4781184f845f25758b6385fea"><img style='width:12px;height:12px;' src='../media/images/external_link.png'/></a>. Gas consumed 91,981, with 45,990.5 gas per ETH transfer.
</li>
</ul>
</p>
<p>
<h6 class="mt-3">ABI</h6>
<textarea class="form-control" rows="5" id="comment" readonly><?php echo $abi;?></textarea>
</p>
<p>
<h6 class="mt-3">Bytecode</h6>
<textarea class="form-control" rows="5" id="comment" readonly><?php echo $bytecode;?></textarea>
</p>
<p>
<button id="deploy-contract" class="btn btn-primary">Deploy Contract</button> Via Meta Mask <b> OR </b> <a href="eth_sc_deploy_contract.php?data=<?php echo $bytecode?>&disable_fields=data,to" target="_parent">Use Manual Way</a>
</p>
<script>
try {
// the abi
const contractABI = JSON.parse('<?php echo $abi?>');
// bytecode
const contractBytecode = '<?php echo $bytecode?>';
var button = document.querySelector('button#deploy-contract');
button.addEventListener('click', async (e) => {
e.srcElement.innerHTML = "Check Connection ...";
// Modern dapp browsers...
if (window.ethereum) {
window.web3 = new Web3(ethereum);
try {
// Request account access if needed
await ethereum.enable();
e.srcElement.innerHTML = "Deploy Contract";
// Modern dapp browsers...
// Acccounts now exposed
startApp(window.web3);
} catch (error) {
e.srcElement.innerHTML = "Deploy Contract";
// User denied account access...
}
}
// Legacy dapp browsers...
else if (window.web3) {
window.web3 = new Web3(web3.currentProvider);
// Acccounts always exposed
e.srcElement.innerHTML = "Deploy Contract";
startApp(window.web3);
}
// Non-dapp browsers...
else {
e.srcElement.innerHTML = "Deploy Contract";
alert('Non-Ethereum browser detected. You should consider trying MetaMask!');
}
});
function startApp(web3) {
const eth = new Eth(web3.currentProvider);
const contract = new EthContract(eth);
eth.accounts().then((accounts) => {
const contractFactory = eth.contract(contractABI, contractBytecode, {
from: accounts[0],
gas: 470000,
});
// create a new contract
contractFactory.new((error, result) => {
// result null '0x928sdfk...' (i.e. the transaction hash)
});
});
}
} catch (e) {
alert("Caught! " + e.message);
}
</script>
</div>
<!-- /#page-content-wrapper -->
</div>
<!-- /#wrapper -->
</body>
<script src="../media/vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
<script src="../media/vendor/enlighter/js/enlighterjs.min.js"></script>
<!-- Menu Toggle Script -->
<script type="text/javascript">
$("#menu-toggle").click(function(e) {
e.preventDefault();
$("#wrapper").toggleClass("toggled");
});
// - highlight all pre + code tags (CSS3 selectors)
// - use javascript as default language
// - use theme "enlighter" as default theme
// - replace tabs with 2 spaces
EnlighterJS.init('pre', 'code', {
language : 'php',
theme: 'enlighter',
indent : 4
});
$(document).ready(function(){
$('[data-toggle="tooltip"]').tooltip();
});
</script>
<script type="text/javascript" src="../media/vendor/iframeresizer/js/iframeResizer.contentWindow.min.js"></script>
</html>
pragma solidity ^0.5.0;
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.4.0/contracts/math/SafeMath.sol";
contract MultiSendEth {
using SafeMath for uint256;
address public owner;
modifier onlyOwner(){
require(msg.sender == owner);
_;
}
constructor() public{
owner = msg.sender;
}
//accept eth deposit
function() external payable {
require(msg.data.length == 0); //to prevent invalid calls.
}
//getowner
function getOwner() public view returns (address) {
return owner;
}
//get eth balance
function getEthBalance() public view returns (uint256) {
return address(this).balance;
}
//withdraw whole balance
function withdraw() public onlyOwner{
msg.sender.transfer(address(this).balance);
}
//batch send fixed eth amount from sender
function multiSendFixedEth(address payable[] memory recipients, uint256 amount) public payable {
require(recipients.length > 0);
require(amount > 0);
require(recipients.length * amount == msg.value);
uint256 length = recipients.length;
for(uint256 i=0;i<length;i++) {
recipients[i].transfer(amount);
}
}
//batch send different eth amount from sender
function multiSendDiffEth(address payable[] memory recipients, uint256[] memory amounts) public payable {
require(recipients.length > 0);
require(recipients.length == amounts.length);
uint256 length = recipients.length;
uint256 currentSum = 0;
for (uint256 i = 0; i < length; i++) {
uint256 amount = amounts[i];
require(amount > 0);
currentSum = currentSum.add(amount);
require(currentSum <= msg.value);
recipients[i].transfer(amount);
}
}
//batch send fixed eth amount from contract
function multiSendFixedEthFromContract(address payable[] memory recipients, uint256 amount) public onlyOwner {
require(recipients.length > 0);
require(amount > 0);
require(recipients.length * amount <= address(this).balance);
uint256 length = recipients.length;
for(uint256 i=0;i<length;i++) {
recipients[i].transfer(amount);
}
}
//batch send different eth amount from contract
function multiSendDiffEthFromContract(address payable[] memory recipients, uint256[] memory amounts) public onlyOwner {
require(recipients.length > 0);
require(recipients.length == amounts.length);
uint256 length = recipients.length;
uint256 currentSum = 0;
uint256 currentEthBalance = address(this).balance;
for (uint256 i = 0; i < length; i++) {
uint256 amount = amounts[i];
require(amount > 0);
currentSum = currentSum.add(amount);
require(currentSum <= currentEthBalance);
recipients[i].transfer(amount);
}
}
}