Our particular constraints:
- using addresses as identification
- minimal UX trade-off
The solution: A contract that generates receiver contracts
The receiver address producing contract:
contract Factory {
address public owner;
mapping ( uint256 => address ) public receiversMap;
uint256 public receiverCount = 0;
constructor() public {
/*
Deployer's address ( Factory in our case )
do not pass this as a constructor argument because
etherscan will have issues displaying our validated source code
*/
owner = msg.sender;
}
/*
@notice Transfer Ownership of this contract to another address
@param newOwner - Address of the next Owner of the contract
*/
function transferOwner(address newOwner) public {
require (msg.sender == owner);
owner = newOwner;
}
/*
@notice Create a number of receiver contracts
@param number - 0-255
*/
function createReceivers( uint8 number ) public {
require(msg.sender == owner);
for(uint8 i = 0; i < number; i++) {
// Create and index our new receiver
receiversMap[++receiverCount] = new Receiver();
}
// add event here if you need it
}
/*
@notice Send funds in a receiver to another address
@param ID - Receiver indexed ID
@param tracker - ERC20 token tracker ( DAI / MKR / etc. )
@param amount - Amount of tokens to send
@param receiver - Address we're sending tokens to
@return true if transfer succeeded, false otherwise
*/
function sendFundsFromReceiverTo( uint256 ID, address tracker, uint256 amount, address receiver ) public returns (bool) {
require(msg.sender == owner);
return Receiver( receiversMap[ID] ).sendFundsTo( tracker, amount, receiver);
}
/*
Batch Collection - Should support a few hundred transansfers
@param tracker - ERC20 token tracker ( DAI / MKR / etc. )
@param receiver - Address we're sending tokens to
@param contractAddresses - we send an array of addresses instead of ids, so we don't need to read them ( lower gas cost )
@param amounts - array of amounts
*/
function batchCollect( address tracker, address receiver, address[] contractAddresses, uint256[] amounts ) public {
require(msg.sender == owner);
for(uint256 i = 0; i < contractAddresses.length; i++) {
// add exception handling
Receiver( contractAddresses[i] ).sendFundsTo( tracker, amounts[i], receiver);
}
}
}
The receiver contract:
contract Receiver {
address public owner;
constructor() public {
/*
Deployer's address ( Factory in our case )
do not pass this as a constructor argument because
etherscan will have issues displaying our validated source code
*/
owner = msg.sender;
}
/*
@notice Transfer Ownership of this contract to another address
@param newOwner - Address of the next Owner of the contract
*/
function transferOwner(address newOwner) public {
require (msg.sender == owner);
owner = newOwner;
}
/*
@notice Send funds owned by this contract to another address
@param tracker - ERC20 token tracker ( DAI / MKR / etc. )
@param amount - Amount of tokens to send
@param receiver - Address we're sending these tokens to
@return true if transfer succeeded, false otherwise
*/
function sendFundsTo( address tracker, uint256 amount, address receiver) public returns ( bool ) {
// callable only by the owner, not using modifiers to improve readability
require(msg.sender == owner);
// Transfer tokens from this address to the receiver
return ERC20(tracker).transfer(receiver, amount);
}
// depending on your system, you probably want to suicide this at some
// point in the future, or reuse it for other clients
}
The answer is based on Micky Socaci's solution here. The entire solution, with the necessary modifications, is hosted on My Github with a Creative Commons License so it can be dropped into any codebase.
A star would be appreciated if this helps you :).
https://github.com/Meshugah/ERC20-CommonGasWallet
transfer
event in the ERC20 token contract with the amount, sender and receiver. You will need a backend checking transactions to your master contract. The gain with this approach is reuced use of gas, if your service have many users this could be a substantial reduction in costs unless you are making your users to pay for this. Also the backend implementation is fairly simple for this application.