From 4353add9a0e7acd5dfeb477c50a9815ed8103489 Mon Sep 17 00:00:00 2001 From: bl0up Date: Wed, 27 Mar 2024 10:54:01 +0100 Subject: [PATCH] chore: init from https://github.com/settlemint/solidity-empty at e94314e --- .env.example | 5 ++ .github/labeler.yml | 15 +++++ .github/release.yaml | 20 ++++++ .github/renovate.json | 28 ++++++++ .github/workflows/branch.yml | 110 ++++++++++++++++++++++++++++++++ .github/workflows/pr-labels.yml | 28 ++++++++ .gitignore | 24 +++++++ .gitmodules | 3 + .vscode/extensions.json | 11 ++++ .vscode/tasks.json | 102 +++++++++++++++++++++++++++++ Dockerfile | 20 ++++++ LICENSE | 21 ++++++ Makefile | 82 ++++++++++++++++++++++++ README.md | 101 +++++++++++++++++++++++++++++ foundry.toml | 31 +++++++++ lib/forge-std | 1 + script/Counter.s.sol | 28 ++++++++ src/Counter.sol | 14 ++++ test/Counter.t.sol | 24 +++++++ 19 files changed, 668 insertions(+) create mode 100644 .env.example create mode 100644 .github/labeler.yml create mode 100644 .github/release.yaml create mode 100644 .github/renovate.json create mode 100644 .github/workflows/branch.yml create mode 100644 .github/workflows/pr-labels.yml create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 .vscode/extensions.json create mode 100644 .vscode/tasks.json create mode 100644 Dockerfile create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 foundry.toml create mode 160000 lib/forge-std create mode 100644 script/Counter.s.sol create mode 100644 src/Counter.sol create mode 100644 test/Counter.t.sol diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..d600a7e --- /dev/null +++ b/.env.example @@ -0,0 +1,5 @@ +## Add your Etherscan (or altrnative network variant) to verify your smart contract on Etherscam +ETHERSCAN_API_KEY= + +## Override the default --from address when deploying on BTP +# ETH_FROM=0x0000000000000000000000000000000000000000 diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 0000000..553e227 --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,15 @@ +version: v1 + +labels: + - label: 'feat' + matcher: + title: '^feat:.*' + - label: 'fix' + matcher: + title: '^fix:.*' + - label: 'chore' + matcher: + title: '^chore:.*' + - label: 'docs' + matcher: + title: '^docs:.*' diff --git a/.github/release.yaml b/.github/release.yaml new file mode 100644 index 0000000..b2eacb4 --- /dev/null +++ b/.github/release.yaml @@ -0,0 +1,20 @@ +changelog: + categories: + - title: Breaking Changes 🛠 + labels: + - breaking-change + - title: Exciting New Features 🎉 + labels: + - feat + - title: Important Bug Fixes 🐛 + labels: + - fix + - title: Documentation 📚 + labels: + - docs + - title: Other changes 🏗️ + labels: + - chore + - title: Dependencies 📦 + labels: + - dependencies diff --git a/.github/renovate.json b/.github/renovate.json new file mode 100644 index 0000000..e107555 --- /dev/null +++ b/.github/renovate.json @@ -0,0 +1,28 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended", + ":automergeMinor", + ":automergePr", + ":automergeRequireAllStatusChecks", + ":gitSignOff", + ":pinVersions", + ":semanticCommits", + ":semanticCommitTypeAll(chore)", + ":enableVulnerabilityAlerts", + ":combinePatchMinorReleases", + ":prConcurrentLimitNone", + ":prHourlyLimitNone", + "security:openssf-scorecard", + "schedule:nonOfficeHours", + ":disableDependencyDashboard" + ], + "labels": ["dependencies"], + "rebaseWhen": "conflicted", + "packageRules": [], + "hostRules": [ + { + "timeout": 3000000 + } + ] +} diff --git a/.github/workflows/branch.yml b/.github/workflows/branch.yml new file mode 100644 index 0000000..b9d6933 --- /dev/null +++ b/.github/workflows/branch.yml @@ -0,0 +1,110 @@ +name: Branch + +on: + pull_request: + branches: + - main + push: + branches: + - main + tags: + - "v*" + +env: + FOUNDRY_PROFILE: ci + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + actions: write + checks: write + contents: write + deployments: write + id-token: write + issues: write + discussions: write + packages: write + pages: write + pull-requests: write + repository-projects: write + security-events: write + statuses: write + +jobs: + check: + strategy: + fail-fast: true + + name: Foundry project + runs-on: namespace-profile-foundry + steps: + - name: Checkout + uses: namespacelabs/nscloud-checkout-action@v2 + with: + submodules: recursive + + - name: Setup caches + uses: namespacelabs/nscloud-cache-action@v1 + with: + path: | + cache + out + ~/.foundry + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Login to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USER }} + password: ${{ secrets.DOCKERHUB_PASS }} + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly + + - name: Run Forge build + run: | + forge --version + forge build --sizes + id: build + + - name: Run Forge tests + run: | + forge test -vvv + id: test + + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + # list of Docker images to use as base name for tags + images: | + ghcr.io/settlemint/solidity-empty + # generate Docker tags based on the following events/attributes + tags: | + type=schedule + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=sha + + - name: Build and push + uses: docker/build-push-action@v5 + with: + load: false + push: true + platforms: linux/amd64,linux/arm64 + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + no-cache: true diff --git a/.github/workflows/pr-labels.yml b/.github/workflows/pr-labels.yml new file mode 100644 index 0000000..20bf969 --- /dev/null +++ b/.github/workflows/pr-labels.yml @@ -0,0 +1,28 @@ +name: PR Labels + +on: + pull_request: + types: [opened, closed] + branches: + - main + +permissions: + actions: write + checks: write + contents: write + deployments: write + id-token: write + issues: write + discussions: write + packages: write + pages: write + pull-requests: write + repository-projects: write + security-events: write + statuses: write + +jobs: + labels: + runs-on: ubuntu-latest + steps: + - uses: fuxingloh/multi-labeler@v4 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8dca3a6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Compiler files +cache/ +out/ + +# Ignores development broadcast logs +!/broadcast +/broadcast/*/31337/ +/broadcast/**/dry-run/ + +# Docs +docs/ + +# Dotenv file +.env + +# Subgraphs +deployment.txt +deployment-anvil.txt +subgraph/subgraph.config.json +subgraph/node_modules +subgraph/generated +subgraph/build + +.pnpm \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..888d42d --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "lib/forge-std"] + path = lib/forge-std + url = https://github.com/foundry-rs/forge-std diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..00574f3 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,11 @@ +{ + "recommendations": [ + "nomicfoundation.hardhat-solidity", + "miguelsolorio.fluent-icons", + "vscode-icons-team.vscode-icons", + "genieai.chatgpt-vscode", + "esbenp.prettier-vscode", + "dracula-theme.theme-dracula", + "cnshenj.vscode-task-manager" + ] +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..2f29289 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,102 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "type": "shell", + "command": "make build", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [] + }, + { + "label": "test", + "type": "shell", + "command": "make test", + "group": "test", + "problemMatcher": [] + }, + { + "label": "format", + "type": "shell", + "command": "make format", + "problemMatcher": [] + }, + { + "label": "snapshot", + "type": "shell", + "command": "make snapshot", + "problemMatcher": [] + }, + { + "label": "anvil", + "type": "shell", + "command": "make anvil", + "problemMatcher": [] + }, + { + "label": "deploy-anvil", + "type": "shell", + "command": "make deploy-anvil", + "problemMatcher": [] + }, + { + "label": "deploy-btp", + "type": "shell", + "command": "EXTRA_ARGS=\"${input:extra-deployment-verify} ${input:extra-deployment-other}\" make deploy-btp", + "problemMatcher": [] + }, + { + "label": "script-anvil", + "type": "shell", + "command": "EXTRA_ARGS=\"${input:extra-script-broadcast} ${input:extra-script-other}\" make script-anvil", + "problemMatcher": [] + }, + { + "label": "script", + "type": "shell", + "command": "EXTRA_ARGS=\"${input:extra-script-broadcast} ${input:extra-script-other}\" make script", + "problemMatcher": [] + }, + { + "label": "help", + "type": "shell", + "command": "make help", + "problemMatcher": [] + } + ], + "inputs": [ + { + "id": "extra-deployment-verify", + "description": "Extra deployment options?", + "default": "", + "type": "pickString", + "options": [ + "", + "--verify --verifier sourcify", + "--verify --verifier etherscan --etherscan-api-key ${ETHERSCAN_API_KEY}" + ] + }, + { + "id": "extra-deployment-other", + "description": "Other extra deployment options?", + "default": "", + "type": "promptString" + }, + { + "id": "extra-script-broadcast", + "description": "Broadcast?", + "default": "", + "type": "pickString", + "options": ["", "--broadcast"] + }, + { + "id": "extra-script-other", + "description": "Other extra script options?", + "default": "", + "type": "promptString" + } + ] +} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..97e11bb --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +FROM node:20.11.1-bookworm as build + +ENV FOUNDRY_DIR /usr/local +RUN curl -L https://foundry.paradigm.xyz | bash && \ + /usr/local/bin/foundryup + +WORKDIR / + +RUN git config --global user.email "hello@settlemint.com" && \ + git config --global user.name "SettleMint" && \ + forge init usecase --template settlemint/solidity-empty && \ + cd usecase && \ + forge build + +USER root + +FROM busybox + +COPY --from=build /usecase /usecase +COPY --from=build /root/.svm /usecase-svm diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5ab2be5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 SettleMint + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c7894e4 --- /dev/null +++ b/Makefile @@ -0,0 +1,82 @@ +# Makefile for Foundry Ethereum Development Toolkit + +.PHONY: build test format snapshot anvil deploy deploy-anvil cast help subgraph clear-anvil-port + +build: + @echo "Building with Forge..." + @forge build + +test: + @echo "Testing with Forge..." + @forge test + +format: + @echo "Formatting with Forge..." + @forge fmt + +snapshot: + @echo "Creating gas snapshot with Forge..." + @forge snapshot + +anvil: + @echo "Starting Anvil local Ethereum node..." + @make clear-anvil-port + @anvil + +deploy-anvil: + @echo "Deploying with Forge to Anvil..." + @forge create ./src/Counter.sol:Counter --rpc-url anvil --interactive | tee deployment-anvil.txt + +deploy-btp: + @eval $$(curl -H "x-auth-token: $${BTP_SERVICE_TOKEN}" -s $${BTP_CLUSTER_MANAGER_URL}/ide/foundry/$${BTP_SCS_ID}/env | sed 's/^/export /'); \ + args=""; \ + if [ ! -z "$${BTP_FROM}" ]; then \ + args="--unlocked --from $${BTP_FROM}"; \ + else \ + echo "\033[1;33mWARNING: No keys are activated on the node, falling back to interactive mode...\033[0m"; \ + echo ""; \ + args="--interactive"; \ + fi; \ + if [ ! -z "$${BTP_GAS_PRICE}" ]; then \ + args="$$args --gas-price $${BTP_GAS_PRICE}"; \ + fi; \ + if [ "$${BTP_EIP_1559_ENABLED}" = "false" ]; then \ + args="$$args --legacy"; \ + fi; \ + forge create ./src/Counter.sol:Counter $${EXTRA_ARGS} --rpc-url $${BTP_RPC_URL} $$args --constructor-args "GenericToken" "GT" | tee deployment.txt; + +script-anvil: + @if [ ! -f deployment-anvil.txt ]; then \ + echo "\033[1;31mERROR: Contract was not deployed or the deployment-anvil.txt went missing.\033[0m"; \ + exit 1; \ + fi + @DEPLOYED_ADDRESS=$$(grep "Deployed to:" deployment-anvil.txt | awk '{print $$3}') forge script script/Counter.s.sol:CounterScript ${EXTRA_ARGS} --rpc-url anvil -i=1 + +script: + @if [ ! -f deployment.txt ]; then \ + echo "\033[1;31mERROR: Contract was not deployed or the deployment.txt went missing.\033[0m"; \ + exit 1; \ + fi + @eval $$(curl -H "x-auth-token: $${BTP_SERVICE_TOKEN}" -s $${BTP_CLUSTER_MANAGER_URL}/ide/foundry/$${BTP_SCS_ID}/env | sed 's/^/export /'); \ + if [ -z "${BTP_FROM}" ]; then \ + echo "\033[1;33mWARNING: No keys are activated on the node, falling back to interactive mode...\033[0m"; \ + echo ""; \ + @DEPLOYED_ADDRESS=$$(grep "Deployed to:" deployment.txt | awk '{print $$3}') forge script script/Counter.s.sol:CounterScript ${EXTRA_ARGS} --rpc-url ${BTP_RPC_URL} -i=1; \ + else \ + @DEPLOYED_ADDRESS=$$(grep "Deployed to:" deployment.txt | awk '{print $$3}') forge script script/Counter.s.sol:CounterScript ${EXTRA_ARGS} --rpc-url ${BTP_RPC_URL} --unlocked --froms ${BTP_FROM}; \ + fi + +cast: + @echo "Interacting with EVM via Cast..." + @cast $(SUBCOMMAND) + +help: + @echo "Forge help..." + @forge --help + @echo "Anvil help..." + @anvil --help + @echo "Cast help..." + @cast --help + +clear-anvil-port: + -fuser -k -n tcp 8545 \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..d2a86ea --- /dev/null +++ b/README.md @@ -0,0 +1,101 @@ +# ERC20 + +A basic ERC20 token contract. + +## Get started + +Launch this smart contract set in the SettleMint Blockchain Transformation platform under the `Smart Contract Sets` section. This will automatically link it to your own blockchain node and make use of the private keys living in the platform. + +If you want to use it separately, bootstrap a new project using + +```shell +forge init my-erc20-token --template settlemint/solidity-token-erc20 +``` + +## DX: Foundry + +**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.** + +Foundry consists of: + +- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). +- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. +- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. +- **Chisel**: Fast, utilitarian, and verbose solidity REPL. + +## Documentation + +- https://console.settlemint.com/documentation/docs/using-platform/integrated-development-environment/ +- https://book.getfoundry.sh/ + +## Usage + +### Build + +```shell +$ forge build +``` + +### Test + +```shell +$ forge test +``` + +### Format + +```shell +$ forge fmt +``` + +### Gas Snapshots + +```shell +$ forge snapshot +``` + +### Anvil + +Anvil is a local development node, open a terminal in the IDE and launch anvil. You can then deploy to it using `make deploy-anvil` + +```shell +$ anvil +``` + +### Deploy + +Deploy to a local anvil node: + +```shell +$ make deploy-anvil +``` + +When prompted to enter a private key, copy one of the private keys shown in the terminal when you start the anvil node. + +Deploy to the connected platform node: + +```shell +$ make deploy-btp +``` + +If you have a private key activated on the connected node, it will be used automatically. Else, you will be prompted to enter a private key. You can copy-paste a private key from the platform. + +### Cast + +```shell +$ cast +``` + +### Deploy your subgraph + +```shell +$ make subgraph +``` + +### Help + +```shell +$ forge --help +$ anvil --help +$ cast --help +``` diff --git a/foundry.toml b/foundry.toml new file mode 100644 index 0000000..0584d8f --- /dev/null +++ b/foundry.toml @@ -0,0 +1,31 @@ +[profile.ci] + fuzz = { runs = 10_000 } + verbosity = 4 + +[fmt] + bracket_spacing = true + int_types = "long" + line_length = 120 + multiline_func_header = "all" + number_underscore = "thousands" + quote_style = "double" + tab_width = 4 + wrap_comments = true + +[rpc_endpoints] +anvil = "http://localhost:8545" +btp = "${BTP_RPC_URL}" + +[profile.default] + src = "src" + test = "test" + out = "out" + libs = ["lib"] + solc = "0.8.24" + optimizer = true + optimizer_runs = 10_000 + gas_reports = ["*"] + fuzz = { runs = 1_000 } + auto_detect_solc = false + extra_output_files = [ "metadata" ] + fs_permissions = [{ access = "read", path = "./deployment-anvil.txt"}, { access = "read", path = "./deployment.txt"} ] \ No newline at end of file diff --git a/lib/forge-std b/lib/forge-std new file mode 160000 index 0000000..ae570fe --- /dev/null +++ b/lib/forge-std @@ -0,0 +1 @@ +Subproject commit ae570fec082bfe1c1f45b0acca4a2b4f84d345ce diff --git a/script/Counter.s.sol b/script/Counter.s.sol new file mode 100644 index 0000000..0c646b9 --- /dev/null +++ b/script/Counter.s.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +import {Script, console} from "forge-std/Script.sol"; +import "../src/Counter.sol"; + +contract CounterScript is Script { + Counter public counter; // Instance of the Counter contract + + function setUp() public { + string memory key = "DEPLOYED_ADDRESS"; + counter = Counter(vm.envAddress(key)); + } + + function run() public { + vm.startBroadcast(); + + counter.setNumber(5); // Set the initial number + + uint256 initialCount = counter.number(); // Read the current value of the counter + console.log("Current Counter Value:", initialCount); // Log the current counter value + + counter.increment(); // Call the increment function of the Counter contract + + uint256 currentCount = counter.number(); // Read the current value of the counter + console.log("Current Counter Value:", currentCount); // Log the current counter value + } +} \ No newline at end of file diff --git a/src/Counter.sol b/src/Counter.sol new file mode 100644 index 0000000..fbd0176 --- /dev/null +++ b/src/Counter.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +contract Counter { + uint256 public number; + + function setNumber(uint256 newNumber) public { + number = newNumber; + } + + function increment() public { + number++; + } +} diff --git a/test/Counter.t.sol b/test/Counter.t.sol new file mode 100644 index 0000000..924f97b --- /dev/null +++ b/test/Counter.t.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +import {Test, console} from "forge-std/Test.sol"; +import {Counter} from "../src/Counter.sol"; + +contract CounterTest is Test { + Counter public counter; + + function setUp() public { + counter = new Counter(); + counter.setNumber(0); + } + + function test_Increment() public { + counter.increment(); + assertEq(counter.number(), 1); + } + + function testFuzz_SetNumber(uint256 x) public { + counter.setNumber(x); + assertEq(counter.number(), x); + } +}