Skip to content

Commit

Permalink
changed distributor contract to be a lot more gas efficient
Browse files Browse the repository at this point in the history
  • Loading branch information
pk910 committed Jun 29, 2023
1 parent e0d12b6 commit 9bc7dba
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 21 deletions.
34 changes: 34 additions & 0 deletions Contracts/Distributor.input.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"language": "Solidity",
"sources": {
"Contracts/Distributor.sol": {
"content": "// SPDX-License-Identifier: MIT\r\npragma solidity ^0.8.18;\r\n\r\ncontract Distributor {\r\n\r\n function bytesToAddress(bytes memory b) internal pure returns(address addr) {\r\n assembly {\r\n addr := mload(add(b, 20))\r\n }\r\n }\r\n\r\n function distribute(bytes calldata addrs, uint256[] calldata values) public payable {\r\n uint32 addrsLen = uint32(addrs.length);\r\n uint32 pos = 0;\r\n uint32 idx = 0;\r\n while (pos < addrsLen) {\r\n payable(bytesToAddress(addrs[pos:pos+20])).call{\r\n value: values[idx]\r\n }(\"\");\r\n unchecked { pos += 20; idx++; }\r\n }\r\n }\r\n\r\n function distributeGwei(bytes calldata addrs, uint64[] calldata values) public payable {\r\n uint32 addrsLen = uint32(addrs.length);\r\n uint32 pos = 0;\r\n uint32 idx = 0;\r\n while (pos < addrsLen) {\r\n payable(bytesToAddress(addrs[pos:pos+20])).call{\r\n value: uint256(values[idx]) * 1 gwei\r\n }(\"\");\r\n unchecked { pos += 20; idx++; }\r\n }\r\n }\r\n\r\n function distributeEther(bytes calldata addrs, uint32[] calldata values) public payable {\r\n uint32 addrsLen = uint32(addrs.length);\r\n uint32 pos = 0;\r\n uint32 idx = 0;\r\n while (pos < addrsLen) {\r\n payable(bytesToAddress(addrs[pos:pos+20])).call{\r\n value: uint256(values[idx]) * 1 ether\r\n }(\"\");\r\n unchecked { pos += 20; idx++; }\r\n }\r\n }\r\n\r\n function distributeEqual(bytes calldata addrs) public payable {\r\n uint32 addrsLen = uint32(addrs.length);\r\n uint256 amount = msg.value * 20 / addrsLen;\r\n uint32 pos = 0;\r\n while (pos < addrsLen) {\r\n payable(bytesToAddress(addrs[pos:pos+20])).call{value: amount}(\"\");\r\n unchecked { pos += 20; }\r\n }\r\n }\r\n\r\n}\r\n"
}
},
"settings": {
"optimizer": {
"enabled": true,
"runs": 2000
},
"outputSelection": {
"*": {
"": [
"ast"
],
"*": [
"abi",
"metadata",
"devdoc",
"userdoc",
"storageLayout",
"evm.legacyAssembly",
"evm.bytecode",
"evm.deployedBytecode",
"evm.methodIdentifiers",
"evm.gasEstimates",
"evm.assembly"
]
}
}
}
}
6 changes: 3 additions & 3 deletions Contracts/Distributor.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"abi": [{"inputs":[{"internalType":"address[]","name":"addrs","type":"address[]"},{"internalType":"uint256[]","name":"values","type":"uint256[]"}],"name":"distribute","outputs":[],"stateMutability":"payable","type":"function"}],
"bytecode": "608060405234801561001057600080fd5b5061033b806100206000396000f3fe60806040526004361061001e5760003560e01c80632929abe614610023575b600080fd5b61003661003136600461020b565b610038565b005b600034845b808310156101b65784848481811061005757610057610277565b905060200201358210156100a55760405162461bcd60e51b815260206004820152601060248201526f6e6f7420656e6f7567682066756e647360801b60448201526064015b60405180910390fd5b8484848181106100b7576100b7610277565b90506020020135826100c991906102a3565b915060008787858181106100df576100df610277565b90506020020160208101906100f491906102bc565b6001600160a01b031686868681811061010f5761010f610277565b9050602002013560405160006040518083038185875af1925050503d8060008114610156576040519150601f19603f3d011682016040523d82523d6000602084013e61015b565b606091505b50509050806101a35760405162461bcd60e51b81526020600482015260146024820152733330b4b632b2103a379039b2b7321032ba3432b960611b604482015260640161009c565b836101ad816102ec565b9450505061003d565b50505050505050565b60008083601f8401126101d157600080fd5b50813567ffffffffffffffff8111156101e957600080fd5b6020830191508360208260051b850101111561020457600080fd5b9250929050565b6000806000806040858703121561022157600080fd5b843567ffffffffffffffff8082111561023957600080fd5b610245888389016101bf565b9096509450602087013591508082111561025e57600080fd5b5061026b878288016101bf565b95989497509550505050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b818103818111156102b6576102b661028d565b92915050565b6000602082840312156102ce57600080fd5b81356001600160a01b03811681146102e557600080fd5b9392505050565b6000600182016102fe576102fe61028d565b506001019056fea26469706673582212209256378bc1a7f74d7cb00e29f243d33db16658195a339adca915519cb0553cdb64736f6c63430008110033",
"deployed": "60806040526004361061001e5760003560e01c80632929abe614610023575b600080fd5b61003661003136600461020b565b610038565b005b600034845b808310156101b65784848481811061005757610057610277565b905060200201358210156100a55760405162461bcd60e51b815260206004820152601060248201526f6e6f7420656e6f7567682066756e647360801b60448201526064015b60405180910390fd5b8484848181106100b7576100b7610277565b90506020020135826100c991906102a3565b915060008787858181106100df576100df610277565b90506020020160208101906100f491906102bc565b6001600160a01b031686868681811061010f5761010f610277565b9050602002013560405160006040518083038185875af1925050503d8060008114610156576040519150601f19603f3d011682016040523d82523d6000602084013e61015b565b606091505b50509050806101a35760405162461bcd60e51b81526020600482015260146024820152733330b4b632b2103a379039b2b7321032ba3432b960611b604482015260640161009c565b836101ad816102ec565b9450505061003d565b50505050505050565b60008083601f8401126101d157600080fd5b50813567ffffffffffffffff8111156101e957600080fd5b6020830191508360208260051b850101111561020457600080fd5b9250929050565b6000806000806040858703121561022157600080fd5b843567ffffffffffffffff8082111561023957600080fd5b610245888389016101bf565b9096509450602087013591508082111561025e57600080fd5b5061026b878288016101bf565b95989497509550505050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b818103818111156102b6576102b661028d565b92915050565b6000602082840312156102ce57600080fd5b81356001600160a01b03811681146102e557600080fd5b9392505050565b6000600182016102fe576102fe61028d565b506001019056fea26469706673582212209256378bc1a7f74d7cb00e29f243d33db16658195a339adca915519cb0553cdb64736f6c63430008110033"
"abi": [{"inputs":[{"internalType":"bytes","name":"addrs","type":"bytes"},{"internalType":"uint256[]","name":"values","type":"uint256[]"}],"name":"distribute","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"addrs","type":"bytes"}],"name":"distributeEqual","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"addrs","type":"bytes"},{"internalType":"uint32[]","name":"values","type":"uint32[]"}],"name":"distributeEther","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"addrs","type":"bytes"},{"internalType":"uint64[]","name":"values","type":"uint64[]"}],"name":"distributeGwei","outputs":[],"stateMutability":"payable","type":"function"}],
"bytecode": "608060405234801561001057600080fd5b506106a6806100206000396000f3fe60806040526004361061003f5760003560e01c806337b4a7b214610044578063985b7d6b14610059578063cb2223021461006c578063f85409631461007f575b600080fd5b6100576100523660046104b2565b610092565b005b6100576100673660046104b2565b6101c6565b61005761007a36600461051e565b6102a4565b61005761008d3660046104b2565b610361565b826000805b8263ffffffff168263ffffffff1610156101bd5761010f8763ffffffff8416886100c2866014610576565b63ffffffff16926100d59392919061059a565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061041d92505050565b73ffffffffffffffffffffffffffffffffffffffff1685858363ffffffff1681811061013d5761013d6105c4565b905060200201602081019061015291906105da565b61016a9063ffffffff16670de0b6b3a7640000610607565b604051600081818185875af1925050503d80600081146101a6576040519150601f19603f3d011682016040523d82523d6000602084013e6101ab565b606091505b50505060149190910190600101610097565b50505050505050565b826000805b8263ffffffff168263ffffffff1610156101bd576101f68763ffffffff8416886100c2866014610576565b73ffffffffffffffffffffffffffffffffffffffff1685858363ffffffff16818110610224576102246105c4565b90506020020160208101906102399190610624565b6102519067ffffffffffffffff16633b9aca00610607565b604051600081818185875af1925050503d806000811461028d576040519150601f19603f3d011682016040523d82523d6000602084013e610292565b606091505b505050601491909101906001016101cb565b80600063ffffffff82166102b9346014610607565b6102c3919061064e565b905060005b8263ffffffff168163ffffffff16101561035a576102f38563ffffffff8316866100c2856014610576565b73ffffffffffffffffffffffffffffffffffffffff168260405160006040518083038185875af1925050503d806000811461034a576040519150601f19603f3d011682016040523d82523d6000602084013e61034f565b606091505b5050506014016102c8565b5050505050565b826000805b8263ffffffff168263ffffffff1610156101bd576103918763ffffffff8416886100c2866014610576565b73ffffffffffffffffffffffffffffffffffffffff1685858363ffffffff168181106103bf576103bf6105c4565b9050602002013560405160006040518083038185875af1925050503d8060008114610406576040519150601f19603f3d011682016040523d82523d6000602084013e61040b565b606091505b50505060149190910190600101610366565b6014015190565b60008083601f84011261043657600080fd5b50813567ffffffffffffffff81111561044e57600080fd5b60208301915083602082850101111561046657600080fd5b9250929050565b60008083601f84011261047f57600080fd5b50813567ffffffffffffffff81111561049757600080fd5b6020830191508360208260051b850101111561046657600080fd5b600080600080604085870312156104c857600080fd5b843567ffffffffffffffff808211156104e057600080fd5b6104ec88838901610424565b9096509450602087013591508082111561050557600080fd5b506105128782880161046d565b95989497509550505050565b6000806020838503121561053157600080fd5b823567ffffffffffffffff81111561054857600080fd5b61055485828601610424565b90969095509350505050565b634e487b7160e01b600052601160045260246000fd5b63ffffffff81811683821601908082111561059357610593610560565b5092915050565b600080858511156105aa57600080fd5b838611156105b757600080fd5b5050820193919092039150565b634e487b7160e01b600052603260045260246000fd5b6000602082840312156105ec57600080fd5b813563ffffffff8116811461060057600080fd5b9392505050565b808202811582820484141761061e5761061e610560565b92915050565b60006020828403121561063657600080fd5b813567ffffffffffffffff8116811461060057600080fd5b60008261066b57634e487b7160e01b600052601260045260246000fd5b50049056fea264697066735822122064a073572ba17daeef9b562533a4b9d5bffaa2106d3f69d0367214fedf1c2f9164736f6c63430008120033",
"deployed": "60806040526004361061003f5760003560e01c806337b4a7b214610044578063985b7d6b14610059578063cb2223021461006c578063f85409631461007f575b600080fd5b6100576100523660046104b2565b610092565b005b6100576100673660046104b2565b6101c6565b61005761007a36600461051e565b6102a4565b61005761008d3660046104b2565b610361565b826000805b8263ffffffff168263ffffffff1610156101bd5761010f8763ffffffff8416886100c2866014610576565b63ffffffff16926100d59392919061059a565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061041d92505050565b73ffffffffffffffffffffffffffffffffffffffff1685858363ffffffff1681811061013d5761013d6105c4565b905060200201602081019061015291906105da565b61016a9063ffffffff16670de0b6b3a7640000610607565b604051600081818185875af1925050503d80600081146101a6576040519150601f19603f3d011682016040523d82523d6000602084013e6101ab565b606091505b50505060149190910190600101610097565b50505050505050565b826000805b8263ffffffff168263ffffffff1610156101bd576101f68763ffffffff8416886100c2866014610576565b73ffffffffffffffffffffffffffffffffffffffff1685858363ffffffff16818110610224576102246105c4565b90506020020160208101906102399190610624565b6102519067ffffffffffffffff16633b9aca00610607565b604051600081818185875af1925050503d806000811461028d576040519150601f19603f3d011682016040523d82523d6000602084013e610292565b606091505b505050601491909101906001016101cb565b80600063ffffffff82166102b9346014610607565b6102c3919061064e565b905060005b8263ffffffff168163ffffffff16101561035a576102f38563ffffffff8316866100c2856014610576565b73ffffffffffffffffffffffffffffffffffffffff168260405160006040518083038185875af1925050503d806000811461034a576040519150601f19603f3d011682016040523d82523d6000602084013e61034f565b606091505b5050506014016102c8565b5050505050565b826000805b8263ffffffff168263ffffffff1610156101bd576103918763ffffffff8416886100c2866014610576565b73ffffffffffffffffffffffffffffffffffffffff1685858363ffffffff168181106103bf576103bf6105c4565b9050602002013560405160006040518083038185875af1925050503d8060008114610406576040519150601f19603f3d011682016040523d82523d6000602084013e61040b565b606091505b50505060149190910190600101610366565b6014015190565b60008083601f84011261043657600080fd5b50813567ffffffffffffffff81111561044e57600080fd5b60208301915083602082850101111561046657600080fd5b9250929050565b60008083601f84011261047f57600080fd5b50813567ffffffffffffffff81111561049757600080fd5b6020830191508360208260051b850101111561046657600080fd5b600080600080604085870312156104c857600080fd5b843567ffffffffffffffff808211156104e057600080fd5b6104ec88838901610424565b9096509450602087013591508082111561050557600080fd5b506105128782880161046d565b95989497509550505050565b6000806020838503121561053157600080fd5b823567ffffffffffffffff81111561054857600080fd5b61055485828601610424565b90969095509350505050565b634e487b7160e01b600052601160045260246000fd5b63ffffffff81811683821601908082111561059357610593610560565b5092915050565b600080858511156105aa57600080fd5b838611156105b757600080fd5b5050820193919092039150565b634e487b7160e01b600052603260045260246000fd5b6000602082840312156105ec57600080fd5b813563ffffffff8116811461060057600080fd5b9392505050565b808202811582820484141761061e5761061e610560565b92915050565b60006020828403121561063657600080fd5b813567ffffffffffffffff8116811461060057600080fd5b60008261066b57634e487b7160e01b600052601260045260246000fd5b50049056fea264697066735822122064a073572ba17daeef9b562533a4b9d5bffaa2106d3f69d0367214fedf1c2f9164736f6c63430008120033"
}
59 changes: 48 additions & 11 deletions Contracts/Distributor.sol
Original file line number Diff line number Diff line change
@@ -1,20 +1,57 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
pragma solidity ^0.8.18;

