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.