Contract:
pragma solidity ^0.4.4;
contract SafeMath {
function safeMul(uint a, uint b) internal returns (uint) {
uint c = a * b;
assert(a == 0 || c / a == b);
return c;
}
function safeSub(uint a, uint b) internal returns (uint) {
assert(b <= a);
return a - b;
}
function safeAdd(uint a, uint b) internal returns (uint) {
uint c = a + b;
assert(c>=a && c>=b);
return c;
}
function assert(bool assertion) internal {
if (!assertion) throw;
}
}
contract Token {
function balanceOf(address _owner) constant returns (uint256 balance) {}
function transfer(address _to, uint256 _value) returns (bool success) {}
function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {}
function approve(address _spender, uint256 _value) returns (bool success) {}
}
contract Banca is SafeMath{
mapping (address => mapping (address => uint)) public tokens; //questo mappa un indirizzo di un token con un la mappatura di un indirizzo di un proprietario e la quantità di quei tokens
event Deposit(address token, address user, uint amount, uint balance);
event Withdraw(address token, address user, uint amount, uint balance);
function Banca(){
}
function depositToken(address token, uint amount) {
if (token==0) throw;
if (!Token(token).transferFrom(msg.sender, this, amount)) throw;
tokens[token][msg.sender] = safeAdd(tokens[token][msg.sender], amount);
Deposit(token, msg.sender, amount, tokens[token][msg.sender]);
}
function withdrawToken(address token, uint amount) {
if (token==0) throw;
if (tokens[token][msg.sender] < amount) throw;
tokens[token][msg.sender] = safeSub(tokens[token][msg.sender], amount);
if (!Token(token).transfer(msg.sender, amount)) throw;
Withdraw(token, msg.sender, amount, tokens[token][msg.sender]);
}
}
Token contract for testing:
https://github.com/ConsenSys/Tokens/tree/master/contracts/eip20
How to deposit token in the smart contract?
Tokens can be sent directly to the addrress of the smart contract unisng any client, but in this way the smart contract cannot keep trace of who sent them. To solve the problem we can map each token address to the token owner and finally to the quantity of tokens:
mapping (address => mapping (address => uint)) public tokens;
and allow the smart contract to spend part of our tokens. This can be done through the method approve of the token contract that you want to transfer. So,you need to call directly from the token contract:
approve(address _spender, uint256 _value)
At this point the contract can spend part of your tokens, till it reachs the amount specified. In this way the contract can acquire the token and save the detail in the mapping we talked above:
function depositToken(address token, uint amount) {
if (token==0) throw;
if (!Token(token).transferFrom(msg.sender, this, amount)) throw;
tokens[token][msg.sender] = safeAdd(tokens[token][msg.sender], amount);
Deposit(token, msg.sender, amount, tokens[token][msg.sender]);
}
How to withdraw the token from the smart contract?
To withdraw the tokens from the smart contract we just need to call the trasfer function of the token contract:
function transfer(address _to, uint256 _value) returns (bool success) {}
Which you can see in the function:
function withdrawToken(address token, uint amount) public payable{
if (token==0) throw;
if (tokens[token][msg.sender] < amount) throw;
tokens[token][msg.sender] = safeSub(tokens[token][msg.sender], amount);
if (!Token(token).transfer(msg.sender, amount)) throw;
Withdraw(token, msg.sender, amount, tokens[token][msg.sender]);
}
A curiosity:
At first to generate my test tokens i used the code in here without paying much attention: https://www.ethereum.org/token
As you can see in this contract the function transfer is not returning a boolean, so in this case when i call transfer in the contract i wrote above, it always return false and metamask tells there is an error in the contract. After a while i realized this and used a different contract for the token. In any case to solve the problem you can just add:
function transfer(address _from, address _to, uint256 _value) public returns (bool success) {
_transfer(_from, _to, _value);
return true;
}