Skip to content

Instantly share code, notes, and snippets.

@3esmit
Last active August 30, 2018 07:15
Show Gist options
  • Save 3esmit/00b79987e284aec80ad931fc7b9211f9 to your computer and use it in GitHub Desktop.
Save 3esmit/00b79987e284aec80ad931fc7b9211f9 to your computer and use it in GitHub Desktop.
Example of a ethereum Off chain Multi channel Token Bank ERC23 ERC20 ERC223 MiniMe
import "./TokenBank.sol";
pragma solidity ^0.4.9;
/**
* @title MultiChannel
* @author Ricardo Guilherme Schmidt <3esmit>
* MultiChannel is a ERC20 and ETH bank that allows multiple offchain transactions.
**/
contract MultiChannel is TokenBank {
function confirmTransactions(uint8 [] vs, bytes32 [] rs, bytes32 [] ss, address [] tokens, address [] senders, address [] receivers, uint [] values){
uint len = vs.length;
for (uint i = 0; i < len; i++){
confirmTransaction(vs[i],rs[i],ss[i],tokens[i],senders[i],receivers[i],values[i]);
}
}
function confirmTransaction(uint8 v, bytes32 r, bytes32 s, address _token, address _sender, address _receiver, uint _value) constant returns (bool){
require(ecrecover(sha3(_token, _receiver, _value), v, r, s) == _sender); //valid signature
return withdraw(_token, _receiver, _value, _sender);
}
}
// ECR23 standard token interface
pragma solidity ^0.4.11;
contract ERC20 {
uint256 public totalSupply;
function balanceOf(address who) constant returns (uint256);
function allowance(address owner, address spender) constant returns (uint256);
function transfer(address to, uint256 value) returns (bool ok);
function transferFrom(address from, address to, uint256 value) returns (bool ok);
function approve(address spender, uint256 value) returns (bool ok);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
contract ApproveAndCallFallBack {
function receiveApproval(address from, uint256 _amount, address _token, bytes _data);
}
contract ERC23Receiver {
function tokenFallback(address _from, uint _value, bytes _data);
}
contract ERC23 is ERC20 {
uint256 public decimals;
string public name;
string public symbol;
event Transfer(address indexed from, address indexed to, uint256 value, bytes data);
function transfer(address _to, uint _value, bytes _data) returns (bool ok);
}
contract Token is ERC23 {
}
import "./Token.sol";
pragma solidity ^0.4.11;
/**
* @title TokenBank
* @author Ricardo Guilherme Schmidt <3esmit>
* Abstract contract for deposit and withdraw of ETH and ERC20/23 Tokens, with MiniMe token support.
**/
contract TokenBank is ERC23Receiver, ApproveAndCallFallBack {
event Withdrawn(address token, address reciever, uint amount);
event Deposited(address token, address sender, uint value);
mapping (address => mapping (address => uint)) public deposits;
mapping (address => uint) public tokenBalances;
address[] public tokens;
uint public nonce;
/**
* @notice deposit ether in bank
* @param _data might be used by child implementations
**/
function depositEther(bytes _data) payable {
_deposited(msg.sender, msg.value, 0x0, _data);
}
/**
* @notice deposit a ERC20 token. The amount of deposit is the allowance set to this contract.
* @param _token the token contract address
* @param _data might be used by child implementations
**/
function depositToken(address _token, bytes _data){
address sender = msg.sender;
uint amount = ERC20(_token).allowance(sender, this);
deposit(sender, amount, _token, _data);
}
/**
* @notice deposit a ERC20 token. The amount of deposit is the allowance set to this contract.
* @param _token the token contract address
* @param _data might be used by child implementations
**/
function deposit(address _from, uint256 _amount, address _token, bytes _data) {
if(_from == address(this)) return;
uint _nonce = nonce;
ERC20 token = ERC20(_token);
if(!token.transferFrom(_from, this, _amount)) throw;
if(nonce == _nonce){ //ERC23 not executed _deposited tokenFallback by
_deposited(_from, _amount, _token, _data);
}
}
/**
* @notice watches for balance in a token contract
* @param _tokenAddr the token contract address
**/
function watch(address _tokenAddr) {
uint oldBal = tokenBalances[_tokenAddr];
uint newBal = ERC20(_tokenAddr).balanceOf(this);
if(newBal > oldBal){
_deposited(0x0,newBal-oldBal,_tokenAddr,new bytes(0));
}
}
/**
* @notice refunds a deposit.
* @param _token the token you want to refund
**/
function refund(address _token) returns (bool) {
address _sender = msg.sender;
withdraw(_token, _sender, deposits[_sender][_token], _sender);
return true;
}
/**
* @notice ERC23 Token fallback
* @param _from address incoming token
* @param _amount incoming amount
**/
function tokenFallback(address _from, uint _amount, bytes _data) {
_deposited(_from, _amount, msg.sender, _data);
}
/**
* @notice Called MiniMeToken approvesAndCall to this contract, calls deposit.
* @param _from address incoming token
* @param _amount incoming amount
* @param _token the token contract address
* @param _data (might be used by child classes)
*/
function receiveApproval(address _from, uint256 _amount, address _token, bytes _data){
deposit(_from, _amount, _token, _data);
}
/**
* @dev register the deposit to refundings
**/
function _deposited(address _sender, uint _amount, address _tokenAddr, bytes _data)
internal {
Deposited(_tokenAddr, _sender, _amount);
nonce++;
if(_tokenAddr != 0x0){
if(tokenBalances[_tokenAddr] == 0){
tokens.push(_tokenAddr);
tokenBalances[_tokenAddr] = ERC20(_tokenAddr).balanceOf(this);
}else{
tokenBalances[_tokenAddr] += _amount;
}
}
deposits[_sender][_tokenAddr] += _amount;
}
/**
* @dev withdraw token amount to dest
**/
function withdraw(address _tokenAddr, address _dest, uint _amount, address _consumer)
internal returns (bool){
Withdrawn(_tokenAddr, _dest, _amount);
if(_consumer != 0x0) {
require(deposits[_consumer][_tokenAddr] >= _amount);
deposits[_consumer][_tokenAddr] -= _amount;
}
if(_tokenAddr == 0x0){
_dest.transfer(_amount);
return true;
} else {
tokenBalances[_tokenAddr] -= _amount;
ERC20 token = ERC20(_tokenAddr);
token.approve(this, 0);
if(token.approve(this, _amount)){
return token.transferFrom(this, _dest, _amount);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment