From dac813f263679b4792b46606cbcd82607f97ca3d Mon Sep 17 00:00:00 2001 From: Molaryy Date: Sat, 26 Oct 2024 12:04:44 +0200 Subject: [PATCH 1/7] feat: added github action spell check + inco workshop --- .github/workflows/markdown.yaml | 10 ++++++++++ .../README.md | 6 +++--- .../SETUP.md | 2 +- .../step5/ERC20/ERC20.sol | 0 .../step5/ERC20/IERC20.sol | 0 .../step5/ERC20/extensions/IERC20Metadata.sol | 0 .../step5/ERC20/utils/Context.sol | 0 .../step5/ERC20/utils/SafeERC20.sol | 0 .../step5/ERC20/utils/TokenTimelock.sol | 0 9 files changed, 14 insertions(+), 4 deletions(-) rename p2p/{2.Starton_Smart_Contracts => 2.inco_liars-dice}/README.md (95%) rename p2p/{2.Starton_Smart_Contracts => 2.inco_liars-dice}/SETUP.md (98%) rename p2p/{2.Starton_Smart_Contracts => 2.inco_liars-dice}/step5/ERC20/ERC20.sol (100%) rename p2p/{2.Starton_Smart_Contracts => 2.inco_liars-dice}/step5/ERC20/IERC20.sol (100%) rename p2p/{2.Starton_Smart_Contracts => 2.inco_liars-dice}/step5/ERC20/extensions/IERC20Metadata.sol (100%) rename p2p/{2.Starton_Smart_Contracts => 2.inco_liars-dice}/step5/ERC20/utils/Context.sol (100%) rename p2p/{2.Starton_Smart_Contracts => 2.inco_liars-dice}/step5/ERC20/utils/SafeERC20.sol (100%) rename p2p/{2.Starton_Smart_Contracts => 2.inco_liars-dice}/step5/ERC20/utils/TokenTimelock.sol (100%) diff --git a/.github/workflows/markdown.yaml b/.github/workflows/markdown.yaml index 87a51418..f83ece5c 100644 --- a/.github/workflows/markdown.yaml +++ b/.github/workflows/markdown.yaml @@ -35,3 +35,13 @@ jobs: .github/workflows/config/.markdownlint.jsonc, ${{ steps.changed-files.outputs.all_changed_files }} separator: "," + spell-check: + runs-on: ubuntu-latest + steps: + - name: Spell checker + uses: actions/checkout@v3 + uses: rojopolis/spellcheck-github-actions@v0 + name: Spellcheck + with: + source_files: $(find . -type f -name "*.md" -print) + task_name: Markdown diff --git a/p2p/2.Starton_Smart_Contracts/README.md b/p2p/2.inco_liars-dice/README.md similarity index 95% rename from p2p/2.Starton_Smart_Contracts/README.md rename to p2p/2.inco_liars-dice/README.md index e3151142..81777bef 100644 --- a/p2p/2.Starton_Smart_Contracts/README.md +++ b/p2p/2.inco_liars-dice/README.md @@ -4,7 +4,7 @@ ✔️ Learn how to deploy and interact with a contract -✔️ Discover the Starton tools to easily build blockchain applications +✔️ Discover Inco and what's the FHE in an EVM ## Step 0: Initialization @@ -103,8 +103,8 @@ But there's still a lot to discover, here are a few links: ## Authors -| [
Ismaël Fall](https://github.com/Doozers) | [
Reza Rahemtola](https://github.com/RezaRahemtola) -| :---: | :---: | +| [
Mohammed JBILOU](https://github.com/Molaryy) | +| :---: |

Organization

diff --git a/p2p/2.Starton_Smart_Contracts/SETUP.md b/p2p/2.inco_liars-dice/SETUP.md similarity index 98% rename from p2p/2.Starton_Smart_Contracts/SETUP.md rename to p2p/2.inco_liars-dice/SETUP.md index bbf8f0f9..c28f7877 100644 --- a/p2p/2.Starton_Smart_Contracts/SETUP.md +++ b/p2p/2.inco_liars-dice/SETUP.md @@ -6,7 +6,7 @@ To make API calls, you will need to install a tool like [Postman](https://www.po > 💡 You can also use [Hoppscotch](https://hoppscotch.io/), [curl](https://curl.haxx.se/) (often already installed on your computer), or whatever other tool you want for your tests since they will be personal, but we highly recommend `postman` as we will explain you how to use it. -## Starton +## Forge In this workshop, you'll use [Starton](https://www.starton.io/), a service to easily connect Web2 applications to blockchain with a simple API. With it, you can do a lot of things: monitoring on-chain events, deploying an interacting with smart contracts, uploading files to IPFS... diff --git a/p2p/2.Starton_Smart_Contracts/step5/ERC20/ERC20.sol b/p2p/2.inco_liars-dice/step5/ERC20/ERC20.sol similarity index 100% rename from p2p/2.Starton_Smart_Contracts/step5/ERC20/ERC20.sol rename to p2p/2.inco_liars-dice/step5/ERC20/ERC20.sol diff --git a/p2p/2.Starton_Smart_Contracts/step5/ERC20/IERC20.sol b/p2p/2.inco_liars-dice/step5/ERC20/IERC20.sol similarity index 100% rename from p2p/2.Starton_Smart_Contracts/step5/ERC20/IERC20.sol rename to p2p/2.inco_liars-dice/step5/ERC20/IERC20.sol diff --git a/p2p/2.Starton_Smart_Contracts/step5/ERC20/extensions/IERC20Metadata.sol b/p2p/2.inco_liars-dice/step5/ERC20/extensions/IERC20Metadata.sol similarity index 100% rename from p2p/2.Starton_Smart_Contracts/step5/ERC20/extensions/IERC20Metadata.sol rename to p2p/2.inco_liars-dice/step5/ERC20/extensions/IERC20Metadata.sol diff --git a/p2p/2.Starton_Smart_Contracts/step5/ERC20/utils/Context.sol b/p2p/2.inco_liars-dice/step5/ERC20/utils/Context.sol similarity index 100% rename from p2p/2.Starton_Smart_Contracts/step5/ERC20/utils/Context.sol rename to p2p/2.inco_liars-dice/step5/ERC20/utils/Context.sol diff --git a/p2p/2.Starton_Smart_Contracts/step5/ERC20/utils/SafeERC20.sol b/p2p/2.inco_liars-dice/step5/ERC20/utils/SafeERC20.sol similarity index 100% rename from p2p/2.Starton_Smart_Contracts/step5/ERC20/utils/SafeERC20.sol rename to p2p/2.inco_liars-dice/step5/ERC20/utils/SafeERC20.sol diff --git a/p2p/2.Starton_Smart_Contracts/step5/ERC20/utils/TokenTimelock.sol b/p2p/2.inco_liars-dice/step5/ERC20/utils/TokenTimelock.sol similarity index 100% rename from p2p/2.Starton_Smart_Contracts/step5/ERC20/utils/TokenTimelock.sol rename to p2p/2.inco_liars-dice/step5/ERC20/utils/TokenTimelock.sol From 433f13e62be189046b7636b35d46a310a01ac4dc Mon Sep 17 00:00:00 2001 From: Molaryy Date: Sat, 26 Oct 2024 12:07:10 +0200 Subject: [PATCH 2/7] fix: workflow yaml syntax --- .github/workflows/markdown.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/markdown.yaml b/.github/workflows/markdown.yaml index f83ece5c..f9f1ed6e 100644 --- a/.github/workflows/markdown.yaml +++ b/.github/workflows/markdown.yaml @@ -38,10 +38,9 @@ jobs: spell-check: runs-on: ubuntu-latest steps: + - uses: actions/checkout@v3 - name: Spell checker - uses: actions/checkout@v3 uses: rojopolis/spellcheck-github-actions@v0 - name: Spellcheck with: source_files: $(find . -type f -name "*.md" -print) task_name: Markdown From 0fda89712bc0fb60b79fda3d803da0168067107e Mon Sep 17 00:00:00 2001 From: Molaryy Date: Sat, 26 Oct 2024 12:09:30 +0200 Subject: [PATCH 3/7] fix: find files --- .github/workflows/markdown.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/markdown.yaml b/.github/workflows/markdown.yaml index f9f1ed6e..cee8fbf6 100644 --- a/.github/workflows/markdown.yaml +++ b/.github/workflows/markdown.yaml @@ -39,8 +39,12 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 + - name: Find Markdown files + id: find_markdown + run: | + echo "files=$(find . -type f -name '*.md')" >> $GITHUB_ENV - name: Spell checker uses: rojopolis/spellcheck-github-actions@v0 with: - source_files: $(find . -type f -name "*.md" -print) + source_files: ${{ env.files }} task_name: Markdown From 728733f550187ceb524d2d4f7cfcd4765acd2120 Mon Sep 17 00:00:00 2001 From: Molaryy Date: Sat, 26 Oct 2024 12:41:33 +0200 Subject: [PATCH 4/7] feat: added spell config file --- .github/workflows/markdown.yaml | 7 +------ .spellcheck.yaml | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 6 deletions(-) create mode 100644 .spellcheck.yaml diff --git a/.github/workflows/markdown.yaml b/.github/workflows/markdown.yaml index cee8fbf6..a66a74c6 100644 --- a/.github/workflows/markdown.yaml +++ b/.github/workflows/markdown.yaml @@ -39,12 +39,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Find Markdown files - id: find_markdown - run: | - echo "files=$(find . -type f -name '*.md')" >> $GITHUB_ENV - name: Spell checker uses: rojopolis/spellcheck-github-actions@v0 with: - source_files: ${{ env.files }} - task_name: Markdown + config_path: .spellcheck.yaml diff --git a/.spellcheck.yaml b/.spellcheck.yaml new file mode 100644 index 00000000..a2997fa9 --- /dev/null +++ b/.spellcheck.yaml @@ -0,0 +1,16 @@ +matrix: +- name: Markdown + aspell: + lang: en + dictionary: + encoding: utf-8 + pipeline: + - pyspelling.filters.markdown: + - pyspelling.filters.html: + comments: false + ignores: + - code + - pre + sources: + - '**/*.md' + default_encoding: utf-8 From f0bc2cc946ebd929523ef51be9510344956d63b1 Mon Sep 17 00:00:00 2001 From: Molaryy Date: Sat, 26 Oct 2024 12:45:12 +0200 Subject: [PATCH 5/7] rm: spelling workflow --- .github/workflows/markdown.yaml | 8 -------- .spellcheck.yaml | 16 ---------------- 2 files changed, 24 deletions(-) delete mode 100644 .spellcheck.yaml diff --git a/.github/workflows/markdown.yaml b/.github/workflows/markdown.yaml index a66a74c6..87a51418 100644 --- a/.github/workflows/markdown.yaml +++ b/.github/workflows/markdown.yaml @@ -35,11 +35,3 @@ jobs: .github/workflows/config/.markdownlint.jsonc, ${{ steps.changed-files.outputs.all_changed_files }} separator: "," - spell-check: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Spell checker - uses: rojopolis/spellcheck-github-actions@v0 - with: - config_path: .spellcheck.yaml diff --git a/.spellcheck.yaml b/.spellcheck.yaml deleted file mode 100644 index a2997fa9..00000000 --- a/.spellcheck.yaml +++ /dev/null @@ -1,16 +0,0 @@ -matrix: -- name: Markdown - aspell: - lang: en - dictionary: - encoding: utf-8 - pipeline: - - pyspelling.filters.markdown: - - pyspelling.filters.html: - comments: false - ignores: - - code - - pre - sources: - - '**/*.md' - default_encoding: utf-8 From 123245aa5ae1779af56918c8370afcb18d68fe56 Mon Sep 17 00:00:00 2001 From: Molaryy Date: Tue, 5 Nov 2024 09:35:07 +0100 Subject: [PATCH 6/7] feat: first step explained --- .../README.md | 53 ++- p2p/2.Paper_Rock_Scissors/SETUP.md | 33 ++ p2p/2.inco_liars-dice/SETUP.md | 17 - p2p/2.inco_liars-dice/step5/ERC20/ERC20.sol | 360 ------------------ p2p/2.inco_liars-dice/step5/ERC20/IERC20.sol | 82 ---- .../step5/ERC20/extensions/IERC20Metadata.sol | 28 -- .../step5/ERC20/utils/Context.sol | 24 -- .../step5/ERC20/utils/SafeERC20.sol | 116 ------ .../step5/ERC20/utils/TokenTimelock.sol | 76 ---- 9 files changed, 79 insertions(+), 710 deletions(-) rename p2p/{2.inco_liars-dice => 2.Paper_Rock_Scissors}/README.md (71%) create mode 100644 p2p/2.Paper_Rock_Scissors/SETUP.md delete mode 100644 p2p/2.inco_liars-dice/SETUP.md delete mode 100644 p2p/2.inco_liars-dice/step5/ERC20/ERC20.sol delete mode 100644 p2p/2.inco_liars-dice/step5/ERC20/IERC20.sol delete mode 100644 p2p/2.inco_liars-dice/step5/ERC20/extensions/IERC20Metadata.sol delete mode 100644 p2p/2.inco_liars-dice/step5/ERC20/utils/Context.sol delete mode 100644 p2p/2.inco_liars-dice/step5/ERC20/utils/SafeERC20.sol delete mode 100644 p2p/2.inco_liars-dice/step5/ERC20/utils/TokenTimelock.sol diff --git a/p2p/2.inco_liars-dice/README.md b/p2p/2.Paper_Rock_Scissors/README.md similarity index 71% rename from p2p/2.inco_liars-dice/README.md rename to p2p/2.Paper_Rock_Scissors/README.md index 81777bef..b8c09299 100644 --- a/p2p/2.inco_liars-dice/README.md +++ b/p2p/2.Paper_Rock_Scissors/README.md @@ -1,23 +1,61 @@ -# Workshop 2 - Smart Contracts with Starton +# Workshop 2 - Roshambo Smart Contract ✔️ Create your first smart contract in Solidity ✔️ Learn how to deploy and interact with a contract -✔️ Discover Inco and what's the FHE in an EVM - ## Step 0: Initialization All the required information to install the workshop's dependencies are given in the [SETUP.md](./SETUP.md) ## Step 1 - Create a contract -Let's create your first contract in [Solidity](https://docs.soliditylang.org/en/v0.8.0/) 🚀 +### :bookmark_tabs: **Description**: + + +Let's create your Rock Paper Scissors contract in [Solidity](https://docs.soliditylang.org/en/v0.8.0/) 🚀 > 💡 You can use the online IDE [Remix](https://remix.ethereum.org/) which is really useful to develop contracts for the Ethereum blockchain. -Your objective is to create a contract named `Task1`: -- It should have an unsigned int variable named num and a function `incrAndRetrieve()` that increment num and returns its new value. -- It should also have a function named `worldify` that takes a `string` as an argument and return a `string` that is equal to your parameter followed by " world!" like this: `worldify("Hello") => Hello world!` + +Your objective is to create a contract named `Roshambo`. This contract will allow two players to play the game of rock-paper-scissors with an Ethereum bet. + +### Key Concepts: +- **State Variables**: These are variables whose values are permanently stored in the contract's storage. In this contract, we'll use state variables to store information such as players' addresses and their moves. +- **Constants**: Values that remain the same throughout the contract. + + +For example, is set to 0.001 ETH and defines the minimum amount a player must bet to participate. +### :pushpin: **Tasks**: +1. **Let's Raise the Stakes** 🎲 + - Add a constant called `MINIMAL_BET` to set a minimum bet amount—let's make it `0.001 ETH` to keep it interesting! + +2. **Set a Reveal Timeout** ⏳ + - To keep the game moving, add a `REVEAL_TIMEOUT` constant variable set to 5 minutes. This will prevent players from stalling too long. + +3. **Game logic** 🧑‍💻 + - To e able to handle the logic of the game you will need 2 variables, 2 integers that will handle the `initial bet` and one that represents the `first reveal` of the player. + +3. **Representing Rock, Paper, Scissors** ✊🖐✌️ + - Since our game revolves around Rock, Paper, Scissors, find a way to represent these choices in Solidity! Also, think about how to handle any invalid choices a player might enter. + +4. **Define Game Outcomes** 🏆 + - There is a [type](https://docs.soliditylang.org/en/latest/types.html) that can help you represent possible outcomes: one of the 2 players wins, or it's a Draw or is invalid due to incorrect input from the user. This will make it easy to handle the end result. + +5. **Set Up Player Variables** 👤👥 + - Add variables for the two players. They should be able to place bets, so consider that user needs to [pay](https://ethereum.stackexchange.com/questions/64108/whats-the-difference-between-address-and-address-payable)! + +6. **Secret Moves with Encoding** 🔒 + - To keep each player's choice hidden until both have played, create two variables to store encoded moves—one for each player. This will allow us to reveal the moves only at the right time. + +7. **Track Moves for Both Players** ⚔️ + - Lastly, set up two variables that store each player's actual choice (once revealed). These will use the `Moves` type you created above in the step 3. + + +## Tasks: +- **Create the Contract**: Start by creating a new file named Roshambo.sol in Remix or in your IDE in the `src/` folder and define a contract structure. + + +### :books: **Documentation**:
How to easily test your contract 🤔
@@ -29,6 +67,7 @@ Your objective is to create a contract named `Task1`:
+### ✔️ **Validation**: Finally you need to compile your smart contract and copy the [ABI and Bytecode](https://blog.chain.link/what-are-abi-and-bytecode-in-solidity/), it will be useful for the next step 😉 ## Step 2 - Deploy with Starton diff --git a/p2p/2.Paper_Rock_Scissors/SETUP.md b/p2p/2.Paper_Rock_Scissors/SETUP.md new file mode 100644 index 00000000..96defb7e --- /dev/null +++ b/p2p/2.Paper_Rock_Scissors/SETUP.md @@ -0,0 +1,33 @@ +# Setup + +## Postman + +To make API calls, you will need to install a tool like [Postman](https://www.postman.com/downloads/). + +> 💡 You can also use [Hoppscotch](https://hoppscotch.io/), [curl](https://curl.haxx.se/) (often already installed on your computer), or whatever other tool you want for your tests since they will be personal, but we highly recommend `postman` as we will explain you how to use it. + +## Forge + +Please follow the instructions given [here](https://book.getfoundry.sh/getting-started/installation). + +After the installation, run the following command to ensure it has been properly installed on your computer: + +```shell +forge --version +``` + +It should print your current version. + +## Initialize your project + +Now we will initialize the project: + +```shell +forge init Roshambo +``` + +> For those wondering, Roshambo is simply another name for the classic game Rock, Paper, Scissors. + +## Back to the workshop + +[Jump !](./README.md) diff --git a/p2p/2.inco_liars-dice/SETUP.md b/p2p/2.inco_liars-dice/SETUP.md deleted file mode 100644 index c28f7877..00000000 --- a/p2p/2.inco_liars-dice/SETUP.md +++ /dev/null @@ -1,17 +0,0 @@ -# Setup - -## Postman - -To make API calls, you will need to install a tool like [Postman](https://www.postman.com/downloads/). - -> 💡 You can also use [Hoppscotch](https://hoppscotch.io/), [curl](https://curl.haxx.se/) (often already installed on your computer), or whatever other tool you want for your tests since they will be personal, but we highly recommend `postman` as we will explain you how to use it. - -## Forge - -In this workshop, you'll use [Starton](https://www.starton.io/), a service to easily connect Web2 applications to blockchain with a simple API. -With it, you can do a lot of things: monitoring on-chain events, deploying an interacting with smart contracts, uploading files to IPFS... -It's a great starting point for web developers who wants to get involved in blockchain and Web3 without a steep learning curve. - -The only thing you need to do is to create an account on and you're ready to go 🚀 - -[Go back to the exercises](./README.md) diff --git a/p2p/2.inco_liars-dice/step5/ERC20/ERC20.sol b/p2p/2.inco_liars-dice/step5/ERC20/ERC20.sol deleted file mode 100644 index 0ef01466..00000000 --- a/p2p/2.inco_liars-dice/step5/ERC20/ERC20.sol +++ /dev/null @@ -1,360 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/ERC20.sol) - -pragma solidity ^0.8.0; - -import "./IERC20.sol"; -import "./extensions/IERC20Metadata.sol"; -import "./utils/Context.sol"; - -/** - * @dev Implementation of the {IERC20} interface. - * - * This implementation is agnostic to the way tokens are created. This means - * that a supply mechanism has to be added in a derived contract using {_mint}. - * For a generic mechanism see {ERC20PresetMinterPauser}. - * - * TIP: For a detailed writeup see our guide - * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How - * to implement supply mechanisms]. - * - * We have followed general OpenZeppelin Contracts guidelines: functions revert - * instead returning `false` on failure. This behavior is nonetheless - * conventional and does not conflict with the expectations of ERC20 - * applications. - * - * Additionally, an {Approval} event is emitted on calls to {transferFrom}. - * This allows applications to reconstruct the allowance for all accounts just - * by listening to said events. Other implementations of the EIP may not emit - * these events, as it isn't required by the specification. - * - * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} - * functions have been added to mitigate the well-known issues around setting - * allowances. See {IERC20-approve}. - */ -contract ERC20 is Context, IERC20, IERC20Metadata { - mapping(address => uint256) private _balances; - - mapping(address => mapping(address => uint256)) private _allowances; - - uint256 private _totalSupply; - - string private _name; - string private _symbol; - - /** - * @dev Sets the values for {name} and {symbol}. - * - * The default value of {decimals} is 18. To select a different value for - * {decimals} you should overload it. - * - * All two of these values are immutable: they can only be set once during - * construction. - */ - constructor(string memory name_, string memory symbol_) { - _name = name_; - _symbol = symbol_; - } - - /** - * @dev Returns the name of the token. - */ - function name() public view virtual override returns (string memory) { - return _name; - } - - /** - * @dev Returns the symbol of the token, usually a shorter version of the - * name. - */ - function symbol() public view virtual override returns (string memory) { - return _symbol; - } - - /** - * @dev Returns the number of decimals used to get its user representation. - * For example, if `decimals` equals `2`, a balance of `505` tokens should - * be displayed to a user as `5.05` (`505 / 10 ** 2`). - * - * Tokens usually opt for a value of 18, imitating the relationship between - * Ether and Wei. This is the value {ERC20} uses, unless this function is - * overridden; - * - * NOTE: This information is only used for _display_ purposes: it in - * no way affects any of the arithmetic of the contract, including - * {IERC20-balanceOf} and {IERC20-transfer}. - */ - function decimals() public view virtual override returns (uint8) { - return 18; - } - - /** - * @dev See {IERC20-totalSupply}. - */ - function totalSupply() public view virtual override returns (uint256) { - return _totalSupply; - } - - /** - * @dev See {IERC20-balanceOf}. - */ - function balanceOf(address account) public view virtual override returns (uint256) { - return _balances[account]; - } - - /** - * @dev See {IERC20-transfer}. - * - * Requirements: - * - * - `to` cannot be the zero address. - * - the caller must have a balance of at least `amount`. - */ - function transfer(address to, uint256 amount) public virtual override returns (bool) { - address owner = _msgSender(); - _transfer(owner, to, amount); - return true; - } - - /** - * @dev See {IERC20-allowance}. - */ - function allowance(address owner, address spender) public view virtual override returns (uint256) { - return _allowances[owner][spender]; - } - - /** - * @dev See {IERC20-approve}. - * - * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on - * `transferFrom`. This is semantically equivalent to an infinite approval. - * - * Requirements: - * - * - `spender` cannot be the zero address. - */ - function approve(address spender, uint256 amount) public virtual override returns (bool) { - address owner = _msgSender(); - _approve(owner, spender, amount); - return true; - } - - /** - * @dev See {IERC20-transferFrom}. - * - * Emits an {Approval} event indicating the updated allowance. This is not - * required by the EIP. See the note at the beginning of {ERC20}. - * - * NOTE: Does not update the allowance if the current allowance - * is the maximum `uint256`. - * - * Requirements: - * - * - `from` and `to` cannot be the zero address. - * - `from` must have a balance of at least `amount`. - * - the caller must have allowance for ``from``'s tokens of at least - * `amount`. - */ - function transferFrom( - address from, - address to, - uint256 amount - ) public virtual override returns (bool) { - address spender = _msgSender(); - _spendAllowance(from, spender, amount); - _transfer(from, to, amount); - return true; - } - - /** - * @dev Atomically increases the allowance granted to `spender` by the caller. - * - * This is an alternative to {approve} that can be used as a mitigation for - * problems described in {IERC20-approve}. - * - * Emits an {Approval} event indicating the updated allowance. - * - * Requirements: - * - * - `spender` cannot be the zero address. - */ - function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { - address owner = _msgSender(); - _approve(owner, spender, allowance(owner, spender) + addedValue); - return true; - } - - /** - * @dev Atomically decreases the allowance granted to `spender` by the caller. - * - * This is an alternative to {approve} that can be used as a mitigation for - * problems described in {IERC20-approve}. - * - * Emits an {Approval} event indicating the updated allowance. - * - * Requirements: - * - * - `spender` cannot be the zero address. - * - `spender` must have allowance for the caller of at least - * `subtractedValue`. - */ - function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { - address owner = _msgSender(); - uint256 currentAllowance = allowance(owner, spender); - require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero"); - unchecked { - _approve(owner, spender, currentAllowance - subtractedValue); - } - - return true; - } - - /** - * @dev Moves `amount` of tokens from `from` to `to`. - * - * This internal function is equivalent to {transfer}, and can be used to - * e.g. implement automatic token fees, slashing mechanisms, etc. - * - * Emits a {Transfer} event. - * - * Requirements: - * - * - `from` cannot be the zero address. - * - `to` cannot be the zero address. - * - `from` must have a balance of at least `amount`. - */ - function _transfer( - address from, - address to, - uint256 amount - ) internal virtual { - - - // Oh some code is missing ! - - - } - - /** @dev Creates `amount` tokens and assigns them to `account`, increasing - * the total supply. - * - * Emits a {Transfer} event with `from` set to the zero address. - * - * Requirements: - * - * - `account` cannot be the zero address. - */ - function _mint(address account, uint256 amount) internal virtual { - - - // Oh some code is missing ? - - - } - - /** - * @dev Destroys `amount` tokens from `account`, reducing the - * total supply. - * - * Emits a {Transfer} event with `to` set to the zero address. - * - * Requirements: - * - * - `account` cannot be the zero address. - * - `account` must have at least `amount` tokens. - */ - function _burn(address account, uint256 amount) internal virtual { - - - // Oh some code is missing ! - - - } - - /** - * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. - * - * This internal function is equivalent to `approve`, and can be used to - * e.g. set automatic allowances for certain subsystems, etc. - * - * Emits an {Approval} event. - * - * Requirements: - * - * - `owner` cannot be the zero address. - * - `spender` cannot be the zero address. - */ - function _approve( - address owner, - address spender, - uint256 amount - ) internal virtual { - require(owner != address(0), "ERC20: approve from the zero address"); - require(spender != address(0), "ERC20: approve to the zero address"); - - _allowances[owner][spender] = amount; - emit Approval(owner, spender, amount); - } - - /** - * @dev Updates `owner` s allowance for `spender` based on spent `amount`. - * - * Does not update the allowance amount in case of infinite allowance. - * Revert if not enough allowance is available. - * - * Might emit an {Approval} event. - */ - function _spendAllowance( - address owner, - address spender, - uint256 amount - ) internal virtual { - uint256 currentAllowance = allowance(owner, spender); - if (currentAllowance != type(uint256).max) { - require(currentAllowance >= amount, "ERC20: insufficient allowance"); - unchecked { - _approve(owner, spender, currentAllowance - amount); - } - } - } - - /** - * @dev Hook that is called before any transfer of tokens. This includes - * minting and burning. - * - * Calling conditions: - * - * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens - * will be transferred to `to`. - * - when `from` is zero, `amount` tokens will be minted for `to`. - * - when `to` is zero, `amount` of ``from``'s tokens will be burned. - * - `from` and `to` are never both zero. - * - * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. - */ - function _beforeTokenTransfer( - address from, - address to, - uint256 amount - ) internal virtual {} - - /** - * @dev Hook that is called after any transfer of tokens. This includes - * minting and burning. - * - * Calling conditions: - * - * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens - * has been transferred to `to`. - * - when `from` is zero, `amount` tokens have been minted for `to`. - * - when `to` is zero, `amount` of ``from``'s tokens have been burned. - * - `from` and `to` are never both zero. - * - * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. - */ - function _afterTokenTransfer( - address from, - address to, - uint256 amount - ) internal virtual {} -} diff --git a/p2p/2.inco_liars-dice/step5/ERC20/IERC20.sol b/p2p/2.inco_liars-dice/step5/ERC20/IERC20.sol deleted file mode 100644 index b816bfed..00000000 --- a/p2p/2.inco_liars-dice/step5/ERC20/IERC20.sol +++ /dev/null @@ -1,82 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ -interface IERC20 { - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval(address indexed owner, address indexed spender, uint256 value); - - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `to`. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address to, uint256 amount) external returns (bool); - - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - */ - function allowance(address owner, address spender) external view returns (uint256); - - /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 amount) external returns (bool); - - /** - * @dev Moves `amount` tokens from `from` to `to` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom( - address from, - address to, - uint256 amount - ) external returns (bool); -} diff --git a/p2p/2.inco_liars-dice/step5/ERC20/extensions/IERC20Metadata.sol b/p2p/2.inco_liars-dice/step5/ERC20/extensions/IERC20Metadata.sol deleted file mode 100644 index 83ba6ac5..00000000 --- a/p2p/2.inco_liars-dice/step5/ERC20/extensions/IERC20Metadata.sol +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) - -pragma solidity ^0.8.0; - -import "../IERC20.sol"; - -/** - * @dev Interface for the optional metadata functions from the ERC20 standard. - * - * _Available since v4.1._ - */ -interface IERC20Metadata is IERC20 { - /** - * @dev Returns the name of the token. - */ - function name() external view returns (string memory); - - /** - * @dev Returns the symbol of the token. - */ - function symbol() external view returns (string memory); - - /** - * @dev Returns the decimals places of the token. - */ - function decimals() external view returns (uint8); -} diff --git a/p2p/2.inco_liars-dice/step5/ERC20/utils/Context.sol b/p2p/2.inco_liars-dice/step5/ERC20/utils/Context.sol deleted file mode 100644 index f304065b..00000000 --- a/p2p/2.inco_liars-dice/step5/ERC20/utils/Context.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract Context { - function _msgSender() internal view virtual returns (address) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes calldata) { - return msg.data; - } -} diff --git a/p2p/2.inco_liars-dice/step5/ERC20/utils/SafeERC20.sol b/p2p/2.inco_liars-dice/step5/ERC20/utils/SafeERC20.sol deleted file mode 100644 index 37d25f58..00000000 --- a/p2p/2.inco_liars-dice/step5/ERC20/utils/SafeERC20.sol +++ /dev/null @@ -1,116 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/utils/SafeERC20.sol) - -pragma solidity ^0.8.0; - -import "../IERC20.sol"; -import "../extensions/draft-IERC20Permit.sol"; -import "../../../utils/Address.sol"; - -/** - * @title SafeERC20 - * @dev Wrappers around ERC20 operations that throw on failure (when the token - * contract returns false). Tokens that return no value (and instead revert or - * throw on failure) are also supported, non-reverting calls are assumed to be - * successful. - * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, - * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. - */ -library SafeERC20 { - using Address for address; - - function safeTransfer( - IERC20 token, - address to, - uint256 value - ) internal { - _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); - } - - function safeTransferFrom( - IERC20 token, - address from, - address to, - uint256 value - ) internal { - _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); - } - - /** - * @dev Deprecated. This function has issues similar to the ones found in - * {IERC20-approve}, and its usage is discouraged. - * - * Whenever possible, use {safeIncreaseAllowance} and - * {safeDecreaseAllowance} instead. - */ - function safeApprove( - IERC20 token, - address spender, - uint256 value - ) internal { - // safeApprove should only be called when setting an initial allowance, - // or when resetting it to zero. To increase and decrease it, use - // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' - require( - (value == 0) || (token.allowance(address(this), spender) == 0), - "SafeERC20: approve from non-zero to non-zero allowance" - ); - _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); - } - - function safeIncreaseAllowance( - IERC20 token, - address spender, - uint256 value - ) internal { - uint256 newAllowance = token.allowance(address(this), spender) + value; - _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); - } - - function safeDecreaseAllowance( - IERC20 token, - address spender, - uint256 value - ) internal { - unchecked { - uint256 oldAllowance = token.allowance(address(this), spender); - require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); - uint256 newAllowance = oldAllowance - value; - _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); - } - } - - function safePermit( - IERC20Permit token, - address owner, - address spender, - uint256 value, - uint256 deadline, - uint8 v, - bytes32 r, - bytes32 s - ) internal { - uint256 nonceBefore = token.nonces(owner); - token.permit(owner, spender, value, deadline, v, r, s); - uint256 nonceAfter = token.nonces(owner); - require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed"); - } - - /** - * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement - * on the return value: the return value is optional (but if data is returned, it must not be false). - * @param token The token targeted by the call. - * @param data The call data (encoded using abi.encode or one of its variants). - */ - function _callOptionalReturn(IERC20 token, bytes memory data) private { - // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since - // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that - // the target address contains contract code and also asserts for success in the low-level call. - - bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); - if (returndata.length > 0) { - // Return data is optional - require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); - } - } -} diff --git a/p2p/2.inco_liars-dice/step5/ERC20/utils/TokenTimelock.sol b/p2p/2.inco_liars-dice/step5/ERC20/utils/TokenTimelock.sol deleted file mode 100644 index d879a7e7..00000000 --- a/p2p/2.inco_liars-dice/step5/ERC20/utils/TokenTimelock.sol +++ /dev/null @@ -1,76 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/utils/TokenTimelock.sol) - -pragma solidity ^0.8.0; - -import "./SafeERC20.sol"; - -/** - * @dev A token holder contract that will allow a beneficiary to extract the - * tokens after a given release time. - * - * Useful for simple vesting schedules like "advisors get all of their tokens - * after 1 year". - */ -contract TokenTimelock { - using SafeERC20 for IERC20; - - // ERC20 basic token contract being held - IERC20 private immutable _token; - - // beneficiary of tokens after they are released - address private immutable _beneficiary; - - // timestamp when token release is enabled - uint256 private immutable _releaseTime; - - /** - * @dev Deploys a timelock instance that is able to hold the token specified, and will only release it to - * `beneficiary_` when {release} is invoked after `releaseTime_`. The release time is specified as a Unix timestamp - * (in seconds). - */ - constructor( - IERC20 token_, - address beneficiary_, - uint256 releaseTime_ - ) { - require(releaseTime_ > block.timestamp, "TokenTimelock: release time is before current time"); - _token = token_; - _beneficiary = beneficiary_; - _releaseTime = releaseTime_; - } - - /** - * @dev Returns the token being held. - */ - function token() public view virtual returns (IERC20) { - return _token; - } - - /** - * @dev Returns the beneficiary that will receive the tokens. - */ - function beneficiary() public view virtual returns (address) { - return _beneficiary; - } - - /** - * @dev Returns the time when the tokens are released in seconds since Unix epoch (i.e. Unix timestamp). - */ - function releaseTime() public view virtual returns (uint256) { - return _releaseTime; - } - - /** - * @dev Transfers tokens held by the timelock to the beneficiary. Will only succeed if invoked after the release - * time. - */ - function release() public virtual { - require(block.timestamp >= releaseTime(), "TokenTimelock: current time is before release time"); - - uint256 amount = token().balanceOf(address(this)); - require(amount > 0, "TokenTimelock: no tokens to release"); - - token().safeTransfer(beneficiary(), amount); - } -} From 0b11f947315b9da8283b4dc8ea16b17e04c38598 Mon Sep 17 00:00:00 2001 From: Molaryy Date: Tue, 5 Nov 2024 17:44:43 +0100 Subject: [PATCH 7/7] feat: Roshambo workshop --- p2p/2.Paper_Rock_Scissors/README.md | 274 +++++++++++++----- .../krypto-tour/lib/forge-std | 1 + 2 files changed, 209 insertions(+), 66 deletions(-) create mode 160000 p2p/8.Introduction_Smart_Contract/krypto-tour/lib/forge-std diff --git a/p2p/2.Paper_Rock_Scissors/README.md b/p2p/2.Paper_Rock_Scissors/README.md index b8c09299..7e84ec56 100644 --- a/p2p/2.Paper_Rock_Scissors/README.md +++ b/p2p/2.Paper_Rock_Scissors/README.md @@ -9,8 +9,7 @@ All the required information to install the workshop's dependencies are given in the [SETUP.md](./SETUP.md) ## Step 1 - Create a contract -### :bookmark_tabs: **Description**: - +### 📖 **Description**: Let's create your Rock Paper Scissors contract in [Solidity](https://docs.soliditylang.org/en/v0.8.0/) 🚀 > 💡 You can use the online IDE [Remix](https://remix.ethereum.org/) which is really useful to develop contracts for the Ethereum blockchain. @@ -25,7 +24,9 @@ Your objective is to create a contract named `Roshambo`. This contract will allo For example, is set to 0.001 ETH and defines the minimum amount a player must bet to participate. -### :pushpin: **Tasks**: +### 🖌️ **Tasks**: +0. **Create a file named game.sol in the src/ folder** + - After doing a `forge init `, it will create a src/counter.sol, remove it and add a game.sol 1. **Let's Raise the Stakes** 🎲 - Add a constant called `MINIMAL_BET` to set a minimum bet amount—let's make it `0.001 ETH` to keep it interesting! @@ -50,95 +51,236 @@ For example, is set to 0.001 ETH and defines the minimum amount a player must b 7. **Track Moves for Both Players** ⚔️ - Lastly, set up two variables that store each player's actual choice (once revealed). These will use the `Moves` type you created above in the step 3. +### 📕 **Documentation**: +- Solidity [types](https://docs.soliditylang.org/en/latest/types.html) +- Scientific [anotation](https://ethereum.stackexchange.com/questions/85015/convert-a-string-in-scientific-notation-to-a-number) + +### ✔️ **Validation**: + +```sh +# You should build your project with forge +forge build + +➜ Correction git:(feat/p2p-liars-dice) forge build +[⠊] Compiling... +[⠒] Compiling 1 files with Solc 0.8.28 +[⠑] Installing Solc version 0.8.28 +[⠑] Successfully installed Solc 0.8.28 +[⠘] Solc 0.8.28 finished in 4.55s +Compiler run successful! +``` + +## Step 2 - User registration +📖 Description: +Now that you’ve created the main structure of your contract, it’s time to add functionality for user registration! This will allow two players to join the game and place their bets. -## Tasks: -- **Create the Contract**: Start by creating a new file named Roshambo.sol in Remix or in your IDE in the `src/` folder and define a contract structure. +Your contract should ensure that only two unique players can register, and they must each place a bet that meets the minimum bet requirements. +### Key Concepts: +- Modifiers: Modifiers allow us to add reusable logic to restrict or validate certain actions before executing a function. +- Functions: We’ll use functions to define the specific actions players can take, such as registering and placing a bet. + +### 🖌️ **Tasks**: +- Implement a Valid Bet Modifier 💰 + - Define a modifier called validBet that will ensure each player's bet meets the minimum requirement of BET_MIN and matches the initialBet once it's set. + - Ensure the modifier also checks that the initial bet is zero, meaning it's the first bet, or the bet amount matches the initial bet for consistency. +- Prevent Duplicate Registration 🔒 + - Use another modifier called notAlreadyRegistered to make sure the registering player is not already registered. + - This will prevent the same player from registering twice in the same game. +- Create the register Function 🧑‍🤝‍🧑 + - The register function should check both the validBet and notAlreadyRegistered conditions. + - When called, it should add the player’s address to either playerA or playerB depending on availability. +Return an identifier (1 or 2) indicating whether the player is playerA or playerB. + +### 📕 **Documentation**: +- How to deploy a contract with [anvil](https://egghead.io/lessons/solidity-deploy-your-smart-contract-to-anvil) +- What is a solidity [modifier](https://www.tutorialspoint.com/solidity/solidity_function_modifiers.htm) -### :books: **Documentation**: -
- How to easily test your contract 🤔 -
- Open the Deploy and run tab and click on the orange Deploy button:
- -
- This will deploy your contract in a Remix VM to test your contract in a fake environment.
- You can see your deployed contract right below, and interact with your functions:
- -
### ✔️ **Validation**: -Finally you need to compile your smart contract and copy the [ABI and Bytecode](https://blog.chain.link/what-are-abi-and-bytecode-in-solidity/), it will be useful for the next step 😉 -## Step 2 - Deploy with Starton -Now that you have a contract, let's deploy it with Starton 🚀 +Deploy your contract and try registering two different accounts with a minimum bet of 0.01 ETH (1 finney). Only two unique players should be able to register. +Ensure that if a third player tries to register, the contract returns 0, signaling that the registration is closed. + +## Step 3 - Start playing + +### 📖 Description: +With both players registered, it's time to enter the Commit Phase of the game. In this phase, each player will submit their move in an encrypted form to ensure fairness and prevent cheating. By committing to their moves upfront, players guarantee that they cannot change their choices after seeing the opponent's move. + +#### Key Concepts: +- Modifiers: Reusable pieces of code that can enforce certain conditions before executing a function. +- Encryption and Hashing: Techniques to securely encode data. In this context, players will hash their moves to keep them secret until the reveal phase. +- Function Visibility: Ensuring that only authorized users (registered players) can perform certain actions. + +### 🖌️ Tasks: +- Implement the isRegistered Modifier 👥 + - Create a modifier named isRegistered that ensures only players who have successfully registered (playerA or playerB) can call certain functions. + - This modifier will prevent unauthorized users from interacting with the game's core functions. +- Create the play Function 🎮 + - Develop a function called play that allows a registered player to submit their encrypted move. + - The function should accept a bytes32 parameter representing the hashed move. + - Ensure that each player can only submit their move once during the commit phase. +- Store Encrypted Moves Securely 🔒 + - Within the play function, store each player's encrypted move in their respective state variables (encrMovePlayerA and encrMovePlayerB). + - Make sure that once a move is submitted, it cannot be overwritten or changed. +- Validate Move Submissions ✅ + - Ensure that the play function returns true if a move is successfully recorded and false otherwise. + - Handle scenarios where a player attempts to submit a move multiple times or when both players have already committed their moves. +### ✔️ Validation: +- Testing the Commit Phase: + - Register Two Players: Ensure that two unique players have successfully registered and placed their bets. + - Submit Encrypted Moves: Have each player call the play function with their respective encrypted moves. + - Verify Move Storage: Check that the encrypted moves are correctly stored in the contract's state variables. + - Prevent Duplicate Moves: Attempt to submit a second move from the same player and confirm that the contract returns false, indicating that duplicate submissions are not allowed. + - Unauthorized Access: Try to call the play function from an address that is not registered and ensure that the transaction is reverted. + +## Step 4 - Reveal Phase +### 📖 **Description**: + +After both players have committed to their encrypted moves, it's time for the **Reveal Phase**! In this phase, each player will reveal their original (clear) move, allowing the contract to verify the choice against the previously submitted encrypted move. This ensures the integrity of the game by guaranteeing that neither player can change their move after seeing the opponent’s choice. -They provide a lot of [API routes](https://docs.starton.io/connect/api-doc/relayer/smart-contracts) for smart contracts, you have to find the right one to deploy your contract 😄 +### Key Concepts: -> Don't forget to use the information you retrieved in the previous step +- **Hash Comparison**: By comparing hashes of the clear moves to the stored encrypted moves, we can validate each player’s revealed choice. +- **Enums for Game Moves**: The `Moves` enum helps represent Rock, Paper, or Scissors in the game, allowing us to easily manage and compare choices. +- **Function with Custom Logic**: The `reveal` function uses hashing, string manipulation, and timing to securely handle the game’s reveal process. -If your request is successful, you should have a new contract in the **Interact** tab of your [Starton dashboard](https://app.starton.io/) +### 🖌️ **Tasks**: -> The whole objective of using a service like Starton is to integrate blockchain features without advanced knowledge with an API that takes care of it for you ⭐ +1. **Implement a Modifier to Ensure the Commit Phase Has Ended** ⏲️ + - Create a modifier named `commitPhaseEnded` that ensures both players have committed their encrypted moves before entering the reveal phase. + - This will prevent players from revealing their choices before both encrypted moves have been submitted. +2. **Create the `reveal` Function** 🔍 + - Define a function called `reveal` that accepts the clear move (as a string) and validates it against the previously committed encrypted move. + - Use the `isRegistered` and `commitPhaseEnded` modifiers to enforce that only registered players can call this function, and only after both moves are committed. -## Step 3 - Find and import a contract -You successfully deployed a contract, that's great 🎉 -In a real-world scenario though, you might want to interact with a contract that is already deployed by you or someone else. -You need to import a contract in Starton using a single information: -```text -0xE39aC98F23333589558f081870c6Ac8F0bdd6B1c -``` +3. **Hash and Validate Clear Moves** 🔐 + - Inside the `reveal` function, hash the provided clear move using `sha256`. + - Compare this hashed value with the stored encrypted move for the player. If they match, save the clear move in the appropriate variable (`movePlayerA` or `movePlayerB`) for further processing. -> This contract is deployed on Mumbai (the Polygon testnet) 😉 +4. **Store and Track Reveal Timing** ⏱️ + - When a player successfully reveals their move, set a `firstReveal` timestamp if it’s the first move revealed. This will be important for enforcing a reveal timeout and finalizing the game’s outcome. -> It can seems hard, but with a few searches you'll find what you need to import the contract +5. **Extract the Player’s Move Using `getFirstChar`** 📋 + - To determine the player’s actual move (Rock, Paper, or Scissors) from the clear move, use the helper function `getFirstChar`. This function maps the first character of the provided string to a move using the `Moves` enum. + - If an invalid move is provided, the function should return `Moves.None`. -## Step 4 - Interact with a template contract -Writing a contract in Solidity from scratch is great, but sometimes you just want to create something simple and generic and you don't want to spend time learning Solidity in depth, making sure your contract is secure... -Today's your lucky day, Starton has a service exactly for this: contract templates 🚀 +### ✔️ **Validation**: -Go to your [Starton dashboard](https://app.starton.io/) and create an [ERC-20](https://ethereum.org/en/developers/docs/standards/tokens/erc-20/) token with a fixed supply. -> If you want more challenge, you can try to deploy the template contract using the API instead of the UI -> You should find everything you need in their [documentation](https://docs.starton.io/), but don't hesitate to ask for help if you are stuck 😉 +- **Testing the Reveal Phase**: + - **Register and Commit**: Ensure two unique players are registered and have committed encrypted moves using `play`. + - **Reveal Valid Moves**: Each player should call the `reveal` function with the clear move string they used to generate their encrypted move. Verify that the moves are saved correctly if they match the encrypted ones. + - **Handle Invalid Moves**: Submit an invalid clear move and confirm that the function returns `Moves.None`, ensuring invalid inputs are handled properly. + - **Prevent Premature Reveal**: Attempt to reveal moves before both players have committed, ensuring the `commitPhaseEnded` modifier prevents it. -When your contract is deployed, you need to interact with it! -You have to make several API calls: -- The first will check if you really have the generated tokens in your wallet -> 💡 You could also see it in an explorer of the blockchain network you published your contract to, but the safest way is still to call the contract directly -- Then, find an call the route to call the `burn` function of your contract to reduce the token supply 🔥 -- Finally, check the balance again too make sure the tokens were destroyed -## Step 5 - Time for real Solidity -To further understand how a smart contract works, now we'll dive into a contract similar to the one you created in the previous step. -Don't worry, you won't have to write it entirely as it would be too long and complicated here. +## Step 5 - Result Phase +### 📖 **Description**: -In the boilerplate code we gave you in the ([`step5/ERC20`](./step5/ERC20/) folder), you'll find the contract files, but 3 functions were lost: -`_transfer`, `_mint` and `_burn`. -Your objective is to reimplement them in the `ERC20.sol` file to have a better understanding of the Solidity syntax. +With both players' moves revealed, the game can enter the **Result Phase**. In this phase, the contract determines the winner based on the moves provided, handles payouts, and resets the game for potential future rounds. -You'll need to use Remix again, but to import your files you need to use [remixd](https://www.npmjs.com/package/@remix-project/remixd) by running the following commands: -```shell -# Install the npm package globally -npm install -g @remix-project/remixd +### Key Concepts: + +- **Game Outcome Logic**: Determine the winner based on Rock, Paper, Scissors rules. +- **Timed Reveal Handling**: If a player fails to reveal their move within the designated time, the other player may be declared the winner by default. +- **Payout Mechanism**: Transfer the winnings to the appropriate player(s). +- **Reentrancy Protection**: Avoid vulnerabilities by resetting game state before transferring funds. + +### 🖌️ **Tasks**: + +1. **Implement a Modifier to Check if the Reveal Phase Has Ended** 🕰️ + - Define a modifier named `revealPhaseEnded` that ensures one of the following conditions: + - Both players have revealed their moves. + - The reveal timeout has elapsed since the first player revealed, allowing the game to proceed even if one player fails to reveal. + +2. **Create the `getOutcome` Function** 🏆 + - Define a function named `getOutcome` that: + - Uses the `revealPhaseEnded` modifier to ensure the reveal phase has ended. + - Compares the players’ moves to determine the outcome according to Rock, Paper, Scissors rules. + - Sets the `Outcomes` enum value accordingly, such as `Outcomes.PlayerA`, `Outcomes.PlayerB`, or `Outcomes.Draw`. + - Calls the `pay` function to distribute winnings. + - Returns the outcome to indicate who won or if it was a draw. + +3. **Payout Logic with `pay` Function** 💸 + - Implement a private function `pay` that: + - Receives addresses of the players and the outcome. + - Transfers the entire contract balance to the winner in cases of Player A or Player B wins. + - Splits the balance evenly in the event of a draw. + - Adjusts the gas limit if necessary to prevent failure in transaction calls (e.g., using `.call.value()`). + +4. **Reset the Game with `reset` Function** 🔄 + - Implement a private `reset` function to clear out all game-related data, including: + - Setting `playerA` and `playerB` addresses to empty. + - Resetting the moves, encrypted values, and timestamps to their initial states. + - Ensuring the game can start fresh for a new round without carrying over any old state. + +### ✔️ **Validation**: + +- **Testing the Result Phase**: + - **Normal Gameplay**: Register two players, commit, reveal moves, and call `getOutcome` to verify the correct outcome and payout. + - **Timeout Scenario**: Have one player reveal while the other fails to reveal within the timeout. Confirm that the outcome favors the player who revealed. + - **Draw Case**: Commit identical moves to both players, reveal them, and ensure that both receive an equal share of the payout. + - **Reentrancy Protection**: Ensure that the game state is reset before funds are transferred, protecting the contract from potential reentrancy attacks. + +## Step 6 - Helper Functions +### 📖 **Description**: + +In this final step, we'll add some helper functions to make the contract more user-friendly. These functions allow players to check essential game details, such as the contract's balance, their player ID, whether both players have committed or revealed their moves, and how much time is left in the reveal phase. + +### Key Concepts: + +- **View Functions**: These functions do not modify contract state but instead provide helpful information about the current state. +- **Balance Checking**: Enables players to see the contract's balance, which can be useful in understanding the stakes and ensuring proper payouts. +- **Status Checks**: Verifying the game status (e.g., both players committed or revealed) allows for better tracking and flow control. +- **Time Management**: Showing the time left in the reveal phase ensures transparency and helps players monitor deadlines. + +### 🖌️ **Tasks**: + +1. **Return Contract Balance** 💰 + - Implement `getContractBalance`, which returns the current balance of the contract. This helps players know how much ETH is staked for the game. + +2. **Identify Player** 👤 + - Implement `whoAmI`, which returns: + - `1` if the caller is `playerA` + - `2` if the caller is `playerB` + - `0` if the caller is neither player + - This function allows a player to check their role in the game. + +3. **Verify Both Players Have Committed** ⏳ + - Implement `bothPlayed`, which returns `true` if both players have committed their encrypted moves. This function helps determine if the game is ready for the reveal phase. + +4. **Verify Both Players Have Revealed** 🔍 + - Implement `bothRevealed`, which returns `true` if both players have revealed their moves. This is useful for determining if the result phase can proceed. + +5. **Display Reveal Phase Time Left** ⏲️ + - Implement `revealTimeLeft`, which: + - Returns the remaining time (in seconds) before the reveal phase ends if the first reveal has occurred. + - Otherwise, it returns the full reveal timeout. + - This function helps players monitor the remaining time to complete the reveal phase, avoiding accidental forfeiture due to timeout. + +### ✔️ **Validation**: + +- **Checking Helper Functions**: + - **Contract Balance**: Call `getContractBalance` and verify it reflects the correct balance after each bet. + - **Player Identification**: Each player should call `whoAmI` to confirm their player ID is accurately returned. + - **Commitment and Reveal Status**: Test `bothPlayed` and `bothRevealed` at different stages to ensure they accurately reflect whether both players have committed or revealed their moves. + - **Reveal Timer**: Call `revealTimeLeft` during the reveal phase and check if it accurately decreases as time passes. -## Run it in the directory you want to import -remixd -``` -You can then go to [remix](https://remix.ethereum.org/) and click on `default_workspace`, then select `- connect to localhost -`. +## To Go Further -> Take a look at the official [Solidity documentation](https://docs.soliditylang.org/en/v0.8.0/) to get more information about the language. +Congratulations! 🎉 You've successfully completed a decentralized Rock, Paper, Scissors game on Ethereum. You now have a foundational understanding of smart contracts, gameplay logic, and player interaction. Here are a few ideas and resources to deepen your knowledge and expand the functionality of your contract. -> You'll also realize how much effort you're saving by using a Starton template 😉 +- **Add Multiplayer Support** 👥 + Expand the game to support multiple players by creating a lobby system. Players could join a lobby, and once two players are ready, they can start a game. You could even add a queue or match-making feature to pair up players automatically. -## To go further -Congratulations, you now have an overview of smart contracts deployment and interaction, simplified with Starton. -But there's still a lot to discover, here are a few links: +- **Implement a Best-of Series** 🏅 + Take your game beyond a single round by creating a "best of three" or "best of five" format. This would allow players to commit to multiple rounds, where the contract tracks wins and determines an overall champion, adding an extra layer of engagement. + +- **Set up a Timeout Penalty** ⏲️ + To make the game fairer, introduce a penalty if a player fails to reveal their move within the timeout period. This could forfeit their bet to the opponent, ensuring the game flow remains smooth and deterring delays. -- Go deeper into [Ethereum smart contracts](https://ethereum.org/en/developers/docs/#foundational-topics) with concepts like [Opcodes](https://ethereum.org/en/developers/docs/evm/opcodes/) and [useful frameworks](https://ethereum.org/en/developers/docs/frameworks/) -- Use other Starton services like [Notify](https://docs.starton.io/connect/api-doc/notify) and [IPFS hosting](https://docs.starton.io/connect/api-doc/ipfs) for your smart contracts -- Integrate Starton services into a real app (you can take a look at our software workshop to [create a Space Invaders with a Play to Earn model](../../software/26.SpaceInvaders_Starton) 🚀) ## Authors diff --git a/p2p/8.Introduction_Smart_Contract/krypto-tour/lib/forge-std b/p2p/8.Introduction_Smart_Contract/krypto-tour/lib/forge-std new file mode 160000 index 00000000..1de6eecf --- /dev/null +++ b/p2p/8.Introduction_Smart_Contract/krypto-tour/lib/forge-std @@ -0,0 +1 @@ +Subproject commit 1de6eecf821de7fe2c908cc48d3ab3dced20717f