From bda3d7ea61783277c0f10e0da976543855d8277f Mon Sep 17 00:00:00 2001 From: Adnan Haque Date: Fri, 17 Jul 2026 15:12:49 +0600 Subject: [PATCH 1/3] ci: add release artifact pipeline and docs regeneration - release.yml: on every published release, build the contracts at the tag and attach a deterministic ABI bundle (abis-.tar.gz plus SHA256SUMS) as release assets. Stable releases also dispatch a contracts-release-published event to oak-network/sdk (SDK_SYNC_TOKEN secret) so its Contracts Sync workflow opens an ABI update PR. Failures open a deduplicated release-pipeline issue. - docs.yml: regenerate forge-doc markdown on pushes to main that touch src/, sanitize machine-specific link paths, and open an auto PR. - build-abi-bundle.sh: reproducible tarball (sorted entries, fixed mtime) of all src/ ABIs plus DataRegistryKeys.sol and metadata. contractKind from the AST separates deployable contracts from libraries and interfaces. Errors on two sources sharing a contract name. - sanitize-docs.py: rewrites absolute machine paths in generated doc links to correct relative links, and fails on broken targets. --- .../__pycache__/sanitize-docs.cpython-314.pyc | Bin 0 -> 5083 bytes .github/scripts/build-abi-bundle.sh | 118 ++++++++++++ .github/scripts/sanitize-docs.py | 79 ++++++++ .github/workflows/docs.yml | 86 +++++++++ .github/workflows/release.yml | 174 ++++++++++++++++++ .gitignore | 4 + 6 files changed, 461 insertions(+) create mode 100644 .github/scripts/__pycache__/sanitize-docs.cpython-314.pyc create mode 100755 .github/scripts/build-abi-bundle.sh create mode 100755 .github/scripts/sanitize-docs.py create mode 100644 .github/workflows/docs.yml create mode 100644 .github/workflows/release.yml diff --git a/.github/scripts/__pycache__/sanitize-docs.cpython-314.pyc b/.github/scripts/__pycache__/sanitize-docs.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e96f8d1d9a62c07c72fbfc8f9a18ab74b8cdb2d1 GIT binary patch literal 5083 zcmb7HeM}qY8Gp}r{=~+HgpVW;j?e&?6niWAU?fe_(ln%$)HSK;ItO#u2VAl5u6O4G zRzJM1(#%!~P&>i2YGP8ik*QMh$0oH>rfOZK?N3sYK(9#aw!ikzl#EuH`q!TKJ)iBk zwCP^U@4a{T-t#`6zvp=lSNl8&M(p3;f4;X0p?~5VYcPey_Ag5@t4Jg7ipj1G2MC`D3khSxYE8^^V7NO)`gqqML zI$T?#V69rwC1)!-QnV(jhloPC+(Upwe%c{b( zD(V8uk7;r$1B*1Dm=q;}Rd{_;V?_xbs8dOKT4Gb8G^GVSo*_A1pv1l?sG2BC8Y`ql z9X9KNN~n{qEU%Twj*BTlo7Hq7U7#$Fv-+gqv5wOxd7V{xi5(?8vcTgg+bIMmg6wGM z84b<}os{^ra4HlG21D>x3u$VC{K>K7d_vIhyHPIaxi~3mtd>wk1!$TA0xQxi5rNft zP=d}TWJ%{mNt7nwgd)LFR+lkj@LE-cgzmw503s<~7he>tmspx|0{8U z3zB|<)%0Xa9AjlEH4AEcM&l=hUREpE0_#Z&DzofKyyya9x{{)L%5!!`)HPO;rLL@? z%B(of@&y@ysKt9y7VsXO6=pfqun4L{b^3nu6 z7bP$UpJc%j2{{c0)UhtcRCx*xA{GQyUdSGeb#{h+FcQH1J-jzc!%NC&h2oS1O<|F@nMPvoKar#fUP`(nktXOv6=;Mja!l=PmH7qu8(Rk=Q|G zE^z!cGW#nIfZM3u;t?>lLh7hv$k}acL)Mi=hfzm?#02oughX2lYDO*O_vD)==w>8R z-@NN$fu^~ab7o!xf{`MESr{GqNA5Qz$buU)Trpb90Nog!0B|D2tu?k&%B05tA0W;x zFnka`0Nf}(DM5<{;0H*Yl%`#o&d+8xeNZ=kHK`m z01p?823v~*f*#W#BLqt2XcZgFutU@(CROZG9BtTC!LSdKC)mk3N`>5v%&1houq1(G zv{}&>Cz?2!k`sJNI~6PsxiLiVY#jDCOQq1rQRl=}vQOMH%Fczg^3^}5d<=Z44cZi<|HB#I&au5C~WvIF1 ziCAkDBT8GIUi4w8H=59peaG0Ntlcf>KGN+JRkk5#Q934Ki!xq_u7?Otn`Wol464(h zIL>9*CsaA37&ci`vGNS&d0CVUyQZW>fNqliG{eCwiXbHoCnRbN@tjM=)so@RGUMao zjNvhJejKVCqpGmRzG1km%x*Z$#HZTvcP{)g4*6KaLDX2D#{a@o| zh4;k6UV_j6n?sv5^-KE}_bosD=Kj26tFB>bcyV|swisLKx>~EUo$^FH$DH#+$+md8~#Hd`wwk;Yv%{&2AA7!cw4u2)h!)g zJic^#@$^dX>dgACp1fBh-3M%$>U4sT zxUMq1PoBRp5D!0c@#5LY1%uHtW1wYH8**;4)zpk_e9~~@JO>qyie1HUW|Smkv7}*7 z$QgKe5YujlNyARtK8}^go;hPG&v%c`RGd>t-unYAzRaP|J;+m?Pc9E{xDVcRA6%{7 ztlhoTvDon@mv`Q!k*EE(1MS(Dd)(5?t8=d|>+Ajpa{YH12-^SP$o;$))gRcXYrkIC zzA}B)3F`gfh1|JKr#Jt?>$6+FnuQ0J>6M8MU+|_cc=f=hw|c&Ru76?phPN3`s1AKr z6xKRjC1XLtYE zRn@o8eGf!p(8G|n{Qyk&gUArn@0^Xqf{A^Uik0~_fJacT8{TUen9TCQhl>N>8wqkT zQ4HP{2X<@1%_z*})xkGMvlaDFzfD7)1^k3OTf{G@;)>9|Q{PjnI$EFtY(r)F9fNnp z2=9O#W%QYf?QlCnmDhb1ugd+-srf3UQf*R1!`2E}=_vA|hiqjW?a=8}N<4xTDu@(Y zndCcmb*1wI=c0;eHe$#SwLc>%Pi>4S9@(o`$p@AmoWwED{QFi)H zdf#L1D_w_=S$GYc6>Lmuu9@M62g_B!?@k;QaRbk2PCN#agUYw3wwW{y@KAX~kW}l`HE0|13SwW=b{! zQ9ObmlHDYdv+bF0)dE79#o+vtZ4^{hR(naK$Fl&`(2vUMY%iOogXv_}g$qMyHQ8)q zsn5oJTc^eqTEc9wK*B8iaG7Ve<_aWHorOX#EovHW`|yEUZ4Gp`& zK)3v&VOLb#4>_Qv1&ZbQ15zq!s_dc$92(3tpPIsDVp5xi7Kk9c3f>2~SeSwHlR!gO zak`*2LFYVXRyt3Gu)U$`F)nIke^UywN@1GuQK@mzSv<&OKW$-&*ytYeOGA_x^J? zUHzZ<16#Z6m(DGoTY75osdxH*+yATn)u(?wu)g~{xxuX}-~92pY>R+qwxu$)PeLs8C^#oA)%2tjTzs^BB%}96D)eUr9NEbC3Exm~efNR=`qf7k-3uJW;#>-(ri! z;|2qDn|>J)lBRuJC)08=lM+s;5!jClYHbjv+cZT{cNh=FY&Rmx^C@zFiafVHC{R0} z{YiFhZ)Dv+l=FV!fFAjaW03G}A47~cpS?lv*<_qQ?tiU+?&43Qx0u}^2eX$P_df?U BLsb9( literal 0 HcmV?d00001 diff --git a/.github/scripts/build-abi-bundle.sh b/.github/scripts/build-abi-bundle.sh new file mode 100755 index 0000000..4240348 --- /dev/null +++ b/.github/scripts/build-abi-bundle.sh @@ -0,0 +1,118 @@ +#!/usr/bin/env bash +# Builds the deterministic ABI release bundle consumed by the oak-network/sdk +# sync pipeline (see .github/workflows/release.yml). +# +# Usage: .github/scripts/build-abi-bundle.sh +# Requires: `forge build --ast` already run (artifacts/ populated WITH AST - +# contractKind is read from it), jq, tar, sha256sum. +# +# Output: +# dist/abis-.tar.gz - reproducible tarball: +# abis/{ContractName}.json { contractName, sourcePath, abi } +# sources/DataRegistryKeys.sol +# metadata.json { tag, sha, solcVersion, foundryVersion, +# contracts[], deployableContracts[] } +# dist/SHA256SUMS +set -euo pipefail + +TAG="${1:?usage: build-abi-bundle.sh }" +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +ARTIFACTS_DIR="$REPO_ROOT/artifacts" +BUNDLE_DIR="$REPO_ROOT/.abi-bundle" +DIST_DIR="$REPO_ROOT/dist" + +if [[ ! -d "$ARTIFACTS_DIR" ]]; then + echo "error: $ARTIFACTS_DIR not found - run 'forge build' first" >&2 + exit 1 +fi + +rm -rf "$BUNDLE_DIR" "$DIST_DIR" +mkdir -p "$BUNDLE_DIR/abis" "$BUNDLE_DIR/sources" "$DIST_DIR" + +contracts=() +deployable=() +solc_version="" + +# Every artifact whose compilation target lives under src/ (excludes test/, lib/, script/). +while IFS= read -r artifact; do + target="$(jq -r '.metadata.settings.compilationTarget | to_entries[0] | "\(.key):\(.value)"' "$artifact" 2>/dev/null || true)" + [[ "$target" == src/* ]] || continue + + source_path="${target%%:*}" + contract_name="${target##*:}" + + # Skip duplicate artifacts for the same source (same contract compiled under + # multiple solc versions); error on two different sources sharing one name, + # since abis/{name}.json could then hold the wrong contract. + if printf '%s\n' "${contracts[@]:-}" | grep -qx "$contract_name"; then + existing_source="$(jq -r .sourcePath "$BUNDLE_DIR/abis/$contract_name.json")" + if [[ "$existing_source" != "$source_path" ]]; then + echo "error: contract name $contract_name is defined in both $existing_source and $source_path" >&2 + exit 1 + fi + echo "warning: duplicate artifact for $contract_name, keeping first, skipping $artifact" >&2 + continue + fi + + # contractKind comes from the AST (requires forge build --ast). Only concrete, + # non-abstract contracts count as deployable - libraries and interfaces are + # bundled for their ABIs but excluded from new-contract detection. + kind_info="$(jq -r --arg n "$contract_name" \ + '[.ast.nodes[]? | select(.nodeType == "ContractDefinition" and .name == $n)][0] + | if . == null then "missing" else "\(.contractKind):\(.abstract)" end' "$artifact")" + if [[ "$kind_info" == "missing" ]]; then + echo "error: no AST in artifact for $contract_name - run 'forge build --ast'" >&2 + exit 1 + fi + + jq --arg name "$contract_name" --arg src "$source_path" -S \ + '{ contractName: $name, sourcePath: $src, abi: .abi }' \ + "$artifact" > "$BUNDLE_DIR/abis/$contract_name.json" + + contracts+=("$contract_name") + if [[ "$kind_info" == "contract:false" && "$(jq -r '.bytecode.object' "$artifact")" != "0x" ]]; then + deployable+=("$contract_name") + fi + if [[ -z "$solc_version" ]]; then + solc_version="$(jq -r '.metadata.compiler.version' "$artifact")" + fi +done < <(find "$ARTIFACTS_DIR" -name '*.json' -path '*.sol/*' | LC_ALL=C sort) + +if [[ ${#contracts[@]} -eq 0 ]]; then + echo "error: no src/ contracts found in $ARTIFACTS_DIR" >&2 + exit 1 +fi + +cp "$REPO_ROOT/src/constants/DataRegistryKeys.sol" "$BUNDLE_DIR/sources/DataRegistryKeys.sol" + +GIT_SHA="$(git -C "$REPO_ROOT" rev-parse HEAD)" +FOUNDRY_VERSION="$(forge --version | head -n1)" + +# jq --args builds proper JSON arrays; guard the empty case explicitly (a bare +# printf-into-jq pipeline would turn an empty array into [""]). +contracts_json="$(jq -cn '$ARGS.positional | sort' --args "${contracts[@]}")" +if [[ ${#deployable[@]} -gt 0 ]]; then + deployable_json="$(jq -cn '$ARGS.positional | sort' --args "${deployable[@]}")" +else + deployable_json="[]" +fi + +jq -n -S \ + --arg tag "$TAG" \ + --arg sha "$GIT_SHA" \ + --arg solc "$solc_version" \ + --arg foundry "$FOUNDRY_VERSION" \ + --argjson contracts "$contracts_json" \ + --argjson deployable "$deployable_json" \ + '{ tag: $tag, sha: $sha, solcVersion: $solc, foundryVersion: $foundry, + contracts: $contracts, deployableContracts: $deployable }' \ + > "$BUNDLE_DIR/metadata.json" + +# Reproducible tarball: fixed order, ownership, and mtime. +tar --sort=name --owner=0 --group=0 --numeric-owner --mtime='UTC 2020-01-01' \ + -czf "$DIST_DIR/abis-$TAG.tar.gz" -C "$BUNDLE_DIR" . +(cd "$DIST_DIR" && sha256sum "abis-$TAG.tar.gz" > SHA256SUMS) + +echo "Bundle: $DIST_DIR/abis-$TAG.tar.gz" +echo "Contracts (${#contracts[@]}): ${contracts[*]}" +echo "Deployable (${#deployable[@]}): ${deployable[*]}" diff --git a/.github/scripts/sanitize-docs.py b/.github/scripts/sanitize-docs.py new file mode 100755 index 0000000..cb358f6 --- /dev/null +++ b/.github/scripts/sanitize-docs.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +"""Sanitize forge-doc output: rewrite absolute machine paths in markdown links. + +Some forge versions emit inter-doc links as absolute filesystem paths of the +machine that ran `forge doc` (e.g. `/Users//.../docs/src/src/interfaces/...`). +This script rewrites any link target containing `/docs/src/` to the correct +path relative to the file containing the link. Idempotent; stdlib only. + +Usage: sanitize-docs.py +Exits non-zero if a rewritten target does not exist under +(catches upstream layout changes instead of committing broken links). +""" + +import os +import re +import sys + +# Matches links whose target is an absolute path into a forge-doc output root - +# either the committed layout (docs/src/) or the CI temp layout (.forgedoc-tmp/src/). +# Anything that slips through is caught by the grep guard in docs.yml. +LINK_PATTERN = re.compile(r"\((/[^\s)]*?/(?:docs|\.forgedoc-tmp)/src/([^\s)]+))\)") + + +def sanitize_file(path: str, docs_src_root: str) -> tuple[int, list[str]]: + """Rewrites absolute /…/docs/src/… links in one file. + + Returns (number of rewrites, list of rewritten targets that don't exist). + """ + with open(path, encoding="utf-8") as fh: + content = fh.read() + + broken: list[str] = [] + file_dir = os.path.dirname(path) + + def replace(match: re.Match) -> str: + suffix = match.group(2) + target_abs = os.path.join(docs_src_root, suffix) + target_file = target_abs.split("#", 1)[0] + if not os.path.exists(target_file): + broken.append(suffix) + relative = os.path.relpath(target_abs, file_dir) + return f"({relative})" + + updated, count = LINK_PATTERN.subn(replace, content) + if count > 0: + with open(path, "w", encoding="utf-8") as fh: + fh.write(updated) + return count, broken + + +def main() -> int: + if len(sys.argv) != 2: + print("usage: sanitize-docs.py ", file=sys.stderr) + return 2 + docs_src_root = os.path.abspath(sys.argv[1]) + if not os.path.isdir(docs_src_root): + print(f"error: not a directory: {docs_src_root}", file=sys.stderr) + return 2 + + total = 0 + all_broken: list[str] = [] + for dirpath, _dirnames, filenames in os.walk(docs_src_root): + for filename in filenames: + if filename.endswith(".md"): + count, broken = sanitize_file(os.path.join(dirpath, filename), docs_src_root) + total += count + all_broken.extend(broken) + + print(f"Rewrote {total} absolute link(s) under {docs_src_root}") + if all_broken: + print("error: rewritten links point at missing files:", file=sys.stderr) + for target in sorted(set(all_broken)): + print(f" - {target}", file=sys.stderr) + return 1 + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..771ef36 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,86 @@ +# Regenerate Docs +# +# Keeps the committed forge-doc markdown under docs/src/ in sync with the +# Solidity sources on main. Regenerates into a temp dir (never straight into +# docs/ - book.toml, book.css, and solidity.min.js are customized and must be +# preserved), sanitizes machine-specific absolute link paths, and opens (or +# refreshes) an auto-PR when anything changed. +name: Regenerate Docs + +on: + push: + branches: [main] + paths: + - "src/**" + - "docs/book.toml" + - "foundry.toml" + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + +concurrency: + group: docs-regen + cancel-in-progress: true + +env: + FOUNDRY_VERSION: v1.7.1 # keep in sync with release.yml + +jobs: + forge-doc: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - name: Checkout main + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + submodules: recursive + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@82dee4ba654bd2146511f85f0d013af94670c4de # v1.4.0 + with: + version: ${{ env.FOUNDRY_VERSION }} + + - name: Generate docs into temp dir + run: forge doc --out .forgedoc-tmp + + - name: Sanitize machine-specific link paths + run: python3 .github/scripts/sanitize-docs.py .forgedoc-tmp/src + + - name: Guard against surviving absolute machine paths + run: | + if grep -rEl '\]\((/Users/|/home/|/root/)' .forgedoc-tmp/src; then + echo "error: absolute machine paths survived sanitization (files listed above)" >&2 + exit 1 + fi + + - name: Sync generated markdown into docs/src + run: | + rsync -a --delete .forgedoc-tmp/src/ docs/src/ + rm -rf .forgedoc-tmp + + # Known tradeoff: with the default GITHUB_TOKEN the created PR does not + # trigger pull_request workflows (no CI checks on docs PRs). If main ever + # requires status checks, add a CONTRACTS_BOT_TOKEN secret (fine-grained + # PAT, Contents + Pull requests r/w on this repo); the fallback below + # picks it up automatically. + - name: Open or refresh docs PR + uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8 + with: + token: ${{ secrets.CONTRACTS_BOT_TOKEN || github.token }} + branch: docs/regenerate + base: main + delete-branch: true + add-paths: docs/src/** + commit-message: "docs: regenerate forge doc from ${{ github.sha }}" + title: "docs: regenerate contract documentation" + labels: documentation + body: | + Auto-regenerated `forge doc` output for [`${{ github.sha }}`](${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }}). + + Machine-specific absolute link paths are sanitized; `docs/book.toml`, + `book.css`, and `solidity.min.js` are untouched. No-op pushes close + this PR automatically when the diff becomes empty. + + _Auto-generated by the Regenerate Docs workflow._ diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..0219ba3 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,174 @@ +# Release Artifacts +# +# On every published GitHub Release: builds the contracts at the released tag, +# attaches a deterministic ABI bundle (abis-.tar.gz + SHA256SUMS) as +# release assets, and - for stable (non-prerelease) releases - notifies +# oak-network/sdk via repository_dispatch so its Contracts Sync workflow can +# open an ABI-update PR. +# +# Secrets: +# SDK_SYNC_TOKEN - fine-grained PAT scoped to oak-network/sdk only, with +# Contents: read/write (required by POST /repos/{owner}/{repo}/dispatches). +# PATs expire (max 1 year): rotate before expiry; the dispatch job fails +# loudly when the token is dead and the on-failure job opens an issue. +# +# Manual re-runs: workflow_dispatch with an existing release tag re-builds and +# re-attaches the assets (--clobber) and re-fires the dispatch. +name: Release Artifacts + +on: + release: + types: [published] + workflow_dispatch: + inputs: + tag: + description: "Existing release tag (e.g. v1.1.0)" + required: true + +permissions: + contents: write # upload release assets + +concurrency: + group: release-artifacts-${{ github.event.release.tag_name || inputs.tag }} + cancel-in-progress: false + +env: + FOUNDRY_VERSION: v1.7.1 # explicit pin to keep bundle builds reproducible + +jobs: + build-and-attach: + runs-on: ubuntu-latest + timeout-minutes: 30 + outputs: + tag: ${{ steps.meta.outputs.tag }} + sha: ${{ steps.build.outputs.sha }} + previous_tag: ${{ steps.build.outputs.previous_tag }} + bundle_sha256: ${{ steps.build.outputs.bundle_sha256 }} + prerelease: ${{ steps.meta.outputs.prerelease }} + steps: + - name: Resolve and validate tag + id: meta + env: + EVENT_TAG: ${{ github.event.release.tag_name }} + EVENT_PRERELEASE: ${{ github.event.release.prerelease }} + INPUT_TAG: ${{ inputs.tag }} + run: | + TAG="${EVENT_TAG:-$INPUT_TAG}" + if [[ ! "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then + echo "error: tag '$TAG' does not match vMAJOR.MINOR.PATCH[-prerelease]" >&2 + exit 1 + fi + if [[ -n "$EVENT_TAG" ]]; then + PRERELEASE="$EVENT_PRERELEASE" + else + case "$TAG" in *-*) PRERELEASE=true ;; *) PRERELEASE=false ;; esac + fi + { + echo "tag=$TAG" + echo "prerelease=$PRERELEASE" + } >> "$GITHUB_OUTPUT" + + - name: Checkout release tag + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ steps.meta.outputs.tag }} + submodules: recursive + fetch-depth: 0 # full history for previous-tag lookup + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@82dee4ba654bd2146511f85f0d013af94670c4de # v1.4.0 + with: + version: ${{ env.FOUNDRY_VERSION }} + + - name: Build contracts (with AST for contractKind detection) + run: forge build --ast --sizes + + - name: Build and attach ABI bundle + id: build + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ steps.meta.outputs.tag }} + run: | + .github/scripts/build-abi-bundle.sh "$TAG" + gh release upload "$TAG" "dist/abis-$TAG.tar.gz" dist/SHA256SUMS --clobber + { + echo "sha=$(git rev-parse HEAD)" + echo "previous_tag=$(git describe --tags --abbrev=0 "$TAG^" 2>/dev/null || echo '')" + echo "bundle_sha256=$(sha256sum "dist/abis-$TAG.tar.gz" | cut -d' ' -f1)" + } >> "$GITHUB_OUTPUT" + + dispatch-sdk: + needs: build-and-attach + if: needs.build-and-attach.outputs.prerelease == 'false' + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + # No '|| true' anywhere here: a dead SDK_SYNC_TOKEN must fail this job + # loudly. Recovery: rotate the token, then re-run this job (or run the + # SDK's Contracts Sync workflow manually with the tag). + - name: Dispatch contracts-release-published to oak-network/sdk + env: + GH_TOKEN: ${{ secrets.SDK_SYNC_TOKEN }} + TAG: ${{ needs.build-and-attach.outputs.tag }} + SHA: ${{ needs.build-and-attach.outputs.sha }} + PREVIOUS_TAG: ${{ needs.build-and-attach.outputs.previous_tag }} + BUNDLE_SHA256: ${{ needs.build-and-attach.outputs.bundle_sha256 }} + run: | + gh api repos/oak-network/sdk/dispatches \ + -f event_type=contracts-release-published \ + -f "client_payload[tag]=$TAG" \ + -f "client_payload[sha]=$SHA" \ + -f "client_payload[previous_tag]=$PREVIOUS_TAG" \ + -f "client_payload[bundle_sha256]=$BUNDLE_SHA256" \ + -f "client_payload[repo]=oak-network/contracts" + + on-failure: + needs: [build-and-attach, dispatch-sdk] + if: failure() + runs-on: ubuntu-latest + timeout-minutes: 5 + permissions: + issues: write + steps: + - name: Open or update release-pipeline issue + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + env: + TAG: ${{ needs.build-and-attach.outputs.tag || github.event.release.tag_name || inputs.tag }} + with: + script: | + const tag = process.env.TAG || 'unknown'; + const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; + const title = `Release pipeline failed for ${tag}`; + const body = [ + `The Release Artifacts workflow failed for \`${tag}\`.`, + '', + `**Run:** ${runUrl}`, + '', + 'Common causes: forge build failure at the tag, asset upload failure,', + 'or an expired `SDK_SYNC_TOKEN` (dispatch job). Fix the cause, then', + 're-run the failed job. The workflow is idempotent per tag.', + ].join('\n'); + const existing = await github.rest.issues.listForRepo({ + owner: context.repo.owner, + repo: context.repo.repo, + labels: 'release-pipeline', + state: 'open', + per_page: 10, + }); + const match = existing.data.find(i => !i.pull_request && i.title === title); + if (match) { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: match.number, + body: `Failed again: ${runUrl}`, + }); + } else { + await github.rest.issues.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title, + body, + labels: ['release-pipeline'], + }); + } diff --git a/.gitignore b/.gitignore index 80f5c86..e133685 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,7 @@ broadcast #Foundry build output out + +# Release ABI bundle build output +dist +.abi-bundle From 4d1e4164ee073c0e104f8abcd59a7fa41ddd54ff Mon Sep 17 00:00:00 2001 From: Adnan Haque Date: Fri, 17 Jul 2026 15:12:49 +0600 Subject: [PATCH 2/3] docs: regenerate forge doc from current sources Replaces stale committed output: removes the deleted TestUSD contract doc, drops removed Ownable members and eliminates hardcoded absolute paths from the original author's machine. Generated with forge 1.7.1 plus .github/scripts/sanitize-docs.py, the same path docs.yml runs in CI. --- docs/src/README.md | 50 ++- docs/src/SUMMARY.md | 7 + .../CampaignInfo.sol/contract.CampaignInfo.md | 144 ++++-- .../contract.CampaignInfoFactory.md | 68 ++- .../GlobalParams.sol/contract.GlobalParams.md | 106 ++++- docs/src/src/README.md | 1 + docs/src/src/TestUSD.sol/contract.TestUSD.md | 33 -- .../contract.TreasuryFactory.md | 30 +- .../library.DataRegistryKeys.md | 7 +- .../library.ProtocolErrors.md | 64 +++ docs/src/src/errors/README.md | 5 + .../library.TreasuryErrors.md | 55 +++ .../interface.ICampaignData.md | 5 +- .../interface.ICampaignInfo.md | 54 ++- .../interface.ICampaignInfoFactory.md | 42 +- .../interface.ICampaignPaymentTreasury.md | 33 +- .../interface.ICampaignTreasury.md | 20 +- .../interface.IGlobalParams.md | 20 +- .../interfaces/IItem.sol/interface.IItem.md | 5 +- .../IPermit2.sol/interface.IEIP712.md | 15 + .../IPermit2.sol/interface.IPermit2.md | 15 + .../interface.ISignatureTransfer.md | 172 ++++++++ .../IPermit2.sol/struct.PermitData.md | 23 + .../IReward.sol/interface.IReward.md | 6 +- .../interface.ITreasuryFactory.md | 5 +- docs/src/src/interfaces/README.md | 4 + .../library.AdminAccessCheckerStorage.md | 11 +- .../library.CampaignInfoFactoryStorage.md | 11 +- .../library.GlobalParamsStorage.md | 11 +- .../library.TreasuryFactoryStorage.md | 12 +- .../AllOrNothing.sol/contract.AllOrNothing.md | 178 ++++++-- .../contract.KeepWhatsRaised.md | 412 +++++++++++++++--- .../contract.PaymentTreasury.md | 66 +-- ...contract.TimeConstrainedPaymentTreasury.md | 78 +--- .../abstract.AdminAccessChecker.md | 5 +- .../abstract.BasePaymentTreasury.md | 326 ++++++++++---- .../BaseTreasury.sol/abstract.BaseTreasury.md | 71 ++- .../abstract.CampaignAccessChecker.md | 14 +- .../utils/Counters.sol/library.Counters.md | 2 +- .../FiatEnabled.sol/abstract.FiatEnabled.md | 19 +- .../ItemRegistry.sol/contract.ItemRegistry.md | 86 +++- .../abstract.PausableCancellable.md | 21 +- .../utils/PledgeNFT.sol/abstract.PledgeNFT.md | 22 +- .../abstract.TimestampChecker.md | 5 +- 44 files changed, 1827 insertions(+), 512 deletions(-) delete mode 100644 docs/src/src/TestUSD.sol/contract.TestUSD.md create mode 100644 docs/src/src/errors/ProtocolErrors.sol/library.ProtocolErrors.md create mode 100644 docs/src/src/errors/README.md create mode 100644 docs/src/src/errors/TreasuryErrors.sol/library.TreasuryErrors.md create mode 100644 docs/src/src/interfaces/IPermit2.sol/interface.IEIP712.md create mode 100644 docs/src/src/interfaces/IPermit2.sol/interface.IPermit2.md create mode 100644 docs/src/src/interfaces/IPermit2.sol/interface.ISignatureTransfer.md create mode 100644 docs/src/src/interfaces/IPermit2.sol/struct.PermitData.md diff --git a/docs/src/README.md b/docs/src/README.md index 0c8d163..a3fc636 100644 --- a/docs/src/README.md +++ b/docs/src/README.md @@ -1,8 +1,12 @@ # Oak Network Smart Contracts +[![Audited by OpenZeppelin](https://img.shields.io/badge/audited%20by-OpenZeppelin-4E5EE4?logo=openzeppelin&logoColor=white)](./audits/OpenZeppelin%20-%20%2301%20-%20Smart%20Contracts%20Audit-report.pdf) +[![Audited by Immunefi](https://img.shields.io/badge/audited%20by-Immunefi-E11D74)](./audits/ImmuneFi-Audit-Report-OakNetwork-PaymentTreasury.pdf) +[![Audited by PeckShield](https://img.shields.io/badge/audited%20by-PeckShield-2E7CF6)](./audits/PeckShield-Audit-Report-CreativeCrowdfunding_v1.0.pdf) + ## Overview -Oak Network is a decentralized crowdfunding protocol designed to help creators launch and manage campaigns across multiple platforms. By providing a standardized infrastructure, the protocol simplifies the process of creating, funding, and managing crowdfunding initiatives in web3 across different platforms. +Oak Network is programmable commerce and escrow infrastructure — an on-chain backbone for creating and managing conditional payment flows. Each flow is defined once on-chain and shared across platforms, while funds are held and settled by interchangeable treasury models. ## Features @@ -10,11 +14,15 @@ Oak Network is a decentralized crowdfunding protocol designed to help creators l - Multiple treasury models - Secure fund management - Customizable protocol parameters +- Currency-based multi-token campaigns +- Campaign-level Pledge NFTs (one ERC721 collection per campaign) +- ERC-2771 meta-transactions for platform admin operations using multisig wallets +- UUPS upgradeability for core protocol contracts ## Prerequisites - [Foundry](https://book.getfoundry.sh/) -- Solidity ^0.8.20 +- Solidity ^0.8.22 ## Installation @@ -94,6 +102,18 @@ forge script script/DeployAll.s.sol:DeployAll --rpc-url http://localhost:8545 -- forge script script/DeployAll.s.sol:DeployAll --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast ``` +#### Deploy core + setup a specific treasury model + +If you want a one-shot script that deploys the protocol (UUPS proxies), configures `GlobalParams`, and registers + approves a treasury implementation for a platform, you can run one of the `DeployAllAndSetup*.s.sol` scripts. + +```bash +# Example: deploy and setup PaymentTreasury +forge script script/DeployAllAndSetupPaymentTreasury.s.sol:DeployAllAndSetupPaymentTreasury \ + --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast +``` + +> These scripts read configuration from `.env` (e.g. `PLATFORM_NAME`, `PROTOCOL_FEE_PERCENT`, `PLATFORM_FEE_PERCENT`, `CURRENCIES`/`TOKENS_PER_CURRENCY`, and optional `PLATFORM_ADAPTER_ADDRESS` for meta-txs). + ## Contract Architecture ### Core Contracts @@ -105,6 +125,9 @@ forge script script/DeployAll.s.sol:DeployAll --rpc-url $RPC_URL --private-key $ ### Treasury Models - `AllOrNothing`: Funds refunded if campaign goal not met +- `KeepWhatsRaised`: Flexible treasury that keeps funds regardless of goal achievement (tips, configurable fees, withdrawal gating) +- `PaymentTreasury`: Payment-style treasury (off-chain payment creation + on-chain confirmation, line items, optional NFT mint) +- `TimeConstrainedPaymentTreasury`: PaymentTreasury variant gated by `launchTime → deadline + bufferTime` ### Notes on Mock Contracts @@ -113,9 +136,13 @@ forge script script/DeployAll.s.sol:DeployAll --rpc-url $RPC_URL --private-key $ ## Deployment Workflow -1. Deploy `GlobalParams` -2. Deploy `TreasuryFactory` -3. Deploy `CampaignInfoFactory` +At a high level: + +1. Deploy `GlobalParams` (UUPS proxy + implementation) +2. Deploy `TreasuryFactory` (UUPS proxy + implementation) +3. Deploy `CampaignInfoFactory` (UUPS proxy + implementation) +4. Configure currencies/tokens + data registry keys + platforms (and optional platform adapters) +5. Register and approve treasury implementations per platform, then deploy treasuries per campaign > For local testing or development, the `TestToken` mock token needs to be deployed before interacting with contracts requiring an ERC20 token. @@ -130,11 +157,20 @@ Key environment variables to configure in `.env`: For a complete list of variables, refer to `.env.example`. +> Tip: `script/` contains deployment, setup, and upgrade scripts for each treasury type (including UUPS upgrade scripts). + ## Security ### Audits -Security audit reports can be found in the [`audits/`](./audits/) folder. We regularly conduct security audits to ensure the safety and reliability of the protocol. +The protocol has undergone multiple independent security reviews. Full reports live in the [`audits/`](./audits/) folder; see the [audit index](./audits/README.md) for per-finding status and remediation details. + +| Date | Auditor | Scope | Report | +| --- | --- | --- | --- | +| Jun 17, 2026 | OpenZeppelin | Full protocol (commit `479241c`) | [PDF](./audits/OpenZeppelin%20-%20%2301%20-%20Smart%20Contracts%20Audit-report.pdf) | +| Dec 10, 2025 | Immunefi (Neplox) | `PaymentTreasury` | [PDF](./audits/ImmuneFi-Audit-Report-OakNetwork-PaymentTreasury.pdf) | +| Aug 5, 2025 | Immunefi (Neplox) | Creative Crowdfunding Protocol v1.0 | [PDF](./audits/Immunefi-Audit-Report-CreativeCrowdfunding_v1.0.pdf) | +| May 20, 2025 | PeckShield | Creative Crowdfunding Protocol v1.0 | [PDF](./audits/PeckShield-Audit-Report-CreativeCrowdfunding_v1.0.pdf) | ## Contributing @@ -154,7 +190,7 @@ Before contributing, please read our detailed [Contributing Guidelines](./CONTRI ### Community -Join our community on [Discord](https://discord.gg/tnBhVxSDDS) for questions and discussions. +Join our community on [Discord](https://discord.gg/NnPKaB2Qdr) for questions and discussions. Read our [Code of Conduct](./CODE_OF_CONDUCT.md) to keep our community approachable and respectful. diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index b8ac6e2..f677caf 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -3,6 +3,9 @@ # src - [❱ constants](src/constants/README.md) - [DataRegistryKeys](src/constants/DataRegistryKeys.sol/library.DataRegistryKeys.md) + - [❱ errors](src/errors/README.md) + - [ProtocolErrors](src/errors/ProtocolErrors.sol/library.ProtocolErrors.md) + - [TreasuryErrors](src/errors/TreasuryErrors.sol/library.TreasuryErrors.md) - [❱ interfaces](src/interfaces/README.md) - [ICampaignData](src/interfaces/ICampaignData.sol/interface.ICampaignData.md) - [ICampaignInfo](src/interfaces/ICampaignInfo.sol/interface.ICampaignInfo.md) @@ -11,6 +14,10 @@ - [ICampaignTreasury](src/interfaces/ICampaignTreasury.sol/interface.ICampaignTreasury.md) - [IGlobalParams](src/interfaces/IGlobalParams.sol/interface.IGlobalParams.md) - [IItem](src/interfaces/IItem.sol/interface.IItem.md) + - [IEIP712](src/interfaces/IPermit2.sol/interface.IEIP712.md) + - [ISignatureTransfer](src/interfaces/IPermit2.sol/interface.ISignatureTransfer.md) + - [IPermit2](src/interfaces/IPermit2.sol/interface.IPermit2.md) + - [PermitData](src/interfaces/IPermit2.sol/struct.PermitData.md) - [IReward](src/interfaces/IReward.sol/interface.IReward.md) - [ITreasuryFactory](src/interfaces/ITreasuryFactory.sol/interface.ITreasuryFactory.md) - [❱ storage](src/storage/README.md) diff --git a/docs/src/src/CampaignInfo.sol/contract.CampaignInfo.md b/docs/src/src/CampaignInfo.sol/contract.CampaignInfo.md index f867bb7..ea3e457 100644 --- a/docs/src/src/CampaignInfo.sol/contract.CampaignInfo.md +++ b/docs/src/src/CampaignInfo.sol/contract.CampaignInfo.md @@ -1,8 +1,11 @@ # CampaignInfo -[Git Source](https://github.com/oak-network/contracts/blob/0ce055a8ba31ca09404e9d09ecd2549534cbec61/src/CampaignInfo.sol) +[Git Source](https://github.com/oak-network/contracts/blob/6c7f67f5ed14ef0f4f9444b95ac6770ae2af756a/src/CampaignInfo.sol) **Inherits:** -[ICampaignData](/Users/mahabubalahi/Documents/ccp/contracts/docs/src/src/interfaces/ICampaignData.sol/interface.ICampaignData.md), [ICampaignInfo](/Users/mahabubalahi/Documents/ccp/contracts/docs/src/src/interfaces/ICampaignInfo.sol/interface.ICampaignInfo.md), Ownable, [PausableCancellable](/Users/mahabubalahi/Documents/ccp/contracts/docs/src/src/utils/PausableCancellable.sol/abstract.PausableCancellable.md), [TimestampChecker](/Users/mahabubalahi/Documents/ccp/contracts/docs/src/src/utils/TimestampChecker.sol/abstract.TimestampChecker.md), [AdminAccessChecker](/Users/mahabubalahi/Documents/ccp/contracts/docs/src/src/utils/AdminAccessChecker.sol/abstract.AdminAccessChecker.md), [PledgeNFT](/Users/mahabubalahi/Documents/ccp/contracts/docs/src/src/utils/PledgeNFT.sol/abstract.PledgeNFT.md), Initializable +[ICampaignData](/src/interfaces/ICampaignData.sol/interface.ICampaignData.md), [ICampaignInfo](/src/interfaces/ICampaignInfo.sol/interface.ICampaignInfo.md), Ownable, [PausableCancellable](/src/utils/PausableCancellable.sol/abstract.PausableCancellable.md), [TimestampChecker](/src/utils/TimestampChecker.sol/abstract.TimestampChecker.md), [AdminAccessChecker](/src/utils/AdminAccessChecker.sol/abstract.AdminAccessChecker.md), [PledgeNFT](/src/utils/PledgeNFT.sol/abstract.PledgeNFT.md), Initializable + +**Title:** +CampaignInfo Manages campaign information and platform data. @@ -116,7 +119,7 @@ Constructor passes empty strings to ERC721 ```solidity -constructor() Ownable(_msgSender()) ERC721("", ""); +constructor() Ownable(msg.sender) ERC721("", ""); ``` ### initialize @@ -225,13 +228,13 @@ This excludes cancelled treasuries and is affected by refunds. ```solidity -function getTotalRaisedAmount() external view override returns (uint256); +function getTotalRaisedAmount() external view override returns (uint256 amount); ``` **Returns** |Name|Type|Description| |----|----|-----------| -|``|`uint256`|The total amount raised in the campaign.| +|`amount`|`uint256`|The total amount raised in the campaign.| ### getTotalLifetimeRaisedAmount @@ -244,13 +247,13 @@ regardless of cancellations or refunds. ```solidity -function getTotalLifetimeRaisedAmount() external view returns (uint256); +function getTotalLifetimeRaisedAmount() external view returns (uint256 amount); ``` **Returns** |Name|Type|Description| |----|----|-----------| -|``|`uint256`|The total lifetime raised amount as a uint256 value.| +|`amount`|`uint256`|The total lifetime raised amount as a uint256 value.| ### getTotalRefundedAmount @@ -263,13 +266,13 @@ that have been processed across all treasuries. ```solidity -function getTotalRefundedAmount() external view returns (uint256); +function getTotalRefundedAmount() external view returns (uint256 amount); ``` **Returns** |Name|Type|Description| |----|----|-----------| -|``|`uint256`|The total refunded amount as a uint256 value.| +|`amount`|`uint256`|The total refunded amount as a uint256 value.| ### getTotalAvailableRaisedAmount @@ -282,13 +285,13 @@ balance of funds across all treasuries. ```solidity -function getTotalAvailableRaisedAmount() external view returns (uint256); +function getTotalAvailableRaisedAmount() external view returns (uint256 amount); ``` **Returns** |Name|Type|Description| |----|----|-----------| -|``|`uint256`|The total available raised amount as a uint256 value.| +|`amount`|`uint256`|The total available raised amount as a uint256 value.| ### getTotalCancelledAmount @@ -301,13 +304,13 @@ from treasuries that have been cancelled. ```solidity -function getTotalCancelledAmount() external view returns (uint256); +function getTotalCancelledAmount() external view returns (uint256 amount); ``` **Returns** |Name|Type|Description| |----|----|-----------| -|``|`uint256`|The total raised amount from cancelled treasuries as a uint256 value.| +|`amount`|`uint256`|The total raised amount from cancelled treasuries as a uint256 value.| ### getTotalExpectedAmount @@ -319,13 +322,13 @@ have been created but not yet confirmed. Regular treasuries are skipped. ```solidity -function getTotalExpectedAmount() external view returns (uint256); +function getTotalExpectedAmount() external view returns (uint256 amount); ``` **Returns** |Name|Type|Description| |----|----|-----------| -|``|`uint256`|The total expected amount as a uint256 value.| +|`amount`|`uint256`|The total expected amount as a uint256 value.| ### getPlatformAdminAddress @@ -349,6 +352,27 @@ function getPlatformAdminAddress(bytes32 platformHash) external view override re |``|`address`|The address of the platform administrator.| +### getPlatformAdapter + +Retrieves the adapter (trusted forwarder) address for a platform from GlobalParams. + + +```solidity +function getPlatformAdapter(bytes32 platformHash) external view override returns (address); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`platformHash`|`bytes32`|The bytes32 identifier of the platform.| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`address`|The adapter address for ERC-2771 meta-transactions, or address(0) if none is set.| + + ### getLaunchTime Retrieves the campaign's launch time. @@ -469,15 +493,6 @@ Returns true if the campaign is paused, and false otherwise. function paused() public view override(ICampaignInfo, PausableCancellable) returns (bool); ``` -### cancelled - -Returns true if the campaign is cancelled, and false otherwise. - - -```solidity -function cancelled() public view override(ICampaignInfo, PausableCancellable) returns (bool); -``` - ### getPlatformFeePercent Retrieves the platform fee percentage for a specific platform. @@ -592,6 +607,21 @@ function getBufferTime() external view override returns (uint256 bufferTime); |`bufferTime`|`uint256`|The buffer time value.| +### getPermit2Address + +Returns the canonical Permit2 contract address from GlobalParams. + + +```solidity +function getPermit2Address() external view override returns (address); +``` +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`address`|The Permit2 contract address.| + + ### getLineItemType Retrieves a platform-specific line item type configuration from GlobalParams. @@ -655,6 +685,7 @@ function updateLaunchTime(uint256 launchTime) external override onlyOwner + currentTimeIsLess(getLaunchTime()) whenNotPaused whenNotCancelled whenNotLocked; @@ -672,7 +703,14 @@ Updates the campaign's deadline. ```solidity -function updateDeadline(uint256 deadline) external override onlyOwner whenNotPaused whenNotCancelled whenNotLocked; +function updateDeadline(uint256 deadline) + external + override + onlyOwner + currentTimeIsLess(getLaunchTime()) + whenNotPaused + whenNotCancelled + whenNotLocked; ``` **Parameters** @@ -691,6 +729,7 @@ function updateGoalAmount(uint256 goalAmount) external override onlyOwner + currentTimeIsLess(getLaunchTime()) whenNotPaused whenNotCancelled whenNotLocked; @@ -727,31 +766,31 @@ function updateSelectedPlatform( |`platformDataValue`|`bytes32[]`|An array of platform-specific data values.| -### _pauseCampaign +### pauseCampaign External function to pause the campaign. ```solidity -function _pauseCampaign(bytes32 message) external onlyProtocolAdmin; +function pauseCampaign(bytes32 message) external onlyProtocolAdmin; ``` -### _unpauseCampaign +### unpauseCampaign External function to unpause the campaign. ```solidity -function _unpauseCampaign(bytes32 message) external onlyProtocolAdmin; +function unpauseCampaign(bytes32 message) external onlyProtocolAdmin; ``` -### _cancelCampaign +### cancelCampaign External function to cancel the campaign. ```solidity -function _cancelCampaign(bytes32 message) external; +function cancelCampaign(bytes32 message) external; ``` ### setImageURI @@ -812,18 +851,32 @@ function mintNFTForPledge( ### burn +Burns a pledge NFT + +Override required: ICampaignInfo and PledgeNFT both define burn(); forwards to PledgeNFT implementation. + ```solidity function burn(uint256 tokenId) public override(ICampaignInfo, PledgeNFT); ``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`tokenId`|`uint256`|The token ID to burn| -### _setPlatformInfo + +### setPlatformInfo Sets platform information for the campaign and grants treasury role. ```solidity -function _setPlatformInfo(bytes32 platformHash, address platformTreasuryAddress) external whenNotPaused; +function setPlatformInfo(bytes32 platformHash, address platformTreasuryAddress) + external + whenNotPaused + whenNotCancelled + currentTimeIsLess(getDeadline()); ``` **Parameters** @@ -935,9 +988,15 @@ Emitted when an invalid input is detected. ```solidity -error CampaignInfoInvalidInput(); +error CampaignInfoInvalidInput(ProtocolErrors.CampaignInfoInvalidInput code); ``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`code`|`ProtocolErrors.CampaignInfoInvalidInput`|Which validation failed.| + ### CampaignInfoPlatformNotSelected Emitted when a platform is not selected for the campaign. @@ -974,8 +1033,25 @@ Emitted when an operation is attempted on a locked campaign. error CampaignInfoIsLocked(); ``` +### CampaignInfoPlatformDataKeyNotOwnedByPlatform +Throws when a platform data key is not owned by the platform being updated. + + +```solidity +error CampaignInfoPlatformDataKeyNotOwnedByPlatform(bytes32 platformHash, bytes32 platformDataKey); +``` + +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`platformHash`|`bytes32`|The platform being updated.| +|`platformDataKey`|`bytes32`|The key that does not belong to this platform.| + ## Structs ### Config +Struct to hold campaign configuration information. + ```solidity struct Config { diff --git a/docs/src/src/CampaignInfoFactory.sol/contract.CampaignInfoFactory.md b/docs/src/src/CampaignInfoFactory.sol/contract.CampaignInfoFactory.md index ce2f480..a7856d0 100644 --- a/docs/src/src/CampaignInfoFactory.sol/contract.CampaignInfoFactory.md +++ b/docs/src/src/CampaignInfoFactory.sol/contract.CampaignInfoFactory.md @@ -1,8 +1,11 @@ # CampaignInfoFactory -[Git Source](https://github.com/oak-network/contracts/blob/0ce055a8ba31ca09404e9d09ecd2549534cbec61/src/CampaignInfoFactory.sol) +[Git Source](https://github.com/oak-network/contracts/blob/6c7f67f5ed14ef0f4f9444b95ac6770ae2af756a/src/CampaignInfoFactory.sol) **Inherits:** -Initializable, [ICampaignInfoFactory](/Users/mahabubalahi/Documents/ccp/contracts/docs/src/src/interfaces/ICampaignInfoFactory.sol/interface.ICampaignInfoFactory.md), OwnableUpgradeable, UUPSUpgradeable +Initializable, [ICampaignInfoFactory](/src/interfaces/ICampaignInfoFactory.sol/interface.ICampaignInfoFactory.md), UUPSUpgradeable + +**Title:** +CampaignInfoFactory Factory contract for creating campaign information contracts. @@ -10,6 +13,13 @@ UUPS Upgradeable contract with ERC-7201 namespaced storage ## Functions +### onlyProtocolAdmin + + +```solidity +modifier onlyProtocolAdmin() ; +``` + ### constructor Constructor that disables initializers to prevent implementation contract initialization @@ -25,18 +35,14 @@ Initializes the CampaignInfoFactory contract. ```solidity -function initialize( - address initialOwner, - IGlobalParams globalParams, - address campaignImplementation, - address treasuryFactoryAddress -) public initializer; +function initialize(IGlobalParams globalParams, address campaignImplementation, address treasuryFactoryAddress) + public + initializer; ``` **Parameters** |Name|Type|Description| |----|----|-----------| -|`initialOwner`|`address`|The address that will own the factory| |`globalParams`|`IGlobalParams`|The address of the global parameters contract.| |`campaignImplementation`|`address`|The address of the campaign implementation contract.| |`treasuryFactoryAddress`|`address`|The address of the treasury factory contract.| @@ -48,7 +54,7 @@ Function that authorizes an upgrade to a new implementation ```solidity -function _authorizeUpgrade(address newImplementation) internal override onlyOwner; +function _authorizeUpgrade(address newImplementation) internal override onlyProtocolAdmin; ``` **Parameters** @@ -104,7 +110,7 @@ Updates the campaign implementation address. ```solidity -function updateImplementation(address newImplementation) external override onlyOwner; +function updateImplementation(address newImplementation) external override onlyProtocolAdmin; ``` **Parameters** @@ -161,7 +167,45 @@ Emitted when invalid input is provided. ```solidity -error CampaignInfoFactoryInvalidInput(); +error CampaignInfoFactoryInvalidInput(ProtocolErrors.CampaignInfoFactoryInvalidInput code); +``` + +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`code`|`ProtocolErrors.CampaignInfoFactoryInvalidInput`|Which validation failed.| + +### CampaignInfoFactoryUnauthorized +Reverts when the caller is not the protocol admin. + + +```solidity +error CampaignInfoFactoryUnauthorized(); +``` + +### CampaignInfoFactoryZeroGlobalParams +Reverts when globalParams is the zero address. + + +```solidity +error CampaignInfoFactoryZeroGlobalParams(); +``` + +### CampaignInfoFactoryZeroCampaignImplementation +Reverts when campaignImplementation is the zero address. + + +```solidity +error CampaignInfoFactoryZeroCampaignImplementation(); +``` + +### CampaignInfoFactoryZeroTreasuryFactoryAddress +Reverts when treasuryFactoryAddress is the zero address. + + +```solidity +error CampaignInfoFactoryZeroTreasuryFactoryAddress(); ``` ### CampaignInfoFactoryCampaignInitializationFailed diff --git a/docs/src/src/GlobalParams.sol/contract.GlobalParams.md b/docs/src/src/GlobalParams.sol/contract.GlobalParams.md index 4bafc29..af799e3 100644 --- a/docs/src/src/GlobalParams.sol/contract.GlobalParams.md +++ b/docs/src/src/GlobalParams.sol/contract.GlobalParams.md @@ -1,15 +1,18 @@ # GlobalParams -[Git Source](https://github.com/oak-network/contracts/blob/0ce055a8ba31ca09404e9d09ecd2549534cbec61/src/GlobalParams.sol) +[Git Source](https://github.com/oak-network/contracts/blob/6c7f67f5ed14ef0f4f9444b95ac6770ae2af756a/src/GlobalParams.sol) **Inherits:** -Initializable, [IGlobalParams](/Users/mahabubalahi/Documents/ccp/contracts/docs/src/src/interfaces/IGlobalParams.sol/interface.IGlobalParams.md), OwnableUpgradeable, UUPSUpgradeable +Initializable, [IGlobalParams](/src/interfaces/IGlobalParams.sol/interface.IGlobalParams.md), UUPSUpgradeable + +**Title:** +GlobalParams Manages global parameters and platform information. UUPS Upgradeable contract with ERC-7201 namespaced storage -## State Variables +## Constants ### ZERO_BYTES ```solidity @@ -17,6 +20,24 @@ bytes32 private constant ZERO_BYTES = 0x0000000000000000000000000000000000000000 ``` +### PERMIT2_ADDRESS +The canonical Permit2 deployment address (same on all EVM chains). + + +```solidity +address private constant PERMIT2_ADDRESS = 0x000000000022D473030F116dDEE9F6B43aC78BA3 +``` + + +### PERCENT_DIVIDER +100% in basis points; fee percentages must not exceed this and their sum must be below it. + + +```solidity +uint256 private constant PERCENT_DIVIDER = 10000 +``` + + ## Functions ### notAddressZero @@ -50,6 +71,13 @@ modifier onlyPlatformAdmin(bytes32 platformHash) ; modifier platformIsListed(bytes32 platformHash) ; ``` +### onlyProtocolAdmin + + +```solidity +modifier onlyProtocolAdmin() ; +``` + ### constructor Constructor that disables initializers to prevent implementation contract initialization @@ -88,7 +116,7 @@ Function that authorizes an upgrade to a new implementation ```solidity -function _authorizeUpgrade(address newImplementation) internal override onlyOwner; +function _authorizeUpgrade(address newImplementation) internal override onlyProtocolAdmin; ``` **Parameters** @@ -103,7 +131,7 @@ Adds a key-value pair to the data registry. ```solidity -function addToRegistry(bytes32 key, bytes32 value) external onlyOwner; +function addToRegistry(bytes32 key, bytes32 value) external onlyProtocolAdmin; ``` **Parameters** @@ -134,6 +162,21 @@ function getFromRegistry(bytes32 key) external view returns (bytes32 value); |`value`|`bytes32`|The registry value.| +### getPermit2Address + +Returns the canonical Permit2 contract address. + + +```solidity +function getPermit2Address() external pure returns (address); +``` +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`address`|The Permit2 contract address.| + + ### getPlatformAdminAddress Retrieves the admin address of a platform. @@ -333,7 +376,7 @@ function enlistPlatform( address platformAdminAddress, uint256 platformFeePercent, address platformAdapter -) external onlyOwner notAddressZero(platformAdminAddress); +) external onlyProtocolAdmin notAddressZero(platformAdminAddress); ``` **Parameters** @@ -351,7 +394,7 @@ Delists a platform. ```solidity -function delistPlatform(bytes32 platformHash) external onlyOwner platformIsListed(platformHash); +function delistPlatform(bytes32 platformHash) external onlyProtocolAdmin platformIsListed(platformHash); ``` **Parameters** @@ -407,7 +450,7 @@ Updates the admin address of the protocol. function updateProtocolAdminAddress(address protocolAdminAddress) external override - onlyOwner + onlyProtocolAdmin notAddressZero(protocolAdminAddress); ``` **Parameters** @@ -423,7 +466,7 @@ Updates the protocol fee percentage. ```solidity -function updateProtocolFeePercent(uint256 protocolFeePercent) external override onlyOwner; +function updateProtocolFeePercent(uint256 protocolFeePercent) external override onlyProtocolAdmin; ``` **Parameters** @@ -441,7 +484,7 @@ Updates the admin address of a platform. function updatePlatformAdminAddress(bytes32 platformHash, address platformAdminAddress) external override - onlyOwner + onlyProtocolAdmin platformIsListed(platformHash) notAddressZero(platformAdminAddress); ``` @@ -510,7 +553,7 @@ Only callable by the protocol admin (owner). function setPlatformAdapter(bytes32 platformHash, address adapter) external override - onlyOwner + onlyProtocolAdmin platformIsListed(platformHash); ``` **Parameters** @@ -527,7 +570,11 @@ Adds a token to a currency. ```solidity -function addTokenToCurrency(bytes32 currency, address token) external override onlyOwner notAddressZero(token); +function addTokenToCurrency(bytes32 currency, address token) + external + override + onlyProtocolAdmin + notAddressZero(token); ``` **Parameters** @@ -546,7 +593,7 @@ Removes a token from a currency. function removeTokenFromCurrency(bytes32 currency, address token) external override - onlyOwner + onlyProtocolAdmin notAddressZero(token); ``` **Parameters** @@ -676,6 +723,13 @@ Reverts if the input address is zero. function _revertIfAddressZero(address account) internal pure; ``` +### _onlyProtocolAdmin + + +```solidity +function _onlyProtocolAdmin() private view; +``` + ### _onlyPlatformAdmin Internal function to check if the sender is the platform administrator for a specific platform. @@ -909,13 +963,19 @@ event PlatformLineItemTypeRemoved(bytes32 indexed platformHash, bytes32 indexed ## Errors ### GlobalParamsInvalidInput -Throws when the input address is zero. +Throws when input validation fails. ```solidity -error GlobalParamsInvalidInput(); +error GlobalParamsInvalidInput(ProtocolErrors.GlobalParamsInvalidInput code); ``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`code`|`ProtocolErrors.GlobalParamsInvalidInput`|Which validation failed.| + ### GlobalParamsPlatformNotListed Throws when the platform is not listed. @@ -1056,3 +1116,19 @@ error GlobalParamsPlatformLineItemTypeNotFound(bytes32 platformHash, bytes32 typ |`platformHash`|`bytes32`|The identifier of the platform.| |`typeId`|`bytes32`|The identifier of the line item type.| +### GlobalParamsFeePercentExceedsMax +Throws when a fee percentage exceeds the maximum allowed (PERCENT_DIVIDER / 100%). + + +```solidity +error GlobalParamsFeePercentExceedsMax(); +``` + +### GlobalParamsCombinedFeesExceedMax +Throws when the sum of protocol and platform fee percentages would exceed 100%. + + +```solidity +error GlobalParamsCombinedFeesExceedMax(); +``` + diff --git a/docs/src/src/README.md b/docs/src/src/README.md index 954c176..67baef4 100644 --- a/docs/src/src/README.md +++ b/docs/src/src/README.md @@ -2,6 +2,7 @@ # Contents - [constants](/src/constants) +- [errors](/src/errors) - [interfaces](/src/interfaces) - [storage](/src/storage) - [treasuries](/src/treasuries) diff --git a/docs/src/src/TestUSD.sol/contract.TestUSD.md b/docs/src/src/TestUSD.sol/contract.TestUSD.md deleted file mode 100644 index 011be3b..0000000 --- a/docs/src/src/TestUSD.sol/contract.TestUSD.md +++ /dev/null @@ -1,33 +0,0 @@ -# TestUSD -[Git Source](https://github.com/ccprotocol/ccprotocol-contracts-internal/blob/4245ef0ad7914158999986aa0d8b5d2614efc6c2/src/TestUSD.sol) - -**Inherits:** -[ERC20](/src/.deps/npm/@openzeppelin/contracts/token/ERC20/ERC20.sol/abstract.ERC20.md), [Ownable](/src/.deps/npm/@openzeppelin/contracts/access/Ownable.sol/abstract.Ownable.md) - -A test token `tUSD` which is used in the tests. - - -## Functions -### constructor - - -```solidity -constructor() ERC20("testUSD", "tUSD") Ownable(msg.sender); -``` - -### mint - -Mints testUSD token. - - -```solidity -function mint(address to, uint256 amount) public onlyOwner; -``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`to`|`address`|The token receivers address.| -|`amount`|`uint256`|The amount of tokens to mint.| - - diff --git a/docs/src/src/TreasuryFactory.sol/contract.TreasuryFactory.md b/docs/src/src/TreasuryFactory.sol/contract.TreasuryFactory.md index 34f4ab8..7b42de7 100644 --- a/docs/src/src/TreasuryFactory.sol/contract.TreasuryFactory.md +++ b/docs/src/src/TreasuryFactory.sol/contract.TreasuryFactory.md @@ -1,8 +1,11 @@ # TreasuryFactory -[Git Source](https://github.com/oak-network/contracts/blob/0ce055a8ba31ca09404e9d09ecd2549534cbec61/src/TreasuryFactory.sol) +[Git Source](https://github.com/oak-network/contracts/blob/6c7f67f5ed14ef0f4f9444b95ac6770ae2af756a/src/TreasuryFactory.sol) **Inherits:** -Initializable, [ITreasuryFactory](/Users/mahabubalahi/Documents/ccp/contracts/docs/src/src/interfaces/ITreasuryFactory.sol/interface.ITreasuryFactory.md), [AdminAccessChecker](/Users/mahabubalahi/Documents/ccp/contracts/docs/src/src/utils/AdminAccessChecker.sol/abstract.AdminAccessChecker.md), UUPSUpgradeable +Initializable, [ITreasuryFactory](/src/interfaces/ITreasuryFactory.sol/interface.ITreasuryFactory.md), [AdminAccessChecker](/src/utils/AdminAccessChecker.sol/abstract.AdminAccessChecker.md), UUPSUpgradeable + +**Title:** +TreasuryFactory Factory contract for creating treasury contracts @@ -49,6 +52,23 @@ function _authorizeUpgrade(address newImplementation) internal override onlyProt |`newImplementation`|`address`|Address of the new implementation| +### setCampaignInfoFactory + +Sets the CampaignInfoFactory address used to validate infoAddress inputs in deploy(). + +Callable only by the protocol admin. + + +```solidity +function setCampaignInfoFactory(address campaignInfoFactory) external onlyProtocolAdmin; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`campaignInfoFactory`|`address`|The address of the CampaignInfoFactory contract.| + + ### registerTreasuryImplementation Registers a treasury implementation for a given platform. @@ -204,3 +224,9 @@ error TreasuryFactoryTreasuryInitializationFailed(); error TreasuryFactorySettingPlatformInfoFailed(); ``` +### TreasuryFactoryInvalidCampaignInfo + +```solidity +error TreasuryFactoryInvalidCampaignInfo(); +``` + diff --git a/docs/src/src/constants/DataRegistryKeys.sol/library.DataRegistryKeys.md b/docs/src/src/constants/DataRegistryKeys.sol/library.DataRegistryKeys.md index 07ee7fe..1ef1a15 100644 --- a/docs/src/src/constants/DataRegistryKeys.sol/library.DataRegistryKeys.md +++ b/docs/src/src/constants/DataRegistryKeys.sol/library.DataRegistryKeys.md @@ -1,5 +1,8 @@ # DataRegistryKeys -[Git Source](https://github.com/oak-network/contracts/blob/0ce055a8ba31ca09404e9d09ecd2549534cbec61/src/constants/DataRegistryKeys.sol) +[Git Source](https://github.com/oak-network/contracts/blob/6c7f67f5ed14ef0f4f9444b95ac6770ae2af756a/src/constants/DataRegistryKeys.sol) + +**Title:** +DataRegistryKeys Centralized storage for all dataRegistry keys used in GlobalParams @@ -7,7 +10,7 @@ This library provides a single source of truth for all dataRegistry keys to ensure consistency across contracts and prevent key collisions. -## State Variables +## Constants ### BUFFER_TIME ```solidity diff --git a/docs/src/src/errors/ProtocolErrors.sol/library.ProtocolErrors.md b/docs/src/src/errors/ProtocolErrors.sol/library.ProtocolErrors.md new file mode 100644 index 0000000..ce99e58 --- /dev/null +++ b/docs/src/src/errors/ProtocolErrors.sol/library.ProtocolErrors.md @@ -0,0 +1,64 @@ +# ProtocolErrors +[Git Source](https://github.com/oak-network/contracts/blob/6c7f67f5ed14ef0f4f9444b95ac6770ae2af756a/src/errors/ProtocolErrors.sol) + +**Title:** +ProtocolErrors + +Shared error-code enums for GlobalParams, CampaignInfo, and CampaignInfoFactory invalid-input reverts. + + +## Enums +### GlobalParamsInvalidInput +Codes for `GlobalParamsInvalidInput` (input-validation failures). + + +```solidity +enum GlobalParamsInvalidInput { + ZERO_TOKEN, + ZERO_REGISTRY_KEY, + ZERO_PLATFORM_HASH, + ZERO_PLATFORM_DATA_KEY, + ZERO_CURRENCY, + ZERO_LINE_ITEM_TYPE_ID, + LINE_ITEM_GOAL_APPLIES_PROTOCOL_FEE, + LINE_ITEM_GOAL_NOT_REFUNDABLE, + LINE_ITEM_GOAL_INSTANT_TRANSFER, + LINE_ITEM_NON_GOAL_INSTANT_REFUNDABLE, + ZERO_ADDRESS +} +``` + +### CampaignInfoInvalidInput +Codes for `CampaignInfoInvalidInput` (input-validation failures). + + +```solidity +enum CampaignInfoInvalidInput { + DUPLICATE_ACCEPTED_TOKEN, + PLATFORM_DATA_NOT_SET, + INVALID_LAUNCH_TIME, + INVALID_DEADLINE, + ZERO_GOAL_AMOUNT, + PLATFORM_SELECTION_UNCHANGED, + PLATFORM_DATA_LENGTH_MISMATCH, + INVALID_PLATFORM_DATA_KEY, + ZERO_PLATFORM_DATA_VALUE +} +``` + +### CampaignInfoFactoryInvalidInput +Codes for `CampaignInfoFactoryInvalidInput` (input-validation failures). + + +```solidity +enum CampaignInfoFactoryInvalidInput { + ZERO_CREATOR, + PLATFORM_DATA_LENGTH_MISMATCH, + LAUNCH_TIME_TOO_SOON, + DEADLINE_TOO_SOON, + INVALID_PLATFORM_DATA_KEY, + ZERO_PLATFORM_DATA_VALUE, + ZERO_IMPLEMENTATION +} +``` + diff --git a/docs/src/src/errors/README.md b/docs/src/src/errors/README.md new file mode 100644 index 0000000..0520b6d --- /dev/null +++ b/docs/src/src/errors/README.md @@ -0,0 +1,5 @@ + + +# Contents +- [ProtocolErrors](ProtocolErrors.sol/library.ProtocolErrors.md) +- [TreasuryErrors](TreasuryErrors.sol/library.TreasuryErrors.md) diff --git a/docs/src/src/errors/TreasuryErrors.sol/library.TreasuryErrors.md b/docs/src/src/errors/TreasuryErrors.sol/library.TreasuryErrors.md new file mode 100644 index 0000000..f3af2b2 --- /dev/null +++ b/docs/src/src/errors/TreasuryErrors.sol/library.TreasuryErrors.md @@ -0,0 +1,55 @@ +# TreasuryErrors +[Git Source](https://github.com/oak-network/contracts/blob/6c7f67f5ed14ef0f4f9444b95ac6770ae2af756a/src/errors/TreasuryErrors.sol) + +**Title:** +TreasuryErrors + +Shared error-code enums for all treasury contracts + + +## Enums +### InvalidInput +Codes for `InvalidInput` errors (input-validation failures). + + +```solidity +enum InvalidInput { + INVALID_LINE_ITEM, + LINE_ITEM_TYPE_NOT_FOUND, + EMPTY_SIGNATURE, + INVALID_BUYER, + INVALID_BACKER, + CONFIRM_BATCH_LENGTH_MISMATCH, + ZERO_REFUND_ADDRESS, + ZERO_CLAIMABLE_AMOUNT, + REWARD_NOT_FOUND, + REWARD_LENGTH_MISMATCH, + INVALID_PLEDGE_INPUT, + ZERO_REWARD_NAME, + FEE_LENGTH_MISMATCH, + INVALID_DEADLINE, + ZERO_GOAL_AMOUNT, + INVALID_REWARD_INPUT, + ZERO_TOKEN_SOURCE, + ZERO_AMOUNT +} +``` + +### NotClaimable +Codes for `NotClaimable` errors (refund / claim-check failures). + + +```solidity +enum NotClaimable { + ZERO_REFUND_AMOUNT, + INSUFFICIENT_LIQUIDITY, + ZERO_REFUND_ADDRESS, + NOT_NFT_PAYMENT, + INSUFFICIENT_GOAL_LIQUIDITY, + INSUFFICIENT_NON_GOAL_LIQUIDITY, + INSUFFICIENT_CONTRACT_BALANCE, + CAMPAIGN_SUCCESSFUL, + INVALID_REFUND_PERIOD +} +``` + diff --git a/docs/src/src/interfaces/ICampaignData.sol/interface.ICampaignData.md b/docs/src/src/interfaces/ICampaignData.sol/interface.ICampaignData.md index 3ff11ea..a939875 100644 --- a/docs/src/src/interfaces/ICampaignData.sol/interface.ICampaignData.md +++ b/docs/src/src/interfaces/ICampaignData.sol/interface.ICampaignData.md @@ -1,5 +1,8 @@ # ICampaignData -[Git Source](https://github.com/oak-network/contracts/blob/0ce055a8ba31ca09404e9d09ecd2549534cbec61/src/interfaces/ICampaignData.sol) +[Git Source](https://github.com/oak-network/contracts/blob/6c7f67f5ed14ef0f4f9444b95ac6770ae2af756a/src/interfaces/ICampaignData.sol) + +**Title:** +ICampaignData An interface for managing campaign data in a CCP. diff --git a/docs/src/src/interfaces/ICampaignInfo.sol/interface.ICampaignInfo.md b/docs/src/src/interfaces/ICampaignInfo.sol/interface.ICampaignInfo.md index e42e508..ea864d9 100644 --- a/docs/src/src/interfaces/ICampaignInfo.sol/interface.ICampaignInfo.md +++ b/docs/src/src/interfaces/ICampaignInfo.sol/interface.ICampaignInfo.md @@ -1,9 +1,12 @@ # ICampaignInfo -[Git Source](https://github.com/oak-network/contracts/blob/0ce055a8ba31ca09404e9d09ecd2549534cbec61/src/interfaces/ICampaignInfo.sol) +[Git Source](https://github.com/oak-network/contracts/blob/6c7f67f5ed14ef0f4f9444b95ac6770ae2af756a/src/interfaces/ICampaignInfo.sol) **Inherits:** IERC721 +**Title:** +ICampaignInfo + An interface for managing campaign information in a crowdfunding system. Inherits from IERC721 as CampaignInfo is an ERC721 NFT collection @@ -193,6 +196,27 @@ function getPlatformAdminAddress(bytes32 platformHash) external view returns (ad |``|`address`|The address of the platform administrator.| +### getPlatformAdapter + +Retrieves the adapter (trusted forwarder) address for a platform from GlobalParams. + + +```solidity +function getPlatformAdapter(bytes32 platformHash) external view returns (address); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`platformHash`|`bytes32`|The bytes32 identifier of the platform.| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`address`|The adapter address for ERC-2771 meta-transactions, or address(0) if none is set.| + + ### getLaunchTime Retrieves the campaign's launch time. @@ -476,15 +500,6 @@ Returns true if the campaign is paused, and false otherwise. function paused() external view returns (bool); ``` -### cancelled - -Returns true if the campaign is cancelled, and false otherwise. - - -```solidity -function cancelled() external view returns (bool); -``` - ### getDataFromRegistry Retrieves a value from the GlobalParams data registry. @@ -521,6 +536,21 @@ function getBufferTime() external view returns (uint256 bufferTime); |`bufferTime`|`uint256`|The buffer time value.| +### getPermit2Address + +Returns the canonical Permit2 contract address from GlobalParams. + + +```solidity +function getPermit2Address() external view returns (address); +``` +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`address`|The Permit2 contract address.| + + ### getLineItemType Retrieves a platform-specific line item type configuration from GlobalParams. @@ -562,7 +592,7 @@ function getLineItemType(bytes32 platformHash, bytes32 typeId) Mints a pledge NFT for a backer -Can only be called by treasuries with MINTER_ROLE +Can only be called by treasuries with TREASURY_ROLE ```solidity @@ -627,6 +657,8 @@ function updateContractURI(string calldata newContractURI) external; Burns a pledge NFT +Can only be called by treasuries with TREASURY_ROLE + ```solidity function burn(uint256 tokenId) external; diff --git a/docs/src/src/interfaces/ICampaignInfoFactory.sol/interface.ICampaignInfoFactory.md b/docs/src/src/interfaces/ICampaignInfoFactory.sol/interface.ICampaignInfoFactory.md index 5faecbe..bcef06c 100644 --- a/docs/src/src/interfaces/ICampaignInfoFactory.sol/interface.ICampaignInfoFactory.md +++ b/docs/src/src/interfaces/ICampaignInfoFactory.sol/interface.ICampaignInfoFactory.md @@ -1,8 +1,11 @@ # ICampaignInfoFactory -[Git Source](https://github.com/oak-network/contracts/blob/0ce055a8ba31ca09404e9d09ecd2549534cbec61/src/interfaces/ICampaignInfoFactory.sol) +[Git Source](https://github.com/oak-network/contracts/blob/6c7f67f5ed14ef0f4f9444b95ac6770ae2af756a/src/interfaces/ICampaignInfoFactory.sol) **Inherits:** -[ICampaignData](/Users/mahabubalahi/Documents/ccp/contracts/docs/src/src/interfaces/ICampaignData.sol/interface.ICampaignData.md) +[ICampaignData](/src/interfaces/ICampaignData.sol/interface.ICampaignData.md) + +**Title:** +ICampaignInfoFactory An interface for creating and managing campaign information contracts. @@ -64,6 +67,27 @@ function updateImplementation(address newImplementation) external; |`newImplementation`|`address`|The address of the camapaignInfo implementation contract.| +### isValidCampaignInfo + +Returns whether the given address is a CampaignInfo contract created by this factory. + + +```solidity +function isValidCampaignInfo(address campaignInfo) external view returns (bool); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`campaignInfo`|`address`|The address to check.| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`bool`|True if the address was deployed through this factory, false otherwise.| + + ## Events ### CampaignInfoFactoryCampaignCreated Emitted when a campaign is successfully created. @@ -88,3 +112,17 @@ Emitted when the campaign after creation is initialized. event CampaignInfoFactoryCampaignInitialized(); ``` +### CampaignInfoFactoryImplementationUpdated +Emitted when the campaign implementation address is updated. + + +```solidity +event CampaignInfoFactoryImplementationUpdated(address indexed newImplementation); +``` + +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`newImplementation`|`address`|The new implementation address.| + diff --git a/docs/src/src/interfaces/ICampaignPaymentTreasury.sol/interface.ICampaignPaymentTreasury.md b/docs/src/src/interfaces/ICampaignPaymentTreasury.sol/interface.ICampaignPaymentTreasury.md index 6fcc7ec..d55e959 100644 --- a/docs/src/src/interfaces/ICampaignPaymentTreasury.sol/interface.ICampaignPaymentTreasury.md +++ b/docs/src/src/interfaces/ICampaignPaymentTreasury.sol/interface.ICampaignPaymentTreasury.md @@ -1,5 +1,8 @@ # ICampaignPaymentTreasury -[Git Source](https://github.com/oak-network/contracts/blob/0ce055a8ba31ca09404e9d09ecd2549534cbec61/src/interfaces/ICampaignPaymentTreasury.sol) +[Git Source](https://github.com/oak-network/contracts/blob/6c7f67f5ed14ef0f4f9444b95ac6770ae2af756a/src/interfaces/ICampaignPaymentTreasury.sol) + +**Title:** +ICampaignPaymentTreasury An interface for managing campaign payment treasury contracts. @@ -71,7 +74,10 @@ function createPaymentBatch( Allows a buyer to make a direct crypto payment for an item. -This function transfers tokens directly from the buyer's wallet and confirms the payment immediately. +Tokens are transferred from `buyerAddress` via Permit2 `permitWitnessTransferFrom`. +The permit's witness commits to `paymentId`, `itemId`, `buyerAddress`, `amount`, and +a hash of `lineItems`, ensuring the caller cannot tamper with any of these values +after the buyer has signed the permit. ```solidity @@ -82,7 +88,8 @@ function processCryptoPayment( address paymentToken, uint256 amount, LineItem[] calldata lineItems, - ExternalFees[] calldata externalFees + ExternalFees[] calldata externalFees, + PermitData calldata permitData ) external; ``` **Parameters** @@ -91,11 +98,12 @@ function processCryptoPayment( |----|----|-----------| |`paymentId`|`bytes32`|The unique identifier of the payment.| |`itemId`|`bytes32`|The identifier of the item being purchased.| -|`buyerAddress`|`address`|The address of the buyer making the payment.| +|`buyerAddress`|`address`|The address of the buyer making the payment (must be the permit signer).| |`paymentToken`|`address`|The token to use for the payment.| -|`amount`|`uint256`|The amount to be paid for the item.| +|`amount`|`uint256`|The amount to be associated with the NFT (in token's native decimals).| |`lineItems`|`LineItem[]`|Array of line items associated with this payment.| |`externalFees`|`ExternalFees[]`|Array of external fee metadata captured for this payment (informational only).| +|`permitData`|`PermitData`|Permit2 permit data (nonce, deadline, signature) signed by `buyerAddress`.| ### cancelPayment @@ -336,21 +344,6 @@ function getExpectedAmount() external view returns (uint256); |``|`uint256`|The total expected amount as a uint256 value.| -### cancelled - -Checks if the treasury has been cancelled. - - -```solidity -function cancelled() external view returns (bool); -``` -**Returns** - -|Name|Type|Description| -|----|----|-----------| -|``|`bool`|True if the treasury is cancelled, false otherwise.| - - ## Structs ### PaymentLineItem Represents a stored line item with its configuration snapshot. diff --git a/docs/src/src/interfaces/ICampaignTreasury.sol/interface.ICampaignTreasury.md b/docs/src/src/interfaces/ICampaignTreasury.sol/interface.ICampaignTreasury.md index f69115a..5d6268a 100644 --- a/docs/src/src/interfaces/ICampaignTreasury.sol/interface.ICampaignTreasury.md +++ b/docs/src/src/interfaces/ICampaignTreasury.sol/interface.ICampaignTreasury.md @@ -1,5 +1,8 @@ # ICampaignTreasury -[Git Source](https://github.com/oak-network/contracts/blob/0ce055a8ba31ca09404e9d09ecd2549534cbec61/src/interfaces/ICampaignTreasury.sol) +[Git Source](https://github.com/oak-network/contracts/blob/6c7f67f5ed14ef0f4f9444b95ac6770ae2af756a/src/interfaces/ICampaignTreasury.sol) + +**Title:** +ICampaignTreasury An interface for managing campaign treasury contracts. @@ -113,18 +116,3 @@ function getRefundedAmount() external view returns (uint256); |``|`uint256`|The total refunded amount as a uint256 value.| -### cancelled - -Checks if the treasury has been cancelled. - - -```solidity -function cancelled() external view returns (bool); -``` -**Returns** - -|Name|Type|Description| -|----|----|-----------| -|``|`bool`|True if the treasury is cancelled, false otherwise.| - - diff --git a/docs/src/src/interfaces/IGlobalParams.sol/interface.IGlobalParams.md b/docs/src/src/interfaces/IGlobalParams.sol/interface.IGlobalParams.md index 5701c39..f166347 100644 --- a/docs/src/src/interfaces/IGlobalParams.sol/interface.IGlobalParams.md +++ b/docs/src/src/interfaces/IGlobalParams.sol/interface.IGlobalParams.md @@ -1,5 +1,8 @@ # IGlobalParams -[Git Source](https://github.com/oak-network/contracts/blob/0ce055a8ba31ca09404e9d09ecd2549534cbec61/src/interfaces/IGlobalParams.sol) +[Git Source](https://github.com/oak-network/contracts/blob/6c7f67f5ed14ef0f4f9444b95ac6770ae2af756a/src/interfaces/IGlobalParams.sol) + +**Title:** +IGlobalParams An interface for accessing and managing global parameters of the protocol. @@ -351,6 +354,21 @@ function getFromRegistry(bytes32 key) external view returns (bytes32 value); |`value`|`bytes32`|The registry value.| +### getPermit2Address + +Returns the canonical Permit2 contract address. + + +```solidity +function getPermit2Address() external pure returns (address); +``` +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`address`|The Permit2 contract address.| + + ### setPlatformLineItemType Sets or updates a platform-specific line item type configuration. diff --git a/docs/src/src/interfaces/IItem.sol/interface.IItem.md b/docs/src/src/interfaces/IItem.sol/interface.IItem.md index 0eb6410..0ebb543 100644 --- a/docs/src/src/interfaces/IItem.sol/interface.IItem.md +++ b/docs/src/src/interfaces/IItem.sol/interface.IItem.md @@ -1,5 +1,8 @@ # IItem -[Git Source](https://github.com/oak-network/contracts/blob/0ce055a8ba31ca09404e9d09ecd2549534cbec61/src/interfaces/IItem.sol) +[Git Source](https://github.com/oak-network/contracts/blob/6c7f67f5ed14ef0f4f9444b95ac6770ae2af756a/src/interfaces/IItem.sol) + +**Title:** +IItem An interface for managing items and their attributes. diff --git a/docs/src/src/interfaces/IPermit2.sol/interface.IEIP712.md b/docs/src/src/interfaces/IPermit2.sol/interface.IEIP712.md new file mode 100644 index 0000000..825321b --- /dev/null +++ b/docs/src/src/interfaces/IPermit2.sol/interface.IEIP712.md @@ -0,0 +1,15 @@ +# IEIP712 +[Git Source](https://github.com/oak-network/contracts/blob/6c7f67f5ed14ef0f4f9444b95ac6770ae2af756a/src/interfaces/IPermit2.sol) + +**Title:** +IEIP712 + + +## Functions +### DOMAIN_SEPARATOR + + +```solidity +function DOMAIN_SEPARATOR() external view returns (bytes32); +``` + diff --git a/docs/src/src/interfaces/IPermit2.sol/interface.IPermit2.md b/docs/src/src/interfaces/IPermit2.sol/interface.IPermit2.md new file mode 100644 index 0000000..7894c68 --- /dev/null +++ b/docs/src/src/interfaces/IPermit2.sol/interface.IPermit2.md @@ -0,0 +1,15 @@ +# IPermit2 +[Git Source](https://github.com/oak-network/contracts/blob/6c7f67f5ed14ef0f4f9444b95ac6770ae2af756a/src/interfaces/IPermit2.sol) + +**Inherits:** +[ISignatureTransfer](/src/interfaces/IPermit2.sol/interface.ISignatureTransfer.md) + +**Title:** +IPermit2 + +Re-exports ISignatureTransfer so that existing import paths work unchanged. + +The canonical Permit2 deployment address is +0x000000000022D473030F116dDEE9F6B43aC78BA3 across all supported EVM chains. + + diff --git a/docs/src/src/interfaces/IPermit2.sol/interface.ISignatureTransfer.md b/docs/src/src/interfaces/IPermit2.sol/interface.ISignatureTransfer.md new file mode 100644 index 0000000..2e5d47f --- /dev/null +++ b/docs/src/src/interfaces/IPermit2.sol/interface.ISignatureTransfer.md @@ -0,0 +1,172 @@ +# ISignatureTransfer +[Git Source](https://github.com/oak-network/contracts/blob/6c7f67f5ed14ef0f4f9444b95ac6770ae2af756a/src/interfaces/IPermit2.sol) + +**Inherits:** +[IEIP712](/src/interfaces/IPermit2.sol/interface.IEIP712.md) + +**Title:** +ISignatureTransfer + +Handles ERC20 token transfers through signature based actions + +Requires user's token approval on the Permit2 contract + + +## Functions +### nonceBitmap + +A map from token owner address and a caller specified word index to a bitmap. + + +```solidity +function nonceBitmap(address, uint256) external view returns (uint256); +``` + +### permitTransferFrom + +Transfers a token using a signed permit message + + +```solidity +function permitTransferFrom( + PermitTransferFrom memory permit, + SignatureTransferDetails calldata transferDetails, + address owner, + bytes calldata signature +) external; +``` + +### permitWitnessTransferFrom + +Transfers a token using a signed permit message with extra witness data + + +```solidity +function permitWitnessTransferFrom( + PermitTransferFrom memory permit, + SignatureTransferDetails calldata transferDetails, + address owner, + bytes32 witness, + string calldata witnessTypeString, + bytes calldata signature +) external; +``` + +### permitTransferFrom + +Transfers multiple tokens using a signed permit message + + +```solidity +function permitTransferFrom( + PermitBatchTransferFrom memory permit, + SignatureTransferDetails[] calldata transferDetails, + address owner, + bytes calldata signature +) external; +``` + +### permitWitnessTransferFrom + +Transfers multiple tokens using a signed permit message with extra witness data + + +```solidity +function permitWitnessTransferFrom( + PermitBatchTransferFrom memory permit, + SignatureTransferDetails[] calldata transferDetails, + address owner, + bytes32 witness, + string calldata witnessTypeString, + bytes calldata signature +) external; +``` + +### invalidateUnorderedNonces + +Invalidates the bits specified in mask for the bitmap at the word position + + +```solidity +function invalidateUnorderedNonces(uint256 wordPos, uint256 mask) external; +``` + +## Events +### UnorderedNonceInvalidation +Emits an event when the owner successfully invalidates an unordered nonce. + + +```solidity +event UnorderedNonceInvalidation(address indexed owner, uint256 word, uint256 mask); +``` + +## Errors +### InvalidAmount +Thrown when the requested amount for a transfer is larger than the permissioned amount + + +```solidity +error InvalidAmount(uint256 maxAmount); +``` + +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`maxAmount`|`uint256`|The maximum amount a spender can request to transfer| + +### LengthMismatch +Thrown when the number of tokens permissioned to a spender does not match the number of tokens being transferred + + +```solidity +error LengthMismatch(); +``` + +## Structs +### TokenPermissions +The token and amount details for a transfer signed in the permit transfer signature + + +```solidity +struct TokenPermissions { + address token; + uint256 amount; +} +``` + +### PermitTransferFrom +The signed permit message for a single token transfer + + +```solidity +struct PermitTransferFrom { + TokenPermissions permitted; + uint256 nonce; + uint256 deadline; +} +``` + +### SignatureTransferDetails +Specifies the recipient address and amount for batched transfers. + + +```solidity +struct SignatureTransferDetails { + address to; + uint256 requestedAmount; +} +``` + +### PermitBatchTransferFrom +Used to reconstruct the signed permit message for multiple token transfers + + +```solidity +struct PermitBatchTransferFrom { + TokenPermissions[] permitted; + uint256 nonce; + uint256 deadline; +} +``` + diff --git a/docs/src/src/interfaces/IPermit2.sol/struct.PermitData.md b/docs/src/src/interfaces/IPermit2.sol/struct.PermitData.md new file mode 100644 index 0000000..fa44d56 --- /dev/null +++ b/docs/src/src/interfaces/IPermit2.sol/struct.PermitData.md @@ -0,0 +1,23 @@ +# PermitData +[Git Source](https://github.com/oak-network/contracts/blob/6c7f67f5ed14ef0f4f9444b95ac6770ae2af756a/src/interfaces/IPermit2.sol) + +Application-specific struct bundling the Permit2 fields a caller must +supply alongside each signature-based token transfer. + + +```solidity +struct PermitData { +uint256 nonce; +uint256 deadline; +bytes signature; +} +``` + +**Properties** + +|Name|Type|Description| +|----|----|-----------| +|`nonce`|`uint256`| Unique nonce preventing signature replay (managed by Permit2).| +|`deadline`|`uint256`| Unix timestamp after which the permit is no longer valid.| +|`signature`|`bytes`|EIP-712 signature produced by the token owner.| + diff --git a/docs/src/src/interfaces/IReward.sol/interface.IReward.md b/docs/src/src/interfaces/IReward.sol/interface.IReward.md index 957d116..42f5bb7 100644 --- a/docs/src/src/interfaces/IReward.sol/interface.IReward.md +++ b/docs/src/src/interfaces/IReward.sol/interface.IReward.md @@ -1,5 +1,8 @@ # IReward -[Git Source](https://github.com/oak-network/contracts/blob/0ce055a8ba31ca09404e9d09ecd2549534cbec61/src/interfaces/IReward.sol) +[Git Source](https://github.com/oak-network/contracts/blob/6c7f67f5ed14ef0f4f9444b95ac6770ae2af756a/src/interfaces/IReward.sol) + +**Title:** +IReward An interface for managing rewards in a campaign. @@ -11,6 +14,7 @@ An interface for managing rewards in a campaign. struct Reward { uint256 rewardValue; bool isRewardTier; + bool canBeAddOn; bytes32[] itemId; uint256[] itemValue; uint256[] itemQuantity; diff --git a/docs/src/src/interfaces/ITreasuryFactory.sol/interface.ITreasuryFactory.md b/docs/src/src/interfaces/ITreasuryFactory.sol/interface.ITreasuryFactory.md index 9d6b987..1a24033 100644 --- a/docs/src/src/interfaces/ITreasuryFactory.sol/interface.ITreasuryFactory.md +++ b/docs/src/src/interfaces/ITreasuryFactory.sol/interface.ITreasuryFactory.md @@ -1,5 +1,8 @@ # ITreasuryFactory -[Git Source](https://github.com/oak-network/contracts/blob/0ce055a8ba31ca09404e9d09ecd2549534cbec61/src/interfaces/ITreasuryFactory.sol) +[Git Source](https://github.com/oak-network/contracts/blob/6c7f67f5ed14ef0f4f9444b95ac6770ae2af756a/src/interfaces/ITreasuryFactory.sol) + +**Title:** +ITreasuryFactory Interface for the TreasuryFactory contract, which registers, approves, and deploys treasury clones. diff --git a/docs/src/src/interfaces/README.md b/docs/src/src/interfaces/README.md index be6bb97..9f998f7 100644 --- a/docs/src/src/interfaces/README.md +++ b/docs/src/src/interfaces/README.md @@ -8,5 +8,9 @@ - [ICampaignTreasury](ICampaignTreasury.sol/interface.ICampaignTreasury.md) - [IGlobalParams](IGlobalParams.sol/interface.IGlobalParams.md) - [IItem](IItem.sol/interface.IItem.md) +- [IEIP712](IPermit2.sol/interface.IEIP712.md) +- [ISignatureTransfer](IPermit2.sol/interface.ISignatureTransfer.md) +- [IPermit2](IPermit2.sol/interface.IPermit2.md) +- [PermitData](IPermit2.sol/struct.PermitData.md) - [IReward](IReward.sol/interface.IReward.md) - [ITreasuryFactory](ITreasuryFactory.sol/interface.ITreasuryFactory.md) diff --git a/docs/src/src/storage/AdminAccessCheckerStorage.sol/library.AdminAccessCheckerStorage.md b/docs/src/src/storage/AdminAccessCheckerStorage.sol/library.AdminAccessCheckerStorage.md index 6c9f2fa..48e1548 100644 --- a/docs/src/src/storage/AdminAccessCheckerStorage.sol/library.AdminAccessCheckerStorage.md +++ b/docs/src/src/storage/AdminAccessCheckerStorage.sol/library.AdminAccessCheckerStorage.md @@ -1,17 +1,20 @@ # AdminAccessCheckerStorage -[Git Source](https://github.com/oak-network/contracts/blob/0ce055a8ba31ca09404e9d09ecd2549534cbec61/src/storage/AdminAccessCheckerStorage.sol) +[Git Source](https://github.com/oak-network/contracts/blob/6c7f67f5ed14ef0f4f9444b95ac6770ae2af756a/src/storage/AdminAccessCheckerStorage.sol) + +**Title:** +AdminAccessCheckerStorage Storage contract for AdminAccessChecker using ERC-7201 namespaced storage This contract contains the storage layout and accessor functions for AdminAccessChecker -## State Variables +## Constants ### ADMIN_ACCESS_CHECKER_STORAGE_LOCATION ```solidity bytes32 private constant ADMIN_ACCESS_CHECKER_STORAGE_LOCATION = - 0x7c2f08fa04c2c7c7ab255a45dbf913d4c236b91c59858917e818398e997f8800 + 0x7608703513d219ecdd1e84aa0951e3c83cfe601f872259e1340c97792f4b8200 ``` @@ -26,7 +29,7 @@ function _getAdminAccessCheckerStorage() internal pure returns (Storage storage ## Structs ### Storage **Note:** -storage-location: erc7201:ccprotocol.storage.AdminAccessChecker +storage-location: erc7201:oaknetwork.storage.AdminAccessChecker ```solidity diff --git a/docs/src/src/storage/CampaignInfoFactoryStorage.sol/library.CampaignInfoFactoryStorage.md b/docs/src/src/storage/CampaignInfoFactoryStorage.sol/library.CampaignInfoFactoryStorage.md index 26fe653..56e8986 100644 --- a/docs/src/src/storage/CampaignInfoFactoryStorage.sol/library.CampaignInfoFactoryStorage.md +++ b/docs/src/src/storage/CampaignInfoFactoryStorage.sol/library.CampaignInfoFactoryStorage.md @@ -1,17 +1,20 @@ # CampaignInfoFactoryStorage -[Git Source](https://github.com/oak-network/contracts/blob/0ce055a8ba31ca09404e9d09ecd2549534cbec61/src/storage/CampaignInfoFactoryStorage.sol) +[Git Source](https://github.com/oak-network/contracts/blob/6c7f67f5ed14ef0f4f9444b95ac6770ae2af756a/src/storage/CampaignInfoFactoryStorage.sol) + +**Title:** +CampaignInfoFactoryStorage Storage contract for CampaignInfoFactory using ERC-7201 namespaced storage This contract contains the storage layout and accessor functions for CampaignInfoFactory -## State Variables +## Constants ### CAMPAIGN_INFO_FACTORY_STORAGE_LOCATION ```solidity bytes32 private constant CAMPAIGN_INFO_FACTORY_STORAGE_LOCATION = - 0x2857858a392b093e1f8b3f368c2276ce911f27cef445605a2932ebe945968d00 + 0x6dcebba7d782f7ff546a8ee2af2a142213ed91f5c14e411be41cf3be65358c00 ``` @@ -26,7 +29,7 @@ function _getCampaignInfoFactoryStorage() internal pure returns (Storage storage ## Structs ### Storage **Note:** -storage-location: erc7201:ccprotocol.storage.CampaignInfoFactory +storage-location: erc7201:oaknetwork.storage.CampaignInfoFactory ```solidity diff --git a/docs/src/src/storage/GlobalParamsStorage.sol/library.GlobalParamsStorage.md b/docs/src/src/storage/GlobalParamsStorage.sol/library.GlobalParamsStorage.md index 071475e..676166c 100644 --- a/docs/src/src/storage/GlobalParamsStorage.sol/library.GlobalParamsStorage.md +++ b/docs/src/src/storage/GlobalParamsStorage.sol/library.GlobalParamsStorage.md @@ -1,17 +1,20 @@ # GlobalParamsStorage -[Git Source](https://github.com/oak-network/contracts/blob/0ce055a8ba31ca09404e9d09ecd2549534cbec61/src/storage/GlobalParamsStorage.sol) +[Git Source](https://github.com/oak-network/contracts/blob/6c7f67f5ed14ef0f4f9444b95ac6770ae2af756a/src/storage/GlobalParamsStorage.sol) + +**Title:** +GlobalParamsStorage Storage contract for GlobalParams using ERC-7201 namespaced storage This contract contains the storage layout and accessor functions for GlobalParams -## State Variables +## Constants ### GLOBAL_PARAMS_STORAGE_LOCATION ```solidity bytes32 private constant GLOBAL_PARAMS_STORAGE_LOCATION = - 0x83d0145f7c1378f10048390769ec94f999b3ba6d94904b8fd7251512962b1c00 + 0xcab368c4291c205bbe63a595130eb08714925d02705f410a55bf1a45b8ddaf00 ``` @@ -52,7 +55,7 @@ struct LineItemType { ### Storage **Note:** -storage-location: erc7201:ccprotocol.storage.GlobalParams +storage-location: erc7201:oaknetwork.storage.GlobalParams ```solidity diff --git a/docs/src/src/storage/TreasuryFactoryStorage.sol/library.TreasuryFactoryStorage.md b/docs/src/src/storage/TreasuryFactoryStorage.sol/library.TreasuryFactoryStorage.md index b0a68ad..f2dd757 100644 --- a/docs/src/src/storage/TreasuryFactoryStorage.sol/library.TreasuryFactoryStorage.md +++ b/docs/src/src/storage/TreasuryFactoryStorage.sol/library.TreasuryFactoryStorage.md @@ -1,17 +1,20 @@ # TreasuryFactoryStorage -[Git Source](https://github.com/oak-network/contracts/blob/0ce055a8ba31ca09404e9d09ecd2549534cbec61/src/storage/TreasuryFactoryStorage.sol) +[Git Source](https://github.com/oak-network/contracts/blob/6c7f67f5ed14ef0f4f9444b95ac6770ae2af756a/src/storage/TreasuryFactoryStorage.sol) + +**Title:** +TreasuryFactoryStorage Storage contract for TreasuryFactory using ERC-7201 namespaced storage This contract contains the storage layout and accessor functions for TreasuryFactory -## State Variables +## Constants ### TREASURY_FACTORY_STORAGE_LOCATION ```solidity bytes32 private constant TREASURY_FACTORY_STORAGE_LOCATION = - 0x96b7de8c171ef460648aea35787d043e89feb6b6de2623a1e6f17a91b9c9e900 + 0xac5f58af051caf3154d38fdfab53396f7d32e9ef6bb41b866435ed38c5426600 ``` @@ -26,13 +29,14 @@ function _getTreasuryFactoryStorage() internal pure returns (Storage storage $); ## Structs ### Storage **Note:** -storage-location: erc7201:ccprotocol.storage.TreasuryFactory +storage-location: erc7201:oaknetwork.storage.TreasuryFactory ```solidity struct Storage { mapping(bytes32 => mapping(uint256 => address)) implementationMap; mapping(address => bool) approvedImplementations; + address campaignInfoFactory; } ``` diff --git a/docs/src/src/treasuries/AllOrNothing.sol/contract.AllOrNothing.md b/docs/src/src/treasuries/AllOrNothing.sol/contract.AllOrNothing.md index 3b74f7b..ac25dde 100644 --- a/docs/src/src/treasuries/AllOrNothing.sol/contract.AllOrNothing.md +++ b/docs/src/src/treasuries/AllOrNothing.sol/contract.AllOrNothing.md @@ -1,10 +1,46 @@ # AllOrNothing -[Git Source](https://github.com/oak-network/contracts/blob/0ce055a8ba31ca09404e9d09ecd2549534cbec61/src/treasuries/AllOrNothing.sol) +[Git Source](https://github.com/oak-network/contracts/blob/6c7f67f5ed14ef0f4f9444b95ac6770ae2af756a/src/treasuries/AllOrNothing.sol) **Inherits:** -[IReward](/Users/mahabubalahi/Documents/ccp/contracts/docs/src/src/interfaces/IReward.sol/interface.IReward.md), [BaseTreasury](/Users/mahabubalahi/Documents/ccp/contracts/docs/src/src/utils/BaseTreasury.sol/abstract.BaseTreasury.md), [TimestampChecker](/Users/mahabubalahi/Documents/ccp/contracts/docs/src/src/utils/TimestampChecker.sol/abstract.TimestampChecker.md), ReentrancyGuard +[IReward](/src/interfaces/IReward.sol/interface.IReward.md), [BaseTreasury](/src/utils/BaseTreasury.sol/abstract.BaseTreasury.md), [TimestampChecker](/src/utils/TimestampChecker.sol/abstract.TimestampChecker.md) -A contract for handling crowdfunding campaigns with rewards. +**Title:** +AllOrNothing + +A contract for handling "all-or-nothing" crowdfunding campaigns. Funds are only claimable by the campaign owner if the funding goal is met by the deadline; otherwise, backers can claim refunds. + + +## Constants +### AON_PLEDGE_FOR_REWARD_WITNESS_TYPEHASH + +```solidity +bytes32 internal constant AON_PLEDGE_FOR_REWARD_WITNESS_TYPEHASH = + keccak256("PledgeForRewardWitness(address backer,bytes32 rewardsHash,uint256 shippingFee)") +``` + + +### AON_PLEDGE_FOR_REWARD_WITNESS_TYPE_STRING + +```solidity +string internal constant AON_PLEDGE_FOR_REWARD_WITNESS_TYPE_STRING = + "PledgeForRewardWitness witness)PledgeForRewardWitness(address backer,bytes32 rewardsHash,uint256 shippingFee)TokenPermissions(address token,uint256 amount)" +``` + + +### AON_PLEDGE_WITHOUT_REWARD_WITNESS_TYPEHASH + +```solidity +bytes32 internal constant AON_PLEDGE_WITHOUT_REWARD_WITNESS_TYPEHASH = + keccak256("PledgeWithoutRewardWitness(address backer,uint256 pledgeAmount)") +``` + + +### AON_PLEDGE_WITHOUT_REWARD_WITNESS_TYPE_STRING + +```solidity +string internal constant AON_PLEDGE_WITHOUT_REWARD_WITNESS_TYPE_STRING = + "PledgeWithoutRewardWitness witness)PledgeWithoutRewardWitness(address backer,uint256 pledgeAmount)TokenPermissions(address token,uint256 amount)" +``` ## State Variables @@ -57,7 +93,7 @@ constructor() ; ```solidity -function initialize(bytes32 _platformHash, address _infoAddress, address _trustedForwarder) external initializer; +function initialize(bytes32 _platformHash, address _infoAddress) external initializer; ``` ### getReward @@ -87,13 +123,13 @@ Retrieves the total raised amount in the treasury. ```solidity -function getRaisedAmount() external view override returns (uint256); +function getRaisedAmount() external view override returns (uint256 amount); ``` **Returns** |Name|Type|Description| |----|----|-----------| -|``|`uint256`|The total raised amount as a uint256 value.| +|`amount`|`uint256`|Total raised amount across all tokens, normalized to 18 decimals.| ### getLifetimeRaisedAmount @@ -102,13 +138,13 @@ Retrieves the lifetime raised amount in the treasury (never decreases with refun ```solidity -function getLifetimeRaisedAmount() external view override returns (uint256); +function getLifetimeRaisedAmount() external view override returns (uint256 amount); ``` **Returns** |Name|Type|Description| |----|----|-----------| -|``|`uint256`|The lifetime raised amount as a uint256 value.| +|`amount`|`uint256`|Lifetime total raised amount across all tokens, normalized to 18 decimals.| ### getRefundedAmount @@ -117,13 +153,13 @@ Retrieves the total refunded amount in the treasury. ```solidity -function getRefundedAmount() external view override returns (uint256); +function getRefundedAmount() external view override returns (uint256 amount); ``` **Returns** |Name|Type|Description| |----|----|-----------| -|``|`uint256`|The total refunded amount as a uint256 value.| +|`amount`|`uint256`|Total refunded amount across all tokens, normalized to 18 decimals.| ### addRewards @@ -162,6 +198,7 @@ Removes a reward from the campaign. function removeReward(bytes32 rewardName) external onlyCampaignOwner + currentTimeIsLess(INFO.getLaunchTime()) whenCampaignNotPaused whenNotPaused whenCampaignNotCancelled @@ -176,14 +213,21 @@ function removeReward(bytes32 rewardName) ### pledgeForAReward -Allows a backer to pledge for a reward. +Allows a backer to pledge for a reward using a Permit2 signature. -The first element of the `reward` array must be a reward tier and the other elements can be either reward tiers or non-reward tiers. -The non-reward tiers cannot be pledged for without a reward. +Tokens are transferred from `backer` via Permit2 `permitWitnessTransferFrom`. +The permit's witness commits to `backer`, the reward array hash, and `shippingFee`, +so the caller cannot change those values after the backer has signed. ```solidity -function pledgeForAReward(address backer, address pledgeToken, uint256 shippingFee, bytes32[] calldata reward) +function pledgeForAReward( + address backer, + address pledgeToken, + uint256 shippingFee, + bytes32[] calldata reward, + PermitData calldata permitData +) external nonReentrant currentTimeIsWithinRange(INFO.getLaunchTime(), INFO.getDeadline()) @@ -196,19 +240,28 @@ function pledgeForAReward(address backer, address pledgeToken, uint256 shippingF |Name|Type|Description| |----|----|-----------| -|`backer`|`address`|The address of the backer making the pledge.| +|`backer`|`address`|The address of the backer making the pledge (must be the permit signer).| |`pledgeToken`|`address`|The token address to use for the pledge.| |`shippingFee`|`uint256`|The shipping fee amount.| |`reward`|`bytes32[]`|An array of reward names.| +|`permitData`|`PermitData`|Permit2 permit data (nonce, deadline, signature) signed by `backer`.| ### pledgeWithoutAReward -Allows a backer to pledge without selecting a reward. +Allows a backer to pledge without selecting a reward using a Permit2 signature. + +Tokens are transferred from `backer` via Permit2 `permitWitnessTransferFrom`. +The permit's witness commits to `backer` and `pledgeAmount`. ```solidity -function pledgeWithoutAReward(address backer, address pledgeToken, uint256 pledgeAmount) +function pledgeWithoutAReward( + address backer, + address pledgeToken, + uint256 pledgeAmount, + PermitData calldata permitData +) external nonReentrant currentTimeIsWithinRange(INFO.getLaunchTime(), INFO.getDeadline()) @@ -221,9 +274,10 @@ function pledgeWithoutAReward(address backer, address pledgeToken, uint256 pledg |Name|Type|Description| |----|----|-----------| -|`backer`|`address`|The address of the backer making the pledge.| +|`backer`|`address`|The address of the backer making the pledge (must be the permit signer).| |`pledgeToken`|`address`|The token address to use for the pledge.| -|`pledgeAmount`|`uint256`|The amount of the pledge.| +|`pledgeAmount`|`uint256`|The amount of the pledge (in token's native decimals).| +|`permitData`|`PermitData`|Permit2 permit data (nonce, deadline, signature) signed by `backer`.| ### claimRefund @@ -289,6 +343,10 @@ function _checkSuccessCondition() internal view virtual override returns (bool); ### _pledge +Processes a pledge: transfers tokens, mints NFT, and updates state. + +Mints a pledge NFT via `_safeMint`; reverts if `backer` is a contract that does not implement `IERC721Receiver`. + ```solidity function _pledge( @@ -297,9 +355,22 @@ function _pledge( bytes32 reward, uint256 pledgeAmount, uint256 shippingFee, - bytes32[] memory rewards + bytes32[] memory rewards, + PermitData calldata permitData ) private; ``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`backer`|`address`|Recipient of the pledge NFT.| +|`pledgeToken`|`address`|Token used for the pledge.| +|`reward`|`bytes32`|First reward tier (ZERO_BYTES for non-reward pledges).| +|`pledgeAmount`|`uint256`|Pledge amount in the token's native decimals (must be denormalized by caller).| +|`shippingFee`|`uint256`|Shipping fee in the token's native decimals (must be denormalized by caller; use 0 for non-reward).| +|`rewards`|`bytes32[]`|Full reward selection (for event).| +|`permitData`|`PermitData`|| + ## Events ### Receipt @@ -389,7 +460,61 @@ Emitted when an invalid input is detected. ```solidity -error AllOrNothingInvalidInput(); +error AllOrNothingInvalidInput(TreasuryErrors.InvalidInput code); +``` + +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`code`|`TreasuryErrors.InvalidInput`|Error code defined in {TreasuryErrors.InvalidInput}.| + +### AllOrNothingZeroRewardName +Reverts when reward name is zero bytes. + + +```solidity +error AllOrNothingZeroRewardName(); +``` + +### AllOrNothingZeroRewardValue +Reverts when reward value is zero. + + +```solidity +error AllOrNothingZeroRewardValue(); +``` + +### AllOrNothingRewardItemArrayLengthMismatch +Reverts when reward item arrays have mismatched lengths. + + +```solidity +error AllOrNothingRewardItemArrayLengthMismatch(); +``` + +### AllOrNothingZeroBacker +Reverts when backer address is zero. + + +```solidity +error AllOrNothingZeroBacker(); +``` + +### AllOrNothingRewardSelectionLengthMismatch +Reverts when reward selection length exceeds number of rewards. + + +```solidity +error AllOrNothingRewardSelectionLengthMismatch(); +``` + +### AllOrNothingFirstRewardNotTier +Reverts when first reward is not a reward tier. + + +```solidity +error AllOrNothingFirstRewardNotTier(); ``` ### AllOrNothingTransferFailed @@ -416,14 +541,6 @@ Emitted when fees are not disbursed. error AllOrNothingFeeNotDisbursed(); ``` -### AllOrNothingFeeAlreadyDisbursed -Emitted when `disburseFees` after fee is disbursed already. - - -```solidity -error AllOrNothingFeeAlreadyDisbursed(); -``` - ### AllOrNothingRewardExists Emitted when a `Reward` already exists for given input. @@ -445,7 +562,7 @@ Emitted when claiming an unclaimable refund. ```solidity -error AllOrNothingNotClaimable(uint256 tokenId); +error AllOrNothingNotClaimable(uint256 tokenId, TreasuryErrors.NotClaimable code); ``` **Parameters** @@ -453,4 +570,5 @@ error AllOrNothingNotClaimable(uint256 tokenId); |Name|Type|Description| |----|----|-----------| |`tokenId`|`uint256`|The ID of the token representing the pledge.| +|`code`|`TreasuryErrors.NotClaimable`|Error code defined in {TreasuryErrors.NotClaimable}.| diff --git a/docs/src/src/treasuries/KeepWhatsRaised.sol/contract.KeepWhatsRaised.md b/docs/src/src/treasuries/KeepWhatsRaised.sol/contract.KeepWhatsRaised.md index 6f13e93..d43c72f 100644 --- a/docs/src/src/treasuries/KeepWhatsRaised.sol/contract.KeepWhatsRaised.md +++ b/docs/src/src/treasuries/KeepWhatsRaised.sol/contract.KeepWhatsRaised.md @@ -1,12 +1,48 @@ # KeepWhatsRaised -[Git Source](https://github.com/oak-network/contracts/blob/0ce055a8ba31ca09404e9d09ecd2549534cbec61/src/treasuries/KeepWhatsRaised.sol) +[Git Source](https://github.com/oak-network/contracts/blob/6c7f67f5ed14ef0f4f9444b95ac6770ae2af756a/src/treasuries/KeepWhatsRaised.sol) **Inherits:** -[IReward](/Users/mahabubalahi/Documents/ccp/contracts/docs/src/src/interfaces/IReward.sol/interface.IReward.md), [BaseTreasury](/Users/mahabubalahi/Documents/ccp/contracts/docs/src/src/utils/BaseTreasury.sol/abstract.BaseTreasury.md), [TimestampChecker](/Users/mahabubalahi/Documents/ccp/contracts/docs/src/src/utils/TimestampChecker.sol/abstract.TimestampChecker.md), [ICampaignData](/Users/mahabubalahi/Documents/ccp/contracts/docs/src/src/interfaces/ICampaignData.sol/interface.ICampaignData.md), ReentrancyGuard +[IReward](/src/interfaces/IReward.sol/interface.IReward.md), [BaseTreasury](/src/utils/BaseTreasury.sol/abstract.BaseTreasury.md), [TimestampChecker](/src/utils/TimestampChecker.sol/abstract.TimestampChecker.md), [ICampaignData](/src/interfaces/ICampaignData.sol/interface.ICampaignData.md) + +**Title:** +KeepWhatsRaised A contract that keeps all the funds raised, regardless of the success condition. +## Constants +### KWR_PLEDGE_FOR_REWARD_WITNESS_TYPEHASH + +```solidity +bytes32 internal constant KWR_PLEDGE_FOR_REWARD_WITNESS_TYPEHASH = + keccak256("KWRPledgeForRewardWitness(bytes32 pledgeId,address backer,bytes32 rewardsHash,uint256 tip)") +``` + + +### KWR_PLEDGE_FOR_REWARD_WITNESS_TYPE_STRING + +```solidity +string internal constant KWR_PLEDGE_FOR_REWARD_WITNESS_TYPE_STRING = + "KWRPledgeForRewardWitness witness)KWRPledgeForRewardWitness(bytes32 pledgeId,address backer,bytes32 rewardsHash,uint256 tip)TokenPermissions(address token,uint256 amount)" +``` + + +### KWR_PLEDGE_WITHOUT_REWARD_WITNESS_TYPEHASH + +```solidity +bytes32 internal constant KWR_PLEDGE_WITHOUT_REWARD_WITNESS_TYPEHASH = + keccak256("KWRPledgeWithoutRewardWitness(bytes32 pledgeId,address backer,uint256 pledgeAmount,uint256 tip)") +``` + + +### KWR_PLEDGE_WITHOUT_REWARD_WITNESS_TYPE_STRING + +```solidity +string internal constant KWR_PLEDGE_WITHOUT_REWARD_WITNESS_TYPE_STRING = + "KWRPledgeWithoutRewardWitness witness)KWRPledgeWithoutRewardWitness(bytes32 pledgeId,address backer,uint256 pledgeAmount,uint256 tip)TokenPermissions(address token,uint256 amount)" +``` + + ## State Variables ### s_tokenToPledgedAmount @@ -37,7 +73,7 @@ mapping(bytes32 => Reward) private s_reward ### s_processedPledges -Tracks whether a pledge with a specific ID has already been processed +Tracks whether an external pledge ID has already been processed. ```solidity @@ -54,12 +90,28 @@ mapping(bytes32 => uint256) public s_paymentGatewayFees ``` -### s_feeValues -Mapping that stores fee values indexed by their corresponding fee keys. +### s_flatFeeValue +Flat fee values (token amounts, 18 decimals). Units are unambiguous. + + +```solidity +uint256 private s_flatFeeValue +``` + + +### s_cumulativeFlatFeeValue + +```solidity +uint256 private s_cumulativeFlatFeeValue +``` + + +### s_grossPercentageFeeValues +Gross percentage fee values (basis points, 0 to PERCENT_DIVIDER - 1). Stored in same order as s_feeKeys.grossPercentageFeeKeys. ```solidity -mapping(bytes32 => uint256) private s_feeValues +uint256[] private s_grossPercentageFeeValues ``` @@ -105,13 +157,6 @@ Counters.Counter private s_rewardCounter ``` -### s_cancellationTime - -```solidity -uint256 private s_cancellationTime -``` - - ### s_isWithdrawalApproved ```solidity @@ -133,6 +178,13 @@ bool private s_fundClaimed ``` +### s_configured + +```solidity +bool private s_configured +``` + + ### s_feeKeys ```solidity @@ -201,7 +253,7 @@ constructor() ; ```solidity -function initialize(bytes32 _platformHash, address _infoAddress, address _trustedForwarder) external initializer; +function initialize(bytes32 _platformHash, address _infoAddress) external initializer; ``` ### getWithdrawalApprovalStatus @@ -240,13 +292,13 @@ Retrieves the total raised amount in the treasury. ```solidity -function getRaisedAmount() external view override returns (uint256); +function getRaisedAmount() external view override returns (uint256 amount); ``` **Returns** |Name|Type|Description| |----|----|-----------| -|``|`uint256`|The total raised amount as a uint256 value.| +|`amount`|`uint256`|Total raised amount across all tokens, normalized to 18 decimals.| ### getLifetimeRaisedAmount @@ -255,13 +307,13 @@ Retrieves the lifetime raised amount in the treasury (never decreases with refun ```solidity -function getLifetimeRaisedAmount() external view override returns (uint256); +function getLifetimeRaisedAmount() external view override returns (uint256 amount); ``` **Returns** |Name|Type|Description| |----|----|-----------| -|``|`uint256`|The lifetime raised amount as a uint256 value.| +|`amount`|`uint256`|Lifetime total raised amount across all tokens, normalized to 18 decimals.| ### getRefundedAmount @@ -270,13 +322,13 @@ Retrieves the total refunded amount in the treasury. ```solidity -function getRefundedAmount() external view override returns (uint256); +function getRefundedAmount() external view override returns (uint256 amount); ``` **Returns** |Name|Type|Description| |----|----|-----------| -|``|`uint256`|The total refunded amount as a uint256 value.| +|`amount`|`uint256`|Total refunded amount across all tokens, normalized to 18 decimals.| ### getAvailableRaisedAmount @@ -285,13 +337,13 @@ Retrieves the currently available raised amount in the treasury. ```solidity -function getAvailableRaisedAmount() external view returns (uint256); +function getAvailableRaisedAmount() external view returns (uint256 amount); ``` **Returns** |Name|Type|Description| |----|----|-----------| -|``|`uint256`|The current available raised amount as a uint256 value.| +|`amount`|`uint256`|Available raised amount across all tokens, normalized to 18 decimals.| ### getLaunchTime @@ -363,6 +415,7 @@ function getPaymentGatewayFee(bytes32 pledgeId) public view returns (uint256); ### getFeeValue Retrieves the fee value associated with a specific fee key from storage. +Flat fee keys return token amounts (18 decimals); percentage keys return basis points. ```solidity @@ -372,13 +425,13 @@ function getFeeValue(bytes32 feeKey) public view returns (uint256); |Name|Type|Description| |----|----|-----------| -|`feeKey`|`bytes32`|| +|`feeKey`|`bytes32`|The unique identifier key used to reference a specific fee type.| **Returns** |Name|Type|Description| |----|----|-----------| -|``|`uint256`|{uint256} The fee value corresponding to the provided fee key.| +|``|`uint256`|The fee value corresponding to the provided fee key (0 if key is unknown).| ### setPaymentGatewayFee @@ -442,7 +495,7 @@ function configureTreasury( |Name|Type|Description| |----|----|-----------| -|`config`|`Config`|The configuration settings including withdrawal delay, refund delay, fee exemption threshold, and configuration lock period.| +|`config`|`Config`|The configuration settings including withdrawal delay, refund delay, fee exemption threshold, and configuration lock period. Must satisfy withdrawalDelay >= refundDelay so claimFund is only callable after the refund window ends.| |`campaignData`|`CampaignData`|The campaign-related metadata such as deadlines and funding goals.| |`feeKeys`|`FeeKeys`|The set of keys used to reference applicable flat and percentage-based fees.| |`feeValues`|`FeeValues`|The fee values corresponding to the fee keys.| @@ -524,6 +577,7 @@ Removes a reward from the campaign. function removeReward(bytes32 rewardName) external onlyCampaignOwner + currentTimeIsLess(getLaunchTime()) whenCampaignNotPaused whenNotPaused whenCampaignNotCancelled @@ -555,6 +609,7 @@ function setFeeAndPledge( external nonReentrant onlyPlatformAdmin(PLATFORM_HASH) + currentTimeIsWithinRange(getLaunchTime(), getDeadline()) whenCampaignNotPaused whenNotPaused whenCampaignNotCancelled @@ -576,10 +631,11 @@ function setFeeAndPledge( ### pledgeForAReward -Allows a backer to pledge for a reward. +Allows a backer to pledge for a reward using a Permit2 signature. -The first element of the `reward` array must be a reward tier and the other elements can be either reward tiers or non-reward tiers. -The non-reward tiers cannot be pledged for without a reward. +Tokens are transferred from `backer` via Permit2 `permitWitnessTransferFrom`. +The permit's witness commits to `pledgeId`, `backer`, the reward array hash, and +`tip`, so the caller cannot tamper with those parameters after the backer has signed. ```solidity @@ -588,7 +644,8 @@ function pledgeForAReward( address backer, address pledgeToken, uint256 tip, - bytes32[] calldata reward + bytes32[] calldata reward, + PermitData calldata permitData ) public nonReentrant @@ -603,20 +660,19 @@ function pledgeForAReward( |Name|Type|Description| |----|----|-----------| |`pledgeId`|`bytes32`|The unique identifier of the pledge.| -|`backer`|`address`|The address of the backer making the pledge.| +|`backer`|`address`|The address of the backer making the pledge (must be the permit signer).| |`pledgeToken`|`address`|The token to use for the pledge.| |`tip`|`uint256`|An optional tip can be added during the process.| |`reward`|`bytes32[]`|An array of reward names.| +|`permitData`|`PermitData`|Permit2 permit data (nonce, deadline, signature) signed by `backer`.| ### _pledgeForAReward -Internal function that allows a backer to pledge for a reward with tokens transferred from a specified source. +Internal function that allows a backer to pledge for a reward. -The first element of the `reward` array must be a reward tier and the other elements can be either reward tiers or non-reward tiers. -The non-reward tiers cannot be pledged for without a reward. -This function is called internally by both public pledgeForAReward (with backer as token source) and -setFeeAndPledge (with admin as token source). +Called by both the public `pledgeForAReward` (Permit2 transfer) and +`setFeeAndPledge` (admin ERC20 transfer). ```solidity @@ -625,8 +681,10 @@ function _pledgeForAReward( address backer, address pledgeToken, uint256 tip, - bytes32[] calldata reward, - address tokenSource + bytes32[] memory reward, + address tokenSource, + bool usePermit2, + PermitData memory permitData ) internal; ``` **Parameters** @@ -638,12 +696,17 @@ function _pledgeForAReward( |`pledgeToken`|`address`|The token to use for the pledge.| |`tip`|`uint256`|An optional tip can be added during the process.| |`reward`|`bytes32[]`|An array of reward names.| -|`tokenSource`|`address`|The address from which tokens will be transferred (either backer for direct calls or admin for setFeeAndPledge calls).| +|`tokenSource`|`address`|Token source address for the admin (ERC20) path.| +|`usePermit2`|`bool`|Whether to transfer tokens via Permit2 or direct ERC20 transfer.| +|`permitData`|`PermitData`|Permit2 data for the direct user path.| ### pledgeWithoutAReward -Allows a backer to pledge without selecting a reward. +Allows a backer to pledge without selecting a reward using a Permit2 signature. + +Tokens are transferred from `backer` via Permit2 `permitWitnessTransferFrom`. +The permit's witness commits to `pledgeId`, `backer`, `pledgeAmount`, and `tip`. ```solidity @@ -652,7 +715,8 @@ function pledgeWithoutAReward( address backer, address pledgeToken, uint256 pledgeAmount, - uint256 tip + uint256 tip, + PermitData calldata permitData ) public nonReentrant @@ -667,18 +731,19 @@ function pledgeWithoutAReward( |Name|Type|Description| |----|----|-----------| |`pledgeId`|`bytes32`|The unique identifier of the pledge.| -|`backer`|`address`|The address of the backer making the pledge.| +|`backer`|`address`|The address of the backer making the pledge (must be the permit signer).| |`pledgeToken`|`address`|The token to use for the pledge.| -|`pledgeAmount`|`uint256`|The amount of the pledge.| -|`tip`|`uint256`|An optional tip can be added during the process.| +|`pledgeAmount`|`uint256`|The amount of the pledge (in token's native decimals).| +|`tip`|`uint256`|An optional tip (in token's native decimals).| +|`permitData`|`PermitData`|Permit2 permit data (nonce, deadline, signature) signed by `backer`.| ### _pledgeWithoutAReward -Internal function that allows a backer to pledge without selecting a reward with tokens transferred from a specified source. +Internal function that allows a backer to pledge without a reward. -This function is called internally by both public pledgeWithoutAReward (with backer as token source) and -setFeeAndPledge (with admin as token source). +Called by both the public `pledgeWithoutAReward` (Permit2 transfer) and +`setFeeAndPledge` (admin ERC20 transfer). ```solidity @@ -688,7 +753,9 @@ function _pledgeWithoutAReward( address pledgeToken, uint256 pledgeAmount, uint256 tip, - address tokenSource + address tokenSource, + bool usePermit2, + PermitData memory permitData ) internal; ``` **Parameters** @@ -699,8 +766,10 @@ function _pledgeWithoutAReward( |`backer`|`address`|The address of the backer making the pledge (receives the NFT).| |`pledgeToken`|`address`|The token to use for the pledge.| |`pledgeAmount`|`uint256`|The amount of the pledge.| -|`tip`|`uint256`|An optional tip can be added during the process.| -|`tokenSource`|`address`|The address from which tokens will be transferred (either backer for direct calls or admin for setFeeAndPledge calls).| +|`tip`|`uint256`|An optional tip.| +|`tokenSource`|`address`|Token source address for the admin (ERC20) path.| +|`usePermit2`|`bool`|Whether to transfer tokens via Permit2 or direct ERC20 transfer.| +|`permitData`|`PermitData`|Permit2 data for the direct user path.| ### withdraw @@ -709,12 +778,50 @@ Withdraws funds from the treasury. ```solidity -function withdraw() public view override whenNotPaused whenNotCancelled; +function withdraw() + public + view + override + whenCampaignNotPaused + whenCampaignNotCancelled + whenNotPaused + whenNotCancelled; ``` +### _colombianCreatorTax + +Computes Colombian creator tax with a single accounting model to avoid double-counting. +- Partial withdrawal: `amount` is NET (what the creator receives). Tax is additive (fee on top). +Formula: tax = ceil(net * 40 / 10000). Rounded up per Colombian Peso precision requirements. +- Final withdrawal: `amount` is GROSS (full remaining balance). Tax is deducted from it. +Formula: tax = ceil(gross * 40 / 10040) (tax-inclusive rate). Rounded up per Colombian Peso. + + +```solidity +function _colombianCreatorTax(uint256 amount, bool isFromGross) internal pure returns (uint256); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`amount`|`uint256`|The net amount (partial) or gross amount (final) in token units.| +|`isFromGross`|`bool`|True for final withdrawal (amount = full balance), false for partial (amount = net to creator).| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`uint256`|Tax amount in token units (rounded up).| + + ### withdraw Allows the campaign owner or platform admin to withdraw funds, applying required fees and taxes. +Accounting model (per product requirement): +- Partial withdrawal: Creator receives the full requested amount; fees (including Colombian tax) are additive +(deducted from the pool in addition). So: pool -= amount + totalFee, creator gets amount (net). +- Final withdrawal: Fees (including Colombian tax) are cut from the remaining balance; creator receives +the remainder. So: pool -= withdrawalAmount, creator gets withdrawalAmount - totalFee (net). ```solidity @@ -722,6 +829,8 @@ function withdraw(address token, uint256 amount) public onlyPlatformAdminOrCampaignOwner currentTimeIsLess(getDeadline() + s_config.withdrawalDelay) + whenCampaignNotPaused + whenCampaignNotCancelled whenNotPaused whenNotCancelled withdrawalEnabled; @@ -731,7 +840,7 @@ function withdraw(address token, uint256 amount) |Name|Type|Description| |----|----|-----------| |`token`|`address`|The token to withdraw.| -|`amount`|`uint256`|The withdrawal amount (ignored for final withdrawals). Requirements: - Caller must be authorized. - Withdrawals must be enabled, not paused, and within the allowed time. - Token must be accepted for the campaign. - For partial withdrawals: - `amount` > 0 and `amount + fees` ≤ available balance. - For final withdrawals: - Available balance > 0 and fees ≤ available balance. Effects: - Deducts fees (flat, cumulative, and Colombian tax if applicable). - Updates available balance per token. - Transfers net funds to the recipient. Reverts: - If insufficient funds or invalid input. Emits: - `WithdrawalWithFeeSuccessful`.| +|`amount`|`uint256`|The withdrawal amount (ignored for final withdrawals). For partial, this is the NET amount to transfer to the creator; fees are additive. Requirements: - Caller must be authorized. - Withdrawals must be enabled, not paused, and within the withdrawal window (current time < deadline + withdrawalDelay). - Token must be accepted for the campaign. - For partial withdrawals: - `amount` > 0 and `amount + fees` ≤ available balance. - For final withdrawals: - Available balance > 0 and fees ≤ available balance. Effects: - Deducts fees (flat, cumulative, and Colombian tax if applicable). - Updates available balance per token. - Transfers net funds to the recipient. Reverts: - If insufficient funds or invalid input. Emits: - `WithdrawalWithFeeSuccessful`.| ### claimRefund @@ -756,12 +865,13 @@ function claimRefund(uint256 tokenId) ### disburseFees Disburses all accumulated fees to the appropriate fee collector or treasury. +Callable before or after cancellation so that accrued fees are never trapped. Requirements: - Only callable when fees are available. ```solidity -function disburseFees() public override whenNotPaused whenNotCancelled; +function disburseFees() public override whenCampaignNotPaused whenNotPaused; ``` ### claimTip @@ -814,6 +924,10 @@ function _checkSuccessCondition() internal view virtual override returns (bool); ### _pledge +Processes a pledge: transfers tokens, mints NFT, and updates state. + +Mints a pledge NFT via `_safeMint`; reverts if `backer` is a contract that does not implement `IERC721Receiver`. + ```solidity function _pledge( @@ -824,9 +938,26 @@ function _pledge( uint256 pledgeAmount, uint256 tip, bytes32[] memory rewards, - address tokenSource + address tokenSource, + bool usePermit2, + PermitData memory permitData ) private; ``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`pledgeId`|`bytes32`|Unique identifier for the pledge.| +|`backer`|`address`|Recipient of the pledge NFT.| +|`pledgeToken`|`address`|Token used for the pledge.| +|`reward`|`bytes32`|First reward tier (ZERO_BYTES for non-reward pledges).| +|`pledgeAmount`|`uint256`|Pledge amount in the token's native decimals (must be denormalized by caller).| +|`tip`|`uint256`|Tip amount in the token's native decimals.| +|`rewards`|`bytes32[]`|Full reward selection (for event).| +|`tokenSource`|`address`|Address from which tokens are transferred.| +|`usePermit2`|`bool`|| +|`permitData`|`PermitData`|| + ### _calculateNetAvailable @@ -862,11 +993,23 @@ function _calculateNetAvailable(bytes32 pledgeId, address pledgeToken, uint256 t |``|`uint256`|The net available amount after all fees are deducted| +### _getEffectiveCancellationTime + +Returns the effective cancellation time by consulting both the treasury's own +cancellation state and the campaign's cancellation state. If both are cancelled, +returns the earlier timestamp so the refund window starts from the first cancellation event. +Returns 0 if neither is cancelled. + + +```solidity +function _getEffectiveCancellationTime() private view returns (uint256); +``` + ### _checkRefundPeriodStatus Refund period logic: -- If campaign is cancelled: refund period is active until s_cancellationTime + s_config.refundDelay -- If campaign is not cancelled: refund period is active until deadline + s_config.refundDelay +- If cancelled (treasury or campaign): refund period is active until cancellationTime + s_config.refundDelay +- If not cancelled: refund period is active until deadline + s_config.refundDelay - Before deadline (non-cancelled): not in refund period Checks the refund period status based on campaign state @@ -1092,7 +1235,125 @@ Emitted when an invalid input is detected. ```solidity -error KeepWhatsRaisedInvalidInput(); +error KeepWhatsRaisedInvalidInput(TreasuryErrors.InvalidInput code); +``` + +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`code`|`TreasuryErrors.InvalidInput`|Error code defined in {TreasuryErrors.InvalidInput}.| + +### KeepWhatsRaisedDuplicateFeeKey +Emitted when fee keys are not unique (duplicate or overlap between flat and percentage keys). + + +```solidity +error KeepWhatsRaisedDuplicateFeeKey(); +``` + +### KeepWhatsRaisedPercentageFeeExceedsMax +Emitted when a percentage fee value is >= PERCENT_DIVIDER (100%). + + +```solidity +error KeepWhatsRaisedPercentageFeeExceedsMax(); +``` + +### KeepWhatsRaisedAggregatePercentageExceedsMax +Emitted when the sum of gross percentage fees is >= PERCENT_DIVIDER (100%). + + +```solidity +error KeepWhatsRaisedAggregatePercentageExceedsMax(); +``` + +### KeepWhatsRaisedLaunchTimeInPast +Reverts when campaign launch time is in the past. + + +```solidity +error KeepWhatsRaisedLaunchTimeInPast(); +``` + +### KeepWhatsRaisedDeadlineNotAfterLaunch +Reverts when campaign deadline is not after launch time. + + +```solidity +error KeepWhatsRaisedDeadlineNotAfterLaunch(); +``` + +### KeepWhatsRaisedZeroRewardName +Reverts when reward name is zero bytes. + + +```solidity +error KeepWhatsRaisedZeroRewardName(); +``` + +### KeepWhatsRaisedZeroRewardValue +Reverts when reward value is zero. + + +```solidity +error KeepWhatsRaisedZeroRewardValue(); +``` + +### KeepWhatsRaisedRewardItemArrayLengthMismatch +Reverts when reward item arrays have mismatched lengths. + + +```solidity +error KeepWhatsRaisedRewardItemArrayLengthMismatch(); +``` + +### KeepWhatsRaisedZeroBacker +Reverts when backer address is zero. + + +```solidity +error KeepWhatsRaisedZeroBacker(); +``` + +### KeepWhatsRaisedRewardSelectionLengthMismatch +Reverts when reward selection length exceeds number of rewards. + + +```solidity +error KeepWhatsRaisedRewardSelectionLengthMismatch(); +``` + +### KeepWhatsRaisedFirstRewardNotTier +Reverts when first reward is not a reward tier. + + +```solidity +error KeepWhatsRaisedFirstRewardNotTier(); +``` + +### KeepWhatsRaisedRefundAmountZero +Reverts when refund amount is zero. + + +```solidity +error KeepWhatsRaisedRefundAmountZero(); +``` + +### KeepWhatsRaisedInsufficientAvailableForRefund +Reverts when insufficient available balance for refund. + + +```solidity +error KeepWhatsRaisedInsufficientAvailableForRefund(uint256 tokenId); +``` + +### KeepWhatsRaisedClaimFundWindowNotReached +Reverts when claimFund is called before refund delay (cancelled) or withdrawal delay (not cancelled). + + +```solidity +error KeepWhatsRaisedClaimFundWindowNotReached(); ``` ### KeepWhatsRaisedTokenNotAccepted @@ -1176,12 +1437,20 @@ Emitted when funds or rewards have already been claimed for the given context. error KeepWhatsRaisedAlreadyClaimed(); ``` +### KeepWhatsRaisedFundAlreadyClaimed +Emitted when an operation is attempted after the platform admin has already claimed the treasury funds. + + +```solidity +error KeepWhatsRaisedFundAlreadyClaimed(); +``` + ### KeepWhatsRaisedNotClaimable Emitted when a token or pledge is not eligible for claiming (e.g., claim period not reached or not valid). ```solidity -error KeepWhatsRaisedNotClaimable(uint256 tokenId); +error KeepWhatsRaisedNotClaimable(uint256 tokenId, TreasuryErrors.NotClaimable code); ``` **Parameters** @@ -1189,6 +1458,7 @@ error KeepWhatsRaisedNotClaimable(uint256 tokenId); |Name|Type|Description| |----|----|-----------| |`tokenId`|`uint256`|The ID of the token that was attempted to be claimed.| +|`code`|`TreasuryErrors.NotClaimable`|Error code defined in {TreasuryErrors.NotClaimable}.| ### KeepWhatsRaisedNotClaimableAdmin Emitted when an admin attempts to claim funds that are not yet claimable according to the rules. @@ -1206,6 +1476,30 @@ Emitted when a configuration change is attempted during the lock period. error KeepWhatsRaisedConfigLocked(); ``` +### KeepWhatsRaisedAlreadyConfigured +Thrown when configureTreasury is called after the treasury has already been configured. + + +```solidity +error KeepWhatsRaisedAlreadyConfigured(); +``` + +### KeepWhatsRaisedWithdrawalBeforeRefundEnd +Reverts when withdrawalDelay is less than refundDelay, which would allow claimFund +to be callable before the refund window ends (refund window: (deadline, deadline + refundDelay]). + + +```solidity +error KeepWhatsRaisedWithdrawalBeforeRefundEnd(uint256 withdrawalDelay, uint256 refundDelay); +``` + +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`withdrawalDelay`|`uint256`|The configured withdrawal delay.| +|`refundDelay`|`uint256`|The configured refund delay.| + ### KeepWhatsRaisedDisbursementBlocked Emitted when a disbursement is attempted before the refund period has ended. @@ -1270,7 +1564,9 @@ System configuration parameters related to withdrawal and refund behavior. struct Config { /// @dev The minimum withdrawal amount required to qualify for fee exemption. uint256 minimumWithdrawalForFeeExemption; - /// @dev Time delay (in timestamp) enforced before a withdrawal can be completed. + /// @dev Time delay (in timestamp) after the campaign deadline until which the campaign owner may withdraw. + /// Withdrawal is allowed only while current time is less than deadline + withdrawalDelay. + /// After deadline + withdrawalDelay, the withdrawal function is no longer callable. uint256 withdrawalDelay; /// @dev Time delay (in timestamp) before a refund becomes claimable or processed. uint256 refundDelay; diff --git a/docs/src/src/treasuries/PaymentTreasury.sol/contract.PaymentTreasury.md b/docs/src/src/treasuries/PaymentTreasury.sol/contract.PaymentTreasury.md index da91f77..70a3e29 100644 --- a/docs/src/src/treasuries/PaymentTreasury.sol/contract.PaymentTreasury.md +++ b/docs/src/src/treasuries/PaymentTreasury.sol/contract.PaymentTreasury.md @@ -1,8 +1,8 @@ # PaymentTreasury -[Git Source](https://github.com/oak-network/contracts/blob/0ce055a8ba31ca09404e9d09ecd2549534cbec61/src/treasuries/PaymentTreasury.sol) +[Git Source](https://github.com/oak-network/contracts/blob/6c7f67f5ed14ef0f4f9444b95ac6770ae2af756a/src/treasuries/PaymentTreasury.sol) **Inherits:** -[BasePaymentTreasury](/Users/mahabubalahi/Documents/ccp/contracts/docs/src/src/utils/BasePaymentTreasury.sol/abstract.BasePaymentTreasury.md) +[BasePaymentTreasury](/src/utils/BasePaymentTreasury.sol/abstract.BasePaymentTreasury.md) ## Functions @@ -19,13 +19,11 @@ constructor() ; ```solidity -function initialize(bytes32 _platformHash, address _infoAddress, address _trustedForwarder) external initializer; +function initialize(bytes32 _platformHash, address _infoAddress) external initializer; ``` ### createPayment -Creates a new payment entry with the specified details. - ```solidity function createPayment( @@ -39,24 +37,9 @@ function createPayment( ICampaignPaymentTreasury.ExternalFees[] calldata externalFees ) public override whenNotPaused whenNotCancelled; ``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`paymentId`|`bytes32`|A unique identifier for the payment.| -|`buyerId`|`bytes32`|The id of the buyer initiating the payment.| -|`itemId`|`bytes32`|The identifier of the item being purchased.| -|`paymentToken`|`address`|The token to use for the payment.| -|`amount`|`uint256`|The amount to be paid for the item.| -|`expiration`|`uint256`|The timestamp after which the payment expires.| -|`lineItems`|`ICampaignPaymentTreasury.LineItem[]`|Array of line items associated with this payment.| -|`externalFees`|`ICampaignPaymentTreasury.ExternalFees[]`|Array of external fee metadata captured for this payment (informational only).| - ### createPaymentBatch -Creates multiple payment entries in a single transaction to prevent nonce conflicts. - ```solidity function createPaymentBatch( @@ -70,26 +53,9 @@ function createPaymentBatch( ICampaignPaymentTreasury.ExternalFees[][] calldata externalFeesArray ) public override whenNotPaused whenNotCancelled; ``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`paymentIds`|`bytes32[]`|An array of unique identifiers for the payments.| -|`buyerIds`|`bytes32[]`|An array of buyer IDs corresponding to each payment.| -|`itemIds`|`bytes32[]`|An array of item identifiers corresponding to each payment.| -|`paymentTokens`|`address[]`|An array of tokens corresponding to each payment.| -|`amounts`|`uint256[]`|An array of amounts corresponding to each payment.| -|`expirations`|`uint256[]`|An array of expiration timestamps corresponding to each payment.| -|`lineItemsArray`|`ICampaignPaymentTreasury.LineItem[][]`|An array of line item arrays, one for each payment.| -|`externalFeesArray`|`ICampaignPaymentTreasury.ExternalFees[][]`|An array of external fee metadata arrays, one for each payment (informational only).| - ### processCryptoPayment -Allows a buyer to make a direct crypto payment for an item. - -This function transfers tokens directly from the buyer's wallet and confirms the payment immediately. - ```solidity function processCryptoPayment( @@ -99,21 +65,10 @@ function processCryptoPayment( address paymentToken, uint256 amount, ICampaignPaymentTreasury.LineItem[] calldata lineItems, - ICampaignPaymentTreasury.ExternalFees[] calldata externalFees + ICampaignPaymentTreasury.ExternalFees[] calldata externalFees, + PermitData calldata permitData ) public override whenNotPaused whenNotCancelled; ``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`paymentId`|`bytes32`|The unique identifier of the payment.| -|`itemId`|`bytes32`|The identifier of the item being purchased.| -|`buyerAddress`|`address`|The address of the buyer making the payment.| -|`paymentToken`|`address`|The token to use for the payment.| -|`amount`|`uint256`|The amount to be paid for the item.| -|`lineItems`|`ICampaignPaymentTreasury.LineItem[]`|Array of line items associated with this payment.| -|`externalFees`|`ICampaignPaymentTreasury.ExternalFees[]`|Array of external fee metadata captured for this payment (informational only).| - ### cancelPayment @@ -174,7 +129,7 @@ Only callable by platform admin. Used for payments confirmed without a buyer add ```solidity -function claimRefund(bytes32 paymentId, address refundAddress) public override whenNotPaused whenNotCancelled; +function claimRefund(bytes32 paymentId, address refundAddress) public override whenNotPaused; ``` **Parameters** @@ -186,19 +141,20 @@ function claimRefund(bytes32 paymentId, address refundAddress) public override w ### claimRefund -Claims a refund for non-NFT payments (payments without minted NFTs). +Claims a refund for NFT payments (payments with minted NFTs). -Only callable by platform admin. Used for payments confirmed without a buyer address. +Burns the NFT associated with the payment. Caller must have approved the treasury for the NFT. +Used for processCryptoPayment and confirmPayment (with buyer address) transactions. ```solidity -function claimRefund(bytes32 paymentId) public override whenNotPaused whenNotCancelled; +function claimRefund(bytes32 paymentId) public override whenNotPaused; ``` **Parameters** |Name|Type|Description| |----|----|-----------| -|`paymentId`|`bytes32`|The unique identifier of the refundable payment (must NOT have an NFT).| +|`paymentId`|`bytes32`|The unique identifier of the refundable payment (must have an NFT).| ### claimExpiredFunds diff --git a/docs/src/src/treasuries/TimeConstrainedPaymentTreasury.sol/contract.TimeConstrainedPaymentTreasury.md b/docs/src/src/treasuries/TimeConstrainedPaymentTreasury.sol/contract.TimeConstrainedPaymentTreasury.md index ce4fb89..6664f85 100644 --- a/docs/src/src/treasuries/TimeConstrainedPaymentTreasury.sol/contract.TimeConstrainedPaymentTreasury.md +++ b/docs/src/src/treasuries/TimeConstrainedPaymentTreasury.sol/contract.TimeConstrainedPaymentTreasury.md @@ -1,8 +1,8 @@ # TimeConstrainedPaymentTreasury -[Git Source](https://github.com/oak-network/contracts/blob/0ce055a8ba31ca09404e9d09ecd2549534cbec61/src/treasuries/TimeConstrainedPaymentTreasury.sol) +[Git Source](https://github.com/oak-network/contracts/blob/6c7f67f5ed14ef0f4f9444b95ac6770ae2af756a/src/treasuries/TimeConstrainedPaymentTreasury.sol) **Inherits:** -[BasePaymentTreasury](/Users/mahabubalahi/Documents/ccp/contracts/docs/src/src/utils/BasePaymentTreasury.sol/abstract.BasePaymentTreasury.md), [TimestampChecker](/Users/mahabubalahi/Documents/ccp/contracts/docs/src/src/utils/TimestampChecker.sol/abstract.TimestampChecker.md) +[BasePaymentTreasury](/src/utils/BasePaymentTreasury.sol/abstract.BasePaymentTreasury.md), [TimestampChecker](/src/utils/TimestampChecker.sol/abstract.TimestampChecker.md) ## Functions @@ -19,31 +19,29 @@ constructor() ; ```solidity -function initialize(bytes32 _platformHash, address _infoAddress, address _trustedForwarder) external initializer; +function initialize(bytes32 _platformHash, address _infoAddress) external initializer; ``` -### _checkTimeWithinRange +### _checkTimeWithinCampaignWindow -Internal function to check if current time is within the allowed range. +Internal function to check if current time is within the campaign window (launchTime to deadline + bufferTime). ```solidity -function _checkTimeWithinRange() internal view; +function _checkTimeWithinCampaignWindow() internal view; ``` -### _checkTimeIsGreater +### _checkTimeIsAfterLaunch -Internal function to check if current time is greater than launch time. +Internal function to check if current time is after launch time. ```solidity -function _checkTimeIsGreater() internal view; +function _checkTimeIsAfterLaunch() internal view; ``` ### createPayment -Creates a new payment entry with the specified details. - ```solidity function createPayment( @@ -57,24 +55,9 @@ function createPayment( ICampaignPaymentTreasury.ExternalFees[] calldata externalFees ) public override whenNotPaused whenNotCancelled; ``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`paymentId`|`bytes32`|A unique identifier for the payment.| -|`buyerId`|`bytes32`|The id of the buyer initiating the payment.| -|`itemId`|`bytes32`|The identifier of the item being purchased.| -|`paymentToken`|`address`|The token to use for the payment.| -|`amount`|`uint256`|The amount to be paid for the item.| -|`expiration`|`uint256`|The timestamp after which the payment expires.| -|`lineItems`|`ICampaignPaymentTreasury.LineItem[]`|Array of line items associated with this payment.| -|`externalFees`|`ICampaignPaymentTreasury.ExternalFees[]`|Array of external fee metadata captured for this payment (informational only).| - ### createPaymentBatch -Creates multiple payment entries in a single transaction to prevent nonce conflicts. - ```solidity function createPaymentBatch( @@ -88,26 +71,9 @@ function createPaymentBatch( ICampaignPaymentTreasury.ExternalFees[][] calldata externalFeesArray ) public override whenNotPaused whenNotCancelled; ``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`paymentIds`|`bytes32[]`|An array of unique identifiers for the payments.| -|`buyerIds`|`bytes32[]`|An array of buyer IDs corresponding to each payment.| -|`itemIds`|`bytes32[]`|An array of item identifiers corresponding to each payment.| -|`paymentTokens`|`address[]`|An array of tokens corresponding to each payment.| -|`amounts`|`uint256[]`|An array of amounts corresponding to each payment.| -|`expirations`|`uint256[]`|An array of expiration timestamps corresponding to each payment.| -|`lineItemsArray`|`ICampaignPaymentTreasury.LineItem[][]`|An array of line item arrays, one for each payment.| -|`externalFeesArray`|`ICampaignPaymentTreasury.ExternalFees[][]`|An array of external fee metadata arrays, one for each payment (informational only).| - ### processCryptoPayment -Allows a buyer to make a direct crypto payment for an item. - -This function transfers tokens directly from the buyer's wallet and confirms the payment immediately. - ```solidity function processCryptoPayment( @@ -117,21 +83,10 @@ function processCryptoPayment( address paymentToken, uint256 amount, ICampaignPaymentTreasury.LineItem[] calldata lineItems, - ICampaignPaymentTreasury.ExternalFees[] calldata externalFees + ICampaignPaymentTreasury.ExternalFees[] calldata externalFees, + PermitData calldata permitData ) public override whenNotPaused whenNotCancelled; ``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`paymentId`|`bytes32`|The unique identifier of the payment.| -|`itemId`|`bytes32`|The identifier of the item being purchased.| -|`buyerAddress`|`address`|The address of the buyer making the payment.| -|`paymentToken`|`address`|The token to use for the payment.| -|`amount`|`uint256`|The amount to be paid for the item.| -|`lineItems`|`ICampaignPaymentTreasury.LineItem[]`|Array of line items associated with this payment.| -|`externalFees`|`ICampaignPaymentTreasury.ExternalFees[]`|Array of external fee metadata captured for this payment (informational only).| - ### cancelPayment @@ -192,7 +147,7 @@ Only callable by platform admin. Used for payments confirmed without a buyer add ```solidity -function claimRefund(bytes32 paymentId, address refundAddress) public override whenNotPaused whenNotCancelled; +function claimRefund(bytes32 paymentId, address refundAddress) public override whenNotPaused; ``` **Parameters** @@ -204,19 +159,20 @@ function claimRefund(bytes32 paymentId, address refundAddress) public override w ### claimRefund -Claims a refund for non-NFT payments (payments without minted NFTs). +Claims a refund for NFT payments (payments with minted NFTs). -Only callable by platform admin. Used for payments confirmed without a buyer address. +Burns the NFT associated with the payment. Caller must have approved the treasury for the NFT. +Used for processCryptoPayment and confirmPayment (with buyer address) transactions. ```solidity -function claimRefund(bytes32 paymentId) public override whenNotPaused whenNotCancelled; +function claimRefund(bytes32 paymentId) public override whenNotPaused; ``` **Parameters** |Name|Type|Description| |----|----|-----------| -|`paymentId`|`bytes32`|The unique identifier of the refundable payment (must NOT have an NFT).| +|`paymentId`|`bytes32`|The unique identifier of the refundable payment (must have an NFT).| ### claimExpiredFunds diff --git a/docs/src/src/utils/AdminAccessChecker.sol/abstract.AdminAccessChecker.md b/docs/src/src/utils/AdminAccessChecker.sol/abstract.AdminAccessChecker.md index 886f15d..1ba0c22 100644 --- a/docs/src/src/utils/AdminAccessChecker.sol/abstract.AdminAccessChecker.md +++ b/docs/src/src/utils/AdminAccessChecker.sol/abstract.AdminAccessChecker.md @@ -1,9 +1,12 @@ # AdminAccessChecker -[Git Source](https://github.com/oak-network/contracts/blob/0ce055a8ba31ca09404e9d09ecd2549534cbec61/src/utils/AdminAccessChecker.sol) +[Git Source](https://github.com/oak-network/contracts/blob/6c7f67f5ed14ef0f4f9444b95ac6770ae2af756a/src/utils/AdminAccessChecker.sol) **Inherits:** Context +**Title:** +AdminAccessChecker + This abstract contract provides access control mechanisms to restrict the execution of specific functions to authorized protocol administrators and platform administrators. diff --git a/docs/src/src/utils/BasePaymentTreasury.sol/abstract.BasePaymentTreasury.md b/docs/src/src/utils/BasePaymentTreasury.sol/abstract.BasePaymentTreasury.md index 538cb34..088661f 100644 --- a/docs/src/src/utils/BasePaymentTreasury.sol/abstract.BasePaymentTreasury.md +++ b/docs/src/src/utils/BasePaymentTreasury.sol/abstract.BasePaymentTreasury.md @@ -1,15 +1,18 @@ # BasePaymentTreasury -[Git Source](https://github.com/oak-network/contracts/blob/0ce055a8ba31ca09404e9d09ecd2549534cbec61/src/utils/BasePaymentTreasury.sol) +[Git Source](https://github.com/oak-network/contracts/blob/6c7f67f5ed14ef0f4f9444b95ac6770ae2af756a/src/utils/BasePaymentTreasury.sol) **Inherits:** -Initializable, [ICampaignPaymentTreasury](/Users/mahabubalahi/Documents/ccp/contracts/docs/src/src/interfaces/ICampaignPaymentTreasury.sol/interface.ICampaignPaymentTreasury.md), [CampaignAccessChecker](/Users/mahabubalahi/Documents/ccp/contracts/docs/src/src/utils/CampaignAccessChecker.sol/abstract.CampaignAccessChecker.md), [PausableCancellable](/Users/mahabubalahi/Documents/ccp/contracts/docs/src/src/utils/PausableCancellable.sol/abstract.PausableCancellable.md), ReentrancyGuard +Initializable, [ICampaignPaymentTreasury](/src/interfaces/ICampaignPaymentTreasury.sol/interface.ICampaignPaymentTreasury.md), [CampaignAccessChecker](/src/utils/CampaignAccessChecker.sol/abstract.CampaignAccessChecker.md), [PausableCancellable](/src/utils/PausableCancellable.sol/abstract.PausableCancellable.md), ReentrancyGuard + +**Title:** +BasePaymentTreasury Base contract for payment treasury implementations. Supports ERC-2771 meta-transactions via adapter contracts for platform admin operations. -## State Variables +## Constants ### ZERO_BYTES ```solidity @@ -38,6 +41,51 @@ address internal constant ZERO_ADDRESS = address(0) ``` +### CRYPTO_PAYMENT_WITNESS_TYPEHASH + +```solidity +bytes32 internal constant CRYPTO_PAYMENT_WITNESS_TYPEHASH = keccak256( + "CryptoPaymentWitness(bytes32 paymentId,bytes32 itemId,address buyerAddress,uint256 amount,bytes32 lineItemsHash)" +) +``` + + +### CRYPTO_PAYMENT_WITNESS_TYPE_STRING + +```solidity +string internal constant CRYPTO_PAYMENT_WITNESS_TYPE_STRING = + "CryptoPaymentWitness witness)CryptoPaymentWitness(bytes32 paymentId,bytes32 itemId,address buyerAddress,uint256 amount,bytes32 lineItemsHash)TokenPermissions(address token,uint256 amount)" +``` + + +### MAX_LINE_ITEMS +Maximum number of line items per payment. Ensures confirmPayment can always succeed if createPayment did. + + +```solidity +uint256 internal constant MAX_LINE_ITEMS = 50 +``` + + +### MAX_EXTERNAL_FEES +Maximum number of external fee entries per payment. + + +```solidity +uint256 internal constant MAX_EXTERNAL_FEES = 50 +``` + + +### MAX_BATCH_SIZE +Maximum number of payments in a single batch call. + + +```solidity +uint256 internal constant MAX_BATCH_SIZE = 50 +``` + + +## State Variables ### PLATFORM_HASH ```solidity @@ -46,6 +94,14 @@ bytes32 internal PLATFORM_HASH ### PLATFORM_FEE_PERCENT +Snapshot of the platform fee percent captured at treasury initialization via +INFO.getPlatformFeePercent(platformHash). This value is fixed for the lifetime of the +treasury and will not reflect any subsequent changes to the platform fee in GlobalParams. +The protocol fee accessed during fee calculations via INFO.getProtocolFeePercent() is also +a snapshot — it is stored in the campaign's CampaignInfo clone at creation time and is +likewise immutable for the campaign's lifecycle. Despite the asymmetry in how they are +accessed (cached field vs. getter call), both fees are effectively campaign-level snapshots. + ```solidity uint256 internal PLATFORM_FEE_PERCENT @@ -73,10 +129,10 @@ mapping(address => uint256) internal s_protocolFeePerToken ``` -### s_paymentIdToTokenId +### s_paymentIdToNFTId ```solidity -mapping(bytes32 => uint256) internal s_paymentIdToTokenId +mapping(bytes32 => uint256) internal s_paymentIdToNFTId ``` @@ -157,21 +213,28 @@ mapping(address => uint256) internal s_nonGoalLineItemClaimablePerToken ``` -### s_refundableNonGoalLineItemPerToken +### s_nonGoalRefundableLineItemPerToken ```solidity -mapping(address => uint256) internal s_refundableNonGoalLineItemPerToken +mapping(address => uint256) internal s_nonGoalRefundableLineItemPerToken ``` ## Functions -### _scopePaymentIdForOffChain +### constructor + + +```solidity +constructor() ; +``` + +### _getInternalPaymentIdForOffChain Scopes a payment ID for off-chain payments (createPayment/createPaymentBatch). ```solidity -function _scopePaymentIdForOffChain(bytes32 paymentId) internal pure returns (bytes32); +function _getInternalPaymentIdForOffChain(bytes32 paymentId) internal pure returns (bytes32); ``` **Parameters** @@ -190,15 +253,19 @@ function _scopePaymentIdForOffChain(bytes32 paymentId) internal pure returns (by Scopes a payment ID for on-chain crypto payments (processCryptoPayment). +Scoped by the buyer address (the Permit2 signer) rather than the tx sender, +so the payment can be looked up by anyone using the stored creator address. + ```solidity -function _scopePaymentIdForOnChain(bytes32 paymentId) internal view returns (bytes32); +function _scopePaymentIdForOnChain(bytes32 paymentId, address owner) internal pure returns (bytes32); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`paymentId`|`bytes32`|The external payment ID.| +|`owner`|`address`| The buyer/signer address.| **Returns** @@ -248,15 +315,26 @@ function _getMaxExpirationDuration() internal view returns (bool hasLimit, uint2 ### __BaseContract_init +Initializes the base payment treasury with platform and campaign context. + ```solidity -function __BaseContract_init(bytes32 platformHash, address infoAddress, address trustedForwarder_) internal; +function __BaseContract_init(bytes32 platformHash, address infoAddress) internal; ``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`platformHash`|`bytes32`|The platform identifier used for fee lookup and access control.| +|`infoAddress`|`address`|The CampaignInfo contract address for campaign data and admin lookups.| + ### _msgSender Override _msgSender to support ERC-2771 meta-transactions. When called by the trusted forwarder (adapter), extracts the actual sender from calldata. +The adapter address is read dynamically from GlobalParams via CampaignInfo so that +adapter rotations take effect immediately for all deployed treasuries. ```solidity @@ -327,13 +405,13 @@ Retrieves the total raised amount in the treasury. ```solidity -function getRaisedAmount() public view virtual override returns (uint256); +function getRaisedAmount() public view virtual override returns (uint256 amount); ``` **Returns** |Name|Type|Description| |----|----|-----------| -|``|`uint256`|The total raised amount as a uint256 value.| +|`amount`|`uint256`|Total confirmed payment amount across all tokens, normalized to 18 decimals.| ### getAvailableRaisedAmount @@ -342,13 +420,13 @@ Retrieves the currently available raised amount in the treasury. ```solidity -function getAvailableRaisedAmount() external view returns (uint256); +function getAvailableRaisedAmount() external view returns (uint256 amount); ``` **Returns** |Name|Type|Description| |----|----|-----------| -|``|`uint256`|The current available raised amount as a uint256 value.| +|`amount`|`uint256`|Available confirmed amount across all tokens, normalized to 18 decimals.| ### getLifetimeRaisedAmount @@ -357,13 +435,13 @@ Retrieves the lifetime raised amount in the treasury (never decreases with refun ```solidity -function getLifetimeRaisedAmount() external view returns (uint256); +function getLifetimeRaisedAmount() external view returns (uint256 amount); ``` **Returns** |Name|Type|Description| |----|----|-----------| -|``|`uint256`|The lifetime raised amount as a uint256 value.| +|`amount`|`uint256`|Lifetime total confirmed payments across all tokens, normalized to 18 decimals.| ### getRefundedAmount @@ -372,13 +450,13 @@ Retrieves the total refunded amount in the treasury. ```solidity -function getRefundedAmount() external view returns (uint256); +function getRefundedAmount() external view returns (uint256 amount); ``` **Returns** |Name|Type|Description| |----|----|-----------| -|``|`uint256`|The total refunded amount as a uint256 value.| +|`amount`|`uint256`|Total refunded amount across all tokens, normalized to 18 decimals.| ### getExpectedAmount @@ -389,13 +467,13 @@ This represents payments that have been created but not yet confirmed. ```solidity -function getExpectedAmount() external view returns (uint256); +function getExpectedAmount() external view returns (uint256 amount); ``` **Returns** |Name|Type|Description| |----|----|-----------| -|``|`uint256`|The total expected amount as a uint256 value.| +|`amount`|`uint256`|Total pending payment amount across all tokens, normalized to 18 decimals.| ### _normalizeAmount @@ -443,8 +521,6 @@ function _validateStoreAndTrackLineItems( ### createPayment -Creates a new payment entry with the specified details. - ```solidity function createPayment( @@ -458,24 +534,9 @@ function createPayment( ICampaignPaymentTreasury.ExternalFees[] calldata externalFees ) public virtual override onlyPlatformAdmin(PLATFORM_HASH) whenCampaignNotPaused whenCampaignNotCancelled; ``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`paymentId`|`bytes32`|A unique identifier for the payment.| -|`buyerId`|`bytes32`|The id of the buyer initiating the payment.| -|`itemId`|`bytes32`|The identifier of the item being purchased.| -|`paymentToken`|`address`|The token to use for the payment.| -|`amount`|`uint256`|The amount to be paid for the item.| -|`expiration`|`uint256`|The timestamp after which the payment expires.| -|`lineItems`|`ICampaignPaymentTreasury.LineItem[]`|Array of line items associated with this payment.| -|`externalFees`|`ICampaignPaymentTreasury.ExternalFees[]`|Array of external fee metadata captured for this payment (informational only).| - ### createPaymentBatch -Creates multiple payment entries in a single transaction to prevent nonce conflicts. - ```solidity function createPaymentBatch( @@ -489,25 +550,11 @@ function createPaymentBatch( ICampaignPaymentTreasury.ExternalFees[][] calldata externalFeesArray ) public virtual override onlyPlatformAdmin(PLATFORM_HASH) whenCampaignNotPaused whenCampaignNotCancelled; ``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`paymentIds`|`bytes32[]`|An array of unique identifiers for the payments.| -|`buyerIds`|`bytes32[]`|An array of buyer IDs corresponding to each payment.| -|`itemIds`|`bytes32[]`|An array of item identifiers corresponding to each payment.| -|`paymentTokens`|`address[]`|An array of tokens corresponding to each payment.| -|`amounts`|`uint256[]`|An array of amounts corresponding to each payment.| -|`expirations`|`uint256[]`|An array of expiration timestamps corresponding to each payment.| -|`lineItemsArray`|`ICampaignPaymentTreasury.LineItem[][]`|An array of line item arrays, one for each payment.| -|`externalFeesArray`|`ICampaignPaymentTreasury.ExternalFees[][]`|An array of external fee metadata arrays, one for each payment (informational only).| - ### processCryptoPayment -Allows a buyer to make a direct crypto payment for an item. - -This function transfers tokens directly from the buyer's wallet and confirms the payment immediately. +Mints a pledge NFT to `buyerAddress` via `_safeMint`. Reverts if `buyerAddress` is +a contract that does not implement `IERC721Receiver`. ```solidity @@ -518,21 +565,10 @@ function processCryptoPayment( address paymentToken, uint256 amount, ICampaignPaymentTreasury.LineItem[] calldata lineItems, - ICampaignPaymentTreasury.ExternalFees[] calldata externalFees + ICampaignPaymentTreasury.ExternalFees[] calldata externalFees, + PermitData calldata permitData ) public virtual override nonReentrant whenCampaignNotPaused whenCampaignNotCancelled; ``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`paymentId`|`bytes32`|The unique identifier of the payment.| -|`itemId`|`bytes32`|The identifier of the item being purchased.| -|`buyerAddress`|`address`|The address of the buyer making the payment.| -|`paymentToken`|`address`|The token to use for the payment.| -|`amount`|`uint256`|The amount to be paid for the item.| -|`lineItems`|`ICampaignPaymentTreasury.LineItem[]`|Array of line items associated with this payment.| -|`externalFees`|`ICampaignPaymentTreasury.ExternalFees[]`|Array of external fee metadata captured for this payment (informational only).| - ### cancelPayment @@ -582,7 +618,7 @@ function _calculateLineItemTotals( ### _checkBalanceForConfirmation -Checks if there's sufficient balance for payment confirmation. +Checks if the treasury's actual token balance is sufficient to cover the total amount of the payment being confirmed, plus all previously committed funds (available for withdrawal, fees, and refundable items). ```solidity @@ -630,6 +666,9 @@ function _updateLineItemsForConfirmation( Confirms and finalizes the payment associated with the given payment ID. +If `buyerAddress` is non-zero, mints a pledge NFT via `_safeMint`. Reverts if +`buyerAddress` is a contract that does not implement `IERC721Receiver`. + ```solidity function confirmPayment(bytes32 paymentId, address buyerAddress) @@ -653,6 +692,9 @@ function confirmPayment(bytes32 paymentId, address buyerAddress) Confirms and finalizes multiple payments in a single transaction. +For each non-zero `buyerAddress`, mints a pledge NFT via `_safeMint`. Reverts if +any such address is a contract that does not implement `IERC721Receiver`. + ```solidity function confirmPaymentBatch(bytes32[] calldata paymentIds, address[] calldata buyerAddresses) @@ -698,7 +740,7 @@ function claimRefund(bytes32 paymentId, address refundAddress) ### claimRefund -Claims a refund for non-NFT payments (payments without minted NFTs). +Claims a refund for NFT payments (payments with minted NFTs). For NFT payments only. Requires an NFT exists and burns it. Refund is sent to current NFT owner. @@ -710,7 +752,40 @@ function claimRefund(bytes32 paymentId) public virtual override whenCampaignNotP |Name|Type|Description| |----|----|-----------| -|`paymentId`|`bytes32`|The unique identifier of the refundable payment (must NOT have an NFT).| +|`paymentId`|`bytes32`|The unique identifier of the refundable payment (must have an NFT).| + + +### _executeRefund + +Shared refund logic for both claimRefund overloads. +Calculates refund amounts from line item snapshots, validates balances, +updates state, removes common storage entries, and returns the total refund amount. + + +```solidity +function _executeRefund( + bytes32 internalPaymentId, + address paymentToken, + uint256 amountToRefund, + uint256 availablePaymentAmount, + bytes32 revertId +) private returns (uint256 totalRefundAmount); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`internalPaymentId`|`bytes32`|The scoped internal payment ID.| +|`paymentToken`|`address`|The token used for the payment.| +|`amountToRefund`|`uint256`|The base payment amount to refund.| +|`availablePaymentAmount`|`uint256`|The available confirmed amount for this token.| +|`revertId`|`bytes32`|The payment ID to use in revert messages (preserves original error context).| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|`totalRefundAmount`|`uint256`|The total amount to transfer to the refund recipient.| ### disburseFees @@ -792,21 +867,6 @@ External function to cancel the campaign. function cancelTreasury(bytes32 message) public virtual onlyPlatformAdmin(PLATFORM_HASH); ``` -### cancelled - -Returns true if the treasury has been cancelled. - - -```solidity -function cancelled() public view virtual override(ICampaignPaymentTreasury, PausableCancellable) returns (bool); -``` -**Returns** - -|Name|Type|Description| -|----|----|-----------| -|``|`bool`|True if cancelled, false otherwise.| - - ### _revertIfCampaignPaused Internal function to check if the campaign is paused. @@ -835,13 +895,14 @@ Reverts if: ```solidity -function _validatePaymentForAction(bytes32 paymentId) internal view; +function _validatePaymentForAction(bytes32 internalPaymentId, bytes32 paymentId) internal view; ``` **Parameters** |Name|Type|Description| |----|----|-----------| -|`paymentId`|`bytes32`|The unique identifier of the payment to validate.| +|`internalPaymentId`|`bytes32`|The scoped internal payment ID used for storage lookup.| +|`paymentId`|`bytes32`|The external payment ID used in revert messages for caller clarity.| ### getPaymentData @@ -1062,7 +1123,77 @@ Reverts when one or more provided inputs to the payment treasury are invalid. ```solidity -error PaymentTreasuryInvalidInput(); +error PaymentTreasuryInvalidInput(TreasuryErrors.InvalidInput code); +``` + +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`code`|`TreasuryErrors.InvalidInput`|Error code defined in {TreasuryErrors.InvalidInput}.| + +### PaymentTreasuryZeroPaymentId +Reverts when paymentId is zero. + + +```solidity +error PaymentTreasuryZeroPaymentId(); +``` + +### PaymentTreasuryZeroBuyerId +Reverts when buyerId is zero. + + +```solidity +error PaymentTreasuryZeroBuyerId(); +``` + +### PaymentTreasuryZeroAmount +Reverts when amount is zero. + + +```solidity +error PaymentTreasuryZeroAmount(); +``` + +### PaymentTreasuryExpirationNotInFuture +Reverts when expiration is not in the future. + + +```solidity +error PaymentTreasuryExpirationNotInFuture(); +``` + +### PaymentTreasuryZeroItemId +Reverts when itemId is zero. + + +```solidity +error PaymentTreasuryZeroItemId(); +``` + +### PaymentTreasuryZeroPaymentToken +Reverts when paymentToken is the zero address. + + +```solidity +error PaymentTreasuryZeroPaymentToken(); +``` + +### PaymentTreasuryZeroBuyerAddress +Reverts when buyerAddress is the zero address. + + +```solidity +error PaymentTreasuryZeroBuyerAddress(); +``` + +### PaymentTreasuryBatchArrayLengthMismatch +Reverts when batch array lengths are inconsistent. + + +```solidity +error PaymentTreasuryBatchArrayLengthMismatch(); ``` ### PaymentTreasuryPaymentAlreadyExist @@ -1142,7 +1273,7 @@ Emitted when claiming an unclaimable refund. ```solidity -error PaymentTreasuryPaymentNotClaimable(bytes32 paymentId); +error PaymentTreasuryPaymentNotClaimable(bytes32 paymentId, TreasuryErrors.NotClaimable code); ``` **Parameters** @@ -1150,6 +1281,7 @@ error PaymentTreasuryPaymentNotClaimable(bytes32 paymentId); |Name|Type|Description| |----|----|-----------| |`paymentId`|`bytes32`|The unique identifier of the refundable payment.| +|`code`|`TreasuryErrors.NotClaimable`|Error code defined in {TreasuryErrors.NotClaimable}.| ### PaymentTreasuryAlreadyWithdrawn Emitted when an attempt is made to withdraw funds from the treasury but the payment has already been withdrawn. @@ -1233,6 +1365,22 @@ Throws when there are no funds available to claim. error PaymentTreasuryNoFundsToClaim(); ``` +### PaymentTreasuryInvalidSender +Throws when the forwarder appends address(0) as the sender. + + +```solidity +error PaymentTreasuryInvalidSender(); +``` + +### PaymentTreasuryArrayTooLong +Throws when an input array exceeds the maximum allowed length. + + +```solidity +error PaymentTreasuryArrayTooLong(); +``` + ## Structs ### PaymentInfo Stores information about a payment in the treasury. diff --git a/docs/src/src/utils/BaseTreasury.sol/abstract.BaseTreasury.md b/docs/src/src/utils/BaseTreasury.sol/abstract.BaseTreasury.md index a15e56b..6c9a8c8 100644 --- a/docs/src/src/utils/BaseTreasury.sol/abstract.BaseTreasury.md +++ b/docs/src/src/utils/BaseTreasury.sol/abstract.BaseTreasury.md @@ -1,8 +1,11 @@ # BaseTreasury -[Git Source](https://github.com/oak-network/contracts/blob/0ce055a8ba31ca09404e9d09ecd2549534cbec61/src/utils/BaseTreasury.sol) +[Git Source](https://github.com/oak-network/contracts/blob/6c7f67f5ed14ef0f4f9444b95ac6770ae2af756a/src/utils/BaseTreasury.sol) **Inherits:** -Initializable, [ICampaignTreasury](/Users/mahabubalahi/Documents/ccp/contracts/docs/src/src/interfaces/ICampaignTreasury.sol/interface.ICampaignTreasury.md), [CampaignAccessChecker](/Users/mahabubalahi/Documents/ccp/contracts/docs/src/src/utils/CampaignAccessChecker.sol/abstract.CampaignAccessChecker.md), [PausableCancellable](/Users/mahabubalahi/Documents/ccp/contracts/docs/src/src/utils/PausableCancellable.sol/abstract.PausableCancellable.md) +Initializable, [ICampaignTreasury](/src/interfaces/ICampaignTreasury.sol/interface.ICampaignTreasury.md), [CampaignAccessChecker](/src/utils/CampaignAccessChecker.sol/abstract.CampaignAccessChecker.md), [PausableCancellable](/src/utils/PausableCancellable.sol/abstract.PausableCancellable.md), ReentrancyGuard + +**Title:** +BaseTreasury A base contract for creating and managing treasuries in crowdfunding campaigns. @@ -13,7 +16,7 @@ Supports ERC-2771 meta-transactions via adapter contracts for platform admin ope Contracts implementing this base contract should provide specific success conditions. -## State Variables +## Constants ### ZERO_BYTES ```solidity @@ -35,6 +38,7 @@ uint256 internal constant STANDARD_DECIMALS = 18 ``` +## State Variables ### PLATFORM_HASH ```solidity @@ -43,6 +47,14 @@ bytes32 internal PLATFORM_HASH ### PLATFORM_FEE_PERCENT +Snapshot of the platform fee percent captured at treasury initialization via +INFO.getPlatformFeePercent(platformHash). This value is fixed for the lifetime of the +treasury and will not reflect any subsequent changes to the platform fee in GlobalParams. +The protocol fee accessed during disburseFees() via INFO.getProtocolFeePercent() is also +a snapshot — it is stored in the campaign's CampaignInfo clone at creation time and is +likewise immutable for the campaign's lifecycle. Despite the asymmetry in how they are +accessed (cached field vs. getter call), both fees are effectively campaign-level snapshots. + ```solidity uint256 internal PLATFORM_FEE_PERCENT @@ -71,17 +83,35 @@ mapping(address => uint256) internal s_tokenLifetimeRaisedAmounts ## Functions +### constructor + + +```solidity +constructor() ; +``` + ### __BaseContract_init +Initializes the base treasury with platform and campaign context. + ```solidity -function __BaseContract_init(bytes32 platformHash, address infoAddress, address trustedForwarder_) internal; +function __BaseContract_init(bytes32 platformHash, address infoAddress) internal; ``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`platformHash`|`bytes32`|The platform identifier used for fee lookup and access control.| +|`infoAddress`|`address`|The CampaignInfo contract address for campaign data and admin lookups.| + ### _msgSender Override _msgSender to support ERC-2771 meta-transactions. When called by the trusted forwarder (adapter), extracts the actual sender from calldata. +The adapter address is read dynamically from GlobalParams via CampaignInfo so that +adapter rotations take effect immediately for all deployed treasuries. ```solidity @@ -184,7 +214,7 @@ Disburses fees collected by the treasury. ```solidity -function disburseFees() public virtual override whenCampaignNotPaused whenCampaignNotCancelled; +function disburseFees() public virtual override nonReentrant whenCampaignNotPaused whenCampaignNotCancelled; ``` ### withdraw @@ -223,21 +253,6 @@ External function to cancel the campaign. function cancelTreasury(bytes32 message) public virtual onlyPlatformAdmin(PLATFORM_HASH); ``` -### cancelled - -Returns true if the treasury has been cancelled. - - -```solidity -function cancelled() public view virtual override(ICampaignTreasury, PausableCancellable) returns (bool); -``` -**Returns** - -|Name|Type|Description| -|----|----|-----------| -|``|`bool`|True if cancelled, false otherwise.| - - ### _revertIfCampaignPaused Internal function to check if the campaign is paused. @@ -336,6 +351,14 @@ Throws an error indicating that fees have not been disbursed. error TreasuryFeeNotDisbursed(); ``` +### TreasuryFeeAlreadyDisbursed +Throws an error indicating that fees have already been disbursed. + + +```solidity +error TreasuryFeeAlreadyDisbursed(); +``` + ### TreasuryCampaignInfoIsPaused Throws an error indicating that the campaign is paused. @@ -344,3 +367,11 @@ Throws an error indicating that the campaign is paused. error TreasuryCampaignInfoIsPaused(); ``` +### TreasuryInvalidSender +Throws when the forwarder appends address(0) as the sender. + + +```solidity +error TreasuryInvalidSender(); +``` + diff --git a/docs/src/src/utils/CampaignAccessChecker.sol/abstract.CampaignAccessChecker.md b/docs/src/src/utils/CampaignAccessChecker.sol/abstract.CampaignAccessChecker.md index 2e8a7c7..1d47657 100644 --- a/docs/src/src/utils/CampaignAccessChecker.sol/abstract.CampaignAccessChecker.md +++ b/docs/src/src/utils/CampaignAccessChecker.sol/abstract.CampaignAccessChecker.md @@ -1,9 +1,12 @@ # CampaignAccessChecker -[Git Source](https://github.com/oak-network/contracts/blob/0ce055a8ba31ca09404e9d09ecd2549534cbec61/src/utils/CampaignAccessChecker.sol) +[Git Source](https://github.com/oak-network/contracts/blob/6c7f67f5ed14ef0f4f9444b95ac6770ae2af756a/src/utils/CampaignAccessChecker.sol) **Inherits:** Context +**Title:** +CampaignAccessChecker + This abstract contract provides access control mechanisms to restrict the execution of specific functions to authorized protocol administrators, platform administrators, and campaign owners. @@ -16,15 +19,6 @@ ICampaignInfo internal INFO ``` -### _trustedForwarder -Trusted forwarder address for ERC-2771 meta-transactions (set by derived contracts) - - -```solidity -address internal _trustedForwarder -``` - - ## Functions ### __CampaignAccessChecker_init diff --git a/docs/src/src/utils/Counters.sol/library.Counters.md b/docs/src/src/utils/Counters.sol/library.Counters.md index 8740bae..44f729d 100644 --- a/docs/src/src/utils/Counters.sol/library.Counters.md +++ b/docs/src/src/utils/Counters.sol/library.Counters.md @@ -1,5 +1,5 @@ # Counters -[Git Source](https://github.com/oak-network/contracts/blob/0ce055a8ba31ca09404e9d09ecd2549534cbec61/src/utils/Counters.sol) +[Git Source](https://github.com/oak-network/contracts/blob/6c7f67f5ed14ef0f4f9444b95ac6770ae2af756a/src/utils/Counters.sol) ## Functions diff --git a/docs/src/src/utils/FiatEnabled.sol/abstract.FiatEnabled.md b/docs/src/src/utils/FiatEnabled.sol/abstract.FiatEnabled.md index 087d194..4f41525 100644 --- a/docs/src/src/utils/FiatEnabled.sol/abstract.FiatEnabled.md +++ b/docs/src/src/utils/FiatEnabled.sol/abstract.FiatEnabled.md @@ -1,5 +1,8 @@ # FiatEnabled -[Git Source](https://github.com/oak-network/contracts/blob/0ce055a8ba31ca09404e9d09ecd2549534cbec61/src/utils/FiatEnabled.sol) +[Git Source](https://github.com/oak-network/contracts/blob/6c7f67f5ed14ef0f4f9444b95ac6770ae2af756a/src/utils/FiatEnabled.sol) + +**Title:** +FiatEnabled A contract that provides functionality for tracking and managing fiat transactions. This contract allows tracking the amount of fiat raised, individual fiat transactions, and the state of fiat fee disbursement. @@ -170,3 +173,17 @@ Throws an error indicating that the fiat transaction is invalid. error FiatEnabledInvalidTransaction(); ``` +### FiatEnabledTransactionAlreadyRecorded +Throws when a fiat transaction ID has already been recorded. + + +```solidity +error FiatEnabledTransactionAlreadyRecorded(bytes32 fiatTransactionId); +``` + +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`fiatTransactionId`|`bytes32`|The duplicate fiat transaction identifier.| + diff --git a/docs/src/src/utils/ItemRegistry.sol/contract.ItemRegistry.md b/docs/src/src/utils/ItemRegistry.sol/contract.ItemRegistry.md index 8f23dd3..691d3cc 100644 --- a/docs/src/src/utils/ItemRegistry.sol/contract.ItemRegistry.md +++ b/docs/src/src/utils/ItemRegistry.sol/contract.ItemRegistry.md @@ -1,8 +1,11 @@ # ItemRegistry -[Git Source](https://github.com/oak-network/contracts/blob/0ce055a8ba31ca09404e9d09ecd2549534cbec61/src/utils/ItemRegistry.sol) +[Git Source](https://github.com/oak-network/contracts/blob/6c7f67f5ed14ef0f4f9444b95ac6770ae2af756a/src/utils/ItemRegistry.sol) **Inherits:** -[IItem](/Users/mahabubalahi/Documents/ccp/contracts/docs/src/src/interfaces/IItem.sol/interface.IItem.md), Context +[IItem](/src/interfaces/IItem.sol/interface.IItem.md), Context + +**Title:** +ItemRegistry A contract that manages the registration and retrieval of items. @@ -15,6 +18,13 @@ mapping(address => mapping(bytes32 => Item)) private Items ``` +### s_itemExists + +```solidity +mapping(address => mapping(bytes32 => bool)) private s_itemExists +``` + + ## Functions ### getItem @@ -70,6 +80,21 @@ function addItemsBatch(bytes32[] calldata itemIds, Item[] calldata items) extern |`items`|`Item[]`|An array of `Item` structs containing item attributes.| +### removeItem + +Removes an item from the caller's registry. + + +```solidity +function removeItem(bytes32 itemId) external; +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`itemId`|`bytes32`|The unique identifier of the item to remove.| + + ## Events ### ItemAdded Emitted when a new item is added to the registry. @@ -87,6 +112,21 @@ event ItemAdded(address indexed owner, bytes32 indexed itemId, Item item); |`itemId`|`bytes32`|The unique identifier of the item.| |`item`|`Item`|The item details including actual weight, dimensions, category, and declared currency.| +### ItemRemoved +Emitted when an item is removed from the registry. + + +```solidity +event ItemRemoved(address indexed owner, bytes32 indexed itemId); +``` + +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`owner`|`address`|The address of the item owner.| +|`itemId`|`bytes32`|The unique identifier of the item.| + ## Errors ### ItemRegistryMismatchedArraysLength Thrown when the input arrays have mismatched lengths. @@ -96,3 +136,45 @@ Thrown when the input arrays have mismatched lengths. error ItemRegistryMismatchedArraysLength(); ``` +### ItemRegistryItemAlreadyExists +Thrown when attempting to add an item that already exists (overwrite not allowed). + + +```solidity +error ItemRegistryItemAlreadyExists(bytes32 itemId); +``` + +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`itemId`|`bytes32`|The item identifier that already exists.| + +### ItemRegistryDuplicateItemId +Thrown when the batch contains duplicate itemIds. + + +```solidity +error ItemRegistryDuplicateItemId(bytes32 itemId); +``` + +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`itemId`|`bytes32`|The duplicate item identifier.| + +### ItemRegistryItemDoesNotExist +Thrown when attempting to remove an item that does not exist. + + +```solidity +error ItemRegistryItemDoesNotExist(bytes32 itemId); +``` + +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`itemId`|`bytes32`|The item identifier.| + diff --git a/docs/src/src/utils/PausableCancellable.sol/abstract.PausableCancellable.md b/docs/src/src/utils/PausableCancellable.sol/abstract.PausableCancellable.md index f1f3cf8..6787aff 100644 --- a/docs/src/src/utils/PausableCancellable.sol/abstract.PausableCancellable.md +++ b/docs/src/src/utils/PausableCancellable.sol/abstract.PausableCancellable.md @@ -1,9 +1,12 @@ # PausableCancellable -[Git Source](https://github.com/oak-network/contracts/blob/0ce055a8ba31ca09404e9d09ecd2549534cbec61/src/utils/PausableCancellable.sol) +[Git Source](https://github.com/oak-network/contracts/blob/6c7f67f5ed14ef0f4f9444b95ac6770ae2af756a/src/utils/PausableCancellable.sol) **Inherits:** Context +**Title:** +PausableCancellable + Abstract contract providing pause and cancel state management with events and modifiers @@ -22,6 +25,13 @@ bool private _cancelled ``` +### _cancellationTime + +```solidity +uint256 private _cancellationTime +``` + + ## Functions ### whenNotPaused @@ -128,6 +138,15 @@ function _cancel(bytes32 reason) internal virtual; |`reason`|`bytes32`|A short reason for cancellation| +### cancellationTime + +Returns the timestamp at which the contract was cancelled, or 0 if not cancelled + + +```solidity +function cancellationTime() public view virtual returns (uint256); +``` + ## Events ### Paused Emitted when contract is paused diff --git a/docs/src/src/utils/PledgeNFT.sol/abstract.PledgeNFT.md b/docs/src/src/utils/PledgeNFT.sol/abstract.PledgeNFT.md index c91345d..7300808 100644 --- a/docs/src/src/utils/PledgeNFT.sol/abstract.PledgeNFT.md +++ b/docs/src/src/utils/PledgeNFT.sol/abstract.PledgeNFT.md @@ -1,22 +1,28 @@ # PledgeNFT -[Git Source](https://github.com/oak-network/contracts/blob/0ce055a8ba31ca09404e9d09ecd2549534cbec61/src/utils/PledgeNFT.sol) +[Git Source](https://github.com/oak-network/contracts/blob/6c7f67f5ed14ef0f4f9444b95ac6770ae2af756a/src/utils/PledgeNFT.sol) **Inherits:** ERC721Burnable, AccessControl +**Title:** +PledgeNFT + Abstract contract for NFTs representing pledges with on-chain metadata Contains counter logic and NFT metadata storage -## State Variables -### MINTER_ROLE +## Constants +### TREASURY_ROLE +keccak256(bytes("TREASURY_ROLE")) + ```solidity -bytes32 public constant MINTER_ROLE = 0x9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6 +bytes32 public constant TREASURY_ROLE = 0xe1dcbdb91df27212a29bc27177c840cf2f819ecf2187432e1fac86c2dd5dfca9 ``` +## State Variables ### s_nftName ```solidity @@ -106,7 +112,9 @@ function _validateJsonString(string calldata str) internal pure; Mints a pledge NFT (auto-increments counter) -Called by treasuries - returns the new token ID to use as pledge ID +Called by treasuries - returns the new token ID to use as pledge ID. +Uses `_safeMint`, so `backer` must be an EOA or a contract that implements +`IERC721Receiver`; otherwise the transaction will revert. ```solidity @@ -117,7 +125,7 @@ function mintNFTForPledge( uint256 amount, uint256 shippingFee, uint256 tipAmount -) public virtual onlyRole(MINTER_ROLE) returns (uint256 tokenId); +) public virtual onlyRole(TREASURY_ROLE) returns (uint256 tokenId); ``` **Parameters** @@ -143,7 +151,7 @@ Burns a pledge NFT ```solidity -function burn(uint256 tokenId) public virtual override; +function burn(uint256 tokenId) public virtual override onlyRole(TREASURY_ROLE); ``` **Parameters** diff --git a/docs/src/src/utils/TimestampChecker.sol/abstract.TimestampChecker.md b/docs/src/src/utils/TimestampChecker.sol/abstract.TimestampChecker.md index b0a7c0f..1c15847 100644 --- a/docs/src/src/utils/TimestampChecker.sol/abstract.TimestampChecker.md +++ b/docs/src/src/utils/TimestampChecker.sol/abstract.TimestampChecker.md @@ -1,5 +1,8 @@ # TimestampChecker -[Git Source](https://github.com/oak-network/contracts/blob/0ce055a8ba31ca09404e9d09ecd2549534cbec61/src/utils/TimestampChecker.sol) +[Git Source](https://github.com/oak-network/contracts/blob/6c7f67f5ed14ef0f4f9444b95ac6770ae2af756a/src/utils/TimestampChecker.sol) + +**Title:** +TimestampChecker A contract that provides timestamp-related checks for contract functions. From 4256469c4a6fb7b9e79cc1ca8a8352012d119dd7 Mon Sep 17 00:00:00 2001 From: adnanhq Date: Tue, 21 Jul 2026 09:47:03 +0600 Subject: [PATCH 3/3] fix: address review findings on release/docs pipeline scripts - Drop committed __pycache__ bytecode and ignore Python cache artifacts - Honor the workflow-pinned FOUNDRY_VERSION in metadata.json, falling back to the runtime forge version for local runs - Make sanitize-docs.py truly idempotent: compute all rewrites in memory and write nothing when a rewritten target is missing, so a failed run leaves the tree untouched and re-runs fail identically --- .../__pycache__/sanitize-docs.cpython-314.pyc | Bin 5083 -> 0 bytes .github/scripts/build-abi-bundle.sh | 4 +- .github/scripts/sanitize-docs.py | 36 ++++++++++++------ .gitignore | 4 ++ 4 files changed, 31 insertions(+), 13 deletions(-) delete mode 100644 .github/scripts/__pycache__/sanitize-docs.cpython-314.pyc diff --git a/.github/scripts/__pycache__/sanitize-docs.cpython-314.pyc b/.github/scripts/__pycache__/sanitize-docs.cpython-314.pyc deleted file mode 100644 index e96f8d1d9a62c07c72fbfc8f9a18ab74b8cdb2d1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5083 zcmb7HeM}qY8Gp}r{=~+HgpVW;j?e&?6niWAU?fe_(ln%$)HSK;ItO#u2VAl5u6O4G zRzJM1(#%!~P&>i2YGP8ik*QMh$0oH>rfOZK?N3sYK(9#aw!ikzl#EuH`q!TKJ)iBk zwCP^U@4a{T-t#`6zvp=lSNl8&M(p3;f4;X0p?~5VYcPey_Ag5@t4Jg7ipj1G2MC`D3khSxYE8^^V7NO)`gqqML zI$T?#V69rwC1)!-QnV(jhloPC+(Upwe%c{b( zD(V8uk7;r$1B*1Dm=q;}Rd{_;V?_xbs8dOKT4Gb8G^GVSo*_A1pv1l?sG2BC8Y`ql z9X9KNN~n{qEU%Twj*BTlo7Hq7U7#$Fv-+gqv5wOxd7V{xi5(?8vcTgg+bIMmg6wGM z84b<}os{^ra4HlG21D>x3u$VC{K>K7d_vIhyHPIaxi~3mtd>wk1!$TA0xQxi5rNft zP=d}TWJ%{mNt7nwgd)LFR+lkj@LE-cgzmw503s<~7he>tmspx|0{8U z3zB|<)%0Xa9AjlEH4AEcM&l=hUREpE0_#Z&DzofKyyya9x{{)L%5!!`)HPO;rLL@? z%B(of@&y@ysKt9y7VsXO6=pfqun4L{b^3nu6 z7bP$UpJc%j2{{c0)UhtcRCx*xA{GQyUdSGeb#{h+FcQH1J-jzc!%NC&h2oS1O<|F@nMPvoKar#fUP`(nktXOv6=;Mja!l=PmH7qu8(Rk=Q|G zE^z!cGW#nIfZM3u;t?>lLh7hv$k}acL)Mi=hfzm?#02oughX2lYDO*O_vD)==w>8R z-@NN$fu^~ab7o!xf{`MESr{GqNA5Qz$buU)Trpb90Nog!0B|D2tu?k&%B05tA0W;x zFnka`0Nf}(DM5<{;0H*Yl%`#o&d+8xeNZ=kHK`m z01p?823v~*f*#W#BLqt2XcZgFutU@(CROZG9BtTC!LSdKC)mk3N`>5v%&1houq1(G zv{}&>Cz?2!k`sJNI~6PsxiLiVY#jDCOQq1rQRl=}vQOMH%Fczg^3^}5d<=Z44cZi<|HB#I&au5C~WvIF1 ziCAkDBT8GIUi4w8H=59peaG0Ntlcf>KGN+JRkk5#Q934Ki!xq_u7?Otn`Wol464(h zIL>9*CsaA37&ci`vGNS&d0CVUyQZW>fNqliG{eCwiXbHoCnRbN@tjM=)so@RGUMao zjNvhJejKVCqpGmRzG1km%x*Z$#HZTvcP{)g4*6KaLDX2D#{a@o| zh4;k6UV_j6n?sv5^-KE}_bosD=Kj26tFB>bcyV|swisLKx>~EUo$^FH$DH#+$+md8~#Hd`wwk;Yv%{&2AA7!cw4u2)h!)g zJic^#@$^dX>dgACp1fBh-3M%$>U4sT zxUMq1PoBRp5D!0c@#5LY1%uHtW1wYH8**;4)zpk_e9~~@JO>qyie1HUW|Smkv7}*7 z$QgKe5YujlNyARtK8}^go;hPG&v%c`RGd>t-unYAzRaP|J;+m?Pc9E{xDVcRA6%{7 ztlhoTvDon@mv`Q!k*EE(1MS(Dd)(5?t8=d|>+Ajpa{YH12-^SP$o;$))gRcXYrkIC zzA}B)3F`gfh1|JKr#Jt?>$6+FnuQ0J>6M8MU+|_cc=f=hw|c&Ru76?phPN3`s1AKr z6xKRjC1XLtYE zRn@o8eGf!p(8G|n{Qyk&gUArn@0^Xqf{A^Uik0~_fJacT8{TUen9TCQhl>N>8wqkT zQ4HP{2X<@1%_z*})xkGMvlaDFzfD7)1^k3OTf{G@;)>9|Q{PjnI$EFtY(r)F9fNnp z2=9O#W%QYf?QlCnmDhb1ugd+-srf3UQf*R1!`2E}=_vA|hiqjW?a=8}N<4xTDu@(Y zndCcmb*1wI=c0;eHe$#SwLc>%Pi>4S9@(o`$p@AmoWwED{QFi)H zdf#L1D_w_=S$GYc6>Lmuu9@M62g_B!?@k;QaRbk2PCN#agUYw3wwW{y@KAX~kW}l`HE0|13SwW=b{! zQ9ObmlHDYdv+bF0)dE79#o+vtZ4^{hR(naK$Fl&`(2vUMY%iOogXv_}g$qMyHQ8)q zsn5oJTc^eqTEc9wK*B8iaG7Ve<_aWHorOX#EovHW`|yEUZ4Gp`& zK)3v&VOLb#4>_Qv1&ZbQ15zq!s_dc$92(3tpPIsDVp5xi7Kk9c3f>2~SeSwHlR!gO zak`*2LFYVXRyt3Gu)U$`F)nIke^UywN@1GuQK@mzSv<&OKW$-&*ytYeOGA_x^J? zUHzZ<16#Z6m(DGoTY75osdxH*+yATn)u(?wu)g~{xxuX}-~92pY>R+qwxu$)PeLs8C^#oA)%2tjTzs^BB%}96D)eUr9NEbC3Exm~efNR=`qf7k-3uJW;#>-(ri! z;|2qDn|>J)lBRuJC)08=lM+s;5!jClYHbjv+cZT{cNh=FY&Rmx^C@zFiafVHC{R0} z{YiFhZ)Dv+l=FV!fFAjaW03G}A47~cpS?lv*<_qQ?tiU+?&43Qx0u}^2eX$P_df?U BLsb9( diff --git a/.github/scripts/build-abi-bundle.sh b/.github/scripts/build-abi-bundle.sh index 4240348..90a21b7 100755 --- a/.github/scripts/build-abi-bundle.sh +++ b/.github/scripts/build-abi-bundle.sh @@ -86,7 +86,9 @@ fi cp "$REPO_ROOT/src/constants/DataRegistryKeys.sol" "$BUNDLE_DIR/sources/DataRegistryKeys.sol" GIT_SHA="$(git -C "$REPO_ROOT" rev-parse HEAD)" -FOUNDRY_VERSION="$(forge --version | head -n1)" +# Prefer the pinned FOUNDRY_VERSION exported by release.yml so metadata.json +# embeds the exact pin; fall back to the runtime version for local runs. +FOUNDRY_VERSION="${FOUNDRY_VERSION:-$(forge --version | head -n1)}" # jq --args builds proper JSON arrays; guard the empty case explicitly (a bare # printf-into-jq pipeline would turn an empty array into [""]). diff --git a/.github/scripts/sanitize-docs.py b/.github/scripts/sanitize-docs.py index cb358f6..ea4caec 100755 --- a/.github/scripts/sanitize-docs.py +++ b/.github/scripts/sanitize-docs.py @@ -7,10 +7,14 @@ path relative to the file containing the link. Idempotent; stdlib only. Usage: sanitize-docs.py -Exits non-zero if a rewritten target does not exist under -(catches upstream layout changes instead of committing broken links). +Exits non-zero - without modifying any file - if a rewritten target does not +exist under (catches upstream layout changes instead of +committing broken links). All rewrites are computed in memory first, so a +failed run leaves the tree untouched and fails the same way when re-run. """ +from __future__ import annotations + import os import re import sys @@ -21,10 +25,11 @@ LINK_PATTERN = re.compile(r"\((/[^\s)]*?/(?:docs|\.forgedoc-tmp)/src/([^\s)]+))\)") -def sanitize_file(path: str, docs_src_root: str) -> tuple[int, list[str]]: - """Rewrites absolute /…/docs/src/… links in one file. +def sanitize_file(path: str, docs_src_root: str) -> tuple[str | None, int, list[str]]: + """Computes the rewrite of absolute /…/docs/src/… links in one file. - Returns (number of rewrites, list of rewritten targets that don't exist). + Does not write anything. Returns (rewritten content, or None if no links + matched; number of rewrites; rewritten targets that don't exist). """ with open(path, encoding="utf-8") as fh: content = fh.read() @@ -42,10 +47,7 @@ def replace(match: re.Match) -> str: return f"({relative})" updated, count = LINK_PATTERN.subn(replace, content) - if count > 0: - with open(path, "w", encoding="utf-8") as fh: - fh.write(updated) - return count, broken + return (updated if count > 0 else None), count, broken def main() -> int: @@ -59,19 +61,29 @@ def main() -> int: total = 0 all_broken: list[str] = [] + pending: list[tuple[str, str]] = [] for dirpath, _dirnames, filenames in os.walk(docs_src_root): for filename in filenames: if filename.endswith(".md"): - count, broken = sanitize_file(os.path.join(dirpath, filename), docs_src_root) + path = os.path.join(dirpath, filename) + updated, count, broken = sanitize_file(path, docs_src_root) total += count all_broken.extend(broken) + if updated is not None: + pending.append((path, updated)) - print(f"Rewrote {total} absolute link(s) under {docs_src_root}") if all_broken: - print("error: rewritten links point at missing files:", file=sys.stderr) + print("error: rewritten links point at missing files (no files modified):", + file=sys.stderr) for target in sorted(set(all_broken)): print(f" - {target}", file=sys.stderr) return 1 + + for path, updated in pending: + with open(path, "w", encoding="utf-8") as fh: + fh.write(updated) + + print(f"Rewrote {total} absolute link(s) under {docs_src_root}") return 0 diff --git a/.gitignore b/.gitignore index e133685..73c819f 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,7 @@ out # Release ABI bundle build output dist .abi-bundle + +# Python bytecode +__pycache__/ +*.pyc