Come gestire token in uno smart contract:Dopo tanta pazienza sono riuscito a capire come funzionano i trasferimenti, quindi cerco di spiegarvi come funziona il tutto, come al solito con esempi che potete provare da voi.
Contratto:
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 depositoToken(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 prelievoToken(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]);
}
}
Contratto token per i test:
https://github.com/ConsenSys/Tokens/tree/master/contracts/eip20Come depositare i token nel vostro smart contract?I tokens possono essere spediti direttamente all’indirizzo dello smart contract con il vostro client preferito, però questo modo non permette allo smart contract di tenere traccia di chi ha mandato i token, quindi non è ottimale. Per il risolvere il problema dobbiamo prima mappare un indirizzo di un token con la mappatura di un indirizzo di un proprietario e la quantità di tokens:
mapping (address => mapping (address => uint)) public tokens;
e permettere allo smart contract di spendere parte dei nostri token, questo avviene tramite il metodo approve del contratto del token che volete trasferire. Interfacciandosi direttamente al contratto del token basta chiamare:
approve(address _spender, uint256 _value)
A questo punto il contratto può spendere parte dei vostri token, fino a raggiungere l’ammontare specificato. In questo modo lo smart contract prende in consegna i tokens e può salvare il mittente e la quantità di tokens depositati tramite la seguente funzione:
function depositoToken(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]);
}
Come prelevare i token dal vostro smart contract?Per prelevare i token dallo smart contract non dobbiamo far altro che chiamare dal nostro smart contract la funzione transfer del contratto del token:
function transfer(address _to, uint256 _value) returns (bool success) {}
Che compare nella nostra funzione per il prelievo:
function prelievoToken(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]);
}
Piccola parentesi:In un primo momento per generare un token velocemente avevo utilizzato il codice presente qui, senza guardarci troppo:
https://www.ethereum.org/tokenCome potete vedere questo in questo contratto la funzione transfer non ritorna un boolean, quindi in questo caso quando chiamo la funzione transfer dal mio contratto mi ritorna sempre false e metamask mi avvisa che c’e’ un errore nel contratto, senza specificare dove. Dopo un pò che ci ho sbattuto la testa mi sono reso conto del problema e preso un contratto standard da qui
https://github.com/ConsenSys/Tokens/tree/master/contracts/eip20 anche se per risolvere il problema nel vecchio contratto basta aggiungere:
function transfer(address _from, address _to, uint256 _value) public returns (bool success) {
_transfer(_from, _to, _value);
return true;
}
Questo è tutto. Se qualcosa non ho chiaro chiedete pure.