Author

Topic: [Programming] Smart Contracts by example (Read 245 times)

full member
Activity: 1064
Merit: 166
June 15, 2018, 08:18:53 AM
#5
How to manage tokens in a smart contract:


Contract:

Code:
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:
Code:
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:

Code:
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:

Code:
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:

Code:
function transfer(address _to, uint256 _value) returns (bool success) {}

Which you can see in the function:

Code:
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:

Code:
   function transfer(address _from, address _to, uint256 _value) public returns (bool success) {
        _transfer(_from, _to, _value);
        return true;
    }


full member
Activity: 1064
Merit: 166
If we want restrict certain action based on the address of the caller:


Code:
pragma solidity ^0.4.15;

contract SomeContract {
    mapping (address => bool) public users;
address owner;

modifier isContractOwner(){
require(msg.sender == owner);
_;
}
function SomeContract() public{
    owner = msg.sender;
}

    function addUsers (address[] userList) isContractOwner {
        for (uint i = 0; i < userList.length; i++) {
            users[userList[i]] = true;
        }
    }
   
    function doSomething() {
    // check if caller is in the list
    if (users[msg.sender] == false) throw;

    // do what you are supposed to do
    }
}

The user addresses are mapped with a boolean, this  will return automatically false if the address is not found there:

Code:
mapping (address => bool) public users;

We can pass the list of addresses which will be allowed to perform certain actions, by calling addUsers and passing a list like this (in Remix):

Code:
["0xaddress1","0xaddress2","0xaddress3"]

Finally doSomething() is verifing if the caller is in the list before to proceed, you can try to call it before and after and see what actually happen.








full member
Activity: 1064
Merit: 166
Good thread Makkara, I also enjoy coding Smart Contracts. That Cryptozombies game is a fun way to learn the basics.
What do you think of Truffle?

I didn't start yet to use Truffle, but there are quite good feedback from people who are using, that is why i added the link. If i ever start to work on some project i will try it, at the moment using Metamask and Remix for me is enough to understand some basic concept and see smart contract in action.

If you have any example / curiosity you want to share please go ahead  Smiley
sr. member
Activity: 607
Merit: 278
06/19/11 17:51 Bought BTC 259684.77 for 0.0101
Good thread Makkara, I also enjoy coding Smart Contracts. That Cryptozombies game is a fun way to learn the basics.
What do you think of Truffle?
full member
Activity: 1064
Merit: 166
Hi,

This thread has the purpose of showing small code example that should help to understand smart contract functionalities, so i invite everybody who wants to contributes to post example or anything interesting.

being a programmer i have started looking into smart contracts, doing experiments in order to learn it. At the moment i'm just curious to know how they are working and what you can actually do, without any particular end to it, since the time i have available is not much.  

I was wondering if anybody here wrote some smart contract and want to share some brief explanation of how some particular functionality works or ask for help / help somebody else that want to learn as well.

Here are some useful links to start:

Tools:

- http://remix.ethereum.org/
- https://metamask.io/
- http://truffleframework.com/ganache/

Test net:
- https://ropsten.etherscan.io
Guides:

- https://medium.com/@mvmurthy/ethereum-for-web-developers-890be23d1d0c
- https://cryptozombies.io/
- https://medium.com/@mycoralhealth/learn-to-securely-share-files-on-the-blockchain-with-ipfs-219ee47df54c - IPFS

Documentation:

- http://solidity.readthedocs.io




I share something i found out which was not very obvious imo.

I was trying to find a way to store with a smart contract an indeterminate amount of bytes, in a efficient way since the costs can be quite high as we all know. After several attempts i found out the EVM (ethereum virtual machine) is using internally word of 256bit -> 32byte, this means that if you try to save less than 32byte you still pay as if you would have transferred 32byte.


I verified this with this smart contract using http://remix.ethereum.org and Metamask


Code:
pragma solidity ^0.4.19;

contract SimpleStorage {
  bytes input;
  function setInput(bytes enterBytes){
    input = enterBytes;
  }
}

Trying to pass to setInput 1 byte
Code:
["0x00"]
or 32 bytes
Code:
["0x00","0xaa","0xff","0xaa","0xaa","0xaa","0xaa","0xaa",
"0x00","0xaa","0xff","0xaa","0xaa","0xaa","0xaa","0xaa",
"0x00","0xaa","0xff","0xaa","0xaa","0xaa","0xaa","0xaa",
"0x00","0xaa","0xff","0xaa","0xaa","0xaa","0xaa","0xaa"]

Metamask shows the data transmitted is not changing : Data included: 100 bytes

Finally passing 33 bytes
Code:
["0x00","0xaa","0xff","0xaa","0xaa","0xaa","0xaa","0xaa",
"0x00","0xaa","0xff","0xaa","0xaa","0xaa","0xaa","0xaa",
"0x00","0xaa","0xff","0xaa","0xaa","0xaa","0xaa","0xaa",
"0x00","0xaa","0xff","0xaa","0xaa","0xaa","0xaa","0xaa","0xaa"]

You get Data included: 132 bytes

 


another thing i have learned:

Once you create a token, for example with the code you can find here:
https://www.ethereum.org/token i was wondering how to make a system which would allow anyone to interact directly with the smart contract and receive them.


To make something like that working i used this function, but in reality you would need other checks which i didn’t add to make sure is safe and not abusable:

Code:
   // Notify x tokens were generated for the address specified
    event TokenGenerated(address indexed _address, uint _reward);

    function getFreeTokens(){
        uint amount = 100  * 10 ** uint256(decimals);
        
        if (balanceOf[msg.sender] >= amount) throw;
      
        totalSupply += amount;
        balanceOf[msg.sender] += amount;
        
        TokenGenerated(msg.sender, amount);
        
    }

The interesting thing is that if in the event TokenGenerated you put a different value from the actual one, for example:

Code:
TokenGenerated(msg.sender, 1000  * 10 ** uint256(decimals));        

The owner of the address may assume he received 1000 tokens (the transaction coming in will show 1000 tokens), but obviously going to check the balance you could see the amount of tokens is 100.

Instead if we don’t call at all TokenGenerated, the owner of the address still own 100 tokens, but they will not appear in his balance.

All this to show how important are the events and necessary to have updated information on the balances.
More informations here: http://solidity.readthedocs.io/en/v0.4.21/contracts.html#events

You can also verify everything wrote above checking the results on the testnet Ropsten https://ropsten.etherscan.io



How to implement a pattern which allow you to "change" your smart contract

A similar patter can be applied

Make the smart contract that will perform the operations and contain the data
Code:
pragma solidity ^0.4.9;

contract Son{
    uint result;

    function Son(uint initial_value) public {
        result= initial_value;
    }
    function setRisultato(uint value) {
        result= value;
    }
    function getResult() constant returns (uint) {
        return result;
    }
}

after you can create the "Official" contract which will be used, here we need to define the interface of the contract we plan to use underneath (Son), then create an instance of it and call the functions defined in the interface.

If after you want to change the logic, but you want to keep the "Official" contract which maybe is already in production, you can simply swap the address of the son contract.
Code:
pragma solidity ^0.4.9;

contract Son{
    function setResult(uint value);
    function getResult() constant returns(uint);
}

contract Father{
    Son son;
    function Father(address new_son) public {
        son= Son(new_son);
    }
    function setNewSon(address new_son) {
        son= Son(new_son);
    }
    function setResult(uint value) {
        return son.setResult(value);
    }
    function getResult()  constant returns(uint) {
        return son.getResult();
    }
        
}

There are different pattern that allow to change behavior of a smart contract.






Jump to: