diff --git a/.github/template/create-pull-request.md b/.github/template/create-pull-request.md new file mode 100644 index 0000000..a6ec67b --- /dev/null +++ b/.github/template/create-pull-request.md @@ -0,0 +1,2 @@ +### Description +PR to merge from [{{ .fromBranch }}](/axieinfinity/smart-contracts-core/tree/{{ .fromBranch }}) to [{{ .toBranch }}](/axieinfinity/smart-contracts-core/tree/{{ .toBranch }}). diff --git a/.github/workflows/create-PR-implement.yml b/.github/workflows/create-PR-implement.yml new file mode 100644 index 0000000..33d00a5 --- /dev/null +++ b/.github/workflows/create-PR-implement.yml @@ -0,0 +1,50 @@ +name: Create Pull Request From Implement To Feature +on: + push: + branches: + - 'implement-feature/**' + - 'implement-feature/**/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref || github.run_id }} + cancel-in-progress: true + +env: + HEAD_BRANCH: ${{ github.head_ref || github.ref_name }} + +jobs: + createPullRequest: + runs-on: ubuntu-latest + steps: + - name: Set env + run: | + echo "FEATURE_NAME=$(echo ${HEAD_BRANCH} | cut -d'/' -f2)" >> $GITHUB_ENV + echo "FEATURE_BRANCH=feature/$(echo ${HEAD_BRANCH} | cut -d'/' -f2)" >> $GITHUB_ENV + echo "IMPLEMENT_NAME=$(echo ${HEAD_BRANCH} | cut -d'/' -f3)" >> $GITHUB_ENV + + - uses: actions/checkout@v3 + with: + ref: ${{env.FEATURE_BRANCH}} + + - name: Reset promotion branch + run: | + git fetch origin ${HEAD_BRANCH}:${HEAD_BRANCH} + git reset --hard ${HEAD_BRANCH} + + - name: Render template + id: template + uses: chuhlomin/render-template@v1.4 + with: + template: .github/template/create-pull-request.md + vars: | + fromBranch: ${{env.HEAD_BRANCH}} + toBranch: ${{ env.FEATURE_BRANCH }} + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v5 + with: + branch: ${{ env.HEAD_BRANCH }} + base: ${{env.FEATURE_BRANCH}} + labels: automated PR + title: 'feat(${{env.FEATURE_NAME}}): implement `${{env.IMPLEMENT_NAME}}`' + body: ${{ steps.template.outputs.result }} \ No newline at end of file diff --git a/.github/workflows/create-PR-release.yml b/.github/workflows/create-PR-release.yml new file mode 100644 index 0000000..87944c6 --- /dev/null +++ b/.github/workflows/create-PR-release.yml @@ -0,0 +1,84 @@ +name: Create Pull Request From Release to Feature +on: + push: + branches: + - 'release/*' + - 'release*/*' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref || github.run_id }} + cancel-in-progress: true + +env: + HEAD_BRANCH: ${{ github.head_ref || github.ref_name }} + +jobs: + fetchAllFeatureBranches: + runs-on: ubuntu-latest + + steps: + - id: step1 + name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - id: step2 + name: List all the remote feature branches + run: | + branches=$(git branch -r | grep -E '.*origin\/feature\/.*' | sed -e "s/.*origin\///" | tr "\n" " ") + JSON="[" + for branch in ${branches[@]}; do + echo $branch + JSONline="\"$branch\"," + # we don't need to iterate on the same branch over and over, so + # onnly include it when it wasn't included + if [[ "$JSON" != *"$JSONline"* ]]; then + JSON="$JSON$JSONline" + fi + done + # Remove last "," and add the closing bracket + if [[ $JSON == *, ]]; then + JSON="${JSON%?}" + fi + JSON="$JSON]" + echo $JSON + echo "BRANCHES={\"branch_name\": $( echo "$JSON" )}" >> "$GITHUB_OUTPUT" + outputs: + BRANCHES: ${{ steps.step2.outputs.BRANCHES }} + + mergeRelease2FeatureRepo: + runs-on: ubuntu-latest + needs: fetchAllFeatureBranches + strategy: + matrix: ${{ fromJSON(needs.fetchAllFeatureBranches.outputs.BRANCHES) }} + steps: + - name: Set env + run: | + echo "PR_BRANCH=merge/${HEAD_BRANCH}-${{matrix.branch_name}}" >> $GITHUB_ENV + echo "FEATURE_NAME=$(echo ${{matrix.branch_name}} | cut -d'/' -f2)" >> $GITHUB_ENV + - uses: actions/checkout@v3 + with: + ref: ${{matrix.branch_name}} + - name: Reset promotion branch + run: | + git fetch origin ${HEAD_BRANCH}:${HEAD_BRANCH} + git reset --hard ${HEAD_BRANCH} + + - name: Render template + id: template + uses: chuhlomin/render-template@v1.4 + with: + template: .github/template/create-pull-request.md + vars: | + fromBranch: ${{env.HEAD_BRANCH}} + toBranch: ${{matrix.branch_name}} + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v5 + with: + labels: automated PR + delete-branch: true + title: 'chore(`${{env.FEATURE_NAME}}`): merge from `${{env.HEAD_BRANCH}}`' + body: ${{ steps.template.outputs.result }} + branch: ${{env.PR_BRANCH}} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9282e82..1e8d435 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,6 +1,18 @@ name: test -on: workflow_dispatch +on: + push: + branches: + - mainnet + - testnet + - 'feature/*' + - 'features/*' + pull_request: + branches: + - mainnet + - testnet + - 'feature/*' + - 'features/*' env: FOUNDRY_PROFILE: ci @@ -13,7 +25,7 @@ jobs: name: Foundry project runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v3 with: submodules: recursive diff --git a/.gitignore b/.gitignore index 85198aa..5168248 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ docs/ # Dotenv file .env +node_modules +.yarn \ No newline at end of file diff --git a/.husky/.gitignore b/.husky/.gitignore new file mode 100644 index 0000000..c9cdc63 --- /dev/null +++ b/.husky/.gitignore @@ -0,0 +1 @@ +_ \ No newline at end of file diff --git a/.husky/generate-layout.sh b/.husky/generate-layout.sh new file mode 100644 index 0000000..8fc74d4 --- /dev/null +++ b/.husky/generate-layout.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +rm -rf logs/storage/* +dirOutputs=$(ls out | grep '^[^.]*\.sol$') # assuming the out dir is at 'out' +while IFS= read -r contractDir; do + innerdirOutputs=$(ls out/$contractDir) + + while IFS= read -r jsonFile; do + fileIn=out/$contractDir/$jsonFile + fileOut=logs/storage/$contractDir:${jsonFile%.json}.log + node .husky/storage-logger.js $fileIn $fileOut & + done <<< "$innerdirOutputs" +done <<< "$dirOutputs" + +# Wait for all background jobs to finish +wait \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 0000000..259dc79 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,5 @@ +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +forge build +npx lint-staged diff --git a/.husky/pre-push b/.husky/pre-push new file mode 100644 index 0000000..6397927 --- /dev/null +++ b/.husky/pre-push @@ -0,0 +1,26 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +set -ex + +# Workaround: git stash no changes doesn't cause failure but git stash pop cause +output=$(git stash) +stashed=true +if [[ $output == *"No local changes to save"* ]]; then + stashed=false +fi + +forge build --sizes 2>&1 | sed -n '/Contract/,$p' > logs/contract-code-sizes.log +.husky/generate-layout.sh + +git add logs + +output=$(git status -s) +line_count=$(echo "$output" | wc -l) +if [ "$line_count" -gt 1 ]; then + git commit -m "chore: storage layout" +fi + +if $stashed; then + git stash pop +fi \ No newline at end of file diff --git a/.husky/storage-logger.js b/.husky/storage-logger.js new file mode 100644 index 0000000..ec74913 --- /dev/null +++ b/.husky/storage-logger.js @@ -0,0 +1,46 @@ +const fs = require('fs'); +const fileIn = process.argv[2]; +const fileOut = process.argv[3]; + +if (!fileIn) { + console.error('Invalid input'); +} + +fs.readFile(fileIn, 'utf8', (err, data) => { + if (err) { + console.error('Error reading file:', err); + return; + } + + try { + const jsonData = JSON.parse(data); + if (typeof jsonData.storageLayout == 'undefined') { + return; + } + + if (jsonData.storageLayout.storage.length == 0) { + return; + } + + const outputData = jsonData.storageLayout.storage + .map(({ contract, label, offset, slot, type: typeId }) => { + const typeObj = jsonData.storageLayout.types[typeId]; + const typeLabel = typeObj.label; + const numberOfBytes = typeObj.numberOfBytes; + return `${contract}:${label} (storage_slot: ${slot}) (offset: ${offset}) (type: ${typeLabel}) (numberOfBytes: ${numberOfBytes})`; + }) + .join('\n'); + if (!fileOut) { + console.log(outputData); + } else { + fs.writeFile(fileOut, outputData, 'utf-8', err => { + if (err) { + console.error('Error writing file:', err); + return; + } + }); + } + } catch (err) { + console.error('Error parsing JSON:', err); + } +}); \ No newline at end of file diff --git a/.yarnrc.yml b/.yarnrc.yml new file mode 100644 index 0000000..3186f3f --- /dev/null +++ b/.yarnrc.yml @@ -0,0 +1 @@ +nodeLinker: node-modules diff --git a/README.md b/README.md index 9265b45..01c6523 100644 --- a/README.md +++ b/README.md @@ -1,66 +1,35 @@ -## Foundry +# smart-contracts-core -**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.** +The collections of smart contracts that power the Axie Land Delegation. -Foundry consists of: +## Development -- **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. +### Requirement -## Documentation +- [Foundry forge@^0.2.0](https://book.getfoundry.sh/) -https://book.getfoundry.sh/ +### Build & Test -## Usage - -### Build - -```shell -$ forge build -``` - -### Test - -```shell -$ forge test -``` - -### Format +- Install packages ```shell -$ forge fmt +$ forge install ``` -### Gas Snapshots +- Build contracts ```shell -$ forge snapshot +$ forge build ``` -### Anvil +- Run test ```shell -$ anvil +$ forge test ``` ### Deploy ```shell -$ forge script script/Counter.s.sol:CounterScript --rpc-url --private-key -``` - -### Cast - -```shell -$ cast -``` - -### Help - -```shell -$ forge --help -$ anvil --help -$ cast --help +$ forge script -f --private-key ``` diff --git a/foundry.toml b/foundry.toml index 25b918f..596d69b 100644 --- a/foundry.toml +++ b/foundry.toml @@ -2,5 +2,36 @@ src = "src" out = "out" libs = ["lib"] +ffi = true # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options + +remappings = [ + 'forge-std/=lib/forge-std/src/', + 'ds-test/=lib/forge-std/lib/ds-test/src/', + '@openzeppelin/=lib/openzeppelin-contracts/', +] + +solc = '0.8.23' +evm_version = 'istanbul' +use_literal_content = true +extra_output = ["devdoc", "userdoc", "storagelayout"] +fs_permissions = [{ access = "read-write", path = "./" }] + +[fmt] +tab_width = 2 +line_length = 120 +bracket_spacing = true + +[rpc_endpoints] +localhost = "http://localhost:8545" +ethereum = "https://eth.llamarpc.com" +ronin-mainnet = "https://api.roninchain.com/rpc" +goerli = "https://ethereum-goerli.publicnode.com" +ronin-testnet = "https://saigon-archive.roninchain.com/rpc" + +[fuzz] +runs = 256 + +[invariant] +runs = 256 diff --git a/logs/.gitkeep b/logs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/logs/storage/.gitkeep b/logs/storage/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/package.json b/package.json new file mode 100644 index 0000000..dfb52b9 --- /dev/null +++ b/package.json @@ -0,0 +1,22 @@ +{ + "name": "smart-contracts-core", + "version": "1.0.0", + "description": "Smart contracts core.", + "author": "Axie Infinity Engineering ", + "license": "MIT", + "repository": "https://github.com/axieinfinity/smart-contracts-core.git", + "files": [ + "src/**/*.sol" + ], + "devDependencies": { + "husky": "^8.0.3" + }, + "lint-staged": { + "{src,script,test}/**/*.sol": [ + "forge fmt" + ] + }, + "scripts": { + "prepare": "husky install" + } +} diff --git a/pull_request_template.md b/pull_request_template.md new file mode 100644 index 0000000..ad40246 --- /dev/null +++ b/pull_request_template.md @@ -0,0 +1,7 @@ +### Description + +### Checklist + +- [ ] I have clearly commented on all the main functions following the [NatSpec Format](https://docs.soliditylang.org/en/v0.8.0/natspec-format.html) +- [ ] The box that allows repo maintainers to update this PR is checked +- [ ] I tested locally to make sure this feature/fix works diff --git a/script/Counter.s.sol b/script/Counter.s.sol deleted file mode 100644 index 1a47b40..0000000 --- a/script/Counter.s.sol +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import {Script, console2} 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/test/Counter.t.sol b/test/Counter.t.sol deleted file mode 100644 index e9b9e6a..0000000 --- a/test/Counter.t.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import {Test, console2} 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/yarn.lock b/yarn.lock new file mode 100644 index 0000000..45408e5 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,23 @@ +# This file is generated by running "yarn install" inside your project. +# Manual changes might be lost - proceed with caution! + +__metadata: + version: 6 + cacheKey: 8 + +"husky@npm:^8.0.3": + version: 8.0.3 + resolution: "husky@npm:8.0.3" + bin: + husky: lib/bin.js + checksum: 837bc7e4413e58c1f2946d38fb050f5d7324c6f16b0fd66411ffce5703b294bd21429e8ba58711cd331951ee86ed529c5be4f76805959ff668a337dbfa82a1b0 + languageName: node + linkType: hard + +"smart-contracts-core@workspace:.": + version: 0.0.0-use.local + resolution: "smart-contracts-core@workspace:." + dependencies: + husky: ^8.0.3 + languageName: unknown + linkType: soft