diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e5e98c7..87ce080 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,8 +5,34 @@ on: tags: - "*.*.*" +env: + ABI_ARTIFACT_NAME: credible-std-abis + jobs: + create-artifacts: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: stable + + - name: Create ABI artifacts + run: ./shell/create_artifacts.sh + + - name: Upload ABI artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ env.ABI_ARTIFACT_NAME }} + path: artifacts/ + release-npm: + needs: create-artifacts runs-on: ubuntu-latest permissions: contents: read @@ -30,12 +56,26 @@ jobs: - name: Verify package version matches tag run: test "$(node --print "require('./package.json').version")" = "${GITHUB_REF_NAME}" + - name: Download ABI artifacts + uses: actions/download-artifact@v4 + with: + name: ${{ env.ABI_ARTIFACT_NAME }} + path: artifacts/ + + - name: Verify ABI package contents + run: | + jq --exit-status 'type == "array" and length > 0' artifacts/PhEvm.json >/dev/null + npm pack --dry-run --ignore-scripts + - name: Publish to npm run: npm publish --access public --ignore-scripts=true release-github: + needs: create-artifacts permissions: contents: write uses: phylaxsystems/actions/.github/workflows/release-github.yaml@main secrets: SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} + with: + artifact_name: credible-std-abis diff --git a/.github/workflows/solidity-test.yml b/.github/workflows/solidity-test.yml index 8f932f8..0b4229f 100644 --- a/.github/workflows/solidity-test.yml +++ b/.github/workflows/solidity-test.yml @@ -6,6 +6,30 @@ on: pull_request: jobs: + npm-package: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + submodules: recursive + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: stable + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 22 + + - name: Build and verify npm package + run: | + ./shell/create_artifacts.sh + jq --exit-status 'type == "array" and length > 0' artifacts/PhEvm.json >/dev/null + npm pack --dry-run --ignore-scripts + solidity-base: uses: phylaxsystems/actions/.github/workflows/solidity-base.yaml@main with: diff --git a/.gitignore b/.gitignore index 4982444..a092fd6 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,9 @@ cache/ out/ +# Generated ABI package and release assets +/artifacts/ + # pcl-generated assertions cache at repo root /assertions/ diff --git a/README.md b/README.md index 562c954..2083481 100644 --- a/README.md +++ b/README.md @@ -55,12 +55,31 @@ forge-std/=node_modules/@phylax-systems/credible-std/lib/forge-std/src/ @openzeppelin/=node_modules/@phylax-systems/credible-std/lib/openzeppelin-contracts/ ``` +### ABI artifacts + +The npm package includes generated ABI arrays for every ABI-bearing contract and +interface defined under `src/`. Import them directly from `artifacts/`: + +```ts +import phEvmAbi from "@phylax-systems/credible-std/artifacts/PhEvm.json"; +import triggerRecorderAbi from "@phylax-systems/credible-std/artifacts/TriggerRecorder.json"; +import safeTxShapeAssertionAbi from "@phylax-systems/credible-std/artifacts/protection/safe/SafeTxShapeAssertion.json"; +``` + +An artifact whose contract name matches its Solidity filename mirrors the source +path without the `.sol` suffix. Additional contracts or interfaces declared in +the same source use `..json`, for example: +`artifacts/protection/lending/examples/AaveV3Interfaces.IAaveV3PoolLike.json`. + +Each JSON file contains the raw ABI array, matching the artifact format published +by `@phylax-systems/credible-layer-contracts`. + ### Using Foundry (Recommended) Install the latest stable release: ```bash -forge install phylaxsystems/credible-std@0.7.0 +forge install phylaxsystems/credible-std@0.8.1 ``` Or install from master (latest development version): diff --git a/package.json b/package.json index 405ba8c..a50f483 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,14 @@ { "name": "@phylax-systems/credible-std", "description": "Solidity standard library for writing Credible Layer assertions", - "version": "0.8.0", + "version": "0.8.1", "license": "MIT", "author": { "name": "Phylax Systems", "url": "https://phylax.systems" }, "files": [ + "artifacts", "lib/forge-std/src", "lib/openzeppelin-contracts/contracts", "scripts/backtesting/transaction_fetcher.sh", diff --git a/shell/create_artifacts.sh b/shell/create_artifacts.sh new file mode 100755 index 0000000..4277291 --- /dev/null +++ b/shell/create_artifacts.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash + +# Generate stable, source-shaped ABI artifacts for npm and GitHub releases. +# Prerequisites: Foundry and jq. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)" +ROOT_DIR="$(cd "${SCRIPT_DIR}/.." &>/dev/null && pwd)" +ARTIFACTS_DIR="${ROOT_DIR}/artifacts" + +cd "${ROOT_DIR}" +FOUNDRY_PROFILE=default forge clean +FOUNDRY_PROFILE=default forge build + +if [[ "${ARTIFACTS_DIR}" != "${ROOT_DIR}/artifacts" ]]; then + echo "Refusing to replace unexpected artifacts directory: ${ARTIFACTS_DIR}" >&2 + exit 1 +fi + +rm -rf "${ARTIFACTS_DIR}" +mkdir -p "${ARTIFACTS_DIR}" + +artifact_count=0 + +while IFS= read -r forge_artifact; do + compilation_target="$( + jq -r '(.metadata.settings.compilationTarget // {}) | to_entries[0] // empty | select(.key | startswith("src/")) | [.key, .value] | @tsv' "${forge_artifact}" + )" + [[ -n "${compilation_target}" ]] || continue + IFS=$'\t' read -r source_path contract_name <<<"${compilation_target}" + + abi_length="$(jq '.abi | length' "${forge_artifact}")" + (( abi_length > 0 )) || continue + + relative_source="${source_path#src/}" + source_stem="${relative_source%.sol}" + source_name="$(basename "${source_stem}")" + source_dir="$(dirname "${source_stem}")" + + if [[ "${contract_name}" == "${source_name}" ]]; then + relative_output="${source_stem}.json" + elif [[ "${source_dir}" == "." ]]; then + relative_output="${source_name}.${contract_name}.json" + else + relative_output="${source_dir}/${source_name}.${contract_name}.json" + fi + + output_path="${ARTIFACTS_DIR}/${relative_output}" + mkdir -p "$(dirname "${output_path}")" + jq '.abi' "${forge_artifact}" >"${output_path}" + echo "Created artifacts/${relative_output}" + ((artifact_count += 1)) +done < <(find "${ROOT_DIR}/out" -mindepth 2 -maxdepth 2 -type f -name '*.json' | LC_ALL=C sort) + +if (( artifact_count == 0 )); then + echo "No ABI-bearing contracts from src/ were found" >&2 + exit 1 +fi + +duplicate_asset_names="$(find "${ARTIFACTS_DIR}" -type f -name '*.json' -exec basename {} \; | LC_ALL=C sort | uniq -d)" +if [[ -n "${duplicate_asset_names}" ]]; then + echo "Duplicate GitHub release asset names:" >&2 + echo "${duplicate_asset_names}" >&2 + exit 1 +fi + +echo "Created ${artifact_count} ABI artifacts"