contract Distributor {

function distribute(address[] calldata addrs, uint256[] calldata values) public payable {
uint256 i = 0;
uint256 balance = msg.value;
uint256 addrsLen = addrs.length;
while (i < addrsLen) {
require(balance >= values[i], "not enough funds");
balance -= values[i];
function bytesToAddress(bytes memory b) internal pure returns(address addr) {
assembly {
addr := mload(add(b, 20))
}
}

function distribute(bytes calldata addrs, uint256[] calldata values) public payable {
uint32 addrsLen = uint32(addrs.length);
uint32 pos = 0;
uint32 idx = 0;
while (pos < addrsLen) {
payable(bytesToAddress(addrs[pos:pos+20])).call{
value: values[idx]
}("");
unchecked { pos += 20; idx++; }
}
}

(bool sent, ) = payable(addrs[i]).call{value: values[i]}("");
require(sent, "failed to send ether");
function distributeGwei(bytes calldata addrs, uint64[] calldata values) public payable {
uint32 addrsLen = uint32(addrs.length);
uint32 pos = 0;
uint32 idx = 0;
while (pos < addrsLen) {
payable(bytesToAddress(addrs[pos:pos+20])).call{
value: uint256(values[idx]) * 1 gwei
}("");
unchecked { pos += 20; idx++; }
}
}

function distributeEther(bytes calldata addrs, uint32[] calldata values) public payable {
uint32 addrsLen = uint32(addrs.length);
uint32 pos = 0;
uint32 idx = 0;
while (pos < addrsLen) {
payable(bytesToAddress(addrs[pos:pos+20])).call{
value: uint256(values[idx]) * 1 ether
}("");
unchecked { pos += 20; idx++; }
}
}

i++;
function distributeEqual(bytes calldata addrs) public payable {
uint32 addrsLen = uint32(addrs.length);
uint256 amount = msg.value * 20 / addrsLen;
uint32 pos = 0;
while (pos < addrsLen) {
payable(bytesToAddress(addrs[pos:pos+20])).call{value: amount}("");
unchecked { pos += 20; }
}
}

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "testnet-funding-tool",
"version": "1.0.8",
"version": "1.0.9",
"description": "",
"main": "index.js",
"scripts": {
Expand Down
27 changes: 21 additions & 6 deletions src/funding-tool.js
Original file line number Diff line number Diff line change
Expand Up @@ -271,8 +271,8 @@ function loadFundingFile(resArray, fundingList) {
if(fundingEntry.length < 2)
continue;
resArray.push({
address: fundingEntry[0],
amount: BigInt(fundingEntry[1].replace(/ETH/i, "000000000000000000"))
address: fundingEntry[0].trim(),
amount: BigInt(fundingEntry[1].replace(/[ \t]*ETH/i, "000000000000000000").replace(/[ \t]*gwei/i, "000000000").trim()),
});
}
}
Expand Down Expand Up @@ -505,18 +505,36 @@ async function deployDistributor() {

async function processFundingBatch(batch) {
var totalAmount = BigInt(0);
var amountsDict = {};
batch.forEach((entry) => {
if(options['verbose'])
console.log("process funding " + entry.address + ": " + weiToEth(entry.amount) + " ETH");
totalAmount += entry.amount;
stats.transferCount++;
amountsDict[entry.amount] = true;
});

if(totalAmount > wallet.balance && !wallet.offline) {
console.log(" batch size (" + weiToEth(totalAmount) + " ETH) exceeds wallet balance (" + weiToEth(wallet.balance) + " ETH)");
return;
}

var callData = null;
var addrs = Buffer.concat(batch.map((e) => Buffer.from(e.address.replace(/^0x/i, ""), "hex")));
if(Object.keys(amountsDict).length === 1) {
// all same amount, use distributeEqual(bytes calldata addrs)
callData = distributor.contract.methods.distributeEqual(addrs).encodeABI();
} else if(Object.keys(amountsDict).filter(a => (BigInt(a) % 1000000000000000000n) === 0n).length === 0) {
// only full ether amounts, use distributeEther(bytes calldata addrs, uint32[] calldata values)
callData = distributor.contract.methods.distributeEther(addrs, batch.map((e) => e.amount / 1000000000000000000n)).encodeABI();
} else if(Object.keys(amountsDict).filter(a => (BigInt(a) % 1000000000n) === 0n).length === 0) {
// only full gwei amounts, use distributeGwei(bytes calldata addrs, uint64[] calldata values)
callData = distributor.contract.methods.distributeGwei(addrs, batch.map((e) => e.amount / 1000000000n)).encodeABI();
} else {
// use distribute(bytes calldata addrs, uint256[] calldata values)
callData = distributor.contract.methods.distributeGwei(addrs, batch.map((e) => e.amount)).encodeABI();
}

var nonce = wallet.nonce;
var rawTx = {
nonce: nonce,
Expand All @@ -526,10 +544,7 @@ async function processFundingBatch(batch) {
from: wallet.addr,
to: distributor.addr,
value: "0x" + totalAmount.toString(16),
data: distributor.contract.methods.distribute(
batch.map((e) => e.address),
batch.map((e) => e.amount),
).encodeABI()
data: callData
};

var privateKey = Uint8Array.from(wallet.privkey);
Expand Down

0 comments on commit 9bc7dba

Please sign in to comment.