diff --git a/.github/workflows/e2e-evm.yml b/.github/workflows/e2e-evm.yml index 5c6a4b1d3..8e304b0ae 100644 --- a/.github/workflows/e2e-evm.yml +++ b/.github/workflows/e2e-evm.yml @@ -1,4 +1,4 @@ -name: EVM e2e tests +name: EVM E2E tests on: pull_request: @@ -69,7 +69,7 @@ jobs: - name: "just install" run: just install - working-directory: "e2e/evm" + working-directory: "evm-e2e" - name: "Launch localnet" run: | @@ -78,7 +78,7 @@ jobs: - name: "Run tests (just test)" run: just test - working-directory: "e2e/evm" + working-directory: "evm-e2e" env: JSON_RPC_ENDPOINT: http://127.0.0.1:8545 MNEMONIC: guard cream sadness conduct invite crumble clock pudding hole grit liar hotel maid produce squeeze return argue turtle know drive eight casino maze host diff --git a/.github/workflows/e2e-wasm.yml b/.github/workflows/e2e-wasm.yml index 7d5ab19be..93e04146d 100644 --- a/.github/workflows/e2e-wasm.yml +++ b/.github/workflows/e2e-wasm.yml @@ -1,4 +1,4 @@ -name: CosmWasm e2e tests +name: Wasm E2E tests on: # On normal PRs or when workflow goreleaser finishes, as it gets the last release tag. diff --git a/CHANGELOG.md b/CHANGELOG.md index e8ec6f945..a316c53f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -133,6 +133,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 Use effective gas price in RefundGas and make sure that units are properly reflected on all occurences of "base fee" in the codebase. This fixes [#2059](https://github.com/NibiruChain/nibiru/issues/2059) and the [related comments from @Unique-Divine and @berndartmueller](https://github.com/NibiruChain/nibiru/issues/2059#issuecomment-2408625724). +- [#2084](https://github.com/NibiruChain/nibiru/pull/2084) - feat(evm-forge): foundry support and template for Nibiru EVM develoment - [#2088](https://github.com/NibiruChain/nibiru/pull/2088) - refactor(evm): remove outdated comment and improper error message text diff --git a/docs/adr/00-adr-template.md b/docs/adr/00-adr-template.md deleted file mode 100644 index a03bd4fa0..000000000 --- a/docs/adr/00-adr-template.md +++ /dev/null @@ -1,64 +0,0 @@ -# ADR-XXX: - -Brief abstract statement/paragraph. - -- [Changelog](#changelog) -- [Context](#context) -- [Decision](#decision) - - [Design](#design) - - [Implementation](#implementation) -- [Status](#status) -- [Consequences](#consequences) - - [Positive](#positive) - - [Negative](#negative) - - [Alternatives Considered](#alternatives-considered) -- [Related Documents](#related-documents) - -## Changelog - -- yyyy-mm-dd: Initial Draft -- yyyy-mm-dd: Accepted - -## Context - -- What is the background context for this decision? -- What problem does it solve? -- What external issues or forces influenced this decision? - -## Decision - -### Design - -- Outline the details of the solution and how it addresses the context -- Describe the proposed functionality and design - -``` -Code and pseudo-code snippets useful for explanation -``` - -### Implementation - -- Highlight any implementation details or considerations -- Changes to existing code, new modules, flow, etc - -## Status - -- Proposed, Approved, Deprecated, etc - -## Consequences - -### Positive - -- Benefits to the change - -### Negative - -- Costs or downsides to the change - -### Alternatives Considered - -- List any other approaches considered during discussion - -## Related Documents - -- Links to related docs, previous ADRs, etc diff --git a/docs/adr/01-adr-msg-server-keeper.md b/docs/adr/01-adr-msg-server-keeper.md deleted file mode 100644 index c45f51273..000000000 --- a/docs/adr/01-adr-msg-server-keeper.md +++ /dev/null @@ -1,107 +0,0 @@ -# ADR-001: Separation of Concerns between MsgServer and Keeper - -## Introduction - -In developing Nibiru Chain, built on Cosmos SDK, we have identified design and -development practices that require optimization. This document proposes -methodologies to differentiate between `MsgServer` and `Keeper` in the code and -to improve our action-based testing framework. - -## Changelog - -- 2022-06-01: Proposed in [NibiruChain/nibiru - #524](https://github.com/NibiruChain/nibiru/issues/524) by @testinginprod. -- 2023-12-28: Formal ADR drafted and accepted. - -## Context - -Merging MsgServer and Keeper goes against separation of responsibilities -principles in Cosmos SDK: - -- Obscures distinct functions each component should perform -- Complicates code maintenance due to intertwined responsibilities -- Can lead to security vulnerabilities without clear boundaries - -The `MsgServer` should focus on request validation while Keeper handles business logic, like a web app's controller and model respectively. - -## Decision - -This ADR proposes that we separate MsgServer (validation) from Keeper (business -logic). - -For example, Some `MsgServer` methods are restricted to the `x/sudo` group, -showing the need for distinct validation and execution. - -The format should be the following: - -```go -func NewMsgServerImpl(k Keeper) types.MsgServer { return msgServer{k} } - -type msgServer struct { - k Keeper // NOTE: NO EMBEDDING -} - -func NewQueryServerImpl(k Keeper) types.QueryServer { return queryServer{k} } - -type queryServer struct { - k Keeper // NOTE: NO EMBEDDING -} -``` - -Rules to follow: - -- When possible the msg/query server should contain no business logic (not always - possible due to pagination sometimes) -- Focused only at stateless request validation, and conversion from request to - arguments required for the keeper function call. -- No embedding because it always ends up with name conflicts. - -Keepers: - -- Must not have references to request formats, API layer should be totally split - from business logic layer. - -## Benefits - -- Simplifies and improves our action-based testing framework: -- Removes need to prepare complex permission schemes -- Reduces boilerplate code in tests when using the keeper as a dependency for - another module by not requiring explicit "module-name/types" imports. - -### Concerns About Security and Access Control - -Some might argue that sharing Keeper's methods can lead to security risks, mainly -if there are concerns about unauthorized access. This viewpoint stems from the -belief that the `Keeper` should control access, which might lead to apprehensions -about exposing specific methods. - -### Clarifying the Role of the Keeper - -However, this perspective needs to be revised in the fundamental role of the -`Keeper`. The primary responsibility of the `Keeper` is to maintain a consistent -state within the application rather than controlling access. Access control and -validation of requests are the responsibilities of the `MsgServer`, which acts as -the first line of defense. - -### On Function Privacy - -Suppose there's a need to share the Keeper with other modules, and concerns arise -about the safety of exposing specific methods. In that case, the preferred -approach is to keep those sensitive methods private. Implementing access and -permission layers within the `Keeper` goes against the principle of separation of -responsibilities and can lead to a more cohesive and secure system. Instead, -ensuring that only the appropriate methods are exposed and keeping others private -aligns with the philosophy of keeping each component focused on its specific -role. - -## Concerns - -Exposing Keeper methods could enable unauthorized access. However, access control -is the MsgServer’s responsibility. Keeper maintains state consistency. - -Best practice is keeping sensitive methods private, not building permission -schemes in Keeper. This aligns with separation of responsibilities. - -## Conclusion - -Improves code clarity, maintainability, and security. diff --git a/docs/adr/README.md b/docs/adr/README.md deleted file mode 100644 index 6068db20c..000000000 --- a/docs/adr/README.md +++ /dev/null @@ -1,50 +0,0 @@ -# nibiru/docs/adr - -ADRs help us address and track significant software design choices. - -- [Key Terms](#key-terms) -- [Why ADRs Help](#why-adrs-help) -- [Creating ADRs](#creating-adrs) -- [ADR Table of Contents](#adr-table-of-contents) - -## Key Terms - -| Term | Definition | -| --- | --- | -| Architectural Decision Record (ADR) | Captures a single Architectural Decision (AD), often written informally like personal notes or meeting minutes. The collection of ADRs make up a project's decision log. | -| Architectural Knowledge Management (AKM) | The practice of formally documenting architectural decisions to manage critical project knowledge over time. ADRs are a common AKM technique. | -| Architectural Decision (AD) | A software design choice that addresses a functional or non-functional requirement. | -| Architecturally Significant Requirement (ASR) | A requirement that has a large, measurable effect on a system's architecture quality and design. | -| Functional Requirement | Describes functionality that a system must provide to fulfill user needs. These define specific behaviors and outcomes that the system should have. For example calculating taxes on a purchase or sending a message. | -| Non-functional Requirement | Describes quality attributes and constraints on a system. Non-functional requirements do not specify behaviors - instead they constrain functional requirements by setting performance metrics, reliability targets, etc. For example - the system should process 95% of transactions in under 1 second. | - -## Why ADRs Help - -- Docs stay up-to-date: By capturing decisions, not ephemeral state, records have - lasting relevance even as systems evolve. -- Discoverability: ADRs can be tagged and linked to facilitate discovery when - working in related code. -- Onboard new developers: ADRs capture context and rationale to quickly ramp up - new team members. - -To maximize these benefits, Nibiru Chain ADRs will: - -- Contain a tldr summary section -- Cover background and decision details -- ADRs help transform documentation from a chore to an asset! Diligently recording decisions will improve understanding and pass knowledge to future maintainers. -- Use Markdown formatting for README integration -- Link bidirectionally with related code comments - -## Creating ADRs - -1. Start Here: [Nibiru Chain ADR Template](./00-adr-template.md): This provides a starting - template to capture architectural decisions. -2. Read a few of the other ADRs to gain a feel for them. - -Reference Materials: - -- [Request for Comments (RFC) Best Practices](https://datatracker.ietf.org/doc/html/rfc2119) - -## ADR Table of Contents - -- [ADR-001: Separation of Concerns between MsgServer and Keeper ](./01-adr-msg-server-keeper.md) diff --git a/e2e/evm/.env_sample b/evm-e2e/.env_sample similarity index 100% rename from e2e/evm/.env_sample rename to evm-e2e/.env_sample diff --git a/e2e/evm/.nvmrc b/evm-e2e/.nvmrc similarity index 100% rename from e2e/evm/.nvmrc rename to evm-e2e/.nvmrc diff --git a/e2e/evm/README.md b/evm-e2e/README.md similarity index 100% rename from e2e/evm/README.md rename to evm-e2e/README.md diff --git a/e2e/evm/bun.lockb b/evm-e2e/bun.lockb similarity index 100% rename from e2e/evm/bun.lockb rename to evm-e2e/bun.lockb diff --git a/e2e/evm/contracts/InfiniteLoopGas.sol b/evm-e2e/contracts/InfiniteLoopGas.sol similarity index 100% rename from e2e/evm/contracts/InfiniteLoopGas.sol rename to evm-e2e/contracts/InfiniteLoopGas.sol diff --git a/e2e/evm/contracts/InfiniteLoopGasCompiled.json b/evm-e2e/contracts/InfiniteLoopGasCompiled.json similarity index 100% rename from e2e/evm/contracts/InfiniteLoopGasCompiled.json rename to evm-e2e/contracts/InfiniteLoopGasCompiled.json diff --git a/e2e/evm/contracts/ReceiveNibiCompiled.json b/evm-e2e/contracts/ReceiveNibiCompiled.json similarity index 100% rename from e2e/evm/contracts/ReceiveNibiCompiled.json rename to evm-e2e/contracts/ReceiveNibiCompiled.json diff --git a/e2e/evm/contracts/SendNibiCompiled.json b/evm-e2e/contracts/SendNibiCompiled.json similarity index 100% rename from e2e/evm/contracts/SendNibiCompiled.json rename to evm-e2e/contracts/SendNibiCompiled.json diff --git a/e2e/evm/contracts/SendReceiveNibi.sol b/evm-e2e/contracts/SendReceiveNibi.sol similarity index 100% rename from e2e/evm/contracts/SendReceiveNibi.sol rename to evm-e2e/contracts/SendReceiveNibi.sol diff --git a/e2e/evm/contracts/TestERC20.sol b/evm-e2e/contracts/TestERC20.sol similarity index 100% rename from e2e/evm/contracts/TestERC20.sol rename to evm-e2e/contracts/TestERC20.sol diff --git a/e2e/evm/contracts/TestERC20Compiled.json b/evm-e2e/contracts/TestERC20Compiled.json similarity index 100% rename from e2e/evm/contracts/TestERC20Compiled.json rename to evm-e2e/contracts/TestERC20Compiled.json diff --git a/e2e/evm/jest.config.js b/evm-e2e/jest.config.js similarity index 100% rename from e2e/evm/jest.config.js rename to evm-e2e/jest.config.js diff --git a/e2e/evm/justfile b/evm-e2e/justfile similarity index 100% rename from e2e/evm/justfile rename to evm-e2e/justfile diff --git a/e2e/evm/package-lock.json b/evm-e2e/package-lock.json similarity index 100% rename from e2e/evm/package-lock.json rename to evm-e2e/package-lock.json diff --git a/e2e/evm/package.json b/evm-e2e/package.json similarity index 100% rename from e2e/evm/package.json rename to evm-e2e/package.json diff --git a/e2e/evm/prettier.config.mjs b/evm-e2e/prettier.config.mjs similarity index 100% rename from e2e/evm/prettier.config.mjs rename to evm-e2e/prettier.config.mjs diff --git a/e2e/evm/test/contract_infinite_loop_gas.test.ts b/evm-e2e/test/contract_infinite_loop_gas.test.ts similarity index 100% rename from e2e/evm/test/contract_infinite_loop_gas.test.ts rename to evm-e2e/test/contract_infinite_loop_gas.test.ts diff --git a/e2e/evm/test/contract_send_nibi.test.ts b/evm-e2e/test/contract_send_nibi.test.ts similarity index 98% rename from e2e/evm/test/contract_send_nibi.test.ts rename to evm-e2e/test/contract_send_nibi.test.ts index 9ef0e6277..2c47e0ca2 100644 --- a/e2e/evm/test/contract_send_nibi.test.ts +++ b/evm-e2e/test/contract_send_nibi.test.ts @@ -7,7 +7,7 @@ * accordingly. * * The methods tested are from the smart contract, - * "e2e/evm/contracts/SendReceiveNibi.sol". + * "evm-e2e/contracts/SendReceiveNibi.sol". */ import { describe, expect, it } from "@jest/globals" import { parseEther, toBigInt, Wallet } from "ethers" diff --git a/e2e/evm/test/debug_queries.test.ts b/evm-e2e/test/debug_queries.test.ts similarity index 100% rename from e2e/evm/test/debug_queries.test.ts rename to evm-e2e/test/debug_queries.test.ts diff --git a/e2e/evm/test/erc20.test.ts b/evm-e2e/test/erc20.test.ts similarity index 100% rename from e2e/evm/test/erc20.test.ts rename to evm-e2e/test/erc20.test.ts diff --git a/e2e/evm/test/eth_queries.test.ts b/evm-e2e/test/eth_queries.test.ts similarity index 100% rename from e2e/evm/test/eth_queries.test.ts rename to evm-e2e/test/eth_queries.test.ts diff --git a/e2e/evm/test/native_transfer.test.ts b/evm-e2e/test/native_transfer.test.ts similarity index 100% rename from e2e/evm/test/native_transfer.test.ts rename to evm-e2e/test/native_transfer.test.ts diff --git a/e2e/evm/test/setup.ts b/evm-e2e/test/setup.ts similarity index 100% rename from e2e/evm/test/setup.ts rename to evm-e2e/test/setup.ts diff --git a/e2e/evm/test/utils.ts b/evm-e2e/test/utils.ts similarity index 100% rename from e2e/evm/test/utils.ts rename to evm-e2e/test/utils.ts diff --git a/e2e/evm/tsconfig.json b/evm-e2e/tsconfig.json similarity index 100% rename from e2e/evm/tsconfig.json rename to evm-e2e/tsconfig.json diff --git a/e2e/evm/types/ethers-contracts/InfiniteLoopGasCompiled.ts b/evm-e2e/types/ethers-contracts/InfiniteLoopGasCompiled.ts similarity index 100% rename from e2e/evm/types/ethers-contracts/InfiniteLoopGasCompiled.ts rename to evm-e2e/types/ethers-contracts/InfiniteLoopGasCompiled.ts diff --git a/e2e/evm/types/ethers-contracts/ReceiveNibiCompiled.ts b/evm-e2e/types/ethers-contracts/ReceiveNibiCompiled.ts similarity index 100% rename from e2e/evm/types/ethers-contracts/ReceiveNibiCompiled.ts rename to evm-e2e/types/ethers-contracts/ReceiveNibiCompiled.ts diff --git a/e2e/evm/types/ethers-contracts/SendNibiCompiled.ts b/evm-e2e/types/ethers-contracts/SendNibiCompiled.ts similarity index 100% rename from e2e/evm/types/ethers-contracts/SendNibiCompiled.ts rename to evm-e2e/types/ethers-contracts/SendNibiCompiled.ts diff --git a/e2e/evm/types/ethers-contracts/TestERC20Compiled.ts b/evm-e2e/types/ethers-contracts/TestERC20Compiled.ts similarity index 100% rename from e2e/evm/types/ethers-contracts/TestERC20Compiled.ts rename to evm-e2e/types/ethers-contracts/TestERC20Compiled.ts diff --git a/e2e/evm/types/ethers-contracts/common.ts b/evm-e2e/types/ethers-contracts/common.ts similarity index 100% rename from e2e/evm/types/ethers-contracts/common.ts rename to evm-e2e/types/ethers-contracts/common.ts diff --git a/e2e/evm/types/ethers-contracts/factories/InfiniteLoopGasCompiled__factory.ts b/evm-e2e/types/ethers-contracts/factories/InfiniteLoopGasCompiled__factory.ts similarity index 100% rename from e2e/evm/types/ethers-contracts/factories/InfiniteLoopGasCompiled__factory.ts rename to evm-e2e/types/ethers-contracts/factories/InfiniteLoopGasCompiled__factory.ts diff --git a/e2e/evm/types/ethers-contracts/factories/ReceiveNibiCompiled__factory.ts b/evm-e2e/types/ethers-contracts/factories/ReceiveNibiCompiled__factory.ts similarity index 100% rename from e2e/evm/types/ethers-contracts/factories/ReceiveNibiCompiled__factory.ts rename to evm-e2e/types/ethers-contracts/factories/ReceiveNibiCompiled__factory.ts diff --git a/e2e/evm/types/ethers-contracts/factories/SendNibiCompiled__factory.ts b/evm-e2e/types/ethers-contracts/factories/SendNibiCompiled__factory.ts similarity index 100% rename from e2e/evm/types/ethers-contracts/factories/SendNibiCompiled__factory.ts rename to evm-e2e/types/ethers-contracts/factories/SendNibiCompiled__factory.ts diff --git a/e2e/evm/types/ethers-contracts/factories/TestERC20Compiled__factory.ts b/evm-e2e/types/ethers-contracts/factories/TestERC20Compiled__factory.ts similarity index 100% rename from e2e/evm/types/ethers-contracts/factories/TestERC20Compiled__factory.ts rename to evm-e2e/types/ethers-contracts/factories/TestERC20Compiled__factory.ts diff --git a/e2e/evm/types/ethers-contracts/factories/index.ts b/evm-e2e/types/ethers-contracts/factories/index.ts similarity index 100% rename from e2e/evm/types/ethers-contracts/factories/index.ts rename to evm-e2e/types/ethers-contracts/factories/index.ts diff --git a/e2e/evm/types/ethers-contracts/index.ts b/evm-e2e/types/ethers-contracts/index.ts similarity index 100% rename from e2e/evm/types/ethers-contracts/index.ts rename to evm-e2e/types/ethers-contracts/index.ts diff --git a/evm-forge/.editorconfig b/evm-forge/.editorconfig new file mode 100644 index 000000000..746ae3127 --- /dev/null +++ b/evm-forge/.editorconfig @@ -0,0 +1,19 @@ +# EditorConfig http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# All files +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.sol] +indent_size = 4 + +[*.tree] +indent_size = 1 diff --git a/evm-forge/.env.example b/evm-forge/.env.example new file mode 100644 index 000000000..98c102879 --- /dev/null +++ b/evm-forge/.env.example @@ -0,0 +1,11 @@ +export API_KEY_ALCHEMY="YOUR_API_KEY_ALCHEMY" +export API_KEY_ARBISCAN="YOUR_API_KEY_ARBISCAN" +export API_KEY_BSCSCAN="YOUR_API_KEY_BSCSCAN" +export API_KEY_ETHERSCAN="YOUR_API_KEY_ETHERSCAN" +export API_KEY_GNOSISSCAN="YOUR_API_KEY_GNOSISSCAN" +export API_KEY_INFURA="YOUR_API_KEY_INFURA" +export API_KEY_OPTIMISTIC_ETHERSCAN="YOUR_API_KEY_OPTIMISTIC_ETHERSCAN" +export API_KEY_POLYGONSCAN="YOUR_API_KEY_POLYGONSCAN" +export API_KEY_SNOWTRACE="YOUR_API_KEY_SNOWTRACE" +export MNEMONIC="YOUR_MNEMONIC" +export FOUNDRY_PROFILE="default" diff --git a/evm-forge/.github/FUNDING.yml b/evm-forge/.github/FUNDING.yml new file mode 100644 index 000000000..d8463c1d6 --- /dev/null +++ b/evm-forge/.github/FUNDING.yml @@ -0,0 +1,2 @@ +custom: "https://3cities.xyz/#/pay?c=CAESFAKY9DMuOFdjE4Wzl2YyUFipPiSfIgICATICCAJaFURvbmF0aW9uIHRvIFBhdWwgQmVyZw" +github: "PaulRBerg" diff --git a/evm-forge/.github/scripts/rename.sh b/evm-forge/.github/scripts/rename.sh new file mode 100755 index 000000000..62e37dda1 --- /dev/null +++ b/evm-forge/.github/scripts/rename.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +# https://gist.github.com/vncsna/64825d5609c146e80de8b1fd623011ca +set -euo pipefail + +# Define the input vars +GITHUB_REPOSITORY=${1?Error: Please pass username/repo, e.g. prb/foundry-template} +GITHUB_REPOSITORY_OWNER=${2?Error: Please pass username, e.g. prb} +GITHUB_REPOSITORY_DESCRIPTION=${3:-""} # If null then replace with empty string + +echo "GITHUB_REPOSITORY: $GITHUB_REPOSITORY" +echo "GITHUB_REPOSITORY_OWNER: $GITHUB_REPOSITORY_OWNER" +echo "GITHUB_REPOSITORY_DESCRIPTION: $GITHUB_REPOSITORY_DESCRIPTION" + +# jq is like sed for JSON data +JQ_OUTPUT=`jq \ + --arg NAME "@$GITHUB_REPOSITORY" \ + --arg AUTHOR_NAME "$GITHUB_REPOSITORY_OWNER" \ + --arg URL "https://github.com/$GITHUB_REPOSITORY_OWNER" \ + --arg DESCRIPTION "$GITHUB_REPOSITORY_DESCRIPTION" \ + '.name = $NAME | .description = $DESCRIPTION | .author |= ( .name = $AUTHOR_NAME | .url = $URL )' \ + package.json +` + +# Overwrite package.json +echo "$JQ_OUTPUT" > package.json + +# Make sed command compatible in both Mac and Linux environments +# Reference: https://stackoverflow.com/a/38595160/8696958 +sedi () { + sed --version >/dev/null 2>&1 && sed -i -- "$@" || sed -i "" "$@" +} + +# Rename instances of "PaulRBerg/foundry-template" to the new repo name in README.md for badges only +sedi "/gitpod/ s|PaulRBerg/foundry-template|"${GITHUB_REPOSITORY}"|;" "README.md" +sedi "/gitpod-badge/ s|PaulRBerg/foundry-template|"${GITHUB_REPOSITORY}"|;" "README.md" +sedi "/gha/ s|PaulRBerg/foundry-template|"${GITHUB_REPOSITORY}"|;" "README.md" +sedi "/gha-badge/ s|PaulRBerg/foundry-template|"${GITHUB_REPOSITORY}"|;" "README.md" diff --git a/evm-forge/.github/workflows/ci.yml b/evm-forge/.github/workflows/ci.yml new file mode 100644 index 000000000..75507493e --- /dev/null +++ b/evm-forge/.github/workflows/ci.yml @@ -0,0 +1,92 @@ +name: "CI" + +env: + API_KEY_ALCHEMY: ${{ secrets.API_KEY_ALCHEMY }} + FOUNDRY_PROFILE: "ci" + +on: + workflow_dispatch: + pull_request: + push: + branches: + - "main" + +jobs: + lint: + runs-on: "ubuntu-latest" + steps: + - name: "Check out the repo" + uses: "actions/checkout@v4" + + - name: "Install Foundry" + uses: "foundry-rs/foundry-toolchain@v1" + + - name: "Install Bun" + uses: "oven-sh/setup-bun@v1" + + - name: "Install the Node.js dependencies" + run: "bun install" + + - name: "Lint the code" + run: "bun run lint" + + - name: "Add lint summary" + run: | + echo "## Lint result" >> $GITHUB_STEP_SUMMARY + echo "✅ Passed" >> $GITHUB_STEP_SUMMARY + + build: + runs-on: "ubuntu-latest" + steps: + - name: "Check out the repo" + uses: "actions/checkout@v4" + + - name: "Install Foundry" + uses: "foundry-rs/foundry-toolchain@v1" + + - name: "Install Bun" + uses: "oven-sh/setup-bun@v1" + + - name: "Install the Node.js dependencies" + run: "bun install" + + - name: "Build the contracts and print their size" + run: "forge build --sizes" + + - name: "Add build summary" + run: | + echo "## Build result" >> $GITHUB_STEP_SUMMARY + echo "✅ Passed" >> $GITHUB_STEP_SUMMARY + + test: + needs: ["lint", "build"] + runs-on: "ubuntu-latest" + steps: + - name: "Check out the repo" + uses: "actions/checkout@v4" + + - name: "Install Foundry" + uses: "foundry-rs/foundry-toolchain@v1" + + - name: "Install Bun" + uses: "oven-sh/setup-bun@v1" + + - name: "Install the Node.js dependencies" + run: "bun install" + + - name: "Show the Foundry config" + run: "forge config" + + - name: "Generate a fuzz seed that changes weekly to avoid burning through RPC allowance" + run: > + echo "FOUNDRY_FUZZ_SEED=$( + echo $(($EPOCHSECONDS - $EPOCHSECONDS % 604800)) + )" >> $GITHUB_ENV + + - name: "Run the tests" + run: "forge test" + + - name: "Add test summary" + run: | + echo "## Tests result" >> $GITHUB_STEP_SUMMARY + echo "✅ Passed" >> $GITHUB_STEP_SUMMARY diff --git a/evm-forge/.github/workflows/use-template.yml b/evm-forge/.github/workflows/use-template.yml new file mode 100644 index 000000000..183b17782 --- /dev/null +++ b/evm-forge/.github/workflows/use-template.yml @@ -0,0 +1,52 @@ +name: "Create" + +# The workflow will run when the "Use this template" button is used +on: + push: + +jobs: + create: + # We only run this action when the repository isn't the template repository. References: + # - https://docs.github.com/en/actions/learn-github-actions/contexts + # - https://docs.github.com/en/actions/learn-github-actions/expressions + if: ${{ !github.event.repository.is_template }} + permissions: "write-all" + runs-on: "ubuntu-latest" + steps: + - name: "Check out the repo" + uses: "actions/checkout@v4" + + - name: "Update package.json" + env: + GITHUB_REPOSITORY_DESCRIPTION: ${{ github.event.repository.description }} + run: + ./.github/scripts/rename.sh "$GITHUB_REPOSITORY" "$GITHUB_REPOSITORY_OWNER" "$GITHUB_REPOSITORY_DESCRIPTION" + + - name: "Add rename summary" + run: | + echo "## Commit result" >> $GITHUB_STEP_SUMMARY + echo "✅ Passed" >> $GITHUB_STEP_SUMMARY + + - name: "Remove files not needed in the user's copy of the template" + run: | + rm -f "./.github/FUNDING.yml" + rm -f "./.github/scripts/rename.sh" + rm -f "./.github/workflows/create.yml" + + - name: "Add remove summary" + run: | + echo "## Remove result" >> $GITHUB_STEP_SUMMARY + echo "✅ Passed" >> $GITHUB_STEP_SUMMARY + + - name: "Update commit" + uses: "stefanzweifel/git-auto-commit-action@v4" + with: + commit_message: "feat: initial commit" + commit_options: "--amend" + push_options: "--force" + skip_fetch: true + + - name: "Add commit summary" + run: | + echo "## Commit result" >> $GITHUB_STEP_SUMMARY + echo "✅ Passed" >> $GITHUB_STEP_SUMMARY diff --git a/evm-forge/.gitignore b/evm-forge/.gitignore new file mode 100644 index 000000000..0521facf9 --- /dev/null +++ b/evm-forge/.gitignore @@ -0,0 +1,21 @@ +# directories +cache +coverage +node_modules +out +lib + +# files +*.env +*.log +.DS_Store +.pnp.* +lcov.info +package-lock.json +pnpm-lock.yaml +yarn.lock + +# broadcasts +!broadcast +broadcast/* +broadcast/*/31337/ diff --git a/evm-forge/.gitpod.yml b/evm-forge/.gitpod.yml new file mode 100644 index 000000000..b9646d8df --- /dev/null +++ b/evm-forge/.gitpod.yml @@ -0,0 +1,14 @@ +image: "gitpod/workspace-bun" + +tasks: + - name: "Install dependencies" + before: | + curl -L https://foundry.paradigm.xyz | bash + source ~/.bashrc + foundryup + init: "bun install" + +vscode: + extensions: + - "esbenp.prettier-vscode" + - "NomicFoundation.hardhat-solidity" diff --git a/evm-forge/.prettierignore b/evm-forge/.prettierignore new file mode 100644 index 000000000..3996d20be --- /dev/null +++ b/evm-forge/.prettierignore @@ -0,0 +1,17 @@ +# directories +broadcast +cache +coverage +node_modules +out + +# files +*.env +*.log +.DS_Store +.pnp.* +bun.lockb +lcov.info +package-lock.json +pnpm-lock.yaml +yarn.lock diff --git a/evm-forge/.prettierrc.yml b/evm-forge/.prettierrc.yml new file mode 100644 index 000000000..a1ecdbb95 --- /dev/null +++ b/evm-forge/.prettierrc.yml @@ -0,0 +1,7 @@ +bracketSpacing: true +printWidth: 120 +proseWrap: "always" +singleQuote: false +tabWidth: 2 +trailingComma: "all" +useTabs: false diff --git a/evm-forge/.solhint.json b/evm-forge/.solhint.json new file mode 100644 index 000000000..14f780ec6 --- /dev/null +++ b/evm-forge/.solhint.json @@ -0,0 +1,14 @@ +{ + "extends": "solhint:recommended", + "rules": { + "code-complexity": ["error", 8], + "compiler-version": ["error", ">=0.8.25"], + "func-name-mixedcase": "off", + "func-visibility": ["error", { "ignoreConstructors": true }], + "max-line-length": ["error", 120], + "named-parameters-mapping": "warn", + "no-console": "off", + "not-rely-on-time": "off", + "one-contract-per-file": "off" + } +} diff --git a/evm-forge/LICENSE.md b/evm-forge/LICENSE.md new file mode 100644 index 000000000..0424fa020 --- /dev/null +++ b/evm-forge/LICENSE.md @@ -0,0 +1,16 @@ +MIT License + +Copyright (c) 2024 Paul Razvan Berg + +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/evm-forge/README.md b/evm-forge/README.md new file mode 100644 index 000000000..592094343 --- /dev/null +++ b/evm-forge/README.md @@ -0,0 +1,240 @@ +# nibiru/evm-forge + +## What's Inside + +- [Forge](https://github.com/foundry-rs/foundry/blob/master/forge): compile, test, fuzz, format, and deploy smart + contracts +- [Forge Std](https://github.com/foundry-rs/forge-std): collection of helpful contracts and utilities for testing +- [Prettier](https://github.com/prettier/prettier): code formatter for non-Solidity files +- [Solhint](https://github.com/protofire/solhint): linter for Solidity code + +## Hacking: Getting Started + +Note that the shell instructions assume you're using Unix (e.g. MacOS or Ubuntu). + +Foundryup is the official installer for the Foundry toolchain. + +```bash +curl -L https://foundry.paradigm.xyz | bash +foundryup +bun install +forge install # Update ./lib +forge test # sanity check +``` + +Option 2: Build from source with `cargo` + +```bash +rustup update # For up-to-date rustc version +cargo install --git https://github.com/gakonst/foundry forge --bins --locked +bun install +forge install # Update ./lib +forge test # sanity check +``` + +[Foundry Book - Installation Options](https://book.getfoundry.sh/getting-started/installation) + +## Credits and Related Efforts + +This Foundry template is an adaptation of +[PaulRBerg's foundry template](https://github.com/PaulRBerg/foundry-template?tab=readme-ov-file) + +## + +# Foundry Template [![Open in Gitpod][gitpod-badge]][gitpod] [![Github Actions][gha-badge]][gha] [![Foundry][foundry-badge]][foundry] [![License: MIT][license-badge]][license] + +[gitpod]: https://gitpod.io/#https://github.com/PaulRBerg/foundry-template +[gitpod-badge]: https://img.shields.io/badge/Gitpod-Open%20in%20Gitpod-FFB45B?logo=gitpod +[gha]: https://github.com/PaulRBerg/foundry-template/actions +[gha-badge]: https://github.com/PaulRBerg/foundry-template/actions/workflows/ci.yml/badge.svg +[foundry]: https://getfoundry.sh/ +[foundry-badge]: https://img.shields.io/badge/Built%20with-Foundry-FFDB1C.svg +[license]: https://opensource.org/licenses/MIT +[license-badge]: https://img.shields.io/badge/License-MIT-blue.svg + +A Foundry-based template for developing Solidity smart contracts, with sensible defaults. + +## Getting Started + +Click the [`Use this template`](https://github.com/PaulRBerg/foundry-template/generate) button at the top of the page to +create a new repository with this repo as the initial state. + +Or, if you prefer to install the template manually: + +```bash +mkdir my-project +cd my-project +forge init --template PaulRBerg/foundry-template +bun install # install Solhint, Prettier, and other Node.js deps +``` + +If this is your first time with Foundry, check out the +[installation](https://github.com/foundry-rs/foundry#installation) instructions. + +## Features + +This template builds upon the frameworks and libraries mentioned above, so please consult their respective documentation +for details about their specific features. + +For example, if you're interested in exploring Foundry in more detail, you should look at the +[Foundry Book](https://book.getfoundry.sh/). In particular, you may be interested in reading the +[Writing Tests](https://book.getfoundry.sh/forge/writing-tests.html) tutorial. + +### Sensible Defaults + +This template comes with a set of sensible default configurations for you to use. These defaults can be found in the +following files: + +```text +├── .editorconfig +├── .gitignore +├── .prettierignore +├── .prettierrc.yml +├── .solhint.json +├── foundry.toml +└── remappings.txt +``` + +### VSCode Integration + +This template is IDE agnostic, but for the best user experience, you may want to use it in VSCode alongside Nomic +Foundation's [Solidity extension](https://marketplace.visualstudio.com/items?itemName=NomicFoundation.hardhat-solidity). + +For guidance on how to integrate a Foundry project in VSCode, please refer to this +[guide](https://book.getfoundry.sh/config/vscode). + +### GitHub Actions + +This template comes with GitHub Actions pre-configured. Your contracts will be linted and tested on every push and pull +request made to the `main` branch. + +You can edit the CI script in [.github/workflows/ci.yml](./.github/workflows/ci.yml). + +## Installing Dependencies + +Foundry typically uses git submodules to manage dependencies, but this template uses Node.js packages because +[submodules don't scale](https://twitter.com/PaulRBerg/status/1736695487057531328). + +This is how to install dependencies: + +1. Install the dependency using your preferred package manager, e.g. `bun install dependency-name` + - Use this syntax to install from GitHub: `bun install github:username/repo-name` +2. Add a remapping for the dependency in [remappings.txt](./remappings.txt), e.g. + `dependency-name=node_modules/dependency-name` + +Note that OpenZeppelin Contracts is pre-installed, so you can follow that as an example. + +## Writing Tests + +To write a new test contract, you start by importing `Test` from `forge-std`, and then you inherit it in your test +contract. Forge Std comes with a pre-instantiated [cheatcodes](https://book.getfoundry.sh/cheatcodes/) environment +accessible via the `vm` property. If you would like to view the logs in the terminal output, you can add the `-vvv` flag +and use [console.log](https://book.getfoundry.sh/faq?highlight=console.log#how-do-i-use-consolelog). + +This template comes with an example test contract [Foo.t.sol](./test/Foo.t.sol) + +## Usage + +This is a list of the most frequently needed commands. + +### Build + +Build the contracts: + +```bash +forge build +``` + +### Clean + +Delete the build artifacts and cache directories: + +```bash +forge clean +``` + +### Compile + +Compile the contracts: + +```bash +forge build +``` + +### Coverage + +Get a test coverage report: + +```bash +forge coverage +``` + +### Deploy + +Deploy to Anvil: + +```bash +forge script script/Deploy.s.sol --broadcast --fork-url http://localhost:8545 +``` + +For this script to work, you need to have a `MNEMONIC` environment variable set to a valid +[BIP39 mnemonic](https://iancoleman.io/bip39/). + +For instructions on how to deploy to a testnet or mainnet, check out the +[Solidity Scripting](https://book.getfoundry.sh/tutorials/solidity-scripting.html) tutorial. + +### Format + +Format the contracts: + +```bash +forge fmt +``` + +### Gas Usage + +Get a gas report: + +```bash +forge test --gas-report +``` + +### Lint + +Lint the contracts: + +```bash +bun run lint +``` + +### Test + +Run the tests: + +```bash +forge test +``` + +Generate test coverage and output result to the terminal: + +```bash +bun run test:coverage +``` + +Generate test coverage with lcov report (you'll have to open the `./coverage/index.html` file in your browser, to do so +simply copy paste the path): + +```bash +bun run test:coverage:report +``` + +## Related Efforts + +- [abigger87/femplate](https://github.com/abigger87/femplate) +- [cleanunicorn/ethereum-smartcontract-template](https://github.com/cleanunicorn/ethereum-smartcontract-template) +- [foundry-rs/forge-template](https://github.com/foundry-rs/forge-template) +- [FrankieIsLost/forge-template](https://github.com/FrankieIsLost/forge-template) + +## License + +The "NibiruChain/Nibiru/evm-forge" project is licensed under MIT. diff --git a/evm-forge/bun.lockb b/evm-forge/bun.lockb new file mode 100755 index 000000000..80826777b Binary files /dev/null and b/evm-forge/bun.lockb differ diff --git a/evm-forge/foundry.toml b/evm-forge/foundry.toml new file mode 100644 index 000000000..dacd6da68 --- /dev/null +++ b/evm-forge/foundry.toml @@ -0,0 +1,55 @@ +# Full reference https://github.com/foundry-rs/foundry/tree/master/crates/config + +[profile.default] + auto_detect_solc = false + block_timestamp = 1_680_220_800 # March 31, 2023 at 00:00 GMT + bytecode_hash = "none" + evm_version = "shanghai" + fuzz = { runs = 1_000 } + gas_reports = ["*"] + optimizer = true + optimizer_runs = 10_000 + out = "out" + script = "script" + solc = "0.8.25" + src = "src" + test = "test" + +[profile.ci] + fuzz = { runs = 10_000 } + verbosity = 4 + +[etherscan] + arbitrum = { key = "${API_KEY_ARBISCAN}" } + avalanche = { key = "${API_KEY_SNOWTRACE}" } + base = { key = "${API_KEY_BASESCAN}" } + bnb_smart_chain = { key = "${API_KEY_BSCSCAN}" } + gnosis_chain = { key = "${API_KEY_GNOSISSCAN}" } + goerli = { key = "${API_KEY_ETHERSCAN}" } + mainnet = { key = "${API_KEY_ETHERSCAN}" } + optimism = { key = "${API_KEY_OPTIMISTIC_ETHERSCAN}" } + polygon = { key = "${API_KEY_POLYGONSCAN}" } + sepolia = { key = "${API_KEY_ETHERSCAN}" } + +[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] + arbitrum = "https://arbitrum-mainnet.infura.io/v3/${API_KEY_INFURA}" + avalanche = "https://avalanche-mainnet.infura.io/v3/${API_KEY_INFURA}" + base = "https://mainnet.base.org" + bnb_smart_chain = "https://bsc-dataseed.binance.org" + gnosis_chain = "https://rpc.gnosischain.com" + goerli = "https://goerli.infura.io/v3/${API_KEY_INFURA}" + localhost = "http://localhost:8545" + mainnet = "https://eth-mainnet.g.alchemy.com/v2/${API_KEY_ALCHEMY}" + optimism = "https://optimism-mainnet.infura.io/v3/${API_KEY_INFURA}" + polygon = "https://polygon-mainnet.infura.io/v3/${API_KEY_INFURA}" + sepolia = "https://sepolia.infura.io/v3/${API_KEY_INFURA}" diff --git a/evm-forge/justfile b/evm-forge/justfile new file mode 100644 index 000000000..e79d4be3c --- /dev/null +++ b/evm-forge/justfile @@ -0,0 +1,64 @@ +# Displays available recipes by running `just -l`. +setup: + #!/usr/bin/env bash + just -l + +# Remove build artifacts and cache +clean: + rm -rf cache out + +# Build the contracts +build: + forge build + +# Run linters +lint: lint-sol prettier-check + +# Run Solidity linter +lint-sol: + forge fmt --check + bun solhint "{script,src,test}/**/*.sol" + +# Check formatting with Prettier +prettier-check: + prettier --check "**/*.{json,md,yml}" --ignore-path ".prettierignore" + +# Format files with Prettier +prettier-write: + prettier --write "**/*.{json,md,yml}" --ignore-path ".prettierignore" + +# Run tests +test: + forge test + +# Run all tests with verbose output +test-verbose: + forge test -vvv + +# Run test coverage +coverage: + forge coverage + +# Generate test coverage report +coverage-report: + forge coverage --report lcov + genhtml lcov.info --branch-coverage --output-dir coverage + +# Install dependencies +install: + bun install + forge install + +# Update dependencies +update: + bun update + forge update + +# Get gas report +gas-report: + forge test --gas-report + +# Format Solidity files +fmt: + forge fmt + just prettier-write diff --git a/evm-forge/package.json b/evm-forge/package.json new file mode 100644 index 000000000..c91238422 --- /dev/null +++ b/evm-forge/package.json @@ -0,0 +1,39 @@ +{ + "name": "@prb/foundry-template", + "description": "Foundry-based template for developing Solidity smart contracts", + "version": "1.0.0", + "author": { + "name": "Paul Razvan Berg", + "url": "https://github.com/PaulRBerg" + }, + "dependencies": { + "@openzeppelin/contracts": "^5.0.1" + }, + "devDependencies": { + "forge-std": "github:foundry-rs/forge-std#v1.8.1", + "prettier": "^3.3.3", + "prettier-plugin-solidity": "^1.4.1", + "solhint": "^3.6.2" + }, + "keywords": [ + "blockchain", + "ethereum", + "forge", + "foundry", + "smart-contracts", + "solidity", + "template" + ], + "private": true, + "scripts": { + "clean": "rm -rf cache out", + "build": "forge build", + "lint": "bun run lint:sol && bun run prettier:check", + "lint:sol": "forge fmt --check && bun solhint \"{script,src,test}/**/*.sol\"", + "prettier:check": "prettier --check \"**/*.{json,md,yml}\" --ignore-path \".prettierignore\"", + "prettier:write": "prettier --write \"**/*.{json,md,yml}\" --ignore-path \".prettierignore\"", + "test": "forge test", + "test:coverage": "forge coverage", + "test:coverage:report": "forge coverage --report lcov && genhtml lcov.info --branch-coverage --output-dir coverage" + } +} diff --git a/evm-forge/remappings.txt b/evm-forge/remappings.txt new file mode 100644 index 000000000..550f90800 --- /dev/null +++ b/evm-forge/remappings.txt @@ -0,0 +1,2 @@ +@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/ +forge-std/=node_modules/forge-std/ diff --git a/evm-forge/script/Base.s.sol b/evm-forge/script/Base.s.sol new file mode 100644 index 000000000..07135a212 --- /dev/null +++ b/evm-forge/script/Base.s.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.25 <0.9.0; + +import { Script } from "forge-std/src/Script.sol"; + +abstract contract BaseScript is Script { + /// @dev Included to enable compilation of the script without a $MNEMONIC environment variable. + string internal constant TEST_MNEMONIC = "test test test test test test test test test test test junk"; + + /// @dev Needed for the deterministic deployments. + bytes32 internal constant ZERO_SALT = bytes32(0); + + /// @dev The address of the transaction broadcaster. + address internal broadcaster; + + /// @dev Used to derive the broadcaster's address if $ETH_FROM is not defined. + string internal mnemonic; + + /// @dev Initializes the transaction broadcaster like this: + /// + /// - If $ETH_FROM is defined, use it. + /// - Otherwise, derive the broadcaster address from $MNEMONIC. + /// - If $MNEMONIC is not defined, default to a test mnemonic. + /// + /// The use case for $ETH_FROM is to specify the broadcaster key and its address via the command line. + constructor() { + address from = vm.envOr({ name: "ETH_FROM", defaultValue: address(0) }); + if (from != address(0)) { + broadcaster = from; + } else { + mnemonic = vm.envOr({ name: "MNEMONIC", defaultValue: TEST_MNEMONIC }); + (broadcaster,) = deriveRememberKey({ mnemonic: mnemonic, index: 0 }); + } + } + + modifier broadcast() { + vm.startBroadcast(broadcaster); + _; + vm.stopBroadcast(); + } +} diff --git a/evm-forge/script/Deploy.s.sol b/evm-forge/script/Deploy.s.sol new file mode 100644 index 000000000..498db52e5 --- /dev/null +++ b/evm-forge/script/Deploy.s.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.25 <0.9.0; + +import { Foo } from "../src/Foo.sol"; + +import { BaseScript } from "./Base.s.sol"; + +/// @dev See the Solidity Scripting tutorial: https://book.getfoundry.sh/tutorials/solidity-scripting +contract Deploy is BaseScript { + function run() public broadcast returns (Foo foo) { + foo = new Foo(); + } +} diff --git a/evm-forge/src/Foo.sol b/evm-forge/src/Foo.sol new file mode 100644 index 000000000..74830704f --- /dev/null +++ b/evm-forge/src/Foo.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.25; + +contract Foo { + function id(uint256 value) external pure returns (uint256) { + return value; + } +} diff --git a/evm-forge/test/Foo.t.sol b/evm-forge/test/Foo.t.sol new file mode 100644 index 000000000..727337a87 --- /dev/null +++ b/evm-forge/test/Foo.t.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.25 <0.9.0; + +import { Test } from "forge-std/src/Test.sol"; +import { console2 } from "forge-std/src/console2.sol"; + +import { Foo } from "../src/Foo.sol"; + +interface IERC20 { + function balanceOf(address account) external view returns (uint256); +} + +/// @dev If this is your first time with Forge, read this tutorial in the Foundry Book: +/// https://book.getfoundry.sh/forge/writing-tests +contract FooTest is Test { + Foo internal foo; + + /// @dev A function invoked before each test case is run. + function setUp() public virtual { + // Instantiate the contract-under-test. + foo = new Foo(); + } + + /// @dev Basic test. Run it with `forge test -vvv` to see the console log. + function test_Example() external view { + console2.log("Hello World"); + uint256 x = 42; + assertEq(foo.id(x), x, "value mismatch"); + } + + /// @dev Fuzz test that provides random values for an unsigned integer, but which rejects zero as an input. + /// If you need more sophisticated input validation, you should use the `bound` utility instead. + /// See https://twitter.com/PaulRBerg/status/1622558791685242880 + function testFuzz_Example(uint256 x) external view { + vm.assume(x != 0); // or x = bound(x, 1, 100) + assertEq(foo.id(x), x, "value mismatch"); + } + + /// @dev Fork test that runs against an Ethereum Mainnet fork. For this to work, you need to set `API_KEY_ALCHEMY` + /// in your environment You can get an API key for free at https://alchemy.com. + function testFork_Example() external { + // Silently pass this test if there is no API key. + string memory alchemyApiKey = vm.envOr("API_KEY_ALCHEMY", string("")); + if (bytes(alchemyApiKey).length == 0) { + return; + } + + // Otherwise, run the test against the mainnet fork. + vm.createSelectFork({ urlOrAlias: "mainnet", blockNumber: 16_428_000 }); + address usdc = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; + address holder = 0x7713974908Be4BEd47172370115e8b1219F4A5f0; + uint256 actualBalance = IERC20(usdc).balanceOf(holder); + uint256 expectedBalance = 196_307_713.810457e6; + assertEq(actualBalance, expectedBalance); + } +} diff --git a/justfile b/justfile index e1b2aa699..bfdd90458 100644 --- a/justfile +++ b/justfile @@ -43,10 +43,15 @@ lint: golangci-lint run --allow-parallel-runners --fix + # Runs a Nibiru local network. Ex: "just localnet", "just localnet --features featureA" localnet *PASS_FLAGS: make localnet FLAGS="{{PASS_FLAGS}}" +# Runs a Nibiru local network without building and installing. "just localnet --no-build" +localnet-fast: + make localnet FLAGS="--no-build" + # Clears the logs directory log-clear: #!/usr/bin/env bash @@ -75,7 +80,7 @@ test-e2e: source contrib/bashlib.sh log_info "Make sure the localnet is running! (just localnet)" - cd e2e/evm + cd evm-e2e just test diff --git a/x/evm/msg.go b/x/evm/msg.go index 8406ad80a..e1e4edc6e 100644 --- a/x/evm/msg.go +++ b/x/evm/msg.go @@ -315,8 +315,8 @@ func (msg MsgEthereumTx) AsTransaction() *gethcore.Transaction { } // AsMessage creates an Ethereum core.Message from the msg fields -func (msg MsgEthereumTx) AsMessage(signer gethcore.Signer, baseFee *big.Int) (core.Message, error) { - return msg.AsTransaction().AsMessage(signer, baseFee) +func (msg MsgEthereumTx) AsMessage(signer gethcore.Signer, baseFeeWei *big.Int) (core.Message, error) { + return msg.AsTransaction().AsMessage(signer, baseFeeWei) } // GetSender extracts the sender address from the signature values using the latest signer for the given chainID.