diff options
Diffstat (limited to 'Year_3/Blockchain/MafiaToken/MafiaToken.sol')
-rw-r--r-- | Year_3/Blockchain/MafiaToken/MafiaToken.sol | 278 |
1 files changed, 278 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; + } +} + |