diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..9282e82 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,34 @@ +name: test + +on: workflow_dispatch + +env: + FOUNDRY_PROFILE: ci + +jobs: + check: + strategy: + fail-fast: true + + name: Foundry project + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly + + - name: Run Forge build + run: | + forge --version + forge build --sizes + id: build + + - name: Run Forge tests + run: | + forge test -vvv + id: test diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..85198aa --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +# Compiler files +cache/ +out/ + +# Ignores development broadcast logs +!/broadcast +/broadcast/*/31337/ +/broadcast/**/dry-run/ + +# Docs +docs/ + +# Dotenv file +.env diff --git a/README.md b/README.md new file mode 100644 index 0000000..9265b45 --- /dev/null +++ b/README.md @@ -0,0 +1,66 @@ +## Foundry + +**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.** + +Foundry consists of: + +- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). +- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. +- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. +- **Chisel**: Fast, utilitarian, and verbose solidity REPL. + +## Documentation + +https://book.getfoundry.sh/ + +## Usage + +### Build + +```shell +$ forge build +``` + +### Test + +```shell +$ forge test +``` + +### Format + +```shell +$ forge fmt +``` + +### Gas Snapshots + +```shell +$ forge snapshot +``` + +### Anvil + +```shell +$ anvil +``` + +### Deploy + +```shell +$ forge script script/Counter.s.sol:CounterScript --rpc-url --private-key +``` + +### Cast + +```shell +$ cast +``` + +### Help + +```shell +$ forge --help +$ anvil --help +$ cast --help +``` diff --git a/foundry.toml b/foundry.toml new file mode 100644 index 0000000..25b918f --- /dev/null +++ b/foundry.toml @@ -0,0 +1,6 @@ +[profile.default] +src = "src" +out = "out" +libs = ["lib"] + +# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/patch b/patch new file mode 100644 index 0000000..8a48908 --- /dev/null +++ b/patch @@ -0,0 +1,338 @@ +diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml +index 9282e82..9aebfdc 100644 +--- a/.github/workflows/test.yml ++++ b/.github/workflows/test.yml +@@ -1,6 +1,10 @@ + name: test + +-on: workflow_dispatch ++on: ++ pull_request: ++ merge_group: ++ push: ++ branches: [main] + + env: + FOUNDRY_PROFILE: ci +@@ -27,8 +31,3 @@ jobs: + forge --version + forge build --sizes + id: build +- +- - name: Run Forge tests +- run: | +- forge test -vvv +- id: test +diff --git a/.gitmodules b/.gitmodules +new file mode 100644 +index 0000000..888d42d +--- /dev/null ++++ b/.gitmodules +@@ -0,0 +1,3 @@ ++[submodule "lib/forge-std"] ++ path = lib/forge-std ++ url = https://github.com/foundry-rs/forge-std +diff --git a/README.md b/README.md +index 9265b45..4978644 100644 +--- a/README.md ++++ b/README.md +@@ -1,66 +1,35 @@ +-## Foundry ++## forge-alphanet + +-**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.** ++Set of solidity utilities to ease deployment and usage of applications on ++[AlphaNet]. + +-Foundry consists of: ++### BLS library + +-- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). +-- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. +-- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. +-- **Chisel**: Fast, utilitarian, and verbose solidity REPL. ++Functions to allow calling each of the BLS precompiles defined in [EIP-2537] ++without the low level details. + +-## Documentation ++For example, this is how the library can be used from a solidity smart contract: ++```solidity ++// SPDX-License-Identifier: MIT ++pragma solidity ^0.8.25; + +-https://book.getfoundry.sh/ ++import "/path/to/forge-alphanet/src/libraries/BLS.sol"; + +-## Usage ++contract BLSExample { ++ event OperationResult(bool success, bytes result); + +-### Build ++ // Function to perform a BLS12-381 G1 addition with error handling ++ function performG1Add(bytes memory input) public { ++ (bool success, bytes memory output) = BLS.G1Add(input); + +-```shell +-$ forge build ++ if (!success) { ++ emit OperationResult(false, ""); ++ } else { ++ emit OperationResult(true, output); ++ } ++ } ++} + ``` + +-### Test +- +-```shell +-$ forge test +-``` +- +-### Format +- +-```shell +-$ forge fmt +-``` +- +-### Gas Snapshots +- +-```shell +-$ forge snapshot +-``` +- +-### Anvil +- +-```shell +-$ anvil +-``` +- +-### Deploy +- +-```shell +-$ forge script script/Counter.s.sol:CounterScript --rpc-url --private-key +-``` +- +-### Cast +- +-```shell +-$ cast +-``` +- +-### Help +- +-```shell +-$ forge --help +-$ anvil --help +-$ cast --help +-``` ++[AlphaNet]: https://github.com/paradigmxyz/alphanet ++[EIP-2537]: https://eips.ethereum.org/EIPS/eip-2537 +diff --git a/lib/forge-std b/lib/forge-std +new file mode 160000 +index 0000000..bb4ceea +--- /dev/null ++++ b/lib/forge-std +@@ -0,0 +1 @@ ++Subproject commit bb4ceea94d6f10eeb5b41dc2391c6c8bf8e734ef +diff --git a/script/Counter.s.sol b/script/Counter.s.sol +deleted file mode 100644 +index df9ee8b..0000000 +--- a/script/Counter.s.sol ++++ /dev/null +@@ -1,12 +0,0 @@ +-// SPDX-License-Identifier: UNLICENSED +-pragma solidity ^0.8.13; +- +-import {Script, console} from "forge-std/Script.sol"; +- +-contract CounterScript is Script { +- function setUp() public {} +- +- function run() public { +- vm.broadcast(); +- } +-} +diff --git a/src/Counter.sol b/src/Counter.sol +deleted file mode 100644 +index aded799..0000000 +--- a/src/Counter.sol ++++ /dev/null +@@ -1,14 +0,0 @@ +-// SPDX-License-Identifier: UNLICENSED +-pragma solidity ^0.8.13; +- +-contract Counter { +- uint256 public number; +- +- function setNumber(uint256 newNumber) public { +- number = newNumber; +- } +- +- function increment() public { +- number++; +- } +-} +diff --git a/src/libraries/BLS.sol b/src/libraries/BLS.sol +new file mode 100644 +index 0000000..f2d126f +--- /dev/null ++++ b/src/libraries/BLS.sol +@@ -0,0 +1,127 @@ ++// SPDX-License-Identifier: MIT ++pragma solidity ^0.8.25; ++ ++library BLS { ++ // Precompile addresses come from the BLS addresses submodule in AlphaNet, see: ++ // ++ ++ // Output sizes come from the ABI definition for each of the BLS operations ++ // in EIP-2537, see: ++ // G1ADD output size allocation ++ uint256 constant G1ADD_OUTPUT_SIZE = 128; ++ // G1MUL output size allocation ++ uint256 constant G1MUL_OUTPUT_SIZE = 128; ++ // G1MULTIEXP output size allocation ++ uint256 constant G1MULTIEXP_OUTPUT_SIZE = 128; ++ // G2ADD output size allocation ++ uint256 constant G2ADD_OUTPUT_SIZE = 256; ++ // G2MUL output size allocation ++ uint256 constant G2MUL_OUTPUT_SIZE = 256; ++ // G2MULTIEXP output size allocation ++ uint256 constant G2MULTIEXP_OUTPUT_SIZE = 256; ++ // PAIRING output size allocation ++ uint256 constant PAIRING_OUTPUT_SIZE = 32; ++ // MAP_FP_TO_G1 output size allocation ++ uint256 constant MAP_FP_TO_G1_OUTPUT_SIZE = 128; ++ // MAP_FP2_TO_G2 output size allocation ++ uint256 constant MAP_FP2_TO_G2_OUTPUT_SIZE = 256; ++ ++ // Function to perform G1ADD operation ++ function G1Add(bytes memory input) internal view returns (bool, bytes memory) { ++ bytes memory output = new bytes(G1ADD_OUTPUT_SIZE); ++ bool success; ++ assembly { ++ // G1ADD address is 0x0b ++ success := staticcall(gas(), 0x0b, add(input, 0x20), mload(input), output, G1ADD_OUTPUT_SIZE) ++ } ++ return (success, output); ++ } ++ ++ // Function to perform G1MUL operation ++ function G1Mul(bytes memory input) internal view returns (bool, bytes memory) { ++ bytes memory output = new bytes(G1MUL_OUTPUT_SIZE); ++ bool success; ++ assembly { ++ // G1MUL address is 0x0c ++ success := staticcall(gas(), 0x0c, add(input, 0x20), mload(input), output, G1MUL_OUTPUT_SIZE) ++ } ++ return (success, output); ++ } ++ ++ // Function to perform G1MULTIEXP operation ++ function G1MultiExp(bytes memory input) internal view returns (bool, bytes memory) { ++ bytes memory output = new bytes(G1MULTIEXP_OUTPUT_SIZE); ++ bool success; ++ assembly { ++ // G1MULTIEXP address is 0x0d ++ success := staticcall(gas(), 0x0d, add(input, 0x20), mload(input), output, G1MULTIEXP_OUTPUT_SIZE) ++ } ++ return (success, output); ++ } ++ ++ // Function to perform G2ADD operation ++ function G2Add(bytes memory input) internal view returns (bool, bytes memory) { ++ bytes memory output = new bytes(G2ADD_OUTPUT_SIZE); ++ bool success; ++ assembly { ++ // G2ADD address is 0x0e ++ success := staticcall(gas(), 0x0e, add(input, 0x20), mload(input), output, G2ADD_OUTPUT_SIZE) ++ } ++ return (success, output); ++ } ++ ++ // Function to perform G2MUL operation ++ function G2Mul(bytes memory input) internal view returns (bool, bytes memory) { ++ bytes memory output = new bytes(G2MUL_OUTPUT_SIZE); ++ bool success; ++ assembly { ++ // G2MUL address is 0x0f ++ success := staticcall(gas(), 0x0f, add(input, 0x20), mload(input), output, G2MUL_OUTPUT_SIZE) ++ } ++ return (success, output); ++ } ++ ++ // Function to perform G2MULTIEXP operation ++ function G2MultiExp(bytes memory input) internal view returns (bool, bytes memory) { ++ bytes memory output = new bytes(G2MULTIEXP_OUTPUT_SIZE); ++ bool success; ++ assembly { ++ // G2MULTIEXP address is 0x10 ++ success := staticcall(gas(), 0x10, add(input, 0x20), mload(input), output, G2MULTIEXP_OUTPUT_SIZE) ++ } ++ return (success, output); ++ } ++ ++ // Function to perform PAIRING operation ++ function Pairing(bytes memory input) internal view returns (bool, bytes memory) { ++ bytes memory output = new bytes(PAIRING_OUTPUT_SIZE); ++ bool success; ++ assembly { ++ // PAIRING address is 0x11 ++ success := staticcall(gas(), 0x11, add(input, 0x20), mload(input), output, PAIRING_OUTPUT_SIZE) ++ } ++ return (success, output); ++ } ++ ++ // Function to perform MAP_FP_TO_G1 operation ++ function MapFpToG1(bytes memory input) internal view returns (bool, bytes memory) { ++ bytes memory output = new bytes(MAP_FP_TO_G1_OUTPUT_SIZE); ++ bool success; ++ assembly { ++ // MAP_FP_TO_G1 address is 0x12 ++ success := staticcall(gas(), 0x12, add(input, 0x20), mload(input), output, MAP_FP_TO_G1_OUTPUT_SIZE) ++ } ++ return (success, output); ++ } ++ ++ // Function to perform MAP_FP2_TO_G2 operation ++ function MapFp2ToG2(bytes memory input) internal view returns (bool, bytes memory) { ++ bytes memory output = new bytes(MAP_FP2_TO_G2_OUTPUT_SIZE); ++ bool success; ++ assembly { ++ // MAP_FP2_TO_G2 address is 0x13 ++ success := staticcall(gas(), 0x13, add(input, 0x20), mload(input), output, MAP_FP2_TO_G2_OUTPUT_SIZE) ++ } ++ return (success, output); ++ } ++} +diff --git a/test/Counter.t.sol b/test/Counter.t.sol +deleted file mode 100644 +index 54b724f..0000000 +--- a/test/Counter.t.sol ++++ /dev/null +@@ -1,24 +0,0 @@ +-// SPDX-License-Identifier: UNLICENSED +-pragma solidity ^0.8.13; +- +-import {Test, console} from "forge-std/Test.sol"; +-import {Counter} from "../src/Counter.sol"; +- +-contract CounterTest is Test { +- Counter public counter; +- +- function setUp() public { +- counter = new Counter(); +- counter.setNumber(0); +- } +- +- function test_Increment() public { +- counter.increment(); +- assertEq(counter.number(), 1); +- } +- +- function testFuzz_SetNumber(uint256 x) public { +- counter.setNumber(x); +- assertEq(counter.number(), x); +- } +-} diff --git a/script/Counter.s.sol b/script/Counter.s.sol new file mode 100644 index 0000000..df9ee8b --- /dev/null +++ b/script/Counter.s.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Script, console} from "forge-std/Script.sol"; + +contract CounterScript is Script { + function setUp() public {} + + function run() public { + vm.broadcast(); + } +} diff --git a/src/Counter.sol b/src/Counter.sol new file mode 100644 index 0000000..aded799 --- /dev/null +++ b/src/Counter.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +contract Counter { + uint256 public number; + + function setNumber(uint256 newNumber) public { + number = newNumber; + } + + function increment() public { + number++; + } +} diff --git a/test/Counter.t.sol b/test/Counter.t.sol new file mode 100644 index 0000000..54b724f --- /dev/null +++ b/test/Counter.t.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Test, console} from "forge-std/Test.sol"; +import {Counter} from "../src/Counter.sol"; + +contract CounterTest is Test { + Counter public counter; + + function setUp() public { + counter = new Counter(); + counter.setNumber(0); + } + + function test_Increment() public { + counter.increment(); + assertEq(counter.number(), 1); + } + + function testFuzz_SetNumber(uint256 x) public { + counter.setNumber(x); + assertEq(counter.number(), x); + } +}