summaryrefslogtreecommitdiff
path: root/Year_3/Blockchain/MafiaToken
diff options
context:
space:
mode:
authorSanto Cariotti <santo@dcariotti.me>2022-06-30 11:54:52 +0200
committerSanto Cariotti <santo@dcariotti.me>2022-06-30 11:54:52 +0200
commitb7aa53a106d4c48bd29d92219fae2407c86ec379 (patch)
treef58df4382ccedcb210a64a03a5e9cac9412bebd1 /Year_3/Blockchain/MafiaToken
parentdf800f05416aa11bd04501b6dd771307d05dc8b7 (diff)
Add MafiaToken
Diffstat (limited to 'Year_3/Blockchain/MafiaToken')
-rw-r--r--Year_3/Blockchain/MafiaToken/MafiaToken.sol278
-rw-r--r--Year_3/Blockchain/MafiaToken/exam-2022-06-24-specs.sol80
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;
+}