diff options
Diffstat (limited to 'Year_3/Blockchain')
| -rw-r--r-- | Year_3/Blockchain/MafiaToken/MafiaToken.sol | 278 | ||||
| -rw-r--r-- | Year_3/Blockchain/MafiaToken/exam-2022-06-24-specs.sol | 80 | 
2 files changed, 358 insertions, 0 deletions
| diff --git a/Year_3/Blockchain/MafiaToken/MafiaToken.sol b/Year_3/Blockchain/MafiaToken/MafiaToken.sol new file mode 100644 index 0000000..14723cc --- /dev/null +++ b/Year_3/Blockchain/MafiaToken/MafiaToken.sol @@ -0,0 +1,278 @@ +// Cariotti 1000001948 +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "./exam-2022-06-24-specs.sol"; + +contract MafiaToken is ERC20, MafiosoTokenSpecs { +    enum Role { USER, PICCIOTTO, GODFATHER } +    struct Mafioso { +        address id; +        uint tokens; +        Role role; +        bool active; +        uint8 children; +    } +     +    struct Approve { +        address owner; +        uint amount; +    } + +    mapping(address => Mafioso) mafiosi; +    address[] address_users; +    address public override immutable godfather; +    mapping(address => Approve[]) approves; + + +    uint public override totalSupply; + +    uint8 private pizzo; // Use `pizzoRate()` +    uint private salary; // Use `picciottiSalary()` + +    uint immutable creation_time; +    uint last_salary; + +    error GodfatherCantPay(uint min_tokens); + +    constructor(uint initialSupply) { +        totalSupply = initialSupply; +        godfather = msg.sender; + +        mafiosi[msg.sender] = Mafioso({ +            id: msg.sender, +            tokens: initialSupply, +            role: Role.GODFATHER, +            active: true, +            children: 0 +        }); +        address_users.push(msg.sender); +        salary = 100; // Default initial salary + +        creation_time = block.timestamp; +        last_salary = block.timestamp; +    } + +    modifier MustBeAMafioso(address account) { +        require(mafiosi[account].id != address(0), "This address is not a 'mafioso'"); + +        _; +    } + +    modifier MustBeTheGodfather() { +        require(msg.sender == godfather, "This action is available only for the godfather."); + +        _; +    } + +    modifier MustBeAtLeastAPicciotto(address account) { +        require( +            account == godfather || mafiosi[account].role == Role.PICCIOTTO, +            "This action is available only for the godfather and picciottis." +        ); + +        _; +    } + +    function balanceOf(address account) public view override MustBeAMafioso(account) returns (uint256) { +        return mafiosi[account].tokens; +    } + +    function _transfer_money(address sender, address recipient, uint amount) internal { +        uint to_godfather = ( +            (sender == godfather) ? +            0 : amount - (amount * pizzo / 100) +        ); +     +        mafiosi[sender].tokens -= amount; +        mafiosi[recipient].tokens += (amount - to_godfather); +        mafiosi[godfather].tokens += to_godfather; + +        emit Transfer(sender, recipient, (amount - to_godfather)); +        emit Transfer(recipient, godfather, to_godfather); +    } + +    function transfer(address recipient, uint256 amount)  +        public  +        override  +        MustBeAMafioso(recipient) MustBeAMafioso(msg.sender)  +        returns (bool)  +    { +        require(mafiosi[msg.sender].tokens >= amount, "You don't have enough tokens to make this transfer"); + +        _transfer_money(msg.sender, recipient, amount); + +        // Always returns a true +        return true; +    } + + +    function approve(address spender, uint256 amount) +        public +        override +        MustBeAMafioso(msg.sender) MustBeAMafioso(spender) +        returns (bool) +    { +        approves[spender].push(Approve({ +            owner: msg.sender, +            amount: amount +        })); + + +        emit Approval(msg.sender, spender, amount); + +        // Always return a true +        return true; +    } + +    function transferFrom(address sender, address recipient, uint256 amount)  +        public  +        override +        MustBeAMafioso(recipient) MustBeAMafioso(sender) +        returns (bool) +    { +        bool sent = false; +        for (uint8 i = 0; i < approves[msg.sender].length; ++i) { +            if (approves[msg.sender][i].owner == sender) { +                if (approves[msg.sender][i].amount >= amount) {   +                    _transfer_money(sender, recipient, amount); +                    approves[msg.sender][i].amount -= amount; +                    sent = true; +                } +            } +             +            if (sent) return true; +        } + +        return false; +    } + +    function allowance(address owner, address spender)  +        public view  +        override +        returns (uint256) +    { +        uint amount = 0; +        for (uint8 i = 0; i < approves[spender].length; ++i) { +            if (approves[spender][i].owner == owner) { +                amount += approves[spender][i].amount; +            } +        } + +        return amount; +    } + +    function picciotti() override public view returns (address[] memory) { +        address[] memory _mafiosi_list = new address[](address_users.length); + +        uint8 j = 0; + +        for (uint8 i = 0; i < address_users.length; ++i) { +            if (mafiosi[address_users[i]].role == Role.PICCIOTTO && +                mafiosi[address_users[i]].active +            ) { +                _mafiosi_list[j++] = address_users[i]; +            } +        } + +        return _mafiosi_list; +    } + +    function addPicciotto(address id, uint8 children) public override MustBeTheGodfather { +        mafiosi[id] = Mafioso({ +            id: id, +            tokens: 0, +            role: Role.PICCIOTTO, +            active: true, +            children: children +        }); +        address_users.push(id); +    } + +    function removePicciotto(address id) public override MustBeTheGodfather MustBeAMafioso(id) { +        mafiosi[id].active = false; +    } + +    function pizzoRate() public view override returns (uint8) { +        return pizzo; +    } + +    function setPizzoRate(uint8 rate) public override MustBeTheGodfather { +        require(rate >= 0 && rate <= 100, "You entered not valid value"); + +        pizzo = rate; +    } + +    function forgeNewTokens(uint additionalSupply) public override MustBeTheGodfather { +        require(additionalSupply >= 0, "You entered a negative rate"); + +        mafiosi[godfather].tokens += additionalSupply; +        totalSupply += additionalSupply; + +        /* This event have to me emitted? */ +        emit Transfer(address(0), godfather, additionalSupply); +    } + + +    function stealTokens(address robbed, uint amount) +        public +        override +        MustBeAtLeastAPicciotto(robbed)  +        MustBeAtLeastAPicciotto(msg.sender) +    { +        require(robbed != godfather, "You can't steal tokens from the godfather!"); +        require(mafiosi[robbed].tokens >= amount, "Robbed mafioso does not have enough tokens"); + + +        mafiosi[msg.sender].tokens += amount; +        mafiosi[robbed].tokens -= amount; + +        emit Transfer(robbed, msg.sender, amount); +    } + +    function picciottiSalary() public view override returns (uint) { +        return salary; +    } + +    function setPicciottiPerChildSalary(uint amount) public override MustBeTheGodfather { +        require(amount >= 0, "You entered a negative rate"); + +        salary = amount; +    } + +    function triggerMonthlyPicciottiSalary() public override { +        /* Change `30 days` value to test this method. */ +        require(block.timestamp >= (last_salary + 30 days), "Already paid."); + +        uint need_tokens = 0; + +        for (uint8 i = 0; i < address_users.length; ++i) { +            if (mafiosi[address_users[i]].role != Role.GODFATHER && +                mafiosi[address_users[i]].active +            ) { +                need_tokens += (salary * mafiosi[address_users[i]].children); +            } +        } + +        if (mafiosi[godfather].tokens < need_tokens) { +            revert GodfatherCantPay(need_tokens); +        } + +        for (uint8 i = 0; i < address_users.length; ++i) { +            if ( +                mafiosi[address_users[i]].role != Role.GODFATHER && +                mafiosi[address_users[i]].active +            ) { +                uint amount = (salary * mafiosi[address_users[i]].children); +                mafiosi[address_users[i]].tokens += amount; +                _transfer_money(godfather, address_users[i], amount); +            } +        } + +        mafiosi[godfather].tokens -= need_tokens; + +        last_salary = block.timestamp; +    } +} + diff --git a/Year_3/Blockchain/MafiaToken/exam-2022-06-24-specs.sol b/Year_3/Blockchain/MafiaToken/exam-2022-06-24-specs.sol new file mode 100644 index 0000000..ac59c10 --- /dev/null +++ b/Year_3/Blockchain/MafiaToken/exam-2022-06-24-specs.sol @@ -0,0 +1,80 @@ +/* +    Corso Blockchain & Cryptocurrencies - Compito di laboratorio del 24/06/2022 + +    Scrivere un contratto `MafiaToken` che implementi sia l'interfaccia standard `ERC20`, vista a lezione, che  +    quella addizionale `MafiosoTokenSpecs` che realizzi, usando il linguaggio Solidity, un  +    "particolare token" sulla piattaforma Ethereum. + +    I metodi standard dell'interfaccia `ERC20` hanno i seguenti compiti: +    - `totalSupply`: fornisce il totale dei token in circolazione; +    - `balanceOf`: fornisce l'ammontare di token associati ad uno specifico indirizzo; +    - `transfer`: trasferisce all'indirizzo `recipient` l'ammontare specificato attingendo dal conto del  +      chiamante; +    - `approve`: autorizza l'indirizzo `spender` a spendere al piu' l'ammontare specificato attingendo dal +      conto del chiamante; +    - `transferFrom`: trasferisce all'indirizzo `recipient` l'ammontare specificato (purche' pre-autorizzato +      tramite `approve`) attingendo dal conto dell'indirizzo `spender`; +    - `allowance`: riporta l'ammontare pre-autorizzato per delega (vedi `approve`) da `owner` a favore di  +      `spender`. + +    Passando all'interfaccia `MafiosoTokensSpecs`: questa prevede di identificare come "padrino" (godfather) +    il proprietario/creatore del contratto e dal riservargli alcune azioni previste tra cui: `addPicciotto`,  +    `removePicciotto`, `setPizzoRate`, `setMonthlyPicciottiSalary` e `forgeNewTokens`.  +    Viene anche gestito un gruppo di utenti speciali detti "picciotti" che unicamente il padrino puo' gestire. +    A tale gruppo sono permesse alcune azioni e ricevono uno "stipendio" mensile dal padrino. + +    In particolare: +    - `forgeNewTokens`, utilizzabile solo dal padrino, creano nuovi token e li accredita allo stesso; +    - `stealTokens`, utilizzabile dal padrino e dai picciotti, puo' essere usato per "rubare" token  +      all'indirizzo `robbed` specificato; +    - tramite `pizzoRate` e `setPizzoRate` e' possibile consultare e impostare, da parte del padrino, +      la percentuale che lo stesso trattiene ad ogni trasferimento effettuato tramite i metodi standard +      `transfer` e `transferFrom`; la percentuale e' espressa come un intero tra 0 e 100; +    - tramite `picciottiSalary` e `setPicciottiPerChildSalary` e' possibile consultare e impostare,  +      da parte del padrino, l'ammontare in token che lo stesso paghera' mensilmente agli stessi; il metodo  +      `triggerMonthlyPicciottiSalary`, invocabile da chiunque, effettuera' il pagamento agli stessi, con  +      periodicita' di 30 giorni, usando come data di riferimento quella di creazione del contratto. Il salario +      mensile per figlio predefinito alla creazione del contratto e' di 100 token. + +    L'evento standard `Approve` dovra' essere emesso in occasione di una pre-autorizzazione al transferimento e +    quello `Transfer` ogni qualvolta si configuri un trasferimento di proprieta' di token: anche per i metodi +    dell'interfaccia `MafiosoTokenSpecs`. +     +    Il contratto dovrà occuparsi di validare gli input ricevuti secondo criteri ovvi di sensatezza. + +    Declaration: we don't like Mafia, we are just making fun of it! +     +*/ + +// SPDX-License-Identifier: None + +pragma solidity ^0.8.0; + +/* https://eips.ethereum.org/EIPS/eip-20 */ +interface ERC20 { +    function totalSupply() external view returns (uint256); +    function balanceOf(address account) external view returns (uint256); +    function transfer(address recipient, uint256 amount) external returns (bool); +    function allowance(address owner, address spender) external view returns (uint256); +    function approve(address spender, uint256 amount) external returns (bool); +    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); + +    event Transfer(address indexed from, address indexed to, uint256 value); +    event Approval(address indexed owner, address indexed spender, uint256 value); +} + +interface MafiosoTokenSpecs { +  /* constructor(uint initialSupply) */ + +  function godfather() external view returns (address); +  function picciotti() external view returns (address[]); +  function addPicciotto(address id, uint8 children) external; +  function removePicciotto(address id) external; +  function forgeNewTokens(uint additionalSupply) external; +  function stealTokens(address robbed, uint amount) external; +  function pizzoRate() external view returns (uint8); +  function setPizzoRate(uint8 rate) external; +  function picciottiSalary() external view returns (uint); +  function setPicciottiPerChildSalary(uint amount) external; +  function triggerMonthlyPicciottiSalary() external; +} | 
