diff --git a/.github/actions/build-test-image/action.yml b/.github/actions/build-test-image/action.yml index 2ab2bc2e7..1a5f4f2b0 100644 --- a/.github/actions/build-test-image/action.yml +++ b/.github/actions/build-test-image/action.yml @@ -27,6 +27,8 @@ runs: steps: - name: Install Cairo uses: ./.github/actions/install-cairo + with: + use_musl_libc: "true" - name: Check if image exists id: check-image uses: smartcontractkit/chainlink-github-actions/docker/image-exists@fc3e0df622521019f50d772726d6bf8dc919dd38 # v2.3.19 diff --git a/.github/actions/install-cairo/action.yml b/.github/actions/install-cairo/action.yml index d3f1524d6..addd53fd1 100644 --- a/.github/actions/install-cairo/action.yml +++ b/.github/actions/install-cairo/action.yml @@ -2,32 +2,46 @@ name: Install Cairo and Scarb description: A composite action that installs cairo and scarb binaries inputs: - cairo_version: - description: Cairo release version - default: "v2.6.4" - required: false + # cairo_version: + # description: Cairo release version + # default: "v2.8.2" + # required: false scarb_version: description: Scarb release version - default: "v2.6.5" + default: "v2.9.2" + required: false + use_musl_libc: + description: "C library implementation" + default: "false" required: false runs: using: composite steps: - - name: Setup Cairo for Linux - id: install-cairo - shell: bash - run: | - wget https://github.com/starkware-libs/cairo/releases/download/${{ inputs.cairo_version }}/release-x86_64-unknown-linux-musl.tar.gz - tar -xvzf release-x86_64-unknown-linux-musl.tar.gz - mv -vf cairo cairo-build - echo "$GITHUB_WORKSPACE/cairo-build/bin" >> $GITHUB_PATH + # - name: Setup Cairo for Linux + # id: install-cairo + # shell: bash + # run: | + # wget https://github.com/starkware-libs/cairo/releases/download/${{ inputs.cairo_version }}/release-x86_64-unknown-linux-musl.tar.gz + # tar -xvzf release-x86_64-unknown-linux-musl.tar.gz + # mv -vf cairo cairo-build + # echo "$GITHUB_WORKSPACE/cairo-build/bin" >> $GITHUB_PATH + # echo "$GITHUB_WORKSPACE" + # echo "this is the github workspace" - name: Setup Scarb for Linux id: install-scarb shell: bash run: | - wget https://github.com/software-mansion/scarb/releases/download/${{ inputs.scarb_version }}/scarb-${{ inputs.scarb_version }}-x86_64-unknown-linux-musl.tar.gz - tar -xvzf scarb-${{ inputs.scarb_version }}-x86_64-unknown-linux-musl.tar.gz - mv -vf scarb-${{ inputs.scarb_version }}-x86_64-unknown-linux-musl scarb-build + if [ "${USE_MUSL_LIBC}" = "true" ]; then + libc_version="musl" + else + libc_version="gnu" + fi + wget https://github.com/software-mansion/scarb/releases/download/${SCARB_VERSION}/scarb-${SCARB_VERSION}-x86_64-unknown-linux-${libc_version}.tar.gz + tar -xvzf scarb-${SCARB_VERSION}-x86_64-unknown-linux-${libc_version}.tar.gz + mv -vf scarb-${SCARB_VERSION}-x86_64-unknown-linux-${libc_version} scarb-build echo "$GITHUB_WORKSPACE/scarb-build/bin" >> $GITHUB_PATH + env: + USE_MUSL_LIBC: ${{ inputs.use_musl_libc }} + SCARB_VERSION: ${{ inputs.scarb_version }} diff --git a/.github/actions/install-starknet-foundry/action.yml b/.github/actions/install-starknet-foundry/action.yml index 8e4360edf..89d983fdb 100644 --- a/.github/actions/install-starknet-foundry/action.yml +++ b/.github/actions/install-starknet-foundry/action.yml @@ -4,7 +4,7 @@ description: A composite action that installs the snforge and sncast binaries inputs: starknet_foundry_version: description: Starknet Foundry release version - default: "0.27.0" + default: "0.31.0" required: false runs: @@ -15,4 +15,6 @@ runs: shell: bash run: | curl -L https://raw.githubusercontent.com/foundry-rs/starknet-foundry/master/scripts/install.sh | sh - snfoundryup -v ${{ inputs.starknet_foundry_version }} + snfoundryup -v ${STARKNET_FOUNDRY_VERSION} + env: + STARKNET_FOUNDRY_VERSION: ${{ inputs.starknet_foundry_version }} diff --git a/.github/actions/setup-github-private-access/action.yml b/.github/actions/setup-github-private-access/action.yml index 7840de6a4..fe729ff22 100644 --- a/.github/actions/setup-github-private-access/action.yml +++ b/.github/actions/setup-github-private-access/action.yml @@ -12,5 +12,7 @@ runs: - name: Setup Go with private repo access shell: bash run: | - git config --global url."https://x-access-token:${{ inputs.gati_token }}@github.com/".insteadOf "https://github.com/" + git config --global url."https://x-access-token:${GATI_TOKEN}@github.com/".insteadOf "https://github.com/" go env -w GOPRIVATE=github.com/smartcontractkit/* + env: + GATI_TOKEN: ${{ inputs.gati_token }} diff --git a/.github/workflows/changesets.yml b/.github/workflows/changesets.yml index 9206eb907..3bec19491 100644 --- a/.github/workflows/changesets.yml +++ b/.github/workflows/changesets.yml @@ -17,6 +17,7 @@ jobs: # This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits fetch-depth: 0 token: ${{ secrets.GITHUB_TOKEN }} + persist-credentials: false # Install nix - name: Install Nix uses: cachix/install-nix-action@3715ab1a11cac9e991980d7b4a28d80c7ebdd8f9 # nix:v2.24.6 diff --git a/.github/workflows/contracts.yml b/.github/workflows/contracts.yml index dcb61a054..64a4db881 100644 --- a/.github/workflows/contracts.yml +++ b/.github/workflows/contracts.yml @@ -14,6 +14,8 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + persist-credentials: false - name: Install Nix uses: cachix/install-nix-action@3715ab1a11cac9e991980d7b4a28d80c7ebdd8f9 # nix:v2.24.6 @@ -32,6 +34,8 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + persist-credentials: false - name: Install Nix uses: cachix/install-nix-action@3715ab1a11cac9e991980d7b4a28d80c7ebdd8f9 # nix:v2.24.6 diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index c645b4ce0..1c160da13 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -14,6 +14,8 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + persist-credentials: false - name: Install Nix uses: cachix/install-nix-action@3715ab1a11cac9e991980d7b4a28d80c7ebdd8f9 # nix:v2.24.6 diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index b6e9ce359..f9149b7cf 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -10,6 +10,8 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + persist-credentials: false - name: Install Nix uses: cachix/install-nix-action@3715ab1a11cac9e991980d7b4a28d80c7ebdd8f9 # nix:v2.24.6 with: @@ -32,6 +34,8 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + persist-credentials: false - name: Install Nix uses: cachix/install-nix-action@3715ab1a11cac9e991980d7b4a28d80c7ebdd8f9 # nix:v2.24.6 with: @@ -58,6 +62,8 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + persist-credentials: false - name: Setup GitHub Token id: setup-github-token uses: smartcontractkit/.github/actions/setup-github-token@9e7cc0779934cae4a9028b8588c9adb64d8ce68c # setup-github-token@0.1.2 @@ -95,6 +101,8 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + persist-credentials: false - name: Setup GitHub Token id: setup-github-token uses: smartcontractkit/.github/actions/setup-github-token@9e7cc0779934cae4a9028b8588c9adb64d8ce68c # setup-github-token@0.1.2 diff --git a/.github/workflows/integration-tests-publish.yml b/.github/workflows/integration-tests-publish.yml index f5e94e885..b8fa34d78 100644 --- a/.github/workflows/integration-tests-publish.yml +++ b/.github/workflows/integration-tests-publish.yml @@ -30,6 +30,8 @@ jobs: continue-on-error: true - name: Checkout the repo uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + persist-credentials: false - name: Setup GitHub Token id: setup-github-token uses: smartcontractkit/.github/actions/setup-github-token@9e7cc0779934cae4a9028b8588c9adb64d8ce68c # setup-github-token@0.1.2 diff --git a/.github/workflows/integration-tests-smoke.yml b/.github/workflows/integration-tests-smoke.yml index a77ff8f1a..a47aecc98 100644 --- a/.github/workflows/integration-tests-smoke.yml +++ b/.github/workflows/integration-tests-smoke.yml @@ -76,7 +76,9 @@ jobs: - name: Set core reference if workflow dispatch if: steps.check-image.outputs.exists == 'false' && github.event_name == 'workflow_dispatch' run: | - echo "CUSTOM_CORE_REF=${{ github.event.inputs.cl_branch_ref }}" >> "${GITHUB_ENV}" + echo "CUSTOM_CORE_REF=${CL_REF}" >> "${GITHUB_ENV}" + env: + CL_REF: ${{ github.event.inputs.cl_branch_ref }} - name: Build Image ${{ matrix.image.name }} if: steps.check-image.outputs.exists == 'false' uses: smartcontractkit/.github/actions/ctf-build-image@c4705bfdbf6c8e57c080d82a3c4f013aa96a2dfb # v2.3.19 @@ -125,6 +127,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: ref: ${{ github.sha }} + persist-credentials: false - name: Build Image uses: ./.github/actions/build-test-image with: @@ -169,6 +172,8 @@ jobs: continue-on-error: true - name: Checkout the repo uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + persist-credentials: false - name: Install Nix uses: cachix/install-nix-action@3715ab1a11cac9e991980d7b4a28d80c7ebdd8f9 # nix:v2.24.6 with: @@ -194,7 +199,7 @@ jobs: [Network] selected_networks=["SIMULATED"] [Common] - internal_docker_repo = "${{ env.INTERNAL_DOCKER_REPO }}" + internal_docker_repo = "${INTERNAL_DOCKER_REPO}" stateful_db = false EOF # shellcheck disable=SC2002 @@ -206,6 +211,7 @@ jobs: - name: Run Tests ${{ matrix.image.name }} uses: smartcontractkit/.github/actions/ctf-run-tests@4229fea727f6eb36b4559c6eefbbf9f3825fa677 # 0.2.1 with: + cl_internal_docker_repo: ${{ env.INTERNAL_DOCKER_REPO }} aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} test_command_to_run: nix develop -c sh -c "make test=${{ matrix.image.test-name }} test-integration-smoke-ci" test_download_vendor_packages_command: cd integration-tests && nix develop -c go mod download diff --git a/.github/workflows/integration-tests-soak.yml b/.github/workflows/integration-tests-soak.yml index 24c95743c..b051d47b4 100644 --- a/.github/workflows/integration-tests-soak.yml +++ b/.github/workflows/integration-tests-soak.yml @@ -53,6 +53,8 @@ jobs: continue-on-error: true - name: Checkout the repo uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + persist-credentials: false - name: Install Nix uses: cachix/install-nix-action@3715ab1a11cac9e991980d7b4a28d80c7ebdd8f9 # nix:v2.24.6 with: diff --git a/.github/workflows/integration_gauntlet.yml b/.github/workflows/integration_gauntlet.yml index eafe935af..2041729ee 100644 --- a/.github/workflows/integration_gauntlet.yml +++ b/.github/workflows/integration_gauntlet.yml @@ -15,13 +15,15 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + persist-credentials: false - name: Install Nix uses: cachix/install-nix-action@3715ab1a11cac9e991980d7b4a28d80c7ebdd8f9 # nix:v2.24.6 with: nix_path: nixpkgs=channel:nixos-unstable extra_nix_config: "sandbox = false" - name: Cache Nix - uses: cachix/cachix-action@v15 + uses: cachix/cachix-action@ad2ddac53f961de1989924296a1f236fcfbaa4fc with: name: chainlink-cosmos authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' @@ -49,6 +51,8 @@ jobs: this-job-name: Run Integration Gauntlet Tests - name: Checkout sources uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + persist-credentials: false - name: Install Nix uses: cachix/install-nix-action@3715ab1a11cac9e991980d7b4a28d80c7ebdd8f9 # nix:v2.24.6 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 40fb56ad4..1dc582443 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -14,6 +14,8 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + persist-credentials: false - name: Install Nix uses: cachix/install-nix-action@3715ab1a11cac9e991980d7b4a28d80c7ebdd8f9 # nix:v2.24.6 diff --git a/.github/workflows/monitoring-build-push-ecr.yml b/.github/workflows/monitoring-build-push-ecr.yml index 7f0becd91..7ce4b7f94 100644 --- a/.github/workflows/monitoring-build-push-ecr.yml +++ b/.github/workflows/monitoring-build-push-ecr.yml @@ -18,6 +18,8 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + persist-credentials: false - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4.0.2 diff --git a/.github/workflows/relayer.yml b/.github/workflows/relayer.yml index 8815e75a3..135966a34 100644 --- a/.github/workflows/relayer.yml +++ b/.github/workflows/relayer.yml @@ -35,6 +35,8 @@ jobs: test-results-file: '{"testType":"go","filePath":"/tmp/gotest.log"}' - name: Checkout sources uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + persist-credentials: false - name: Install Nix uses: cachix/install-nix-action@3715ab1a11cac9e991980d7b4a28d80c7ebdd8f9 # nix:v2.24.6 with: @@ -48,7 +50,7 @@ jobs: - name: Upload Golangci relayer results if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 with: name: go-unit-tests-results-${{ matrix.test-type.id }} path: | @@ -65,6 +67,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + with: + persist-credentials: false - name: Set up Go uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 with: diff --git a/.github/workflows/release/starknet-gauntlet-cli.yml b/.github/workflows/release/starknet-gauntlet-cli.yml index 7ebbebf0f..935cd6e5e 100644 --- a/.github/workflows/release/starknet-gauntlet-cli.yml +++ b/.github/workflows/release/starknet-gauntlet-cli.yml @@ -11,6 +11,8 @@ jobs: # Checkout this repository - name: Checkout Repo uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + with: + persist-credentials: false # Install nix - name: Install Nix uses: cachix/install-nix-action@3715ab1a11cac9e991980d7b4a28d80c7ebdd8f9 # nix:v2.24.6 diff --git a/.github/workflows/release/starknet-relayer.yml b/.github/workflows/release/starknet-relayer.yml index 3ecb7abd9..44fd236a3 100644 --- a/.github/workflows/release/starknet-relayer.yml +++ b/.github/workflows/release/starknet-relayer.yml @@ -11,6 +11,8 @@ jobs: # Checkout this repository - name: Checkout Repo uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + with: + persist-credentials: false # Store starknet version - name: Set Env Variables run: echo "STARKNET_RELAYER=$(npm info @chainlink/starknet-relayer version)" >> $GITHUB_ENV diff --git a/.github/workflows/sonar-scan.yml b/.github/workflows/sonar-scan.yml index 21ef33d19..71e62deb0 100644 --- a/.github/workflows/sonar-scan.yml +++ b/.github/workflows/sonar-scan.yml @@ -10,13 +10,14 @@ jobs: if: always() steps: - name: Checkout Repository - uses: actions/checkout@v4 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} + persist-credentials: false - name: Wait for Workflows id: wait - uses: smartcontractkit/chainlink-github-actions/utils/wait-for-workflows@main + uses: smartcontractkit/chainlink-github-actions/utils/wait-for-workflows@fb7d3b28c16da25950f4a5d8b15d23b2491dbd8d with: max-timeout: "1200" polling-interval: "30" @@ -33,10 +34,10 @@ jobs: if: always() steps: - name: Checkout the repo - uses: actions/checkout@v4 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: fetch-depth: 0 # fetches all history for all tags and branches to provide more metadata for sonar reports - + persist-credentials: false - name: Download Golangci unit tests reports uses: dawidd6/action-download-artifact@bf251b5aa9c2f7eeb574a96ee720e24f801b7c11 # v6 with: @@ -95,16 +96,20 @@ jobs: - name: Update ESLint report symlinks continue-on-error: true - run: sed -i 's+/home/runner/work/feeds-manager/feeds-manager/+/github/workspace/+g' ${{ steps.sonarqube_report_paths.outputs.sonarqube_eslint_report_paths }} + run: sed -i 's+/home/runner/work/feeds-manager/feeds-manager/+/github/workspace/+g' "${ SONAR_ESLINT_REPORT_PATHS }" - name: SonarQube Scan uses: sonarsource/sonarqube-scan-action@86fe81775628f1c6349c28baab87881a2170f495 # v2.1.0 with: args: > - -Dsonar.go.tests.reportPaths=${{ steps.sonarqube_report_paths.outputs.sonarqube_tests_report_paths }} - -Dsonar.go.coverage.reportPaths=${{ steps.sonarqube_report_paths.outputs.sonarqube_coverage_report_paths }} - -Dsonar.go.golangci-lint.reportPaths=${{ steps.sonarqube_report_paths.outputs.sonarqube_golangci_report_paths }} - -Dsonar.eslint.reportPaths=${{ steps.sonarqube_report_paths.outputs.sonarqube_eslint_report_paths }} + -Dsonar.go.tests.reportPaths="${SONAR_TESTS_REPORT_PATHS}" + -Dsonar.go.coverage.reportPaths="${SONAR_COVERAGE_REPORT_PATHS}" + -Dsonar.go.golangci-lint.reportPaths="${SONAR_GOLANGCI_REPORT_PATHS}" + -Dsonar.eslint.reportPaths="${SONAR_ESLINT_REPORT_PATHS}" env: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} + SONAR_ESLINT_REPORT_PATHS: ${{ steps.sonarqube_report_paths.outputs.sonarqube_eslint_report_paths }} + SONAR_GOLANGCI_REPORT_PATHS: ${{ steps.sonarqube_report_paths.outputs.sonarqube_golangci_report_paths }} + SONAR_COVERAGE_REPORT_PATHS: ${{ steps.sonarqube_report_paths.outputs.sonarqube_coverage_report_paths }} + SONAR_TESTS_REPORT_PATHS: ${{ steps.sonarqube_report_paths.outputs.sonarqube_tests_report_paths }} diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml new file mode 100644 index 000000000..29e7cedb5 --- /dev/null +++ b/.github/workflows/static-analysis.yml @@ -0,0 +1,26 @@ +name: Static Analysis + +on: + push: + branches: + - develop + - main + pull_request: + +jobs: + zizmor_analyzer: + name: Zizmor + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + persist-credentials: false + + - name: Install Nix + uses: cachix/install-nix-action@3715ab1a11cac9e991980d7b4a28d80c7ebdd8f9 # nix:v2.24.6 + with: + nix_path: nixpkgs=channel:nixos-unstable + + - name: Check Zizmor + run: nix develop -c zizmor --pedantic --config zizmor.yml . diff --git a/.golangci.yml b/.golangci.yml index 480ec761a..02b06c91d 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -3,7 +3,7 @@ run: linters: enable: - exhaustive - - exportloopref + - copyloopvar - revive - goimports - gosec diff --git a/.tool-versions b/.tool-versions index 121b6f80a..21acec5a0 100644 --- a/.tool-versions +++ b/.tool-versions @@ -9,9 +9,9 @@ mockery 2.22.1 golangci-lint 1.62.2 actionlint 1.6.12 shellcheck 0.8.0 -scarb 2.6.5 +scarb 2.9.2 postgres 15.1 -starknet-foundry 0.27.0 +starknet-foundry 0.31.0 # Kubernetes k3d 5.4.4 diff --git a/Makefile b/Makefile index bea87f567..5abb7c301 100644 --- a/Makefile +++ b/Makefile @@ -152,13 +152,13 @@ generate: mockery gomods .PHONY: format-cairo format-cairo: - cairo-format -i ./contracts/src/**/*.cairo - cairo-format -i ./examples/**/*.cairo + cd contracts && scarb fmt + cd examples/contracts/aggregator_consumer && scarb fmt .PHONY: format-cairo-check format-cairo-check: - cairo-format -c ./contracts/src/**/*.cairo - cairo-format -c ./examples/**/*.cairo + cd contracts && scarb fmt -c + cd examples/contracts/aggregator_consumer && scarb fmt -c .PHONY: format-ts format-ts: @@ -233,7 +233,7 @@ test-integration-soak-ci: .PHONY: test-examples test-examples: cd ./examples/contracts/aggregator_consumer && \ - snforge test + scarb test .PHONY: test-integration-gauntlet # TODO: fix example diff --git a/contracts/Scarb.lock b/contracts/Scarb.lock index 900ef28e5..842ec40da 100644 --- a/contracts/Scarb.lock +++ b/contracts/Scarb.lock @@ -4,7 +4,7 @@ version = 1 [[package]] name = "alexandria_bytes" version = "0.1.0" -source = "git+https://github.com/keep-starknet-strange/alexandria.git?rev=bcdca70afdf59c9976148e95cebad5cf63d75a7f#bcdca70afdf59c9976148e95cebad5cf63d75a7f" +source = "git+https://github.com/keep-starknet-strange/alexandria.git?rev=162bed1c636d31ccaaa90ed3eb32c9eb1d5e3bd3#162bed1c636d31ccaaa90ed3eb32c9eb1d5e3bd3" dependencies = [ "alexandria_data_structures", "alexandria_math", @@ -13,7 +13,7 @@ dependencies = [ [[package]] name = "alexandria_data_structures" version = "0.2.0" -source = "git+https://github.com/keep-starknet-strange/alexandria.git?rev=bcdca70afdf59c9976148e95cebad5cf63d75a7f#bcdca70afdf59c9976148e95cebad5cf63d75a7f" +source = "git+https://github.com/keep-starknet-strange/alexandria.git?rev=162bed1c636d31ccaaa90ed3eb32c9eb1d5e3bd3#162bed1c636d31ccaaa90ed3eb32c9eb1d5e3bd3" dependencies = [ "alexandria_encoding", ] @@ -21,25 +21,23 @@ dependencies = [ [[package]] name = "alexandria_encoding" version = "0.1.0" -source = "git+https://github.com/keep-starknet-strange/alexandria.git?rev=bcdca70afdf59c9976148e95cebad5cf63d75a7f#bcdca70afdf59c9976148e95cebad5cf63d75a7f" +source = "git+https://github.com/keep-starknet-strange/alexandria.git?rev=162bed1c636d31ccaaa90ed3eb32c9eb1d5e3bd3#162bed1c636d31ccaaa90ed3eb32c9eb1d5e3bd3" dependencies = [ "alexandria_bytes", + "alexandria_data_structures", "alexandria_math", "alexandria_numeric", ] [[package]] name = "alexandria_math" -version = "0.2.0" -source = "git+https://github.com/keep-starknet-strange/alexandria.git?rev=bcdca70afdf59c9976148e95cebad5cf63d75a7f#bcdca70afdf59c9976148e95cebad5cf63d75a7f" -dependencies = [ - "alexandria_data_structures", -] +version = "0.2.1" +source = "git+https://github.com/keep-starknet-strange/alexandria.git?rev=162bed1c636d31ccaaa90ed3eb32c9eb1d5e3bd3#162bed1c636d31ccaaa90ed3eb32c9eb1d5e3bd3" [[package]] name = "alexandria_numeric" version = "0.1.0" -source = "git+https://github.com/keep-starknet-strange/alexandria.git?rev=bcdca70afdf59c9976148e95cebad5cf63d75a7f#bcdca70afdf59c9976148e95cebad5cf63d75a7f" +source = "git+https://github.com/keep-starknet-strange/alexandria.git?rev=162bed1c636d31ccaaa90ed3eb32c9eb1d5e3bd3#162bed1c636d31ccaaa90ed3eb32c9eb1d5e3bd3" dependencies = [ "alexandria_math", "alexandria_searching", @@ -48,7 +46,7 @@ dependencies = [ [[package]] name = "alexandria_searching" version = "0.1.0" -source = "git+https://github.com/keep-starknet-strange/alexandria.git?rev=bcdca70afdf59c9976148e95cebad5cf63d75a7f#bcdca70afdf59c9976148e95cebad5cf63d75a7f" +source = "git+https://github.com/keep-starknet-strange/alexandria.git?rev=162bed1c636d31ccaaa90ed3eb32c9eb1d5e3bd3#162bed1c636d31ccaaa90ed3eb32c9eb1d5e3bd3" dependencies = [ "alexandria_data_structures", ] @@ -66,10 +64,119 @@ dependencies = [ [[package]] name = "openzeppelin" -version = "0.10.0" -source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.10.0#d77082732daab2690ba50742ea41080eb23299d3" +version = "0.20.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.20.0#7756fd1de2b4ebd239fa6e372d75535cea02e5e5" +dependencies = [ + "openzeppelin_access", + "openzeppelin_account", + "openzeppelin_finance", + "openzeppelin_governance", + "openzeppelin_introspection", + "openzeppelin_merkle_tree", + "openzeppelin_presets", + "openzeppelin_security", + "openzeppelin_token", + "openzeppelin_upgrades", + "openzeppelin_utils", +] + +[[package]] +name = "openzeppelin_access" +version = "0.20.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.20.0#7756fd1de2b4ebd239fa6e372d75535cea02e5e5" +dependencies = [ + "openzeppelin_introspection", +] + +[[package]] +name = "openzeppelin_account" +version = "0.20.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.20.0#7756fd1de2b4ebd239fa6e372d75535cea02e5e5" +dependencies = [ + "openzeppelin_introspection", + "openzeppelin_utils", +] + +[[package]] +name = "openzeppelin_finance" +version = "0.20.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.20.0#7756fd1de2b4ebd239fa6e372d75535cea02e5e5" +dependencies = [ + "openzeppelin_access", + "openzeppelin_token", +] + +[[package]] +name = "openzeppelin_governance" +version = "0.20.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.20.0#7756fd1de2b4ebd239fa6e372d75535cea02e5e5" +dependencies = [ + "openzeppelin_access", + "openzeppelin_account", + "openzeppelin_introspection", + "openzeppelin_token", + "openzeppelin_utils", +] + +[[package]] +name = "openzeppelin_introspection" +version = "0.20.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.20.0#7756fd1de2b4ebd239fa6e372d75535cea02e5e5" + +[[package]] +name = "openzeppelin_merkle_tree" +version = "0.20.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.20.0#7756fd1de2b4ebd239fa6e372d75535cea02e5e5" + +[[package]] +name = "openzeppelin_presets" +version = "0.20.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.20.0#7756fd1de2b4ebd239fa6e372d75535cea02e5e5" +dependencies = [ + "openzeppelin_access", + "openzeppelin_account", + "openzeppelin_finance", + "openzeppelin_introspection", + "openzeppelin_token", + "openzeppelin_upgrades", + "openzeppelin_utils", +] + +[[package]] +name = "openzeppelin_security" +version = "0.20.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.20.0#7756fd1de2b4ebd239fa6e372d75535cea02e5e5" + +[[package]] +name = "openzeppelin_token" +version = "0.20.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.20.0#7756fd1de2b4ebd239fa6e372d75535cea02e5e5" +dependencies = [ + "openzeppelin_access", + "openzeppelin_account", + "openzeppelin_introspection", + "openzeppelin_utils", +] + +[[package]] +name = "openzeppelin_upgrades" +version = "0.20.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.20.0#7756fd1de2b4ebd239fa6e372d75535cea02e5e5" + +[[package]] +name = "openzeppelin_utils" +version = "0.20.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.20.0#7756fd1de2b4ebd239fa6e372d75535cea02e5e5" + +[[package]] +name = "snforge_scarb_plugin" +version = "0.31.0" +source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.31.0#72ea785ca354e9e506de3e5d687da9fb2c1b3c67" [[package]] name = "snforge_std" -version = "0.27.0" -source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.27.0#2d99b7c00678ef0363881ee0273550c44a9263de" +version = "0.31.0" +source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.31.0#72ea785ca354e9e506de3e5d687da9fb2c1b3c67" +dependencies = [ + "snforge_scarb_plugin", +] diff --git a/contracts/Scarb.toml b/contracts/Scarb.toml index 97e8ee5d4..49b19eaf9 100644 --- a/contracts/Scarb.toml +++ b/contracts/Scarb.toml @@ -1,7 +1,7 @@ [package] name = "chainlink" version = "0.1.0" -cairo-version = "2.6.3" +cairo-version = "2.9.2" description = "Chainlink contracts for Starknet" homepage = "https://github.com/smartcontractkit/chainlink-starknet" @@ -13,14 +13,14 @@ test = "snforge test" # Uncomment if you want to use dependencies # Note: currently testing doesn't work with dependencies [dependencies] -starknet = ">=2.6.3" -openzeppelin = { git = "https://github.com/OpenZeppelin/cairo-contracts.git", tag = "v0.10.0" } -alexandria_bytes = { git = "https://github.com/keep-starknet-strange/alexandria.git", rev = "bcdca70afdf59c9976148e95cebad5cf63d75a7f" } -alexandria_encoding = { git = "https://github.com/keep-starknet-strange/alexandria.git", rev = "bcdca70afdf59c9976148e95cebad5cf63d75a7f" } -snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.27.0" } +starknet = "2.9.2" +openzeppelin = { git = "https://github.com/OpenZeppelin/cairo-contracts.git", tag = "v0.20.0" } +alexandria_bytes = { git = "https://github.com/keep-starknet-strange/alexandria.git", rev = "162bed1c636d31ccaaa90ed3eb32c9eb1d5e3bd3" } +alexandria_encoding = { git = "https://github.com/keep-starknet-strange/alexandria.git", rev = "162bed1c636d31ccaaa90ed3eb32c9eb1d5e3bd3" } [dev-dependencies] -alexandria_math = { git = "https://github.com/keep-starknet-strange/alexandria.git", rev = "bcdca70afdf59c9976148e95cebad5cf63d75a7f" } +alexandria_math = { git = "https://github.com/keep-starknet-strange/alexandria.git", rev = "162bed1c636d31ccaaa90ed3eb32c9eb1d5e3bd3" } +snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.31.0" } [lib] diff --git a/contracts/cairo_project.toml b/contracts/cairo_project.toml deleted file mode 100644 index df050dd0c..000000000 --- a/contracts/cairo_project.toml +++ /dev/null @@ -1,2 +0,0 @@ -[crate_roots] -chainlink = "src" diff --git a/contracts/src/access_control/access_controller.cairo b/contracts/src/access_control/access_controller.cairo index 37d9ef909..91dcb7102 100644 --- a/contracts/src/access_control/access_controller.cairo +++ b/contracts/src/access_control/access_controller.cairo @@ -4,13 +4,18 @@ mod AccessController { use starknet::class_hash::ClassHash; use openzeppelin::access::ownable::OwnableComponent; + use openzeppelin::upgrades::UpgradeableComponent; use chainlink::libraries::access_control::{AccessControlComponent, IAccessController}; use chainlink::libraries::type_and_version::ITypeAndVersion; - use chainlink::libraries::upgradeable::{Upgradeable, IUpgradeable}; + use chainlink::libraries::upgrades::v2::owner_upgradeable::OwnerUpgradeableComponent; component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); component!(path: AccessControlComponent, storage: access_control, event: AccessControlEvent); + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent); + component!( + path: OwnerUpgradeableComponent, storage: owner_upgradeable, event: OwnerUpgradeableEvent, + ); #[abi(embed_v0)] impl OwnableImpl = OwnableComponent::OwnableTwoStepImpl; @@ -21,6 +26,12 @@ mod AccessController { AccessControlComponent::AccessControlImpl; impl AccessControlInternalImpl = AccessControlComponent::InternalImpl; + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl; + + #[abi(embed_v0)] + impl OwnerUpgradeableImpl = + OwnerUpgradeableComponent::OwnerUpgradeableImpl; + #[event] #[derive(Drop, starknet::Event)] enum Event { @@ -28,6 +39,10 @@ mod AccessController { OwnableEvent: OwnableComponent::Event, #[flat] AccessControlEvent: AccessControlComponent::Event, + #[flat] + UpgradeableEvent: UpgradeableComponent::Event, + #[flat] + OwnerUpgradeableEvent: OwnerUpgradeableComponent::Event, } #[storage] @@ -36,12 +51,16 @@ mod AccessController { ownable: OwnableComponent::Storage, #[substorage(v0)] access_control: AccessControlComponent::Storage, + #[substorage(v0)] + upgradeable: UpgradeableComponent::Storage, + #[substorage(v0)] + owner_upgradeable: OwnerUpgradeableComponent::Storage, } #[constructor] fn constructor(ref self: ContractState, owner_address: ContractAddress) { self.ownable.initializer(owner_address); - self.access_control.initializer(); + self.access_control.initializer(true); } #[abi(embed_v0)] @@ -50,12 +69,4 @@ mod AccessController { 'AccessController 1.0.0' } } - - #[abi(embed_v0)] - impl UpgradeableImpl of IUpgradeable { - fn upgrade(ref self: ContractState, new_impl: ClassHash) { - self.ownable.assert_only_owner(); - Upgradeable::upgrade(new_impl); - } - } } diff --git a/contracts/src/access_control/rbac_timelock.cairo b/contracts/src/access_control/rbac_timelock.cairo index 5fe078996..793f6a4bd 100644 --- a/contracts/src/access_control/rbac_timelock.cairo +++ b/contracts/src/access_control/rbac_timelock.cairo @@ -2,6 +2,7 @@ use starknet::ContractAddress; use alexandria_bytes::{Bytes, BytesTrait}; use alexandria_encoding::sol_abi::sol_bytes::SolBytesTrait; use alexandria_encoding::sol_abi::encode::SolAbiEncodeTrait; +use chainlink::utils::{keccak}; #[derive(Copy, Drop, Serde)] struct Call { @@ -14,26 +15,26 @@ fn _hash_operation_batch(calls: Span, predecessor: u256, salt: u256) -> u2 let mut encoded: Bytes = BytesTrait::new_empty(); let mut i = 0; - while i < calls - .len() { - let call = *calls.at(i); - encoded = encoded.encode(call.target).encode(call.selector); - let mut j = 0; - while j < call.data.len() { - encoded = encoded.encode(*call.data.at(j)); - j += 1; - }; - i += 1; + while i < calls.len() { + let call = *calls.at(i); + encoded = encoded.encode(call.target).encode(call.selector); + let mut j = 0; + while j < call.data.len() { + encoded = encoded.encode(*call.data.at(j)); + j += 1; }; + i += 1; + }; encoded = encoded.encode(predecessor).encode(salt); - encoded.keccak() + + keccak(@encoded.into()) } #[starknet::interface] trait IRBACTimelock { fn schedule_batch( - ref self: TContractState, calls: Span, predecessor: u256, salt: u256, delay: u256 + ref self: TContractState, calls: Span, predecessor: u256, salt: u256, delay: u256, ); fn cancel(ref self: TContractState, id: u256); fn execute_batch(ref self: TContractState, calls: Span, predecessor: u256, salt: u256); @@ -41,8 +42,8 @@ trait IRBACTimelock { fn update_delay(ref self: TContractState, new_delay: u256); fn block_function_selector(ref self: TContractState, selector: felt252); fn unblock_function_selector(ref self: TContractState, selector: felt252); - fn get_blocked_function_selector_count(self: @TContractState) -> u256; - fn get_blocked_function_selector_at(self: @TContractState, index: u256) -> felt252; + fn get_blocked_function_selector_count(self: @TContractState) -> usize; + fn get_blocked_function_selector_at(self: @TContractState, index: usize) -> felt252; fn is_operation(self: @TContractState, id: u256) -> bool; fn is_operation_pending(self: @TContractState, id: u256) -> bool; fn is_operation_ready(self: @TContractState, id: u256) -> bool; @@ -50,15 +51,23 @@ trait IRBACTimelock { fn get_timestamp(self: @TContractState, id: u256) -> u256; fn get_min_delay(self: @TContractState) -> u256; fn hash_operation_batch( - self: @TContractState, calls: Span, predecessor: u256, salt: u256 + self: @TContractState, calls: Span, predecessor: u256, salt: u256, ) -> u256; } +// refer to documentation at +// https://github.com/smartcontractkit/ccip-owner-contracts/blob/4af63f531ee5c3a79f1d070292d848c63b3e4ef9/src/RBACTimelock.sol #[starknet::contract] mod RBACTimelock { use core::traits::TryInto; use core::starknet::SyscallResultTrait; - use starknet::{ContractAddress, call_contract_syscall}; + use starknet::{ + ContractAddress, call_contract_syscall, StorageAddress, + storage::{ + Map, StoragePointerReadAccess, StoragePointerWriteAccess, StorageMapReadAccess, + StorageMapWriteAccess, StoragePathEntry, + }, + }; use openzeppelin::{ access::accesscontrol::AccessControlComponent, introspection::src5::SRC5Component, token::erc1155::erc1155_receiver::ERC1155ReceiverComponent, @@ -74,7 +83,7 @@ mod RBACTimelock { component!(path: AccessControlComponent, storage: access_control, event: AccessControlEvent); component!(path: EnumerableSetComponent, storage: set, event: EnumerableSetEvent); component!( - path: ERC1155ReceiverComponent, storage: erc1155_receiver, event: ERC1155ReceiverEvent + path: ERC1155ReceiverComponent, storage: erc1155_receiver, event: ERC1155ReceiverEvent, ); component!(path: ERC721ReceiverComponent, storage: erc721_receiver, event: ERC721ReceiverEvent); @@ -104,7 +113,7 @@ mod RBACTimelock { // EnumerableSet impl EnumerableSetInternalImpl = EnumerableSetComponent::InternalImpl; - // we use sn_keccak intead of keccak256 + // we use sn_keccak instead of keccak256 const ADMIN_ROLE: felt252 = selector!("ADMIN_ROLE"); const PROPOSER_ROLE: felt252 = selector!("PROPOSER_ROLE"); const EXECUTOR_ROLE: felt252 = selector!("EXECUTOR_ROLE"); @@ -112,7 +121,7 @@ mod RBACTimelock { const BYPASSER_ROLE: felt252 = selector!("BYPASSER_ROLE"); const _DONE_TIMESTAMP: u256 = 0x1; - const BLOCKED_FUNCTIONS: u256 = 'BLOCKED_FUNCTION_SELECTORS'; + const BLOCKED_FUNCTIONS: felt252 = 'BLOCKED_FUNCTION_SELECTORS'; #[storage] struct Storage { @@ -127,14 +136,14 @@ mod RBACTimelock { #[substorage(v0)] access_control: AccessControlComponent::Storage, // id -> timestamp - _timestamps: LegacyMap, // timestamp at which operation is ready to be executed - _min_delay: u256 + _timestamps: Map, // timestamp at which operation is ready to be executed + _min_delay: u256, } #[derive(Drop, starknet::Event)] struct MinDelayChange { old_duration: u256, - new_duration: u256 + new_duration: u256, } #[derive(Drop, starknet::Event)] @@ -154,7 +163,7 @@ mod RBACTimelock { #[derive(Drop, starknet::Event)] struct Cancelled { #[key] - id: u256 + id: u256, } #[derive(Drop, starknet::Event)] @@ -180,13 +189,13 @@ mod RBACTimelock { #[derive(Drop, starknet::Event)] struct FunctionSelectorBlocked { #[key] - selector: felt252 + selector: felt252, } #[derive(Drop, starknet::Event)] struct FunctionSelectorUnblocked { #[key] - selector: felt252 + selector: felt252, } #[event] @@ -208,7 +217,7 @@ mod RBACTimelock { CallExecuted: CallExecuted, BypasserCallExecuted: BypasserCallExecuted, FunctionSelectorBlocked: FunctionSelectorBlocked, - FunctionSelectorUnblocked: FunctionSelectorUnblocked + FunctionSelectorUnblocked: FunctionSelectorUnblocked, } @@ -220,58 +229,54 @@ mod RBACTimelock { proposers: Array, executors: Array, cancellers: Array, - bypassers: Array + bypassers: Array, ) { self.access_control.initializer(); self.erc1155_receiver.initializer(); self.erc721_receiver.initializer(); - self.access_control._set_role_admin(ADMIN_ROLE, ADMIN_ROLE); - self.access_control._set_role_admin(PROPOSER_ROLE, ADMIN_ROLE); - self.access_control._set_role_admin(EXECUTOR_ROLE, ADMIN_ROLE); - self.access_control._set_role_admin(CANCELLER_ROLE, ADMIN_ROLE); - self.access_control._set_role_admin(BYPASSER_ROLE, ADMIN_ROLE); + self.access_control.set_role_admin(ADMIN_ROLE, ADMIN_ROLE); + self.access_control.set_role_admin(PROPOSER_ROLE, ADMIN_ROLE); + self.access_control.set_role_admin(EXECUTOR_ROLE, ADMIN_ROLE); + self.access_control.set_role_admin(CANCELLER_ROLE, ADMIN_ROLE); + self.access_control.set_role_admin(BYPASSER_ROLE, ADMIN_ROLE); self.access_control._grant_role(ADMIN_ROLE, admin); let mut i = 0; - while i < proposers - .len() { - self.access_control._grant_role(PROPOSER_ROLE, *proposers.at(i)); - i += 1; - }; + while i < proposers.len() { + self.access_control._grant_role(PROPOSER_ROLE, *proposers.at(i)); + i += 1; + }; let mut i = 0; - while i < executors - .len() { - self.access_control._grant_role(EXECUTOR_ROLE, *executors.at(i)); - i += 1; - }; + while i < executors.len() { + self.access_control._grant_role(EXECUTOR_ROLE, *executors.at(i)); + i += 1; + }; let mut i = 0; - while i < cancellers - .len() { - self.access_control._grant_role(CANCELLER_ROLE, *cancellers.at(i)); - i += 1 - }; + while i < cancellers.len() { + self.access_control._grant_role(CANCELLER_ROLE, *cancellers.at(i)); + i += 1 + }; let mut i = 0; - while i < bypassers - .len() { - self.access_control._grant_role(BYPASSER_ROLE, *bypassers.at(i)); - i += 1 - }; + while i < bypassers.len() { + self.access_control._grant_role(BYPASSER_ROLE, *bypassers.at(i)); + i += 1 + }; self._min_delay.write(min_delay); self .emit( - Event::MinDelayChange(MinDelayChange { old_duration: 0, new_duration: min_delay, }) + Event::MinDelayChange(MinDelayChange { old_duration: 0, new_duration: min_delay }), ) } #[abi(embed_v0)] impl RBACTimelockImpl of super::IRBACTimelock { fn schedule_batch( - ref self: ContractState, calls: Span, predecessor: u256, salt: u256, delay: u256 + ref self: ContractState, calls: Span, predecessor: u256, salt: u256, delay: u256, ) { self._assert_only_role_or_admin_role(PROPOSER_ROLE); @@ -279,32 +284,28 @@ mod RBACTimelock { self._schedule(id, delay); let mut i = 0; - while i < calls - .len() { - let call = *calls.at(i); - assert( - !self.set.contains(BLOCKED_FUNCTIONS, call.selector.into()), - 'selector is blocked' + while i < calls.len() { + let call = *calls.at(i); + assert(!self.set.contains(BLOCKED_FUNCTIONS, call.selector), 'selector is blocked'); + + self + .emit( + Event::CallScheduled( + CallScheduled { + id: id, + index: i.into(), + target: call.target, + selector: call.selector, + data: call.data, + predecessor: predecessor, + salt: salt, + delay: delay, + }, + ), ); - self - .emit( - Event::CallScheduled( - CallScheduled { - id: id, - index: i.into(), - target: call.target, - selector: call.selector, - data: call.data, - predecessor: predecessor, - salt: salt, - delay: delay - } - ) - ); - - i += 1; - } + i += 1; + } } fn cancel(ref self: ContractState, id: u256) { @@ -318,7 +319,7 @@ mod RBACTimelock { } fn execute_batch( - ref self: ContractState, calls: Span, predecessor: u256, salt: u256 + ref self: ContractState, calls: Span, predecessor: u256, salt: u256, ) { self._assert_only_role_or_admin_role(EXECUTOR_ROLE); @@ -327,24 +328,23 @@ mod RBACTimelock { self._before_call(id, predecessor); let mut i = 0; - while i < calls - .len() { - let call = *(calls.at(i)); - self._execute(call); - self - .emit( - Event::CallExecuted( - CallExecuted { - id: id, - index: i.into(), - target: call.target, - selector: call.selector, - data: call.data - } - ) - ); - i += 1; - }; + while i < calls.len() { + let call = *(calls.at(i)); + self._execute(call); + self + .emit( + Event::CallExecuted( + CallExecuted { + id: id, + index: i.into(), + target: call.target, + selector: call.selector, + data: call.data, + }, + ), + ); + i += 1; + }; self._after_call(id); } @@ -353,24 +353,23 @@ mod RBACTimelock { self._assert_only_role_or_admin_role(BYPASSER_ROLE); let mut i = 0; - while i < calls - .len() { - let call = *calls.at(i); - self._execute(call); - self - .emit( - Event::BypasserCallExecuted( - BypasserCallExecuted { - index: i.into(), - target: call.target, - selector: call.selector, - data: call.data - } - ) - ); - - i += 1; - } + while i < calls.len() { + let call = *calls.at(i); + self._execute(call); + self + .emit( + Event::BypasserCallExecuted( + BypasserCallExecuted { + index: i.into(), + target: call.target, + selector: call.selector, + data: call.data, + }, + ), + ); + + i += 1; + } } // @@ -385,8 +384,8 @@ mod RBACTimelock { Event::MinDelayChange( MinDelayChange { old_duration: self._min_delay.read(), new_duration: new_delay, - } - ) + }, + ), ); self._min_delay.write(new_delay); } @@ -394,13 +393,13 @@ mod RBACTimelock { fn block_function_selector(ref self: ContractState, selector: felt252) { self.access_control.assert_only_role(ADMIN_ROLE); - // cast to u256 because that's what set stores - if self.set.add(BLOCKED_FUNCTIONS, selector.into()) { + // cast to u256 because that's what set stores + if self.set.add(BLOCKED_FUNCTIONS, selector) { self .emit( Event::FunctionSelectorBlocked( - FunctionSelectorBlocked { selector: selector } - ) + FunctionSelectorBlocked { selector: selector }, + ), ); } } @@ -408,12 +407,12 @@ mod RBACTimelock { fn unblock_function_selector(ref self: ContractState, selector: felt252) { self.access_control.assert_only_role(ADMIN_ROLE); - if self.set.remove(BLOCKED_FUNCTIONS, selector.into()) { + if self.set.remove(BLOCKED_FUNCTIONS, selector) { self .emit( Event::FunctionSelectorUnblocked( - FunctionSelectorUnblocked { selector: selector } - ) + FunctionSelectorUnblocked { selector: selector }, + ), ); } } @@ -422,13 +421,13 @@ mod RBACTimelock { // VIEW ONLY // - fn get_blocked_function_selector_count(self: @ContractState) -> u256 { + fn get_blocked_function_selector_count(self: @ContractState) -> usize { self.set.length(BLOCKED_FUNCTIONS) } - fn get_blocked_function_selector_at(self: @ContractState, index: u256) -> felt252 { + fn get_blocked_function_selector_at(self: @ContractState, index: usize) -> felt252 { // cast from u256 to felt252 should never error - self.set.at(BLOCKED_FUNCTIONS, index).try_into().unwrap() + self.set.at(BLOCKED_FUNCTIONS, index) } fn is_operation(self: @ContractState, id: u256) -> bool { @@ -457,7 +456,7 @@ mod RBACTimelock { } fn hash_operation_batch( - self: @ContractState, calls: Span, predecessor: u256, salt: u256 + self: @ContractState, calls: Span, predecessor: u256, salt: u256, ) -> u256 { _hash_operation_batch(calls, predecessor, salt) } @@ -483,7 +482,8 @@ mod RBACTimelock { fn _before_call(self: @ContractState, id: u256, predecessor: u256) { assert(self.is_operation_ready(id), 'rbact: operation not ready'); assert( - predecessor == 0 || self.is_operation_done(predecessor), 'rbact: missing dependency' + predecessor == 0 || self.is_operation_done(predecessor), + 'rbact: missing dependency', ); } diff --git a/contracts/src/account.cairo b/contracts/src/account.cairo index dcda746e8..ea47fe093 100644 --- a/contracts/src/account.cairo +++ b/contracts/src/account.cairo @@ -1,11 +1,14 @@ -// copied from https://raw.githubusercontent.com/OpenZeppelin/cairo-contracts/861fc416f87addbe23a3b47f9d19ab27c10d5dc8/src/presets/account.cairo (0.9.0) +// copied from +// https://raw.githubusercontent.com/OpenZeppelin/cairo-contracts/861fc416f87addbe23a3b47f9d19ab27c10d5dc8/src/presets/account.cairo +// (0.9.0) // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v0.9.0 (presets/account.cairo) /// # Account Preset /// -/// OpenZeppelin's basic account which can change its public key and declare, deploy, or call contracts. +/// OpenZeppelin's basic account which can change its public key and declare, deploy, or call +/// contracts. #[starknet::contract(account)] mod Account { use openzeppelin::account::AccountComponent; @@ -38,7 +41,7 @@ mod Account { #[substorage(v0)] account: AccountComponent::Storage, #[substorage(v0)] - src5: SRC5Component::Storage + src5: SRC5Component::Storage, } #[event] @@ -47,7 +50,7 @@ mod Account { #[flat] AccountEvent: AccountComponent::Event, #[flat] - SRC5Event: SRC5Component::Event + SRC5Event: SRC5Component::Event, } #[constructor] diff --git a/contracts/src/cairo_project.toml b/contracts/src/cairo_project.toml deleted file mode 100644 index df2c45c01..000000000 --- a/contracts/src/cairo_project.toml +++ /dev/null @@ -1,2 +0,0 @@ -[crate_roots] -chainlink = "." diff --git a/contracts/src/emergency/sequencer_uptime_feed.cairo b/contracts/src/emergency/sequencer_uptime_feed.cairo index fad5ad8f7..c66f8454a 100644 --- a/contracts/src/emergency/sequencer_uptime_feed.cairo +++ b/contracts/src/emergency/sequencer_uptime_feed.cairo @@ -20,6 +20,7 @@ mod SequencerUptimeFeed { use starknet::storage_write_syscall; use starknet::storage_address_from_base_and_offset; use starknet::class_hash::ClassHash; + use starknet::storage::Map; use box::BoxTrait; use traits::Into; @@ -35,7 +36,7 @@ mod SequencerUptimeFeed { use chainlink::ocr2::aggregator::Round; use chainlink::ocr2::aggregator::IAggregator; use chainlink::ocr2::aggregator::{Transmission}; - use chainlink::libraries::upgradeable::Upgradeable; + use chainlink::libraries::upgrades::v1::upgradeable::Upgradeable; component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); component!(path: AccessControlComponent, storage: access_control, event: AccessControlEvent); @@ -58,7 +59,7 @@ mod SequencerUptimeFeed { // l1 sender is an starknet validator ethereum address _l1_sender: EthAddress, // maps round id to round transmission - _round_transmissions: LegacyMap, + _round_transmissions: Map, _latest_round_id: u128, } @@ -80,7 +81,7 @@ mod SequencerUptimeFeed { struct RoundUpdated { status: u128, #[key] - updated_at: u64 + updated_at: u64, } #[derive(Drop, starknet::Event)] @@ -89,7 +90,7 @@ mod SequencerUptimeFeed { round_id: u128, #[key] started_by: EthAddress, - started_at: u64 + started_at: u64, } #[derive(Drop, starknet::Event)] @@ -98,7 +99,7 @@ mod SequencerUptimeFeed { #[key] round_id: u128, #[key] - timestamp: u64 + timestamp: u64, } #[derive(Drop, starknet::Event)] @@ -108,7 +109,7 @@ mod SequencerUptimeFeed { latest_timestamp: u64, incoming_status: u128, #[key] - incoming_timestamp: u64 + incoming_timestamp: u64, } #[derive(Drop, starknet::Event)] @@ -116,7 +117,7 @@ mod SequencerUptimeFeed { #[key] from_address: EthAddress, #[key] - to_address: EthAddress + to_address: EthAddress, } #[abi(embed_v0)] @@ -177,7 +178,8 @@ mod SequencerUptimeFeed { #[l1_handler] fn update_status(ref self: ContractState, from_address: felt252, status: u128, timestamp: u64) { - // Cairo enforces from_address to be a felt252 on the method signature, but we can cast it right after + // Cairo enforces from_address to be a felt252 on the method signature, but we can cast it + // right after let from_address: EthAddress = from_address.try_into().unwrap(); assert(self._l1_sender.read() == from_address, 'EXPECTED_FROM_BRIDGE_ONLY'); @@ -192,9 +194,9 @@ mod SequencerUptimeFeed { latest_status: latest_round.answer, latest_timestamp: latest_round.transmission_timestamp, incoming_status: status, - incoming_timestamp: timestamp - } - ) + incoming_timestamp: timestamp, + }, + ), ); return (); } @@ -222,8 +224,8 @@ mod SequencerUptimeFeed { self .emit( Event::L1SenderTransferred( - L1SenderTransferred { from_address: old_address, to_address: address } - ) + L1SenderTransferred { from_address: old_address, to_address: address }, + ), ); } } @@ -255,14 +257,14 @@ mod SequencerUptimeFeed { } fn _initializer( - ref self: ContractState, initial_status: u128, owner_address: ContractAddress + ref self: ContractState, initial_status: u128, owner_address: ContractAddress, ) { self.ownable.initializer(owner_address); - self.access_control.initializer(); + self.access_control.initializer(true); let round_id = 1_u128; let timestamp = starknet::info::get_block_timestamp(); let from_address = EthAddress { - address: 0 + address: 0, }; // initial round is set by the constructor, not by an L1 sender self._record_round(from_address, round_id, initial_status, timestamp); } @@ -272,7 +274,7 @@ mod SequencerUptimeFeed { sender: EthAddress, round_id: u128, status: u128, - timestamp: u64 + timestamp: u64, ) { self._latest_round_id.write(round_id); let block_info = starknet::info::get_block_info().unbox(); @@ -290,14 +292,14 @@ mod SequencerUptimeFeed { self .emit( Event::NewRound( - NewRound { round_id: round_id, started_by: sender, started_at: timestamp } - ) + NewRound { round_id: round_id, started_by: sender, started_at: timestamp }, + ), ); self .emit( Event::AnswerUpdated( - AnswerUpdated { current: status, round_id: round_id, timestamp: timestamp } - ) + AnswerUpdated { current: status, round_id: round_id, timestamp: timestamp }, + ), ); } @@ -309,9 +311,9 @@ mod SequencerUptimeFeed { .emit( Event::RoundUpdated( RoundUpdated { - status: round.answer, updated_at: round.transmission_timestamp - } - ) + status: round.answer, updated_at: round.transmission_timestamp, + }, + ), ); } } diff --git a/contracts/src/libraries.cairo b/contracts/src/libraries.cairo index fbf6911cd..b40c45682 100644 --- a/contracts/src/libraries.cairo +++ b/contracts/src/libraries.cairo @@ -1,6 +1,6 @@ mod access_control; mod token; -mod upgradeable; +mod upgrades; mod mocks; mod type_and_version; mod enumerable_set; diff --git a/contracts/src/libraries/access_control.cairo b/contracts/src/libraries/access_control.cairo index b0a910d55..4c4838e69 100644 --- a/contracts/src/libraries/access_control.cairo +++ b/contracts/src/libraries/access_control.cairo @@ -14,6 +14,7 @@ trait IAccessController { mod AccessControlComponent { use starknet::ContractAddress; use starknet::class_hash::ClassHash; + use starknet::storage::Map; use zeroable::Zeroable; use openzeppelin::access::ownable::OwnableComponent; @@ -23,7 +24,7 @@ mod AccessControlComponent { #[storage] struct Storage { _check_enabled: bool, - _access_list: LegacyMap, + _access_list: Map, } #[event] @@ -38,13 +39,13 @@ mod AccessControlComponent { #[derive(Drop, starknet::Event)] struct AddedAccess { #[key] - user: ContractAddress + user: ContractAddress, } #[derive(Drop, starknet::Event)] struct RemovedAccess { #[key] - user: ContractAddress + user: ContractAddress, } #[derive(Drop, starknet::Event)] @@ -61,7 +62,7 @@ mod AccessControlComponent { +Drop, > of super::IAccessController> { fn has_access( - self: @ComponentState, user: ContractAddress, data: Array + self: @ComponentState, user: ContractAddress, data: Array, ) -> bool { let has_access = self._access_list.read(user); if has_access { @@ -77,7 +78,7 @@ mod AccessControlComponent { } fn has_read_access( - self: @ComponentState, user: ContractAddress, data: Array + self: @ComponentState, user: ContractAddress, data: Array, ) -> bool { let _has_access = self.has_access(user, data); if _has_access { @@ -136,9 +137,11 @@ mod AccessControlComponent { impl Ownable: OwnableComponent::HasComponent, +Drop, > of InternalTrait { - fn initializer(ref self: ComponentState) { - self._check_enabled.write(true); - self.emit(Event::AccessControlEnabled(AccessControlEnabled {})); + fn initializer(ref self: ComponentState, check_enabled: bool) { + self._check_enabled.write(check_enabled); + if check_enabled { + self.emit(Event::AccessControlEnabled(AccessControlEnabled {})); + } } fn check_access(self: @ComponentState, user: ContractAddress) { diff --git a/contracts/src/libraries/enumerable_set.cairo b/contracts/src/libraries/enumerable_set.cairo index 791b35606..0b42461f4 100644 --- a/contracts/src/libraries/enumerable_set.cairo +++ b/contracts/src/libraries/enumerable_set.cairo @@ -1,6 +1,13 @@ #[starknet::component] mod EnumerableSetComponent { use core::array::ArrayTrait; + use starknet::{ + StorageAddress, + storage::{ + Map, StoragePointerReadAccess, StoragePointerWriteAccess, StorageMapReadAccess, + StorageMapWriteAccess, StoragePathEntry, + }, + }; // set is 1-indexed, not 0-indexed #[storage] @@ -8,14 +15,15 @@ mod EnumerableSetComponent { // access index by value // set_id -> item_value -> item_index // note: item_index is +1 because 0 means item is not in set - pub _indexes: LegacyMap::<(u256, u256), u256>, + pub _indexes: Map>, // access value by index - // set_id -> item_id -> item_value + // set_id -> item_index -> item_value // note: item_index is +1 because 0 means item is not in set - // note: _values.read(set_id, item_id) == 0, is only valid iff item_id <= _length.read(set_id) - pub _values: LegacyMap::<(u256, u256), u256>, + // note: _values.read(set_id, item_id) == 0, is only valid iff item_id <= + // _length.read(set_id) + pub _values: Map>, // set_id -> size of set - pub _length: LegacyMap + pub _length: Map, } #[event] @@ -25,15 +33,15 @@ mod EnumerableSetComponent { #[generate_trait] pub impl InternalImpl< - TContractState, +HasComponent + TContractState, +HasComponent, > of InternalTrait { - fn add(ref self: ComponentState, set_id: u256, value: u256) -> bool { + fn add(ref self: ComponentState, set_id: felt252, value: felt252) -> bool { if !self.contains(set_id, value) { // The value is stored at _length-1, but we add 1 to all indexes let index = self._length.read(set_id) + 1; - self._indexes.write((set_id, value), index); - self._values.write((set_id, index), value); - self._length.write(set_id, index); + self._indexes.entry(set_id).entry(value).write(index); + self._values.entry(set_id).entry(index).write(value); + self._length.entry(set_id).write(index); true } else { false @@ -42,52 +50,54 @@ mod EnumerableSetComponent { // swap target value with the last value in the set fn remove( - ref self: ComponentState, set_id: u256, target_value: u256 + ref self: ComponentState, set_id: felt252, target_value: felt252, ) -> bool { - let target_index = self._indexes.read((set_id, target_value)); + let target_index = self._indexes.entry(set_id).entry(target_value).read(); if target_index == 0 { false } else { - let last_index = self._length.read(set_id); - let last_value = self._values.read((set_id, last_index)); + let last_index = self._length.entry(set_id).read(); + let last_value = self._values.entry(set_id).entry(last_index).read(); // if we are NOT trying to remove the last element // update the last element mappings if last_index != target_index { - self._indexes.write((set_id, last_value), target_index); - self._values.write((set_id, target_index), last_value); + self._indexes.entry(set_id).entry(last_value).write(target_index); + self._values.entry(set_id).entry(target_index).write(last_value); } - // if we are removing the last element both target value and last_index - // refer to the same item. - self._indexes.write((set_id, target_value), 0); - self._values.write((set_id, last_index), 0); + // if we are removing the last element both target value and last_index + // refer to the same item. + self._indexes.entry(set_id).entry(target_value).write(0); + self._values.entry(set_id).entry(last_index).write(0); // decrement length of set by 1 - self._length.write(set_id, last_index - 1); + self._length.entry(set_id).write(last_index - 1); true } } - fn contains(self: @ComponentState, set_id: u256, value: u256) -> bool { - self._indexes.read((set_id, value)) != 0 + fn contains( + self: @ComponentState, set_id: felt252, value: felt252, + ) -> bool { + self._indexes.entry(set_id).entry(value).read() != 0 } - fn length(self: @ComponentState, set_id: u256) -> u256 { - self._length.read(set_id) + fn length(self: @ComponentState, set_id: felt252) -> usize { + self._length.entry(set_id).read() } - fn at(self: @ComponentState, set_id: u256, index: u256) -> u256 { + fn at(self: @ComponentState, set_id: felt252, index: usize) -> felt252 { assert(index != 0, 'set is 1-indexed'); - assert(index <= self._length.read(set_id), 'index out of bounds'); - self._values.read((set_id, index)) + assert(index <= self._length.entry(set_id).read(), 'index out of bounds'); + self._values.entry(set_id).entry(index).read() } - fn values(self: @ComponentState, set_id: u256) -> Array { + fn values(self: @ComponentState, set_id: felt252) -> Array { let len = self.length(set_id); - let mut result: Array = ArrayTrait::new(); + let mut result: Array = ArrayTrait::new(); let mut i = 1; while i <= len { diff --git a/contracts/src/libraries/mocks.cairo b/contracts/src/libraries/mocks.cairo index 5042203bc..8bdc83ac0 100644 --- a/contracts/src/libraries/mocks.cairo +++ b/contracts/src/libraries/mocks.cairo @@ -1,4 +1,5 @@ mod mock_upgradeable; +mod mock_owner_upgradeable; mod mock_non_upgradeable; mod mock_multisig_target; mod mock_enumerable_set; diff --git a/contracts/src/libraries/mocks/mock_enumerable_set.cairo b/contracts/src/libraries/mocks/mock_enumerable_set.cairo index e71c12cd6..51905a98b 100644 --- a/contracts/src/libraries/mocks/mock_enumerable_set.cairo +++ b/contracts/src/libraries/mocks/mock_enumerable_set.cairo @@ -1,11 +1,11 @@ #[starknet::interface] trait IMockEnumerableSet { - fn add(ref self: TContractState, set_id: u256, value: u256) -> bool; - fn remove(ref self: TContractState, set_id: u256, target_value: u256) -> bool; - fn contains(self: @TContractState, set_id: u256, value: u256) -> bool; - fn length(self: @TContractState, set_id: u256) -> u256; - fn at(self: @TContractState, set_id: u256, index: u256) -> u256; - fn values(self: @TContractState, set_id: u256) -> Array; + fn add(ref self: TContractState, set_id: felt252, value: felt252) -> bool; + fn remove(ref self: TContractState, set_id: felt252, target_value: felt252) -> bool; + fn contains(self: @TContractState, set_id: felt252, value: felt252) -> bool; + fn length(self: @TContractState, set_id: felt252) -> usize; + fn at(self: @TContractState, set_id: felt252, index: usize) -> felt252; + fn values(self: @TContractState, set_id: felt252) -> Array; } #[starknet::contract] @@ -32,22 +32,22 @@ mod MockEnumerableSet { #[abi(embed_v0)] impl MockEnumerableSetImpl of super::IMockEnumerableSet { - fn add(ref self: ContractState, set_id: u256, value: u256) -> bool { + fn add(ref self: ContractState, set_id: felt252, value: felt252) -> bool { self.set.add(set_id, value) } - fn remove(ref self: ContractState, set_id: u256, target_value: u256) -> bool { + fn remove(ref self: ContractState, set_id: felt252, target_value: felt252) -> bool { self.set.remove(set_id, target_value) } - fn contains(self: @ContractState, set_id: u256, value: u256) -> bool { + fn contains(self: @ContractState, set_id: felt252, value: felt252) -> bool { self.set.contains(set_id, value) } - fn length(self: @ContractState, set_id: u256) -> u256 { + fn length(self: @ContractState, set_id: felt252) -> usize { self.set.length(set_id) } - fn at(self: @ContractState, set_id: u256, index: u256) -> u256 { + fn at(self: @ContractState, set_id: felt252, index: usize) -> felt252 { self.set.at(set_id, index) } - fn values(self: @ContractState, set_id: u256) -> Array { + fn values(self: @ContractState, set_id: felt252) -> Array { self.set.values(set_id) } } diff --git a/contracts/src/libraries/mocks/mock_multisig_target.cairo b/contracts/src/libraries/mocks/mock_multisig_target.cairo index 2c0fd05ee..6520c3d0f 100644 --- a/contracts/src/libraries/mocks/mock_multisig_target.cairo +++ b/contracts/src/libraries/mocks/mock_multisig_target.cairo @@ -16,7 +16,7 @@ mod MockMultisigTarget { #[storage] struct Storage { value: felt252, - toggle: bool + toggle: bool, } #[abi(embed_v0)] diff --git a/contracts/src/libraries/mocks/mock_owner_upgradeable.cairo b/contracts/src/libraries/mocks/mock_owner_upgradeable.cairo new file mode 100644 index 000000000..ef6676e5e --- /dev/null +++ b/contracts/src/libraries/mocks/mock_owner_upgradeable.cairo @@ -0,0 +1,66 @@ +use starknet::class_hash::ClassHash; + +#[starknet::interface] +trait IFoo { + fn foo(self: @TContractState) -> bool; +} + +#[starknet::contract] +mod MockOwnerUpgradeable { + use starknet::class_hash::ClassHash; + use starknet::ContractAddress; + + use openzeppelin::access::ownable::OwnableComponent; + use openzeppelin::upgrades::UpgradeableComponent; + + use chainlink::libraries::upgrades::v2::owner_upgradeable::OwnerUpgradeableComponent; + + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent); + component!( + path: OwnerUpgradeableComponent, storage: owner_upgradeable, event: OwnerUpgradeableEvent, + ); + + #[abi(embed_v0)] + impl OwnableImpl = OwnableComponent::OwnableTwoStepImpl; + impl OwnableInternalImpl = OwnableComponent::InternalImpl; + + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl; + + #[abi(embed_v0)] + impl OwnerUpgradeableImpl = + OwnerUpgradeableComponent::OwnerUpgradeableImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + ownable: OwnableComponent::Storage, + #[substorage(v0)] + upgradeable: UpgradeableComponent::Storage, + #[substorage(v0)] + owner_upgradeable: OwnerUpgradeableComponent::Storage, + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + OwnableEvent: OwnableComponent::Event, + #[flat] + UpgradeableEvent: UpgradeableComponent::Event, + #[flat] + OwnerUpgradeableEvent: OwnerUpgradeableComponent::Event, + } + + #[constructor] + fn constructor(ref self: ContractState, owner: ContractAddress) { + self.ownable.initializer(owner); + } + + #[abi(embed_v0)] + impl FooImpl of super::IFoo { + fn foo(self: @ContractState) -> bool { + true + } + } +} diff --git a/contracts/src/libraries/mocks/mock_upgradeable.cairo b/contracts/src/libraries/mocks/mock_upgradeable.cairo index b83fa7f84..7fcb52b3d 100644 --- a/contracts/src/libraries/mocks/mock_upgradeable.cairo +++ b/contracts/src/libraries/mocks/mock_upgradeable.cairo @@ -10,7 +10,7 @@ trait IMockUpgradeable { mod MockUpgradeable { use starknet::class_hash::ClassHash; - use chainlink::libraries::upgradeable::Upgradeable; + use chainlink::libraries::upgrades::v1::upgradeable::Upgradeable; #[storage] struct Storage {} diff --git a/contracts/src/libraries/token/v1/erc677.cairo b/contracts/src/libraries/token/v1/erc677.cairo index 20fd6c54a..b82c38b6b 100644 --- a/contracts/src/libraries/token/v1/erc677.cairo +++ b/contracts/src/libraries/token/v1/erc677.cairo @@ -3,16 +3,17 @@ use starknet::ContractAddress; #[starknet::interface] trait IERC677 { fn transfer_and_call( - ref self: TContractState, to: ContractAddress, value: u256, data: Array + ref self: TContractState, to: ContractAddress, value: u256, data: Array, ) -> bool; } #[starknet::interface] trait IERC677Receiver { fn on_token_transfer( - ref self: TContractState, sender: ContractAddress, value: u256, data: Array + ref self: TContractState, sender: ContractAddress, value: u256, data: Array, ); - // implements EIP-165, where function selectors are defined by Ethereum ABI using the ethereum function signatures + // implements EIP-165, where function selectors are defined by Ethereum ABI using the ethereum + // function signatures fn supports_interface(ref self: TContractState, interface_id: u32) -> bool; } @@ -47,7 +48,7 @@ mod ERC677Component { #[key] to: ContractAddress, value: u256, - data: Array + data: Array, } #[embeddable_as(ERC677Impl)] @@ -61,7 +62,7 @@ mod ERC677Component { ref self: ComponentState, to: ContractAddress, value: u256, - data: Array + data: Array, ) -> bool { let sender = starknet::info::get_caller_address(); @@ -70,8 +71,8 @@ mod ERC677Component { self .emit( Event::TransferAndCall( - TransferAndCall { from: sender, to: to, value: value, data: data.clone(), } - ) + TransferAndCall { from: sender, to: to, value: value, data: data.clone() }, + ), ); let receiver = IERC677ReceiverDispatcher { contract_address: to }; diff --git a/contracts/src/libraries/token/v2/erc677.cairo b/contracts/src/libraries/token/v2/erc677.cairo index aa86d1c5f..d609172e0 100644 --- a/contracts/src/libraries/token/v2/erc677.cairo +++ b/contracts/src/libraries/token/v2/erc677.cairo @@ -5,7 +5,7 @@ const IERC677_ID: felt252 = 0x3c4538abc63e0cdf912cef3d2e1389d0b2c3f24ee0c06b2173 #[starknet::interface] trait IERC677 { fn transfer_and_call( - ref self: TContractState, to: ContractAddress, value: u256, data: Array + ref self: TContractState, to: ContractAddress, value: u256, data: Array, ) -> bool; } @@ -19,7 +19,7 @@ mod ERC677Component { use clone::Clone; use array::ArrayTCloneImpl; use chainlink::libraries::token::v2::erc677_receiver::{ - IERC677ReceiverDispatcher, IERC677ReceiverDispatcherTrait, IERC677_RECEIVER_ID + IERC677ReceiverDispatcher, IERC677ReceiverDispatcherTrait, IERC677_RECEIVER_ID, }; #[storage] @@ -38,7 +38,7 @@ mod ERC677Component { #[key] to: ContractAddress, value: u256, - data: Array + data: Array, } #[embeddable_as(ERC677Impl)] @@ -52,7 +52,7 @@ mod ERC677Component { ref self: ComponentState, to: ContractAddress, value: u256, - data: Array + data: Array, ) -> bool { let sender = starknet::info::get_caller_address(); @@ -61,8 +61,8 @@ mod ERC677Component { self .emit( Event::TransferAndCall( - TransferAndCall { from: sender, to: to, value: value, data: data.clone(), } - ) + TransferAndCall { from: sender, to: to, value: value, data: data.clone() }, + ), ); let receiver = ISRC5Dispatcher { contract_address: to }; diff --git a/contracts/src/libraries/token/v2/erc677_receiver.cairo b/contracts/src/libraries/token/v2/erc677_receiver.cairo index 11249d037..3e3799626 100644 --- a/contracts/src/libraries/token/v2/erc677_receiver.cairo +++ b/contracts/src/libraries/token/v2/erc677_receiver.cairo @@ -6,7 +6,7 @@ const IERC677_RECEIVER_ID: felt252 = #[starknet::interface] trait IERC677Receiver { fn on_token_transfer( - ref self: TContractState, sender: ContractAddress, value: u256, data: Array + ref self: TContractState, sender: ContractAddress, value: u256, data: Array, ); } @@ -31,7 +31,7 @@ mod ERC677ReceiverComponent { // ensure that the contract implements the IERC677Receiver interface +IERC677Receiver, impl SRC5: SRC5Component::HasComponent, - +Drop + +Drop, > of InternalTrait { /// Initializes the contract by registering the IERC677Receiver interface ID. /// This should be used inside the contract's constructor. diff --git a/contracts/src/libraries/upgrades.cairo b/contracts/src/libraries/upgrades.cairo new file mode 100644 index 000000000..f029fd6d3 --- /dev/null +++ b/contracts/src/libraries/upgrades.cairo @@ -0,0 +1,3 @@ +mod v1; +mod v2; +mod utils; diff --git a/contracts/src/libraries/upgrades/utils.cairo b/contracts/src/libraries/upgrades/utils.cairo new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/contracts/src/libraries/upgrades/utils.cairo @@ -0,0 +1 @@ + diff --git a/contracts/src/libraries/upgrades/v1.cairo b/contracts/src/libraries/upgrades/v1.cairo new file mode 100644 index 000000000..892aaa57e --- /dev/null +++ b/contracts/src/libraries/upgrades/v1.cairo @@ -0,0 +1 @@ +mod upgradeable; diff --git a/contracts/src/libraries/upgradeable.cairo b/contracts/src/libraries/upgrades/v1/upgradeable.cairo similarity index 77% rename from contracts/src/libraries/upgradeable.cairo rename to contracts/src/libraries/upgrades/v1/upgradeable.cairo index 6b9e6af73..7d714be7d 100644 --- a/contracts/src/libraries/upgradeable.cairo +++ b/contracts/src/libraries/upgrades/v1/upgradeable.cairo @@ -1,6 +1,8 @@ use starknet::class_hash::ClassHash; -// TODO: drop for OZ upgradeable +// DEPRECATED: Kept around for the starknet multisig. +// Use OZ for internal upgradeability and v2/owner_upgradeable.cairo for public owner +// upgradeablility #[starknet::interface] trait IUpgradeable { @@ -11,7 +13,7 @@ trait IUpgradeable { #[derive(Drop, starknet::Event)] struct Upgraded { #[key] - new_impl: ClassHash + new_impl: ClassHash, } mod Upgradeable { @@ -25,11 +27,11 @@ mod Upgradeable { // this method assumes replace_class_syscall has a very low possibility of being deprecated // but if it does, we will either have upgraded the contract to be non-upgradeable by then - // because the starknet ecosystem has stabilized or we will be able to upgrade the contract to the proxy pattern - // #[internal] + // because the starknet ecosystem has stabilized or we will be able to upgrade the contract to + // the proxy pattern #[internal] fn upgrade(new_impl: ClassHash) { assert(!new_impl.is_zero(), 'Class hash cannot be zero'); replace_class_syscall(new_impl).unwrap_syscall(); - // TODO: Upgraded(new_impl); + // TODO: Upgraded(new_impl); } } diff --git a/contracts/src/libraries/upgrades/v2.cairo b/contracts/src/libraries/upgrades/v2.cairo new file mode 100644 index 000000000..3179fa0f9 --- /dev/null +++ b/contracts/src/libraries/upgrades/v2.cairo @@ -0,0 +1 @@ +mod owner_upgradeable; diff --git a/contracts/src/libraries/upgrades/v2/owner_upgradeable.cairo b/contracts/src/libraries/upgrades/v2/owner_upgradeable.cairo new file mode 100644 index 000000000..200d66d7b --- /dev/null +++ b/contracts/src/libraries/upgrades/v2/owner_upgradeable.cairo @@ -0,0 +1,41 @@ +#[starknet::component] +mod OwnerUpgradeableComponent { + use openzeppelin::{ + access::ownable::{ + OwnableComponent, OwnableComponent::InternalTrait as OwnableInternalTrait, + }, + upgrades::{ + upgradeable::{ + UpgradeableComponent, + UpgradeableComponent::InternalTrait as UpgradeableInternalTrait, + }, + interface::IUpgradeable, + }, + }; + use starknet::class_hash::ClassHash; + + #[storage] + struct Storage {} + + #[event] + #[derive(Drop, starknet::Event)] + enum Event {} + + #[embeddable_as(OwnerUpgradeableImpl)] + pub impl OwnerUpgradeable< + TContractState, + +HasComponent, + impl Ownable: OwnableComponent::HasComponent, + impl Upgradeable: UpgradeableComponent::HasComponent, + +Drop, + > of IUpgradeable> { + fn upgrade(ref self: ComponentState, new_class_hash: ClassHash) { + let mut ownable_component = get_dep_component_mut!(ref self, Ownable); + ownable_component.assert_only_owner(); + + let mut upgradeable_component = get_dep_component_mut!(ref self, Upgradeable); + upgradeable_component.upgrade(new_class_hash); + } + } +} + diff --git a/contracts/src/mcms.cairo b/contracts/src/mcms.cairo index f795914a3..faa604e2b 100644 --- a/contracts/src/mcms.cairo +++ b/contracts/src/mcms.cairo @@ -2,13 +2,16 @@ use starknet::ContractAddress; use starknet::{ eth_signature::public_key_point_to_eth_address, EthAddress, secp256_trait::{ - Secp256Trait, Secp256PointTrait, recover_public_key, is_signature_entry_valid, Signature + Secp256Trait, Secp256PointTrait, recover_public_key, is_signature_entry_valid, Signature, }, - secp256k1::Secp256k1Point, SyscallResult, SyscallResultTrait + secp256k1::Secp256k1Point, SyscallResult, SyscallResultTrait, }; use alexandria_bytes::{Bytes, BytesTrait}; use alexandria_encoding::sol_abi::sol_bytes::SolBytesTrait; use alexandria_encoding::sol_abi::encode::SolAbiEncodeTrait; +use core::byte_array::ByteArrayTrait; +use core::traits::{Into, TryInto}; +use chainlink::utils::{keccak}; #[starknet::interface] trait IManyChainMultiSig { @@ -19,7 +22,7 @@ trait IManyChainMultiSig { metadata: RootMetadata, metadata_proof: Span, // note: v is a boolean and not uint8 - signatures: Array + signatures: Array, ); fn execute(ref self: TContractState, op: Op, proof: Span); fn set_config( @@ -28,7 +31,7 @@ trait IManyChainMultiSig { signer_groups: Span, group_quorums: Span, group_parents: Span, - clear_root: bool + clear_root: bool, ); fn get_config(self: @TContractState) -> Config; fn get_op_count(self: @TContractState) -> u64; @@ -40,7 +43,7 @@ trait IManyChainMultiSig { struct Signer { address: EthAddress, index: u8, - group: u8 + group: u8, } #[derive(Copy, Drop, Serde, starknet::Store, PartialEq)] @@ -49,7 +52,7 @@ struct RootMetadata { multisig: ContractAddress, pre_op_count: u64, post_op_count: u64, - override_previous_root: bool + override_previous_root: bool, } #[derive(Copy, Drop, Serde)] @@ -59,7 +62,7 @@ struct Op { nonce: u64, to: ContractAddress, selector: felt252, - data: Span + data: Span, } // does not implement Storage trait because structs cannot support arrays or maps @@ -67,17 +70,18 @@ struct Op { struct Config { signers: Span, group_quorums: Span, - group_parents: Span + group_parents: Span, } #[derive(Copy, Drop, Serde, starknet::Store, PartialEq)] struct ExpiringRootAndOpCount { root: u256, valid_until: u32, - op_count: u64 + op_count: u64, } -// based of https://github.com/starkware-libs/cairo/blob/1b747da1ec7e43a6fd0c0a4cbce302616408bc72/corelib/src/starknet/eth_signature.cairo#L25 +// based off +// https://github.com/starkware-libs/cairo/blob/1b747da1ec7e43a6fd0c0a4cbce302616408bc72/corelib/src/starknet/eth_signature.cairo#L25 pub fn recover_eth_ecdsa(msg_hash: u256, signature: Signature) -> Result { if !is_signature_entry_valid::(signature.r) { return Result::Err('Signature out of range'); @@ -96,6 +100,7 @@ pub fn to_u256(address: EthAddress) -> u256 { temp.into() } + pub fn verify_merkle_proof(proof: Span, root: u256, leaf: u256) -> bool { let mut computed_hash = leaf; @@ -115,7 +120,8 @@ fn hash_pair(a: u256, b: u256) -> u256 { } else { (b, a) }; - BytesTrait::new_empty().encode(lower).encode(higher).keccak() + let encoded = BytesTrait::new_empty().encode(lower).encode(higher); + keccak(@encoded.into()) } fn hash_op(op: Op) -> u256 { @@ -128,7 +134,7 @@ fn hash_op(op: Op) -> u256 { .encode(op.to) .encode(op.selector) // dynamic byte offset of data array (relative to beginning of op struct) - // (note: domain seperator not part of the op struct) + // (note: domain separator not part of the op struct) .encode(0xc0) // length prefix .encode(op.data.len()); @@ -139,7 +145,7 @@ fn hash_op(op: Op) -> u256 { encoded_leaf = encoded_leaf.encode(*op.data.at(i)); i += 1; }; - encoded_leaf.keccak() + keccak(@encoded_leaf.into()) } // keccak256("MANY_CHAIN_MULTI_SIG_DOMAIN_SEPARATOR_OP") @@ -158,54 +164,19 @@ fn hash_metadata(metadata: RootMetadata) -> u256 { .encode(metadata.post_op_count) .encode(metadata.override_previous_root); - encoded_metadata.keccak() + keccak(@encoded_metadata.into()) } fn eip_191_message_hash(msg: u256) -> u256 { - let mut eip_191_msg: Bytes = BytesTrait::new_empty(); - - // '\x19Ethereum Signed Message:\n32' in byte array - let prefix = array![ - 0x19, - 0x45, - 0x74, - 0x68, - 0x65, - 0x72, - 0x65, - 0x75, - 0x6d, - 0x20, - 0x53, - 0x69, - 0x67, - 0x6e, - 0x65, - 0x64, - 0x20, - 0x4d, - 0x65, - 0x73, - 0x73, - 0x61, - 0x67, - 0x65, - 0x3a, - 0x0a, - 0x33, - 0x32 - ]; + let mut eip_191_msg: ByteArray = "\x19Ethereum Signed Message:\n32"; + eip_191_msg.append_word(msg.high.into(), 16); + eip_191_msg.append_word(msg.low.into(), 16); - let mut i = 0; - while i < prefix.len() { - eip_191_msg.append_u8(*prefix.at(i)); - i += 1; - }; - eip_191_msg.append_u256(msg); - - eip_191_msg.keccak() + keccak(@eip_191_msg) } +// refer to docmentation at +// https://github.com/smartcontractkit/ccip-owner-contracts/blob/4af63f531ee5c3a79f1d070292d848c63b3e4ef9/src/ManyChainMultiSig.sol #[starknet::contract] mod ManyChainMultiSig { use core::array::ArrayTrait; @@ -213,14 +184,20 @@ mod ManyChainMultiSig { use core::array::SpanTrait; use core::dict::Felt252Dict; use core::traits::PanicDestruct; + use chainlink::utils::{keccak}; use super::{ ExpiringRootAndOpCount, Config, Signer, RootMetadata, Op, Signature, recover_eth_ecdsa, to_u256, verify_merkle_proof, hash_op, hash_metadata, eip_191_message_hash, - MANY_CHAIN_MULTI_SIG_DOMAIN_SEPARATOR_OP, MANY_CHAIN_MULTI_SIG_DOMAIN_SEPARATOR_METADATA + MANY_CHAIN_MULTI_SIG_DOMAIN_SEPARATOR_OP, MANY_CHAIN_MULTI_SIG_DOMAIN_SEPARATOR_METADATA, }; use starknet::{ EthAddress, EthAddressZeroable, EthAddressIntoFelt252, ContractAddress, - call_contract_syscall + call_contract_syscall, + storage::{ + Map, StoragePointerReadAccess, StoragePointerWriteAccess, StorageMapReadAccess, + StorageMapWriteAccess, StoragePathEntry, + }, + StorageAddress, }; use openzeppelin::access::ownable::OwnableComponent; @@ -242,17 +219,17 @@ mod ManyChainMultiSig { #[substorage(v0)] ownable: OwnableComponent::Storage, // s_signers is used to easily validate the existence of the signer by its address. - s_signers: LegacyMap, - // begin s_config (defined in storage bc Config struct cannot support maps) + s_signers: Map, + // begin s_config (defined in storage bc Config struct cannot support maps) _s_config_signers_len: u8, - _s_config_signers: LegacyMap, + _s_config_signers: Map, // no _s_config_group_len because there are always 32 groups - _s_config_group_quorums: LegacyMap, - _s_config_group_parents: LegacyMap, + _s_config_group_quorums: Map, + _s_config_group_parents: Map, // end s_config - s_seen_signed_hashes: LegacyMap, + s_seen_signed_hashes: Map, s_expiring_root_and_op_count: ExpiringRootAndOpCount, - s_root_metadata: RootMetadata + s_root_metadata: RootMetadata, } #[derive(Drop, starknet::Event)] @@ -270,7 +247,7 @@ mod ManyChainMultiSig { to: ContractAddress, selector: felt252, data: Span, - // no value because value is sent through ERC20 tokens, even the native STRK token + // no "value" field because native STRK token is an ERC-20 token } #[derive(Drop, starknet::Event)] @@ -303,80 +280,79 @@ mod ManyChainMultiSig { metadata: RootMetadata, metadata_proof: Span, // note: v is a boolean and not uint8 - mut signatures: Array + mut signatures: Array, ) { - let encoded_root: Bytes = BytesTrait::new_empty().encode(root).encode(valid_until); + let encoded_root = BytesTrait::new_empty().encode(root).encode(valid_until); - let msg_hash = eip_191_message_hash(encoded_root.keccak()); + let msg_hash = eip_191_message_hash(keccak(@encoded_root.into())); assert(!self.s_seen_signed_hashes.read(msg_hash), 'signed hash already seen'); let mut prev_address = EthAddressZeroable::zero(); let mut group_vote_counts: Felt252Dict = Default::default(); - while let Option::Some(signature) = signatures - .pop_front() { - let signer_address = match recover_eth_ecdsa(msg_hash, signature) { - Result::Ok(signer_address) => signer_address, - Result::Err(e) => panic_with_felt252(e), - }; + while let Option::Some(signature) = signatures.pop_front() { + let signer_address = match recover_eth_ecdsa(msg_hash, signature) { + Result::Ok(signer_address) => signer_address, + Result::Err(e) => panic_with_felt252(e), + }; + assert( + to_u256(prev_address) < to_u256(signer_address.clone()), + 'signer address must increase', + ); + prev_address = signer_address; - assert( - to_u256(prev_address) < to_u256(signer_address.clone()), - 'signer address must increase' - ); - prev_address = signer_address; - - let signer = self.get_signer_by_address(signer_address); - assert(signer.address == signer_address, 'invalid signer'); - - let mut group = signer.group; - loop { - let counts = group_vote_counts.get(group.into()); - group_vote_counts.insert(group.into(), counts + 1); - if counts + 1 != self._s_config_group_quorums.read(group) { - break; - } - if group == 0 { - // reached root - break; - } - group = self._s_config_group_parents.read(group) - }; + let signer = self.get_signer_by_address(signer_address); + assert(signer.address == signer_address, 'invalid signer'); + + let mut group = signer.group; + loop { + let counts = group_vote_counts.get(group.into()); + group_vote_counts.insert(group.into(), counts + 1); + if counts + 1 != self._s_config_group_quorums.read(group) { + break; + } + if group == 0 { + // reached root + break; + } + group = self._s_config_group_parents.read(group) }; + }; let root_group_quorum = self._s_config_group_quorums.read(0); assert(root_group_quorum > 0, 'root group missing quorum'); assert(group_vote_counts.get(0) >= root_group_quorum, 'insufficient signers'); assert( valid_until.into() >= starknet::info::get_block_timestamp(), - 'valid until has passed' + 'valid until has passed', ); // verify metadataProof let hashed_metadata_leaf = hash_metadata(metadata); assert( verify_merkle_proof(metadata_proof, root, hashed_metadata_leaf), - 'proof verification failed' + 'proof verification failed', ); // maybe move to beginning of function assert( starknet::info::get_tx_info().unbox().chain_id.into() == metadata.chain_id, - 'wrong chain id' + 'wrong chain id', ); assert( starknet::info::get_contract_address() == metadata.multisig, - 'wrong multisig address' + 'wrong multisig address', ); let op_count = self.s_expiring_root_and_op_count.read().op_count; let current_root_metadata = self.s_root_metadata.read(); - // new root can be set only if the current op_count is the expected post op count (unless an override is requested) + // new root can be set only if the current op_count is the expected post op count + // (unless an override is requested) assert( op_count == current_root_metadata.post_op_count || current_root_metadata.override_previous_root, - 'pending operations remain' + 'pending operations remain', ); assert(op_count == metadata.pre_op_count, 'wrong pre-operation count'); assert(metadata.pre_op_count <= metadata.post_op_count, 'wrong post-operation count'); @@ -386,15 +362,15 @@ mod ManyChainMultiSig { .s_expiring_root_and_op_count .write( ExpiringRootAndOpCount { - root: root, valid_until: valid_until, op_count: metadata.pre_op_count - } + root: root, valid_until: valid_until, op_count: metadata.pre_op_count, + }, ); self.s_root_metadata.write(metadata); self .emit( Event::NewRoot( - NewRoot { root: root, valid_until: valid_until, metadata: metadata, } - ) + NewRoot { root: root, valid_until: valid_until, metadata: metadata }, + ), ); } @@ -407,12 +383,12 @@ mod ManyChainMultiSig { .read() .post_op_count > current_expiring_root_and_op_count .op_count, - 'post-operation count reached' + 'post-operation count reached', ); assert( starknet::info::get_tx_info().unbox().chain_id.into() == op.chain_id, - 'wrong chain id' + 'wrong chain id', ); assert(starknet::info::get_contract_address() == op.multisig, 'wrong multisig address'); @@ -421,7 +397,7 @@ mod ManyChainMultiSig { current_expiring_root_and_op_count .valid_until .into() >= starknet::info::get_block_timestamp(), - 'root has expired' + 'root has expired', ); assert(op.nonce == current_expiring_root_and_op_count.op_count, 'wrong nonce'); @@ -431,7 +407,7 @@ mod ManyChainMultiSig { assert( verify_merkle_proof(proof, current_expiring_root_and_op_count.root, hashed_op_leaf), - 'proof verification failed' + 'proof verification failed', ); let mut new_expiring_root_and_op_count = current_expiring_root_and_op_count; @@ -444,9 +420,9 @@ mod ManyChainMultiSig { .emit( Event::OpExecuted( OpExecuted { - nonce: op.nonce, to: op.to, selector: op.selector, data: op.data - } - ) + nonce: op.nonce, to: op.to, selector: op.selector, data: op.data, + }, + ), ); } @@ -456,13 +432,13 @@ mod ManyChainMultiSig { signer_groups: Span, group_quorums: Span, group_parents: Span, - clear_root: bool + clear_root: bool, ) { self.ownable.assert_only_owner(); assert( signer_addresses.len() != 0 && signer_addresses.len() <= MAX_NUM_SIGNERS.into(), - 'out of bound signers len' + 'out of bound signers len', ); assert(signer_addresses.len() == signer_groups.len(), 'signer groups len mismatch'); @@ -470,20 +446,19 @@ mod ManyChainMultiSig { assert( group_quorums.len() == NUM_GROUPS.into() && group_quorums.len() == group_parents.len(), - 'wrong group quorums/parents len' + 'wrong group quorums/parents len', ); let mut group_children_counts: Felt252Dict = Default::default(); let mut i = 0; - while i < signer_groups - .len() { - let group = *signer_groups.at(i); - assert(group < NUM_GROUPS, 'out of bounds group'); - // increment count for each group - group_children_counts - .insert(group.into(), group_children_counts.get(group.into()) + 1); - i += 1; - }; + while i < signer_groups.len() { + let group = *signer_groups.at(i); + assert(group < NUM_GROUPS, 'out of bounds group'); + // increment count for each group + group_children_counts + .insert(group.into(), group_children_counts.get(group.into()) + 1); + i += 1; + }; let mut j = 0; while j < NUM_GROUPS { @@ -502,14 +477,15 @@ mod ManyChainMultiSig { } else { assert( group_children_counts.get(i.into()) >= *group_quorums.at(i.into()), - 'quorum impossible' + 'quorum impossible', ); group_children_counts .insert( - group_parent.into(), group_children_counts.get(group_parent.into()) + 1 + group_parent.into(), group_children_counts.get(group_parent.into()) + 1, ); - // the above line clobbers group_children_counts[0] in last iteration, don't use it after the loop ends + // the above line clobbers group_children_counts[0] in last iteration, don't use + // it after the loop ends } j += 1; }; @@ -521,11 +497,11 @@ mod ManyChainMultiSig { while i < signers_len { let mut old_signer = self._s_config_signers.read(i); let empty_signer = Signer { - address: EthAddressZeroable::zero(), index: 0, group: 0 + address: EthAddressZeroable::zero(), index: 0, group: 0, }; // reset s_signers self.s_signers.write(old_signer.address, empty_signer); - // reset _s_config_signers + // reset _s_config_signers self._s_config_signers.write(i.into(), empty_signer); i += 1; }; @@ -543,27 +519,25 @@ mod ManyChainMultiSig { let mut signers = ArrayTrait::::new(); let mut prev_signer_address = EthAddressZeroable::zero(); let mut i: u8 = 0; - while i - .into() < signer_addresses - .len() { - let signer_address = *signer_addresses.at(i.into()); - assert( - to_u256(prev_signer_address) < to_u256(signer_address), - 'signer addresses not sorted' - ); + while i.into() < signer_addresses.len() { + let signer_address = *signer_addresses.at(i.into()); + assert( + to_u256(prev_signer_address) < to_u256(signer_address), + 'signer addresses not sorted', + ); - let signer = Signer { - address: signer_address, index: i, group: *signer_groups.at(i.into()) - }; + let signer = Signer { + address: signer_address, index: i, group: *signer_groups.at(i.into()), + }; - self.s_signers.write(signer_address, signer); - self._s_config_signers.write(i.into(), signer); + self.s_signers.write(signer_address, signer); + self._s_config_signers.write(i.into(), signer); - signers.append(signer); + signers.append(signer); - prev_signer_address = signer_address; - i += 1; - }; + prev_signer_address = signer_address; + i += 1; + }; // length will always be less than MAX_NUM_SIGNERS so try_into will never panic self._s_config_signers_len.write(signer_addresses.len().try_into().unwrap()); @@ -581,8 +555,8 @@ mod ManyChainMultiSig { multisig: starknet::info::get_contract_address(), pre_op_count: op_count, post_op_count: op_count, - override_previous_root: true - } + override_previous_root: true, + }, ); } @@ -595,9 +569,9 @@ mod ManyChainMultiSig { group_quorums: group_quorums, group_parents: group_parents, }, - is_root_cleared: clear_root - } - ) + is_root_cleared: clear_root, + }, + ), ); } @@ -625,7 +599,7 @@ mod ManyChainMultiSig { Config { signers: signers.span(), group_quorums: group_quorums.span(), - group_parents: group_parents.span() + group_parents: group_parents.span(), } } @@ -646,7 +620,10 @@ mod ManyChainMultiSig { #[generate_trait] impl InternalFunctions of InternalFunctionsTrait { fn _execute( - ref self: ContractState, target: ContractAddress, selector: felt252, data: Span + ref self: ContractState, + target: ContractAddress, + selector: felt252, + data: Span, ) { let _response = call_contract_syscall(target, selector, data).unwrap_syscall(); } diff --git a/contracts/src/multisig.cairo b/contracts/src/multisig.cairo index b1422df45..e3c06f8fd 100644 --- a/contracts/src/multisig.cairo +++ b/contracts/src/multisig.cairo @@ -3,10 +3,8 @@ use option::OptionTrait; use starknet::ContractAddress; use starknet::class_hash::ClassHash; -fn assert_unique_values< - T, impl TCopy: Copy, impl TDrop: Drop, impl TPartialEq: PartialEq, ->( - a: @Array:: +fn assert_unique_values, impl TDrop: Drop, impl TPartialEq: PartialEq>( + a: @Array::, ) { let len = a.len(); _assert_unique_values_loop(a, len, 0_usize, 1_usize); @@ -15,7 +13,7 @@ fn assert_unique_values< fn _assert_unique_values_loop< T, impl TCopy: Copy, impl TDrop: Drop, impl TPartialEq: PartialEq, >( - a: @Array::, len: usize, j: usize, k: usize + a: @Array::, len: usize, j: usize, k: usize, ) { if j >= len { return (); @@ -53,7 +51,7 @@ trait IMultisig { ref self: TContractState, to: ContractAddress, function_selector: felt252, - calldata: Array + calldata: Array, ); fn confirm_transaction(ref self: TContractState, nonce: u128); fn revoke_confirmation(ref self: TContractState, nonce: u128); @@ -61,7 +59,7 @@ trait IMultisig { fn set_threshold(ref self: TContractState, threshold: usize); fn set_signers(ref self: TContractState, signers: Array); fn set_signers_and_threshold( - ref self: TContractState, signers: Array, threshold: usize + ref self: TContractState, signers: Array, threshold: usize, ); } @@ -90,9 +88,10 @@ mod Multisig { use starknet::storage_read_syscall; use starknet::storage_write_syscall; use starknet::class_hash::ClassHash; + use starknet::storage::Map; use chainlink::libraries::type_and_version::ITypeAndVersion; - use chainlink::libraries::upgradeable::{Upgradeable, IUpgradeable}; + use chainlink::libraries::upgrades::v1::upgradeable::{Upgradeable, IUpgradeable}; #[event] #[derive(Drop, starknet::Event)] @@ -112,7 +111,7 @@ mod Multisig { #[key] nonce: u128, #[key] - to: ContractAddress + to: ContractAddress, } #[derive(Drop, starknet::Event)] @@ -120,7 +119,7 @@ mod Multisig { #[key] signer: ContractAddress, #[key] - nonce: u128 + nonce: u128, } #[derive(Drop, starknet::Event)] @@ -128,7 +127,7 @@ mod Multisig { #[key] signer: ContractAddress, #[key] - nonce: u128 + nonce: u128, } #[derive(Drop, starknet::Event)] @@ -136,32 +135,32 @@ mod Multisig { #[key] executor: ContractAddress, #[key] - nonce: u128 + nonce: u128, } #[derive(Drop, starknet::Event)] struct SignersSet { #[key] - signers: Array + signers: Array, } #[derive(Drop, starknet::Event)] struct ThresholdSet { #[key] - threshold: usize + threshold: usize, } #[storage] struct Storage { _threshold: usize, - _signers: LegacyMap, - _is_signer: LegacyMap, + _signers: Map, + _is_signer: Map, _signers_len: usize, _tx_valid_since: u128, _next_nonce: u128, - _transactions: LegacyMap, - _transaction_calldata: LegacyMap<(u128, usize), felt252>, - _is_confirmed: LegacyMap<(u128, ContractAddress), bool>, + _transactions: Map, + _transaction_calldata: Map<(u128, usize), felt252>, + _is_confirmed: Map<(u128, ContractAddress), bool>, } #[constructor] @@ -174,7 +173,7 @@ mod Multisig { #[abi(embed_v0)] impl TypeAndVersionImpl of ITypeAndVersion { - fn type_and_version(self: @ContractState,) -> felt252 { + fn type_and_version(self: @ContractState) -> felt252 { 'Multisig 1.0.0' } } @@ -195,7 +194,7 @@ mod Multisig { self._is_signer.read(address) } - fn get_signers_len(self: @ContractState,) -> usize { + fn get_signers_len(self: @ContractState) -> usize { self._signers_len.read() } @@ -206,11 +205,11 @@ mod Multisig { signers } - fn get_threshold(self: @ContractState,) -> usize { + fn get_threshold(self: @ContractState) -> usize { self._threshold.read() } - fn get_transactions_len(self: @ContractState,) -> u128 { + fn get_transactions_len(self: @ContractState) -> u128 { self._next_nonce.read() } @@ -251,7 +250,7 @@ mod Multisig { function_selector: function_selector, calldata_len: calldata_len, executed: false, - confirmations: 0_usize + confirmations: 0_usize, }; self._transactions.write(nonce, transaction); @@ -261,8 +260,8 @@ mod Multisig { self .emit( Event::TransactionSubmitted( - TransactionSubmitted { signer: caller, nonce: nonce, to: to } - ) + TransactionSubmitted { signer: caller, nonce: nonce, to: to }, + ), ); self._next_nonce.write(nonce + 1_u128); } @@ -285,8 +284,8 @@ mod Multisig { self .emit( Event::TransactionConfirmed( - TransactionConfirmed { signer: caller, nonce: nonce } - ) + TransactionConfirmed { signer: caller, nonce: nonce }, + ), ); } @@ -307,7 +306,9 @@ mod Multisig { self .emit( - Event::ConfirmationRevoked(ConfirmationRevoked { signer: caller, nonce: nonce }) + Event::ConfirmationRevoked( + ConfirmationRevoked { signer: caller, nonce: nonce }, + ), ); } @@ -333,19 +334,20 @@ mod Multisig { self .emit( Event::TransactionExecuted( - TransactionExecuted { executor: caller, nonce: nonce } - ) + TransactionExecuted { executor: caller, nonce: nonce }, + ), ); let response = call_contract_syscall( - transaction.to, transaction.function_selector, calldata.span() + transaction.to, transaction.function_selector, calldata.span(), ) .unwrap_syscall(); - // TODO: this shouldn't be necessary. call_contract_syscall returns a Span, which - // is a serialized result, but returning a Span results in an error: + // TODO: this shouldn't be necessary. call_contract_syscall returns a Span, + // which is a serialized result, but returning a Span results in an error: // - // Trait has no implementation in context: core::serde::Serde::> + // Trait has no implementation in context: + // core::serde::Serde::> // // Cairo docs also have an example that returns a Span: // https://github.com/starkware-libs/cairo/blob/fe425d0893ff93a936bb3e8bbbac771033074bdb/docs/reference/src/components/cairo/modules/language_constructs/pages/contracts.adoc#L226 @@ -380,7 +382,7 @@ mod Multisig { } fn set_signers_and_threshold( - ref self: ContractState, signers: Array, threshold: usize + ref self: ContractState, signers: Array, threshold: usize, ) { self._require_multisig(); @@ -398,7 +400,7 @@ mod Multisig { #[generate_trait] impl InternalImpl of InternalTrait { fn _set_signers( - ref self: ContractState, signers: Array, signers_len: usize + ref self: ContractState, signers: Array, signers_len: usize, ) { self._require_unique_signers(@signers); @@ -424,7 +426,7 @@ mod Multisig { } fn _set_signers_range( - ref self: ContractState, index: usize, len: usize, signers: @Array + ref self: ContractState, index: usize, len: usize, signers: @Array, ) { if index >= len { return (); @@ -438,7 +440,7 @@ mod Multisig { } fn _get_signers_range( - self: @ContractState, index: usize, len: usize, ref signers: Array + self: @ContractState, index: usize, len: usize, ref signers: Array, ) { if index >= len { return (); @@ -455,7 +457,7 @@ mod Multisig { nonce: u128, index: usize, len: usize, - calldata: @Array + calldata: @Array, ) { if index >= len { return (); @@ -472,7 +474,7 @@ mod Multisig { nonce: u128, index: usize, len: usize, - ref calldata: Array + ref calldata: Array, ) { if index >= len { return (); diff --git a/contracts/src/ocr2/aggregator.cairo b/contracts/src/ocr2/aggregator.cairo index d494c81e8..b497ecd39 100644 --- a/contracts/src/ocr2/aggregator.cairo +++ b/contracts/src/ocr2/aggregator.cairo @@ -106,10 +106,10 @@ trait Billing { fn owed_payment(self: @TContractState, transmitter: ContractAddress) -> u128; fn withdraw_funds(ref self: TContractState, recipient: ContractAddress, amount: u256); fn link_available_for_payment( - self: @TContractState + self: @TContractState, ) -> (bool, u128); // (is negative, absolute difference) fn set_link_token( - ref self: TContractState, link_token: ContractAddress, recipient: ContractAddress + ref self: TContractState, link_token: ContractAddress, recipient: ContractAddress, ); } @@ -123,7 +123,7 @@ struct PayeeConfig { trait PayeeManagement { fn set_payees(ref self: TContractState, payees: Array); fn transfer_payeeship( - ref self: TContractState, transmitter: ContractAddress, proposed: ContractAddress + ref self: TContractState, transmitter: ContractAddress, proposed: ContractAddress, ); fn accept_payeeship(ref self: TContractState, transmitter: ContractAddress); } @@ -134,7 +134,7 @@ use option::OptionTrait; use hash::LegacyHash; fn hash_span, impl TCopy: Copy>( - state: felt252, mut value: Span + state: felt252, mut value: Span, ) -> felt252 { let item = value.pop_front(); match item { @@ -170,48 +170,43 @@ impl SpanLegacyHash, impl TCopy: Copy> of Legacy #[starknet::contract] mod Aggregator { - use super::Round; - use super::{Transmission}; - use super::SpanLegacyHash; - use super::pow; + use super::{Round, Transmission, SpanLegacyHash, pow}; - use array::ArrayTrait; - use array::SpanTrait; + use array::{ArrayTrait, SpanTrait}; use box::BoxTrait; use hash::LegacyHash; - use integer::U128IntoFelt252; - use integer::u128s_from_felt252; - use integer::U128sFromFelt252Result; + use integer::{U128IntoFelt252, u128s_from_felt252, U128sFromFelt252Result}; use zeroable::Zeroable; - use traits::Into; - use traits::TryInto; + use traits::{Into, TryInto}; use option::OptionTrait; - use starknet::ContractAddress; - use starknet::get_caller_address; - use starknet::contract_address_const; - use starknet::StorageBaseAddress; - use starknet::SyscallResult; - use starknet::storage_read_syscall; - use starknet::storage_write_syscall; - use starknet::storage_address_from_base_and_offset; - use starknet::class_hash::ClassHash; + use starknet::{ + ContractAddress, get_caller_address, contract_address_const, StorageBaseAddress, + SyscallResult, storage_read_syscall, storage_write_syscall, + storage_address_from_base_and_offset, class_hash::ClassHash, storage::Map, + }; use openzeppelin::access::ownable::OwnableComponent; + use openzeppelin::upgrades::UpgradeableComponent; use openzeppelin::token::erc20::interface::{IERC20, IERC20Dispatcher, IERC20DispatcherTrait}; use chainlink::utils::split_felt; - use chainlink::libraries::access_control::{AccessControlComponent, IAccessController}; - use chainlink::libraries::access_control::AccessControlComponent::InternalTrait as AccessControlInternalTrait; - use chainlink::libraries::upgradeable::{Upgradeable, IUpgradeable}; - use chainlink::libraries::access_control::{ - IAccessControllerDispatcher, IAccessControllerDispatcherTrait + AccessControlComponent, IAccessController, + AccessControlComponent::InternalTrait as AccessControlInternalTrait, + }; + use chainlink::libraries::upgrades::v2::owner_upgradeable::OwnerUpgradeableComponent; + use chainlink::libraries::access_control::{ + IAccessControllerDispatcher, IAccessControllerDispatcherTrait, }; use chainlink::libraries::type_and_version::ITypeAndVersion; component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); component!(path: AccessControlComponent, storage: access_control, event: AccessControlEvent); + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent); + component!( + path: OwnerUpgradeableComponent, storage: owner_upgradeable, event: OwnerUpgradeableEvent, + ); #[abi(embed_v0)] impl OwnableImpl = OwnableComponent::OwnableTwoStepImpl; @@ -222,6 +217,12 @@ mod Aggregator { AccessControlComponent::AccessControlImpl; impl AccessControlInternalImpl = AccessControlComponent::InternalImpl; + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl; + + #[abi(embed_v0)] + impl OwnerUpgradeableImpl = + OwnerUpgradeableComponent::OwnerUpgradeableImpl; + const GIGA: u128 = 1000000000_u128; const MAX_ORACLES: u32 = 31_u32; @@ -233,6 +234,10 @@ mod Aggregator { OwnableEvent: OwnableComponent::Event, #[flat] AccessControlEvent: AccessControlComponent::Event, + #[flat] + UpgradeableEvent: UpgradeableComponent::Event, + #[flat] + OwnerUpgradeableEvent: OwnerUpgradeableComponent::Event, NewTransmission: NewTransmission, ConfigSet: ConfigSet, LinkTokenSet: LinkTokenSet, @@ -257,7 +262,7 @@ mod Aggregator { gas_price: u128, config_digest: felt252, epoch_and_round: u64, - reimbursement: u128 + reimbursement: u128, } #[derive(Copy, Drop, Serde, starknet::Store)] @@ -293,6 +298,10 @@ mod Aggregator { ownable: OwnableComponent::Storage, #[substorage(v0)] access_control: AccessControlComponent::Storage, + #[substorage(v0)] + upgradeable: UpgradeableComponent::Storage, + #[substorage(v0)] + owner_upgradeable: OwnerUpgradeableComponent::Storage, /// Maximum number of faulty oracles _f: u8, _latest_epoch_and_round: u64, // (u32, u32) @@ -306,22 +315,20 @@ mod Aggregator { _latest_config_digest: felt252, // _oracles: Array, // NOTE: array can't be used in storage _oracles_len: usize, - _transmitters: LegacyMap, // - _signers: LegacyMap, // - _signers_list: LegacyMap, - _transmitters_list: LegacyMap, - _reward_from_aggregator_round_id: LegacyMap, // - _transmissions: LegacyMap, + _transmitters: Map, // + _signers: Map, // + _signers_list: Map, + _transmitters_list: Map, + _reward_from_aggregator_round_id: Map, // + _transmissions: Map, // link token _link_token: ContractAddress, // billing _billing_access_controller: ContractAddress, _billing: BillingConfig, // payee management - _payees: LegacyMap, // - _proposed_payees: LegacyMap< - ContractAddress, ContractAddress - > // + _payees: Map, // + _proposed_payees: Map // } #[generate_trait] @@ -395,10 +402,10 @@ mod Aggregator { max_answer: u128, billing_access_controller: ContractAddress, decimals: u8, - description: felt252 + description: felt252, ) { self.ownable.initializer(owner); - self.access_control.initializer(); + self.access_control.initializer(true); self._link_token.write(link); self._billing_access_controller.write(billing_access_controller); @@ -410,16 +417,6 @@ mod Aggregator { self._description.write(description); } - // --- Upgradeable --- - - #[abi(embed_v0)] - impl UpgradeableImpl of IUpgradeable { - fn upgrade(ref self: ContractState, new_impl: ClassHash) { - self.ownable.assert_only_owner(); - Upgradeable::upgrade(new_impl) - } - } - // --- Validation --- // NOTE: Currently unimplemented: @@ -562,9 +559,9 @@ mod Aggregator { f: f, onchain_config: computed_onchain_config, offchain_config_version: offchain_config_version, - offchain_config: offchain_config - } - ) + offchain_config: offchain_config, + }, + ), ); digest @@ -611,34 +608,33 @@ mod Aggregator { } fn add_oracles( - ref self: ContractState, oracles: @Array, latest_round_id: u128 + ref self: ContractState, oracles: @Array, latest_round_id: u128, ) { let mut index = 0; let mut span = oracles.span(); - while let Option::Some(oracle) = span - .pop_front() { - // NOTE: index should start with 1 here because storage is 0-initialized. - // That way signers(pkey) => 0 indicates "not present" - // This is why we increment first, before using the index - index += 1; - - // check for duplicates - let existing_signer = self._signers.read(*oracle.signer); - assert(existing_signer == 0_usize, 'repeated signer'); - - let existing_transmitter = self._transmitters.read(*oracle.transmitter); - assert(existing_transmitter.index == 0_usize, 'repeated transmitter'); - - self._signers.write(*oracle.signer, index); - self._signers_list.write(index, *oracle.signer); - - self - ._transmitters - .write(*oracle.transmitter, Oracle { index, payment_juels: 0_u128 }); - self._transmitters_list.write(index, *oracle.transmitter); - - self._reward_from_aggregator_round_id.write(index, latest_round_id); - }; + while let Option::Some(oracle) = span.pop_front() { + // NOTE: index should start with 1 here because storage is 0-initialized. + // That way signers(pkey) => 0 indicates "not present" + // This is why we increment first, before using the index + index += 1; + + // check for duplicates + let existing_signer = self._signers.read(*oracle.signer); + assert(existing_signer == 0_usize, 'repeated signer'); + + let existing_transmitter = self._transmitters.read(*oracle.transmitter); + assert(existing_transmitter.index == 0_usize, 'repeated transmitter'); + + self._signers.write(*oracle.signer, index); + self._signers_list.write(index, *oracle.signer); + + self + ._transmitters + .write(*oracle.transmitter, Oracle { index, payment_juels: 0_u128 }); + self._transmitters_list.write(index, *oracle.transmitter); + + self._reward_from_aggregator_round_id.write(index, latest_round_id); + }; self._oracles_len.write(index); } } @@ -669,7 +665,7 @@ mod Aggregator { observers: felt252, observations: @Array, juels_per_fee_coin: u128, - gas_price: u128 + gas_price: u128, ) -> felt252 { let mut state = 0; state = LegacyHash::hash(state, *report_context.config_digest); @@ -696,7 +692,7 @@ mod Aggregator { config_digest, epoch_and_round, transmission.answer, - transmission.transmission_timestamp + transmission.transmission_timestamp, ) } @@ -735,16 +731,16 @@ mod Aggregator { observers, @observations, juels_per_fee_coin, - gas_price + gas_price, ); // Check all signatures are unique (we only saw each pubkey once) - // NOTE: This relies on protocol-level design constraints (MAX_ORACLES = 31, f = 10) which - // ensures we have enough bits to store a count for each oracle. Whenever the MAX_ORACLES - // is updated, the signed_count parameter should be reconsidered. + // NOTE: This relies on protocol-level design constraints (MAX_ORACLES = 31, f = 10) + // which ensures we have enough bits to store a count for each oracle. Whenever the + // MAX_ORACLES is updated, the signed_count parameter should be reconsidered. // - // Although 31 bits is enough, we use a u128 here for simplicity because BitAnd and BitOr - // operators are defined only for u128 and u256. + // Although 31 bits is enough, we use a u128 here for simplicity because BitAnd and + // BitOr operators are defined only for u128 and u256. assert(MAX_ORACLES == 31_u32, ''); self.verify_signatures(msg, ref signatures, 0_u128); @@ -763,7 +759,7 @@ mod Aggregator { let min_answer = self._min_answer.read(); let max_answer = self._max_answer.read(); assert( - (min_answer <= median) & (median <= max_answer), 'median is out of min-max range' + (min_answer <= median) & (median <= max_answer), 'median is out of min-max range', ); let prev_round_id = self._latest_aggregator_round_id.read(); @@ -781,14 +777,14 @@ mod Aggregator { block_num: block_info.block_number, observation_timestamp, transmission_timestamp: block_info.block_timestamp, - } + }, ); // NOTE: Usually validating via validator would happen here, currently disabled let billing = self._billing.read(); let reimbursement_juels = calculate_reimbursement( - juels_per_fee_coin, signatures_len, gas_price, billing + juels_per_fee_coin, signatures_len, gas_price, billing, ); // end report() @@ -808,8 +804,8 @@ mod Aggregator { config_digest: report_context.config_digest, epoch_and_round: report_context.epoch_and_round, reimbursement: reimbursement_juels, - } - ) + }, + ), ); // pay transmitter @@ -824,25 +820,24 @@ mod Aggregator { self: @ContractState, msg: felt252, ref signatures: Array, - mut signed_count: u128 + mut signed_count: u128, ) { let mut span = signatures.span(); - while let Option::Some(signature) = span - .pop_front() { - let index = self._signers.read(*signature.public_key); - assert(index != 0_usize, 'invalid signer'); // 0 index == uninitialized - - let indexed_bit = pow(2_u128, index.into() - 1_u128); - let prev_signed_count = signed_count; - signed_count = signed_count | indexed_bit; - assert(prev_signed_count != signed_count, 'duplicate signer'); - - let is_valid = ecdsa::check_ecdsa_signature( - msg, *signature.public_key, *signature.r, *signature.s - ); + while let Option::Some(signature) = span.pop_front() { + let index = self._signers.read(*signature.public_key); + assert(index != 0_usize, 'invalid signer'); // 0 index == uninitialized + + let indexed_bit = pow(2_u128, index.into() - 1_u128); + let prev_signed_count = signed_count; + signed_count = signed_count | indexed_bit; + assert(prev_signed_count != signed_count, 'duplicate signer'); - assert(is_valid, ''); - }; + let is_valid = ecdsa::check_ecdsa_signature( + msg, *signature.public_key, *signature.r, *signature.s, + ); + + assert(is_valid, ''); + }; } } @@ -868,7 +863,7 @@ mod Aggregator { #[derive(Drop, starknet::Event)] struct BillingSet { - config: BillingConfig + config: BillingConfig, } #[derive(Drop, starknet::Event)] @@ -885,13 +880,13 @@ mod Aggregator { #[key] old_link_token: ContractAddress, #[key] - new_link_token: ContractAddress + new_link_token: ContractAddress, } #[abi(embed_v0)] impl BillingImpl of super::Billing { fn set_link_token( - ref self: ContractState, link_token: ContractAddress, recipient: ContractAddress + ref self: ContractState, link_token: ContractAddress, recipient: ContractAddress, ) { self.ownable.assert_only_owner(); @@ -919,13 +914,13 @@ mod Aggregator { self .emit( Event::LinkTokenSet( - LinkTokenSet { old_link_token: old_token, new_link_token: link_token } - ) + LinkTokenSet { old_link_token: old_token, new_link_token: link_token }, + ), ); } fn set_billing_access_controller( - ref self: ContractState, access_controller: ContractAddress + ref self: ContractState, access_controller: ContractAddress, ) { self.ownable.assert_only_owner(); @@ -939,9 +934,9 @@ mod Aggregator { .emit( Event::BillingAccessControllerSet( BillingAccessControllerSet { - old_controller: old_controller, new_controller: access_controller - } - ) + old_controller: old_controller, new_controller: access_controller, + }, + ), ); } @@ -1002,7 +997,7 @@ mod Aggregator { } fn link_available_for_payment( - self: @ContractState + self: @ContractState, ) -> (bool, u128) { // (is negative, absolute difference) let link_token = self._link_token.read(); let contract_address = starknet::info::get_contract_address(); @@ -1035,11 +1030,11 @@ mod Aggregator { let access_controller = self._billing_access_controller.read(); let access_controller = IAccessControllerDispatcher { - contract_address: access_controller + contract_address: access_controller, }; assert( access_controller.has_access(caller, ArrayTrait::new()), - 'caller does not have access' + 'caller does not have access', ); } @@ -1063,7 +1058,7 @@ mod Aggregator { ref self: ContractState, transmitter: ContractAddress, latest_round_id: u128, - link_token: ContractAddress + link_token: ContractAddress, ) { let oracle = self._transmitters.read(transmitter); if oracle.index == 0_usize { @@ -1097,9 +1092,9 @@ mod Aggregator { transmitter: transmitter, payee: payee, amount: amount, - link_token: link_token - } - ) + link_token: link_token, + }, + ), ); } @@ -1146,7 +1141,7 @@ mod Aggregator { const MARGIN: u128 = 115_u128; fn calculate_reimbursement( - juels_per_fee_coin: u128, signature_count: usize, gas_price: u128, config: BillingConfig + juels_per_fee_coin: u128, signature_count: usize, gas_price: u128, config: BillingConfig, ) -> u128 { // TODO: determine new values for these constants // Based on estimateFee (f=1 14977, f=2 14989, f=3 15002 f=4 15014 f=5 15027, count = f+1) @@ -1189,30 +1184,29 @@ mod Aggregator { impl PayeeManagementImpl of super::PayeeManagement { fn set_payees(ref self: ContractState, mut payees: Array) { self.ownable.assert_only_owner(); - while let Option::Some(payee) = payees - .pop_front() { - let current_payee = self._payees.read(payee.transmitter); - let is_unset = current_payee.is_zero(); - let is_same = current_payee == payee.payee; - assert(is_unset | is_same, 'payee already set'); - - self._payees.write(payee.transmitter, payee.payee); - - self - .emit( - Event::PayeeshipTransferred( - PayeeshipTransferred { - transmitter: payee.transmitter, - previous: current_payee, - current: payee.payee - } - ) - ); - } + while let Option::Some(payee) = payees.pop_front() { + let current_payee = self._payees.read(payee.transmitter); + let is_unset = current_payee.is_zero(); + let is_same = current_payee == payee.payee; + assert(is_unset | is_same, 'payee already set'); + + self._payees.write(payee.transmitter, payee.payee); + + self + .emit( + Event::PayeeshipTransferred( + PayeeshipTransferred { + transmitter: payee.transmitter, + previous: current_payee, + current: payee.payee, + }, + ), + ); + } } fn transfer_payeeship( - ref self: ContractState, transmitter: ContractAddress, proposed: ContractAddress + ref self: ContractState, transmitter: ContractAddress, proposed: ContractAddress, ) { assert(!proposed.is_zero(), 'cannot transfer to zero address'); let caller = starknet::info::get_caller_address(); @@ -1225,9 +1219,9 @@ mod Aggregator { .emit( Event::PayeeshipTransferRequested( PayeeshipTransferRequested { - transmitter: transmitter, current: payee, proposed: proposed - } - ) + transmitter: transmitter, current: payee, proposed: proposed, + }, + ), ); } @@ -1243,9 +1237,9 @@ mod Aggregator { .emit( Event::PayeeshipTransferred( PayeeshipTransferred { - transmitter: transmitter, previous: previous, current: caller - } - ) + transmitter: transmitter, previous: previous, current: caller, + }, + ), ); } } diff --git a/contracts/src/ocr2/aggregator_proxy.cairo b/contracts/src/ocr2/aggregator_proxy.cairo index 9859c9ccd..36c644fe2 100644 --- a/contracts/src/ocr2/aggregator_proxy.cairo +++ b/contracts/src/ocr2/aggregator_proxy.cairo @@ -24,40 +24,32 @@ trait IAggregatorProxyInternal { #[starknet::contract] mod AggregatorProxy { - use super::IAggregatorProxy; - use super::IAggregatorDispatcher; - use super::IAggregatorDispatcherTrait; + use super::{IAggregatorProxy, IAggregatorDispatcher, IAggregatorDispatcherTrait}; - use integer::u128s_from_felt252; + use integer::{u128s_from_felt252, Felt252TryIntoU128, U128IntoFelt252, U128sFromFelt252Result}; use option::OptionTrait; - use traits::Into; - use traits::TryInto; + use traits::{Into, TryInto}; use zeroable::Zeroable; - use starknet::ContractAddress; - use starknet::ContractAddressIntoFelt252; - use starknet::Felt252TryIntoContractAddress; - use integer::Felt252TryIntoU128; - use starknet::StorageBaseAddress; - use starknet::SyscallResult; - use integer::U128IntoFelt252; - use integer::U128sFromFelt252Result; - use starknet::storage_read_syscall; - use starknet::storage_write_syscall; - use starknet::storage_address_from_base_and_offset; - use starknet::class_hash::ClassHash; + use starknet::{ + ContractAddress, ContractAddressIntoFelt252, Felt252TryIntoContractAddress, + StorageBaseAddress, SyscallResult, storage_read_syscall, storage_write_syscall, + storage_address_from_base_and_offset, class_hash::ClassHash, storage::Map, + }; use openzeppelin::access::ownable::OwnableComponent; + use openzeppelin::upgrades::UpgradeableComponent; - use chainlink::ocr2::aggregator::IAggregator; - use chainlink::ocr2::aggregator::Round; - use chainlink::libraries::access_control::{AccessControlComponent, IAccessController}; - use chainlink::libraries::access_control::AccessControlComponent::InternalTrait as AccessControlInternalTrait; + use chainlink::ocr2::aggregator::{IAggregator, Round}; + use chainlink::libraries::access_control::{ + AccessControlComponent, IAccessController, + AccessControlComponent::InternalTrait as AccessControlInternalTrait, + }; + use chainlink::libraries::upgrades::v2::owner_upgradeable::OwnerUpgradeableComponent; use chainlink::utils::split_felt; use chainlink::libraries::type_and_version::{ - ITypeAndVersion, ITypeAndVersionDispatcher, ITypeAndVersionDispatcherTrait + ITypeAndVersion, ITypeAndVersionDispatcher, ITypeAndVersionDispatcherTrait, }; - use chainlink::libraries::upgradeable::{Upgradeable, IUpgradeable}; const SHIFT: felt252 = 0x100000000000000000000000000000000; const MAX_ID: felt252 = 0xffffffffffffffffffffffffffffffff; @@ -65,11 +57,16 @@ mod AggregatorProxy { #[derive(Copy, Drop, Serde, starknet::Store)] struct Phase { id: u128, - aggregator: ContractAddress + aggregator: ContractAddress, } component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); component!(path: AccessControlComponent, storage: access_control, event: AccessControlEvent); + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent); + component!( + path: OwnerUpgradeableComponent, storage: owner_upgradeable, event: OwnerUpgradeableEvent, + ); + #[abi(embed_v0)] impl OwnableImpl = OwnableComponent::OwnableTwoStepImpl; @@ -80,15 +77,25 @@ mod AggregatorProxy { AccessControlComponent::AccessControlImpl; impl AccessControlInternalImpl = AccessControlComponent::InternalImpl; + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl; + + #[abi(embed_v0)] + impl OwnerUpgradeableImpl = + OwnerUpgradeableComponent::OwnerUpgradeableImpl; + #[storage] struct Storage { #[substorage(v0)] ownable: OwnableComponent::Storage, #[substorage(v0)] access_control: AccessControlComponent::Storage, + #[substorage(v0)] + upgradeable: UpgradeableComponent::Storage, + #[substorage(v0)] + owner_upgradeable: OwnerUpgradeableComponent::Storage, _current_phase: Phase, _proposed_aggregator: ContractAddress, - _phases: LegacyMap + _phases: Map, } #[event] @@ -98,6 +105,10 @@ mod AggregatorProxy { OwnableEvent: OwnableComponent::Event, #[flat] AccessControlEvent: AccessControlComponent::Event, + #[flat] + UpgradeableEvent: UpgradeableComponent::Event, + #[flat] + OwnerUpgradeableEvent: OwnerUpgradeableComponent::Event, } // TODO: refactor these events @@ -174,19 +185,10 @@ mod AggregatorProxy { #[constructor] fn constructor(ref self: ContractState, owner: ContractAddress, address: ContractAddress) { self.ownable.initializer(owner); - self.access_control.initializer(); + self.access_control.initializer(false); self._set_aggregator(address); } - // -- Upgradeable -- - - #[abi(embed_v0)] - impl UpgradeableImpl of IUpgradeable { - fn upgrade(ref self: ContractState, new_impl: ClassHash) { - self.ownable.assert_only_owner(); - Upgradeable::upgrade(new_impl) - } - } // diff --git a/contracts/src/ocr2/mocks/mock_aggregator.cairo b/contracts/src/ocr2/mocks/mock_aggregator.cairo index eceb7d3c8..a6b6bc93a 100644 --- a/contracts/src/ocr2/mocks/mock_aggregator.cairo +++ b/contracts/src/ocr2/mocks/mock_aggregator.cairo @@ -5,7 +5,7 @@ trait IMockAggregator { answer: u128, block_num: u64, observation_timestamp: u64, - transmission_timestamp: u64 + transmission_timestamp: u64, ); } @@ -13,6 +13,7 @@ trait IMockAggregator { mod MockAggregator { use array::ArrayTrait; use starknet::contract_address_const; + use starknet::storage::Map; use traits::Into; use chainlink::ocr2::aggregator::IAggregator; @@ -25,9 +26,9 @@ mod MockAggregator { #[storage] struct Storage { - _transmissions: LegacyMap, + _transmissions: Map, _latest_aggregator_round_id: u128, - _decimals: u8 + _decimals: u8, } #[constructor] @@ -42,7 +43,7 @@ mod MockAggregator { answer: u128, block_num: u64, observation_timestamp: u64, - transmission_timestamp: u64 + transmission_timestamp: u64, ) { let new_round_id = self._latest_aggregator_round_id.read() + 1_u128; self @@ -53,8 +54,8 @@ mod MockAggregator { answer: answer, block_num: block_num, observation_timestamp: observation_timestamp, - transmission_timestamp: transmission_timestamp - } + transmission_timestamp: transmission_timestamp, + }, ); let mut observations = ArrayTrait::new(); @@ -77,9 +78,9 @@ mod MockAggregator { gas_price: 1_u128, config_digest: 777, epoch_and_round: 20_u64, - reimbursement: 100_u128 - } - ) + reimbursement: 100_u128, + }, + ), ); } } @@ -106,7 +107,7 @@ mod MockAggregator { answer: transmission.answer, block_num: transmission.block_num, started_at: transmission.observation_timestamp, - updated_at: transmission.transmission_timestamp + updated_at: transmission.transmission_timestamp, } } diff --git a/contracts/src/tests.cairo b/contracts/src/tests.cairo index fb561654c..07abf9ee2 100644 --- a/contracts/src/tests.cairo +++ b/contracts/src/tests.cairo @@ -7,9 +7,11 @@ mod test_ownable; mod test_erc677; mod test_link_token; mod test_upgradeable; +mod test_owner_upgradeable; mod test_access_controller; mod test_mock_aggregator; mod test_sequencer_uptime_feed; mod test_mcms; mod test_enumerable_set; mod test_rbac_timelock; + diff --git a/contracts/src/tests/test_access_controller.cairo b/contracts/src/tests/test_access_controller.cairo index a78715cf4..987f96b6b 100644 --- a/contracts/src/tests/test_access_controller.cairo +++ b/contracts/src/tests/test_access_controller.cairo @@ -1,29 +1,24 @@ -use starknet::ContractAddress; -use starknet::testing::set_caller_address; -use starknet::testing::set_contract_address; -use starknet::contract_address_const; -use starknet::class_hash::class_hash_const; -use starknet::class_hash::Felt252TryIntoClassHash; -use starknet::syscalls::deploy_syscall; +use starknet::{ + ContractAddress, testing::{set_caller_address, set_contract_address}, contract_address_const, + class_hash::{class_hash_const, Felt252TryIntoClassHash}, syscalls::deploy_syscall, +}; use array::ArrayTrait; -use traits::Into; -use traits::TryInto; +use traits::{Into, TryInto}; use option::OptionTrait; use core::result::ResultTrait; use chainlink::access_control::access_controller::AccessController; -use chainlink::access_control::access_controller::AccessController::UpgradeableImpl; - +use chainlink::libraries::upgrades::v2::owner_upgradeable::OwnerUpgradeableComponent::OwnerUpgradeableImpl; use chainlink::libraries::access_control::{ - IAccessController, IAccessControllerDispatcher, IAccessControllerDispatcherTrait + IAccessController, IAccessControllerDispatcher, IAccessControllerDispatcherTrait, }; use snforge_std::{ - declare, ContractClassTrait, start_cheat_caller_address_global, stop_cheat_caller_address_global + declare, ContractClassTrait, start_cheat_caller_address_global, + stop_cheat_caller_address_global, DeclareResultTrait, }; - fn STATE() -> AccessController::ContractState { AccessController::contract_state_for_testing() } @@ -40,17 +35,19 @@ fn test_upgrade_not_owner() { let _ = setup(); let mut state = STATE(); - UpgradeableImpl::upgrade(ref state, class_hash_const::<2>()); + OwnerUpgradeableImpl::upgrade(ref state, class_hash_const::<2>()); } #[test] fn test_access_control() { let owner = setup(); // Deploy access controller - let calldata = array![owner.into(), // owner + let calldata = array![owner.into() // owner ]; - let (accessControllerAddr, _) = declare("AccessController").unwrap().deploy(@calldata).unwrap(); + let contract = declare("AccessController").unwrap().contract_class(); + + let (accessControllerAddr, _) = contract.deploy(@calldata).unwrap(); should_implement_access_control(accessControllerAddr, owner); } diff --git a/contracts/src/tests/test_aggregator.cairo b/contracts/src/tests/test_aggregator.cairo index bed9dad22..0099c39f1 100644 --- a/contracts/src/tests/test_aggregator.cairo +++ b/contracts/src/tests/test_aggregator.cairo @@ -1,35 +1,29 @@ -use starknet::testing::set_caller_address; -use starknet::testing::set_contract_address; -use starknet::ContractAddress; -use starknet::contract_address_const; -use starknet::class_hash::class_hash_const; -use starknet::class_hash::Felt252TryIntoClassHash; -use starknet::syscalls::deploy_syscall; +use starknet::{ + ContractAddress, contract_address_const, testing::{set_caller_address, set_contract_address}, + class_hash::{class_hash_const, Felt252TryIntoClassHash}, syscalls::deploy_syscall, +}; use array::ArrayTrait; use clone::Clone; -use traits::Into; -use traits::TryInto; +use traits::{Into, TryInto}; use option::OptionTrait; -use core::result::ResultTrait; +use core::{result::ResultTrait, panic_with_felt252}; -use chainlink::ocr2::aggregator::pow; -use chainlink::ocr2::aggregator::Aggregator; -use chainlink::ocr2::aggregator::Aggregator::{ - AggregatorImpl, BillingImpl, PayeeManagementImpl, UpgradeableImpl +use chainlink::ocr2::aggregator::{ + pow, Aggregator, + Aggregator::{BillingConfig, PayeeConfig, AggregatorImpl, BillingImpl, PayeeManagementImpl}, }; -use chainlink::ocr2::aggregator::Aggregator::BillingConfig; -use chainlink::ocr2::aggregator::Aggregator::PayeeConfig; +use chainlink::libraries::upgrades::v2::owner_upgradeable::OwnerUpgradeableComponent::OwnerUpgradeableImpl; use chainlink::access_control::access_controller::AccessController; use chainlink::token::v2::link_token::LinkToken; use chainlink::tests::{ test_ownable::should_implement_ownable, test_access_controller::should_implement_access_control, - test_link_token::link_deploy_args + test_link_token::link_deploy_args, }; - use snforge_std::{ - declare, ContractClassTrait, start_cheat_caller_address_global, stop_cheat_caller_address_global + declare, ContractClassTrait, start_cheat_caller_address_global, + stop_cheat_caller_address_global, DeclareResultTrait, }; #[test] @@ -69,7 +63,7 @@ fn test_pow_2_0() { } use chainlink::libraries::access_control::{ - IAccessController, IAccessControllerDispatcher, IAccessControllerDispatcherTrait + IAccessController, IAccessControllerDispatcher, IAccessControllerDispatcherTrait, }; #[starknet::interface] @@ -80,7 +74,7 @@ fn STATE() -> Aggregator::ContractState { } fn setup() -> ( - ContractAddress, ContractAddress, IAccessControllerDispatcher, ILinkTokenDispatcher + ContractAddress, ContractAddress, IAccessControllerDispatcher, ILinkTokenDispatcher, ) { let acc1: ContractAddress = contract_address_const::<777>(); let acc2: ContractAddress = contract_address_const::<888>(); @@ -88,21 +82,26 @@ fn setup() -> ( start_cheat_caller_address_global(acc1); // deploy billing access controller - let calldata = array![acc1.into(), // owner = acc1; + let calldata = array![acc1.into() // owner = acc1; ]; let (billingAccessControllerAddr, _) = declare("AccessController") .unwrap() + .contract_class() .deploy(@calldata) .unwrap(); let billingAccessController = IAccessControllerDispatcher { - contract_address: billingAccessControllerAddr + contract_address: billingAccessControllerAddr, }; let calldata = link_deploy_args(acc1, acc1); - let (linkTokenAddr, _) = declare("LinkToken").unwrap().deploy(@calldata).unwrap(); + let (linkTokenAddr, _) = declare("LinkToken") + .unwrap() + .contract_class() + .deploy(@calldata) + .unwrap(); let linkToken = ILinkTokenDispatcher { contract_address: linkTokenAddr }; @@ -121,10 +120,14 @@ fn test_ownable() { 100, // max_answer contract_address_const::<999>().into(), // billing access controller 8, // decimals - 123, // description + 123 // description ]; - let (aggregatorAddr, _) = declare("Aggregator").unwrap().deploy(@calldata).unwrap(); + let (aggregatorAddr, _) = declare("Aggregator") + .unwrap() + .contract_class() + .deploy(@calldata) + .unwrap(); should_implement_ownable(aggregatorAddr, account); } @@ -140,22 +143,25 @@ fn test_access_control() { 100, // max_answer contract_address_const::<999>().into(), // billing access controller 8, // decimals - 123, // description + 123 // description ]; - let (aggregatorAddr, _) = declare("Aggregator").unwrap().deploy(@calldata).unwrap(); + let (aggregatorAddr, _) = declare("Aggregator") + .unwrap() + .contract_class() + .deploy(@calldata) + .unwrap(); should_implement_access_control(aggregatorAddr, account); } - #[test] #[should_panic(expected: ('Caller is not the owner',))] fn test_upgrade_non_owner() { let _ = setup(); let mut state = STATE(); - UpgradeableImpl::upgrade(ref state, class_hash_const::<123>()); + OwnerUpgradeableImpl::upgrade(ref state, class_hash_const::<123>()); } // --- Billing tests --- @@ -166,7 +172,7 @@ fn test_set_billing_access_controller_not_owner() { let (owner, acc2, billingAccessController, _) = setup(); let mut state = STATE(); Aggregator::constructor( - ref state, owner, contract_address_const::<777>(), 0, 100, acc2, 8, 123 + ref state, owner, contract_address_const::<777>(), 0, 100, acc2, 8, 123, ); // set billing access controller should revert if caller is not owner @@ -187,7 +193,7 @@ fn test_set_billing_config_no_access() { 100, billingAccessController.contract_address, 8, - 123 + 123, ); // set billing config as acc2 with no access @@ -213,7 +219,7 @@ fn test_set_billing_config_as_owner() { 100, billingAccessController.contract_address, 8, - 123 + 123, ); // set billing config as owner @@ -249,7 +255,7 @@ fn test_set_billing_config_as_acc_with_access() { 100, billingAccessController.contract_address, 8, - 123 + 123, ); // set billing config as acc2 with access @@ -278,10 +284,10 @@ fn test_set_payees_caller_not_owner() { let (owner, acc2, _, _) = setup(); let mut state = STATE(); Aggregator::constructor( - ref state, owner, contract_address_const::<777>(), 0, 100, acc2, 8, 123 + ref state, owner, contract_address_const::<777>(), 0, 100, acc2, 8, 123, ); - let payees = array![PayeeConfig { transmitter: acc2, payee: acc2, },]; + let payees = array![PayeeConfig { transmitter: acc2, payee: acc2 }]; // set payee should revert if caller is not owner start_cheat_caller_address_global(acc2); PayeeManagementImpl::set_payees(ref state, payees); @@ -292,10 +298,10 @@ fn test_set_single_payee() { let (owner, acc2, _, _) = setup(); let mut state = STATE(); Aggregator::constructor( - ref state, owner, contract_address_const::<777>(), 0, 100, acc2, 8, 123 + ref state, owner, contract_address_const::<777>(), 0, 100, acc2, 8, 123, ); - let payees = array![PayeeConfig { transmitter: acc2, payee: acc2, },]; + let payees = array![PayeeConfig { transmitter: acc2, payee: acc2 }]; start_cheat_caller_address_global(owner); PayeeManagementImpl::set_payees(ref state, payees); } @@ -305,12 +311,12 @@ fn test_set_multiple_payees() { let (owner, acc2, _, _) = setup(); let mut state = STATE(); Aggregator::constructor( - ref state, owner, contract_address_const::<777>(), 0, 100, acc2, 8, 123 + ref state, owner, contract_address_const::<777>(), 0, 100, acc2, 8, 123, ); let payees = array![ - PayeeConfig { transmitter: acc2, payee: acc2, }, - PayeeConfig { transmitter: owner, payee: owner, }, + PayeeConfig { transmitter: acc2, payee: acc2 }, + PayeeConfig { transmitter: owner, payee: owner }, ]; start_cheat_caller_address_global(owner); PayeeManagementImpl::set_payees(ref state, payees); @@ -322,11 +328,11 @@ fn test_transfer_payeeship_caller_not_payee() { let (owner, acc2, _, _) = setup(); let mut state = STATE(); Aggregator::constructor( - ref state, owner, contract_address_const::<777>(), 0, 100, acc2, 8, 123 + ref state, owner, contract_address_const::<777>(), 0, 100, acc2, 8, 123, ); let transmitter = contract_address_const::<123>(); - let payees = array![PayeeConfig { transmitter: transmitter, payee: acc2, },]; + let payees = array![PayeeConfig { transmitter: transmitter, payee: acc2 }]; start_cheat_caller_address_global(owner); PayeeManagementImpl::set_payees(ref state, payees); @@ -339,11 +345,11 @@ fn test_transfer_payeeship_to_self() { let (owner, acc2, _, _) = setup(); let mut state = STATE(); Aggregator::constructor( - ref state, owner, contract_address_const::<777>(), 0, 100, acc2, 8, 123 + ref state, owner, contract_address_const::<777>(), 0, 100, acc2, 8, 123, ); let transmitter = contract_address_const::<123>(); - let payees = array![PayeeConfig { transmitter: transmitter, payee: acc2, },]; + let payees = array![PayeeConfig { transmitter: transmitter, payee: acc2 }]; start_cheat_caller_address_global(owner); PayeeManagementImpl::set_payees(ref state, payees); @@ -357,11 +363,11 @@ fn test_accept_payeeship_caller_not_proposed_payee() { let (owner, acc2, _, _) = setup(); let mut state = STATE(); Aggregator::constructor( - ref state, owner, contract_address_const::<777>(), 0, 100, acc2, 8, 123 + ref state, owner, contract_address_const::<777>(), 0, 100, acc2, 8, 123, ); let transmitter = contract_address_const::<123>(); - let payees = array![PayeeConfig { transmitter: transmitter, payee: acc2, },]; + let payees = array![PayeeConfig { transmitter: transmitter, payee: acc2 }]; start_cheat_caller_address_global(owner); PayeeManagementImpl::set_payees(ref state, payees); @@ -375,11 +381,11 @@ fn test_transfer_and_accept_payeeship() { let (owner, acc2, _, _) = setup(); let mut state = STATE(); Aggregator::constructor( - ref state, owner, contract_address_const::<777>(), 0, 100, acc2, 8, 123 + ref state, owner, contract_address_const::<777>(), 0, 100, acc2, 8, 123, ); let transmitter = contract_address_const::<123>(); - let payees = array![PayeeConfig { transmitter: transmitter, payee: acc2, },]; + let payees = array![PayeeConfig { transmitter: transmitter, payee: acc2 }]; start_cheat_caller_address_global(owner); PayeeManagementImpl::set_payees(ref state, payees); @@ -400,11 +406,11 @@ fn test_owed_payment_no_rounds() { let (owner, acc2, _, _) = setup(); let mut state = STATE(); Aggregator::constructor( - ref state, owner, contract_address_const::<777>(), 0, 100, acc2, 8, 123 + ref state, owner, contract_address_const::<777>(), 0, 100, acc2, 8, 123, ); let transmitter = contract_address_const::<123>(); - let mut payees = array![PayeeConfig { transmitter: transmitter, payee: acc2, },]; + let mut payees = array![PayeeConfig { transmitter: transmitter, payee: acc2 }]; start_cheat_caller_address_global(owner); PayeeManagementImpl::set_payees(ref state, payees); diff --git a/contracts/src/tests/test_aggregator_proxy.cairo b/contracts/src/tests/test_aggregator_proxy.cairo index eb7e350bc..bdeee5d0c 100644 --- a/contracts/src/tests/test_aggregator_proxy.cairo +++ b/contracts/src/tests/test_aggregator_proxy.cairo @@ -12,20 +12,25 @@ use option::OptionTrait; use core::result::ResultTrait; use chainlink::ocr2::mocks::mock_aggregator::{ - MockAggregator, IMockAggregator, IMockAggregatorDispatcher, IMockAggregatorDispatcherTrait + MockAggregator, IMockAggregator, IMockAggregatorDispatcher, IMockAggregatorDispatcherTrait, }; use chainlink::ocr2::aggregator_proxy::AggregatorProxy; use chainlink::ocr2::aggregator_proxy::AggregatorProxy::{ - AggregatorProxyImpl, AggregatorProxyInternal, UpgradeableImpl + AggregatorProxyImpl, AggregatorProxyInternal, }; -use chainlink::libraries::access_control::AccessControlComponent::AccessControlImpl; +use chainlink::libraries::access_control::{ + IAccessControllerDispatcher, IAccessControllerDispatcherTrait, + AccessControlComponent::AccessControlImpl, +}; +use chainlink::libraries::upgrades::v2::owner_upgradeable::OwnerUpgradeableComponent::OwnerUpgradeableImpl; use chainlink::ocr2::aggregator::Round; use chainlink::utils::split_felt; use chainlink::tests::test_ownable::should_implement_ownable; use chainlink::tests::test_access_controller::should_implement_access_control; use snforge_std::{ - declare, ContractClassTrait, start_cheat_caller_address_global, stop_cheat_caller_address_global + declare, ContractClassTrait, start_cheat_caller_address_global, + stop_cheat_caller_address_global, DeclareResultTrait, }; @@ -38,7 +43,7 @@ fn setup() -> ( ContractAddress, IMockAggregatorDispatcher, ContractAddress, - IMockAggregatorDispatcher + IMockAggregatorDispatcher, ) { // Set account as default caller let account: ContractAddress = contract_address_const::<1>(); @@ -49,19 +54,20 @@ fn setup() -> ( let mut calldata = ArrayTrait::new(); calldata.append(8); // decimals = 8 - let contract_class = declare("MockAggregator").unwrap(); + let contract = declare("MockAggregator").unwrap().contract_class(); - let (mockAggregatorAddr1, _) = contract_class.deploy(@calldata).unwrap(); + let (mockAggregatorAddr1, _) = contract.deploy(@calldata).unwrap(); let mockAggregator1 = IMockAggregatorDispatcher { contract_address: mockAggregatorAddr1 }; // Deploy mock aggregator 2 // note: deployment address is deterministic based on deploy_syscall parameters - // so we need to change the decimals parameter to avoid an address conflict with mock aggregator 1 + // so we need to change the decimals parameter to avoid an address conflict with mock aggregator + // 1 let mut calldata2 = ArrayTrait::new(); calldata2.append(10); // decimals = 10 - let (mockAggregatorAddr2, _) = contract_class.deploy(@calldata).unwrap(); + let (mockAggregatorAddr2, _) = contract.deploy(@calldata).unwrap(); let mockAggregator2 = IMockAggregatorDispatcher { contract_address: mockAggregatorAddr2 }; @@ -74,8 +80,12 @@ fn test_ownable() { let (account, mockAggregatorAddr, _, _, _) = setup(); // Deploy aggregator proxy let calldata = array![account.into(), // owner = account - mockAggregatorAddr.into(),]; - let (aggregatorProxyAddr, _) = declare("AggregatorProxy").unwrap().deploy(@calldata).unwrap(); + mockAggregatorAddr.into()]; + let (aggregatorProxyAddr, _) = declare("AggregatorProxy") + .unwrap() + .contract_class() + .deploy(@calldata) + .unwrap(); should_implement_ownable(aggregatorProxyAddr, account); } @@ -85,9 +95,16 @@ fn test_access_control() { let (account, mockAggregatorAddr, _, _, _) = setup(); // Deploy aggregator proxy let calldata = array![account.into(), // owner = account - mockAggregatorAddr.into(),]; + mockAggregatorAddr.into()]; + + let (aggregatorProxyAddr, _) = declare("AggregatorProxy") + .unwrap() + .contract_class() + .deploy(@calldata) + .unwrap(); - let (aggregatorProxyAddr, _) = declare("AggregatorProxy").unwrap().deploy(@calldata).unwrap(); + // proxy by default disables the access check, so we re-enable for testing purposes + IAccessControllerDispatcher { contract_address: aggregatorProxyAddr }.enable_access_check(); should_implement_access_control(aggregatorProxyAddr, account); } @@ -97,7 +114,7 @@ fn test_access_control() { fn test_upgrade_non_owner() { let (_, _, _, _, _) = setup(); let mut state = STATE(); - UpgradeableImpl::upgrade(ref state, class_hash_const::<123>()); + OwnerUpgradeableImpl::upgrade(ref state, class_hash_const::<123>()); } fn test_query_latest_round_data() { @@ -118,13 +135,12 @@ fn test_query_latest_round_data() { assert(round.started_at == 9, 'started_at should be 9'); assert(round.updated_at == 8, 'updated_at should be 8'); - // latest_answer matches up with latest_round_data + // latest_answer matches up with latest_round_data let latest_answer = AggregatorProxyImpl::latest_answer(@state); assert(latest_answer == 10, '(latest) answer should be 10'); } #[test] -#[should_panic(expected: ('user does not have read access',))] fn test_query_latest_round_data_without_access() { let (owner, mockAggregatorAddr, mockAggregator, _, _) = setup(); let mut state = STATE(); @@ -140,7 +156,6 @@ fn test_query_latest_round_data_without_access() { } #[test] -#[should_panic(expected: ('user does not have read access',))] fn test_query_latest_answer_without_access() { let (owner, mockAggregatorAddr, mockAggregator, _, _) = setup(); let mut state = STATE(); diff --git a/contracts/src/tests/test_enumerable_set.cairo b/contracts/src/tests/test_enumerable_set.cairo index 1f4e008b8..d38451b0c 100644 --- a/contracts/src/tests/test_enumerable_set.cairo +++ b/contracts/src/tests/test_enumerable_set.cairo @@ -2,19 +2,19 @@ use starknet::ContractAddress; use chainlink::libraries::mocks::mock_enumerable_set::{ MockEnumerableSet, IMockEnumerableSet, IMockEnumerableSetDispatcher, IMockEnumerableSetDispatcherTrait, IMockEnumerableSetSafeDispatcher, - IMockEnumerableSetSafeDispatcherTrait + IMockEnumerableSetSafeDispatcherTrait, }; -use snforge_std::{declare, ContractClassTrait}; +use snforge_std::{declare, ContractClassTrait, DeclareResultTrait}; -const MOCK_SET_ID: u256 = 'adfasdf'; -const OTHER_SET_ID: u256 = 'fakeasdf'; +const MOCK_SET_ID: felt252 = 'adfasdf'; +const OTHER_SET_ID: felt252 = 'fakeasdf'; fn expect_out_of_bounds>(result: Result>) { match result { Result::Ok(_) => panic!("expect 'index out of bounds'"), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'index out of bounds', *panic_data.at(0)); - } + }, } } @@ -23,20 +23,24 @@ fn expect_set_is_1_indexed>(result: Result panic!("expect 'set is 1-indexed'"), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'set is 1-indexed', *panic_data.at(0)); - } + }, } } fn setup_mock() -> ( - ContractAddress, IMockEnumerableSetDispatcher, IMockEnumerableSetSafeDispatcher + ContractAddress, IMockEnumerableSetDispatcher, IMockEnumerableSetSafeDispatcher, ) { let calldata = array![]; - let (mock_address, _) = declare("MockEnumerableSet").unwrap().deploy(@calldata).unwrap(); + let (mock_address, _) = declare("MockEnumerableSet") + .unwrap() + .contract_class() + .deploy(@calldata) + .unwrap(); ( mock_address, IMockEnumerableSetDispatcher { contract_address: mock_address }, - IMockEnumerableSetSafeDispatcher { contract_address: mock_address } + IMockEnumerableSetSafeDispatcher { contract_address: mock_address }, ) } @@ -63,12 +67,12 @@ fn test_add() { assert(mock.add(MOCK_SET_ID, second_value), 'should add'); assert( mock.contains(MOCK_SET_ID, first_value) && mock.contains(MOCK_SET_ID, second_value), - 'should contain' + 'should contain', ); assert(mock.length(MOCK_SET_ID) == 2, 'should equal 2'); assert( mock.at(MOCK_SET_ID, 1) == first_value && mock.at(MOCK_SET_ID, 2) == second_value, - 'should return val' + 'should return val', ); assert(mock.values(MOCK_SET_ID) == array![first_value, second_value], 'arrays should equal'); } @@ -106,7 +110,7 @@ fn test_remove() { assert(mock.length(MOCK_SET_ID) == 2, 'length should equal 2'); assert(!mock.contains(MOCK_SET_ID, 300), 'does not contain 300'); assert( - mock.contains(MOCK_SET_ID, 100) && mock.contains(MOCK_SET_ID, 200), 'contains 100 & 200' + mock.contains(MOCK_SET_ID, 100) && mock.contains(MOCK_SET_ID, 200), 'contains 100 & 200', ); assert(mock.at(MOCK_SET_ID, 1) == 100 && mock.at(MOCK_SET_ID, 2) == 200, 'indexes match'); expect_out_of_bounds(safe_mock.at(MOCK_SET_ID, 3)); @@ -120,7 +124,7 @@ fn test_remove() { assert(mock.length(MOCK_SET_ID) == 2, 'length should equal 2'); assert(!mock.contains(MOCK_SET_ID, 100), 'does not contain 100'); assert( - mock.contains(MOCK_SET_ID, 300) && mock.contains(MOCK_SET_ID, 200), 'contains 300 & 200' + mock.contains(MOCK_SET_ID, 300) && mock.contains(MOCK_SET_ID, 200), 'contains 300 & 200', ); assert(mock.at(MOCK_SET_ID, 1) == 300 && mock.at(MOCK_SET_ID, 2) == 200, 'indexes match'); expect_out_of_bounds(safe_mock.at(MOCK_SET_ID, 3)); diff --git a/contracts/src/tests/test_erc677.cairo b/contracts/src/tests/test_erc677.cairo index 128d8c15d..4d376b816 100644 --- a/contracts/src/tests/test_erc677.cairo +++ b/contracts/src/tests/test_erc677.cairo @@ -17,7 +17,8 @@ use chainlink::libraries::token::v2::erc677::ERC677Component; use chainlink::libraries::token::v2::erc677::ERC677Component::ERC677Impl; use snforge_std::{ - declare, ContractClassTrait, start_cheat_caller_address_global, stop_cheat_caller_address_global + declare, ContractClassTrait, start_cheat_caller_address_global, + stop_cheat_caller_address_global, DeclareResultTrait, }; #[starknet::interface] @@ -26,10 +27,11 @@ trait MockInvalidReceiver { } use chainlink::token::mock::valid_erc667_receiver::{ - MockValidReceiver, MockValidReceiverDispatcher, MockValidReceiverDispatcherTrait + MockValidReceiver, MockValidReceiverDispatcher, MockValidReceiverDispatcherTrait, }; -// Ignored tests are dependent on upgrading our version of cairo to include this PR https://github.com/starkware-libs/cairo/pull/2912/files +// Ignored tests are dependent on upgrading our version of cairo to include this PR +// https://github.com/starkware-libs/cairo/pull/2912/files fn setup() -> ContractAddress { let account: ContractAddress = contract_address_const::<1>(); @@ -41,7 +43,11 @@ fn setup() -> ContractAddress { fn setup_valid_receiver() -> (ContractAddress, MockValidReceiverDispatcher) { let calldata = ArrayTrait::new(); - let (address, _) = declare("ValidReceiver").unwrap().deploy(@calldata).unwrap(); + let (address, _) = declare("ValidReceiver") + .unwrap() + .contract_class() + .deploy(@calldata) + .unwrap(); let contract = MockValidReceiverDispatcher { contract_address: address }; (address, contract) @@ -51,7 +57,11 @@ fn setup_valid_receiver() -> (ContractAddress, MockValidReceiverDispatcher) { fn setup_invalid_receiver() -> (ContractAddress, MockInvalidReceiverDispatcher) { let calldata = ArrayTrait::new(); - let (address, _) = declare("InvalidReceiver").unwrap().deploy(@calldata).unwrap(); + let (address, _) = declare("InvalidReceiver") + .unwrap() + .contract_class() + .deploy(@calldata) + .unwrap(); let contract = MockInvalidReceiverDispatcher { contract_address: address }; (address, contract) @@ -62,7 +72,8 @@ type ComponentState = fn transfer_and_call(receiver: ContractAddress) { let data = ArrayTrait::::new(); - // have to send 0 because ERC20 is not initialized with starting supply when using this library by itself + // have to send 0 because ERC20 is not initialized with starting supply when using this library + // by itself let mut state: ComponentState = ERC677Component::component_state_for_testing(); state.transfer_and_call(receiver, u256 { high: 0, low: 0 }, data); } diff --git a/contracts/src/tests/test_link_token.cairo b/contracts/src/tests/test_link_token.cairo index 7e6f9a923..5eb2fb5dc 100644 --- a/contracts/src/tests/test_link_token.cairo +++ b/contracts/src/tests/test_link_token.cairo @@ -1,6 +1,6 @@ use starknet::{ syscalls::deploy_syscall, ContractAddress, testing::set_caller_address, contract_address_const, - class_hash::{class_hash_const, Felt252TryIntoClassHash} + class_hash::{class_hash_const, Felt252TryIntoClassHash}, }; use array::ArrayTrait; @@ -9,18 +9,18 @@ use zeroable::Zeroable; use option::OptionTrait; use core::result::ResultTrait; -use chainlink::token::v2::link_token::{ - LinkToken, LinkToken::{MintableToken, UpgradeableImpl, Minter} -}; +use chainlink::token::v2::link_token::{LinkToken, LinkToken::{MintableToken, Minter}}; +use chainlink::libraries::upgrades::v2::owner_upgradeable::OwnerUpgradeableComponent::OwnerUpgradeableImpl; use openzeppelin::token::erc20::ERC20Component::{ERC20Impl, ERC20MetadataImpl}; use chainlink::tests::test_ownable::should_implement_ownable; use snforge_std::{ - declare, ContractClassTrait, start_cheat_caller_address_global, stop_cheat_caller_address_global + declare, ContractClassTrait, start_cheat_caller_address_global, + stop_cheat_caller_address_global, DeclareResultTrait, }; -// only tests link token specific functionality +// only tests link token specific functionality // erc20 and erc677 functionality is already tested elsewhere fn STATE() -> LinkToken::ContractState { @@ -59,10 +59,10 @@ fn test_ownable() { let account = setup(); // Deploy LINK token let calldata = link_deploy_args(contract_address_const::<123>(), // minter - account // owner + account // owner ); - let (linkAddr, _) = declare("LinkToken").unwrap().deploy(@calldata).unwrap(); + let (linkAddr, _) = declare("LinkToken").unwrap().contract_class().deploy(@calldata).unwrap(); should_implement_ownable(linkAddr, account); } @@ -119,7 +119,7 @@ fn test_permissioned_mint_from_nonminter() { } #[test] -#[should_panic(expected: ('u256_sub Overflow',))] +#[should_panic(expected: ('ERC20: insufficient balance',))] fn test_permissioned_burn_from_minter() { let zero = 0; let sender = setup(); @@ -165,10 +165,10 @@ fn test_upgrade_non_owner() { let sender = setup(); let mut state = STATE(); LinkToken::constructor( - ref state, 0, 0, 0, 0, Zeroable::zero(), sender, contract_address_const::<111>(), 0 + ref state, 0, 0, 0, 0, Zeroable::zero(), sender, contract_address_const::<111>(), 0, ); - UpgradeableImpl::upgrade(ref state, class_hash_const::<123>()); + OwnerUpgradeableImpl::upgrade(ref state, class_hash_const::<123>()); } #[test] @@ -177,7 +177,7 @@ fn test_set_minter_non_owner() { let sender = setup(); let mut state = STATE(); LinkToken::constructor( - ref state, 0, 0, 0, 0, Zeroable::zero(), sender, contract_address_const::<111>(), 0 + ref state, 0, 0, 0, 0, Zeroable::zero(), sender, contract_address_const::<111>(), 0, ); Minter::set_minter(ref state, contract_address_const::<123>()) @@ -195,6 +195,18 @@ fn test_set_minter_already() { Minter::set_minter(ref state, minter); } +#[test] +#[should_panic(expected: ('minter is 0',))] +fn test_set_minter_zero() { + let sender = setup(); + let mut state = STATE(); + + let minter = contract_address_const::<111>(); + LinkToken::constructor(ref state, 0, 0, 0, 0, Zeroable::zero(), minter, sender, 0); + + Minter::set_minter(ref state, Zeroable::zero()); +} + #[test] fn test_set_minter_success() { let sender = setup(); diff --git a/contracts/src/tests/test_mcms/test_execute.cairo b/contracts/src/tests/test_mcms/test_execute.cairo index e4c588867..6bcd028df 100644 --- a/contracts/src/tests/test_mcms/test_execute.cairo +++ b/contracts/src/tests/test_mcms/test_execute.cairo @@ -1,6 +1,6 @@ use starknet::{contract_address_const, EthAddress}; use chainlink::libraries::mocks::mock_multisig_target::{ - IMockMultisigTarget, IMockMultisigTargetDispatcherTrait, IMockMultisigTargetDispatcher + IMockMultisigTarget, IMockMultisigTargetDispatcherTrait, IMockMultisigTargetDispatcher, }; use chainlink::mcms::{ ExpiringRootAndOpCount, RootMetadata, Config, Signer, ManyChainMultiSig, Op, @@ -21,20 +21,20 @@ fn test_success() { mut spy, mcms_address, mcms, - safe_mcms, - config, - signer_addresses, - signer_groups, - group_quorums, - group_parents, - clear_root, + _, + _, + _, + _, + _, + _, + _, root, valid_until, metadata, metadata_proof, signatures, ops, - ops_proof + ops_proof, ) = setup_mcms_deploy_set_config_and_set_root(); @@ -59,11 +59,11 @@ fn test_success() { mcms_address, ManyChainMultiSig::Event::OpExecuted( ManyChainMultiSig::OpExecuted { - nonce: op1.nonce, to: op1.to, selector: op1.selector, data: op1.data - } - ) - ) - ] + nonce: op1.nonce, to: op1.to, selector: op1.selector, data: op1.data, + }, + ), + ), + ], ); assert(mcms.get_op_count() == 1, 'op count should be 1'); @@ -83,11 +83,11 @@ fn test_success() { mcms_address, ManyChainMultiSig::Event::OpExecuted( ManyChainMultiSig::OpExecuted { - nonce: op2.nonce, to: op2.to, selector: op2.selector, data: op2.data - } - ) - ) - ] + nonce: op2.nonce, to: op2.to, selector: op2.selector, data: op2.data, + }, + ), + ), + ], ); assert(mcms.get_op_count() == 2, 'op count should be 2'); @@ -100,23 +100,23 @@ fn test_success() { #[feature("safe_dispatcher")] fn test_no_more_ops_to_execute() { let ( - mut spy, - mcms_address, + _, + _, mcms, safe_mcms, - config, - signer_addresses, - signer_groups, - group_quorums, - group_parents, - clear_root, + _, + _, + _, + _, + _, + _, root, valid_until, metadata, metadata_proof, signatures, ops, - ops_proof + ops_proof, ) = setup_mcms_deploy_set_config_and_set_root(); @@ -136,7 +136,7 @@ fn test_no_more_ops_to_execute() { Result::Ok(_) => panic!("expect 'post-operation count reached'"), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'post-operation count reached', *panic_data.at(0)); - } + }, } } @@ -144,23 +144,23 @@ fn test_no_more_ops_to_execute() { #[feature("safe_dispatcher")] fn test_wrong_chain_id() { let ( - mut spy, - mcms_address, + _, + _, mcms, safe_mcms, - config, - signer_addresses, - signer_groups, - group_quorums, - group_parents, - clear_root, + _, + _, + _, + _, + _, + _, root, valid_until, metadata, metadata_proof, signatures, ops, - ops_proof + ops_proof, ) = setup_mcms_deploy_set_config_and_set_root(); @@ -176,7 +176,7 @@ fn test_wrong_chain_id() { Result::Ok(_) => panic!("expect 'wrong chain id'"), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'wrong chain id', *panic_data.at(0)); - } + }, } } @@ -184,23 +184,23 @@ fn test_wrong_chain_id() { #[feature("safe_dispatcher")] fn test_wrong_multisig_address() { let ( - mut spy, - mcms_address, + _, + _, mcms, safe_mcms, - config, - signer_addresses, - signer_groups, - group_quorums, - group_parents, - clear_root, + _, + _, + _, + _, + _, + _, root, valid_until, metadata, metadata_proof, signatures, ops, - ops_proof + ops_proof, ) = setup_mcms_deploy_set_config_and_set_root(); @@ -216,7 +216,7 @@ fn test_wrong_multisig_address() { Result::Ok(_) => panic!("expect 'wrong multisig address'"), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'wrong multisig address', *panic_data.at(0)); - } + }, } } @@ -225,23 +225,23 @@ fn test_wrong_multisig_address() { #[feature("safe_dispatcher")] fn test_root_expired() { let ( - mut spy, - mcms_address, + _, + _, mcms, safe_mcms, - config, - signer_addresses, - signer_groups, - group_quorums, - group_parents, - clear_root, + _, + _, + _, + _, + _, + _, root, valid_until, metadata, metadata_proof, signatures, ops, - ops_proof + ops_proof, ) = setup_mcms_deploy_set_config_and_set_root(); @@ -257,7 +257,7 @@ fn test_root_expired() { Result::Ok(_) => panic!("expect 'root has expired'"), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'root has expired', *panic_data.at(0)); - } + }, } } @@ -265,23 +265,23 @@ fn test_root_expired() { #[feature("safe_dispatcher")] fn test_wrong_nonce() { let ( - mut spy, - mcms_address, + _, + _, mcms, safe_mcms, - config, - signer_addresses, - signer_groups, - group_quorums, - group_parents, - clear_root, + _, + _, + _, + _, + _, + _, root, valid_until, metadata, metadata_proof, signatures, ops, - ops_proof + ops_proof, ) = setup_mcms_deploy_set_config_and_set_root(); @@ -297,7 +297,7 @@ fn test_wrong_nonce() { Result::Ok(_) => panic!("expect 'wrong nonce'"), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'wrong nonce', *panic_data.at(0)); - } + }, } } @@ -305,23 +305,23 @@ fn test_wrong_nonce() { #[feature("safe_dispatcher")] fn test_proof_verification_failed() { let ( - mut spy, - mcms_address, + _, + _, mcms, safe_mcms, - config, - signer_addresses, - signer_groups, - group_quorums, - group_parents, - clear_root, + _, + _, + _, + _, + _, + _, root, valid_until, metadata, metadata_proof, signatures, ops, - ops_proof + _, ) = setup_mcms_deploy_set_config_and_set_root(); @@ -336,7 +336,7 @@ fn test_proof_verification_failed() { Result::Ok(_) => panic!("expect 'proof verification failed'"), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'proof verification failed', *panic_data.at(0)); - } + }, } } diff --git a/contracts/src/tests/test_mcms/test_set_config.cairo b/contracts/src/tests/test_mcms/test_set_config.cairo index b1039aabd..280cfd4cf 100644 --- a/contracts/src/tests/test_mcms/test_set_config.cairo +++ b/contracts/src/tests/test_mcms/test_set_config.cairo @@ -1,15 +1,11 @@ use core::array::{SpanTrait, ArrayTrait}; use starknet::{ ContractAddress, EthAddress, Felt252TryIntoEthAddress, EthAddressIntoFelt252, - EthAddressZeroable, contract_address_const + EthAddressZeroable, contract_address_const, }; use chainlink::mcms::{ ExpiringRootAndOpCount, RootMetadata, Config, Signer, ManyChainMultiSig, - ManyChainMultiSig::{ - InternalFunctionsTrait, contract_state_for_testing, s_signersContractMemberStateTrait, - s_expiring_root_and_op_countContractMemberStateTrait, - s_root_metadataContractMemberStateTrait - }, + ManyChainMultiSig::{InternalFunctionsTrait, contract_state_for_testing}, IManyChainMultiSigDispatcher, IManyChainMultiSigDispatcherTrait, IManyChainMultiSigSafeDispatcher, IManyChainMultiSigSafeDispatcherTrait, IManyChainMultiSig, ManyChainMultiSig::{MAX_NUM_SIGNERS}, @@ -19,11 +15,11 @@ use snforge_std::{ stop_cheat_caller_address, stop_cheat_caller_address_global, spy_events, EventSpyAssertionsTrait, // Add for assertions on the EventSpy test_address, // the contract being tested, - start_cheat_chain_id, - cheatcodes::{events::{EventSpy}} + start_cheat_chain_id, + cheatcodes::{events::{EventSpy}}, }; use chainlink::tests::test_mcms::utils::{ - setup_mcms_deploy, setup_mcms_deploy_and_set_config_2_of_2, ZERO_ARRAY, fill_array + setup_mcms_deploy, setup_mcms_deploy_and_set_config_2_of_2, ZERO_ARRAY, fill_array, }; #[test] @@ -46,21 +42,21 @@ fn test_not_owner() { signer_groups.span(), group_quorums.span(), group_parents.span(), - clear_root + clear_root, ); match result { Result::Ok(_) => panic!("expect 'Caller is not the owner'"), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'Caller is not the owner', *panic_data.at(0)); - } + }, } } #[test] #[feature("safe_dispatcher")] fn test_set_config_out_of_bound_signers() { - // 1. test if len(signer_address) = 0 => revert + // 1. test if len(signer_address) = 0 => revert let (_, _, mcms_safe) = setup_mcms_deploy(); let signer_addresses = array![]; @@ -75,14 +71,14 @@ fn test_set_config_out_of_bound_signers() { signer_groups.span(), group_quorums.span(), group_parents.span(), - clear_root + clear_root, ); match result { Result::Ok(_) => panic!("expect 'out of bound signers len'"), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'out of bound signers len', *panic_data.at(0)); - } + }, } // 2. test if lena(signer_address) > MAX_NUM_SIGNERS => revert @@ -103,14 +99,14 @@ fn test_set_config_out_of_bound_signers() { signer_groups.span(), group_quorums.span(), group_parents.span(), - clear_root + clear_root, ); match result { Result::Ok(_) => panic!("expect 'out of bound signers len'"), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'out of bound signers len', *panic_data.at(0)); - } + }, } } @@ -132,14 +128,14 @@ fn test_set_config_signer_groups_len_mismatch() { signer_groups.span(), group_quorums.span(), group_parents.span(), - clear_root + clear_root, ); match result { Result::Ok(_) => panic!("expect 'signer groups len mismatch'"), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'signer groups len mismatch', *panic_data.at(0)); - } + }, } } @@ -161,14 +157,14 @@ fn test_set_config_group_quorums_parents_mismatch() { signer_groups.span(), group_quorums.span(), group_parents.span(), - clear_root + clear_root, ); match result { Result::Ok(_) => panic!("expect 'wrong group quorums/parents len'"), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'wrong group quorums/parents len', *panic_data.at(0)); - } + }, } // 5. test if group_quorum and group_parents not equal in length @@ -181,14 +177,14 @@ fn test_set_config_group_quorums_parents_mismatch() { signer_groups.span(), group_quorums.span(), group_parents.span(), - clear_root + clear_root, ); match result { Result::Ok(_) => panic!("expect 'wrong group quorums/parents len'"), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'wrong group quorums/parents len', *panic_data.at(0)); - } + }, } } @@ -223,14 +219,14 @@ fn test_set_config_signers_group_out_of_bounds() { signer_groups.span(), group_quorums.span(), group_parents.span(), - clear_root + clear_root, ); match result { Result::Ok(_) => panic!("expect 'out of bounds group'"), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'out of bounds group', *panic_data.at(0)); - } + }, } } @@ -254,14 +250,14 @@ fn test_set_config_group_tree_malformed() { signer_groups.span(), group_quorums.span(), group_parents.span(), - clear_root + clear_root, ); match result { Result::Ok(_) => panic!("expect 'group tree malformed'"), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'group tree malformed', *panic_data.at(0)); - } + }, } let mut group_parents = array![ @@ -296,7 +292,7 @@ fn test_set_config_group_tree_malformed() { 0, 0, 0, - 0 + 0, ]; let result = mcms_safe @@ -305,14 +301,14 @@ fn test_set_config_group_tree_malformed() { signer_groups.span(), group_quorums.span(), group_parents.span(), - clear_root + clear_root, ); match result { Result::Ok(_) => panic!("expect 'group tree malformed'"), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'group tree malformed', *panic_data.at(0)); - } + }, } } @@ -334,14 +330,14 @@ fn test_set_config_signer_in_disabled_group() { signer_groups.span(), group_quorums.span(), group_parents.span(), - clear_root + clear_root, ); match result { Result::Ok(_) => panic!("expect 'signer in disabled group'"), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'signer in disabled group', *panic_data.at(0)); - } + }, } } @@ -363,14 +359,14 @@ fn test_set_config_quorum_impossible() { signer_groups.span(), group_quorums.span(), group_parents.span(), - clear_root + clear_root, ); match result { Result::Ok(_) => panic!("expect 'quorum impossible'"), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'quorum impossible', *panic_data.at(0)); - } + }, } } @@ -382,7 +378,7 @@ fn test_set_config_signer_addresses_not_sorted() { let mut signer_addresses: Array = array![ // 0x1 address - u256 { high: 0, low: 1 }.into(), EthAddressZeroable::zero() + u256 { high: 0, low: 1 }.into(), EthAddressZeroable::zero(), ]; let signer_groups = array![0, 0]; let mut group_quorums = fill_array(array![(0, 2)]); @@ -395,28 +391,29 @@ fn test_set_config_signer_addresses_not_sorted() { signer_groups.span(), group_quorums.span(), group_parents.span(), - clear_root + clear_root, ); match result { Result::Ok(_) => panic!("expect 'signer addresses not sorted'"), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'signer addresses not sorted', *panic_data.at(0)); - } + }, } } // test success, root not cleared, event emitted -// 12. successful => test without clearing root. test the state of storage variables and that event was emitted +// 12. successful => test without clearing root. test the state of storage variables and that event +// was emitted // // ┌──────┐ // ┌─►│2-of-2│ -// │ └──────┘ -// │ ▲ -// │ │ -// ┌──┴───┐ ┌──┴───┐ -// signer 1 signer 2 -// └──────┘ └──────┘ +// │ └──────┘ +// │ ▲ +// │ │ +// ┌──┴───┐ ┌──┴───┐ +// signer 1 signer 2 +// └──────┘ └──────┘ #[test] fn test_set_config_success_dont_clear_root() { let signer_address_1: EthAddress = (0x141).try_into().unwrap(); @@ -431,10 +428,10 @@ fn test_set_config_success_dont_clear_root() { signer_groups, group_quorums, group_parents, - clear_root + clear_root, ) = setup_mcms_deploy_and_set_config_2_of_2( - signer_address_1, signer_address_2 + signer_address_1, signer_address_2, ); let expected_signer_1 = Signer { address: signer_address_1, index: 0, group: 0 }; @@ -453,11 +450,11 @@ fn test_set_config_success_dont_clear_root() { mcms_address, ManyChainMultiSig::Event::ConfigSet( ManyChainMultiSig::ConfigSet { - config: expected_config, is_root_cleared: false - } - ) - ) - ] + config: expected_config, is_root_cleared: false, + }, + ), + ), + ], ); let config = mcms.get_config(); assert(config == expected_config, 'config should be same'); @@ -477,7 +474,7 @@ fn test_set_config_success_dont_clear_root() { signer_groups.span(), group_quorums.span(), group_parents.span(), - clear_root + clear_root, ); let signer_1 = state.get_signer_by_address(signer_address_1); @@ -497,7 +494,7 @@ fn test_set_config_success_dont_clear_root() { signer_groups.span(), group_quorums.span(), group_parents.span(), - clear_root + clear_root, ); let new_config = mcms.get_config(); @@ -519,7 +516,7 @@ fn test_set_config_success_dont_clear_root() { signer_groups.span(), group_quorums.span(), group_parents.span(), - clear_root + clear_root, ); let new_signer_1 = state.get_signer_by_address(new_signer_address_1); @@ -536,7 +533,7 @@ fn test_set_config_success_dont_clear_root() { } -// test that the config was reset +// test that the config was reset #[test] fn test_set_config_success_and_clear_root() { // mock the contract state @@ -556,8 +553,8 @@ fn test_set_config_success_and_clear_root() { .s_expiring_root_and_op_count .write( ExpiringRootAndOpCount { - root: u256 { high: 777, low: 777 }, valid_until: 102934894, op_count: 134 - } + root: u256 { high: 777, low: 777 }, valid_until: 102934894, op_count: 134, + }, ); state @@ -568,8 +565,8 @@ fn test_set_config_success_and_clear_root() { multisig: contract_address_const::<111>(), pre_op_count: 20, post_op_count: 155, - override_previous_root: false - } + override_previous_root: false, + }, ); let signer_address_1: EthAddress = u256 { high: 0, low: 1 }.into(); @@ -586,16 +583,16 @@ fn test_set_config_success_and_clear_root() { signer_groups.span(), group_quorums.span(), group_parents.span(), - clear_root + clear_root, ); let expected_s_expiring_root_and_op_count = ExpiringRootAndOpCount { - root: u256 { high: 0, low: 0 }, valid_until: 0, op_count: 134 + root: u256 { high: 0, low: 0 }, valid_until: 0, op_count: 134, }; let s_expiring_root_and_op_count = state.s_expiring_root_and_op_count.read(); assert!( s_expiring_root_and_op_count == expected_s_expiring_root_and_op_count, - "s_expiring_root_and_op_count not equal" + "s_expiring_root_and_op_count not equal", ); let expected_s_root_metadata = RootMetadata { @@ -603,7 +600,7 @@ fn test_set_config_success_and_clear_root() { multisig: test_address, pre_op_count: 134, post_op_count: 134, - override_previous_root: true + override_previous_root: true, }; let s_root_metadata = state.s_root_metadata.read(); assert(expected_s_root_metadata == s_root_metadata, 's_root_metadata not equal'); diff --git a/contracts/src/tests/test_mcms/test_set_root.cairo b/contracts/src/tests/test_mcms/test_set_root.cairo index 3cce4db88..d95127160 100644 --- a/contracts/src/tests/test_mcms/test_set_root.cairo +++ b/contracts/src/tests/test_mcms/test_set_root.cairo @@ -1,4 +1,3 @@ -use alexandria_data_structures::array_ext::ArrayTraitExt; use alexandria_bytes::{Bytes, BytesTrait}; use alexandria_encoding::sol_abi::sol_bytes::SolBytesTrait; use alexandria_encoding::sol_abi::encode::SolAbiEncodeTrait; @@ -8,36 +7,32 @@ use starknet::{ EthAddressZeroable, contract_address_const, eth_signature::public_key_point_to_eth_address, secp256_trait::{ Secp256Trait, Secp256PointTrait, recover_public_key, is_signature_entry_valid, Signature, - signature_from_vrs + signature_from_vrs, }, - secp256k1::Secp256k1Point, SyscallResult, SyscallResultTrait + secp256k1::Secp256k1Point, SyscallResult, SyscallResultTrait, }; use chainlink::mcms::{ recover_eth_ecdsa, hash_pair, hash_op, hash_metadata, ExpiringRootAndOpCount, RootMetadata, Config, Signer, eip_191_message_hash, ManyChainMultiSig, Op, - ManyChainMultiSig::{ - NewRoot, InternalFunctionsTrait, contract_state_for_testing, - s_signersContractMemberStateTrait, s_expiring_root_and_op_countContractMemberStateTrait, - s_root_metadataContractMemberStateTrait - }, + ManyChainMultiSig::{NewRoot, InternalFunctionsTrait, contract_state_for_testing}, IManyChainMultiSigDispatcher, IManyChainMultiSigDispatcherTrait, IManyChainMultiSigSafeDispatcher, IManyChainMultiSigSafeDispatcherTrait, IManyChainMultiSig, ManyChainMultiSig::{MAX_NUM_SIGNERS}, }; use chainlink::tests::test_mcms::utils::{ insecure_sign, setup_signers, SignerMetadata, setup_mcms_deploy_and_set_config_2_of_2, - setup_mcms_deploy_set_config_and_set_root, set_root_args, merkle_root + setup_mcms_deploy_set_config_and_set_root, set_root_args, merkle_root, }; - +use chainlink::utils::{keccak}; use snforge_std::{ declare, ContractClassTrait, start_cheat_caller_address_global, start_cheat_caller_address, stop_cheat_caller_address, stop_cheat_caller_address_global, start_cheat_chain_id_global, spy_events, EventSpyAssertionsTrait, // Add for assertions on the EventSpy test_address, // the contract being tested, - start_cheat_chain_id, + start_cheat_chain_id, cheatcodes::{events::{EventSpy}}, start_cheat_block_timestamp_global, start_cheat_block_timestamp, start_cheat_account_contract_address_global, - start_cheat_account_contract_address + start_cheat_account_contract_address, DeclareResultTrait, }; // sets up root but with wrong multisig address in metadata @@ -60,8 +55,7 @@ fn setup_mcms_deploy_set_config_and_set_root_WRONG_MULTISIG() -> ( Array, Span>, ) { - let (signer_address_1, private_key_1, signer_address_2, private_key_2, signer_metadata) = - setup_signers(); + let (signer_address_1, private_key_1, signer_address_2, private_key_2, _) = setup_signers(); let ( mut spy, @@ -73,14 +67,14 @@ fn setup_mcms_deploy_set_config_and_set_root_WRONG_MULTISIG() -> ( signer_groups, group_quorums, group_parents, - clear_root + clear_root, ) = setup_mcms_deploy_and_set_config_2_of_2( - signer_address_1, signer_address_2 + signer_address_1, signer_address_2, ); let calldata = ArrayTrait::new(); - let mock_target_contract = declare("MockMultisigTarget").unwrap(); + let mock_target_contract = declare("MockMultisigTarget").unwrap().contract_class(); let (target_address, _) = mock_target_contract.deploy(@calldata).unwrap(); // mock chain id & timestamp @@ -98,7 +92,7 @@ fn setup_mcms_deploy_set_config_and_set_root_WRONG_MULTISIG() -> ( nonce: 0, to: target_address, selector: selector1, - data: calldata1.span() + data: calldata1.span(), }; // second operation @@ -111,7 +105,7 @@ fn setup_mcms_deploy_set_config_and_set_root_WRONG_MULTISIG() -> ( nonce: 1, to: target_address, selector: selector2, - data: calldata2.span() + data: calldata2.span(), }; let metadata = RootMetadata { @@ -132,7 +126,7 @@ fn setup_mcms_deploy_set_config_and_set_root_WRONG_MULTISIG() -> ( let (root, metadata_proof, ops_proof) = merkle_root(array![op1_hash, op2_hash, metadata_hash]); let encoded_root = BytesTrait::new_empty().encode(root).encode(valid_until); - let message_hash = eip_191_message_hash(encoded_root.keccak()); + let message_hash = eip_191_message_hash(keccak(@encoded_root.into())); let (r_1, s_1, y_parity_1) = insecure_sign(message_hash, private_key_1); let (r_2, s_2, y_parity_2) = insecure_sign(message_hash, private_key_2); @@ -167,30 +161,43 @@ fn setup_mcms_deploy_set_config_and_set_root_WRONG_MULTISIG() -> ( metadata_proof, signatures, ops, - ops_proof + ops_proof, ) } +#[test] +fn test_eip_191_message_hash() { + let mut msg: ByteArray = Default::default(); + msg.append_byte(0x11); + + let msg_hash = eip_191_message_hash(keccak(@msg)); + + let expected_msg_hash: u256 = + 0x01f83f8506ac29b9cbd12376cf298e9b02961776e960e7f768933386c75f5d02; + + assert(msg_hash == expected_msg_hash, 'invalid msg hash') +} + #[test] fn test_set_root_success() { let ( mut spy, mcms_address, mcms, - safe_mcms, - config, - signer_addresses, - signer_groups, - group_quorums, - group_parents, - clear_root, + _, + _, + _, + _, + _, + _, + _, root, valid_until, metadata, metadata_proof, signatures, - ops, - ops_proof + _, + _, ) = setup_mcms_deploy_set_config_and_set_root(); @@ -211,34 +218,35 @@ fn test_set_root_success() { mcms_address, ManyChainMultiSig::Event::NewRoot( ManyChainMultiSig::NewRoot { - root: root, valid_until: valid_until, metadata: metadata - } - ) - ) - ] + root: root, valid_until: valid_until, metadata: metadata, + }, + ), + ), + ], ); } + #[test] #[feature("safe_dispatcher")] fn test_set_root_hash_seen() { let ( - mut spy, - mcms_address, + _, + _, mcms, safe_mcms, - config, - signer_addresses, - signer_groups, - group_quorums, - group_parents, - clear_root, + _, + _, + _, + _, + _, + _, root, valid_until, metadata, metadata_proof, signatures, - ops, - ops_proof + _, + _, ) = setup_mcms_deploy_set_config_and_set_root(); @@ -250,7 +258,7 @@ fn test_set_root_hash_seen() { Result::Ok(_) => panic!("expect 'signed hash already seen'"), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'signed hash already seen', *panic_data.at(0)); - } + }, } } @@ -258,23 +266,23 @@ fn test_set_root_hash_seen() { #[feature("safe_dispatcher")] fn test_set_root_signatures_wrong_order() { let ( - mut spy, - mcms_address, - mcms, + _, + _, + _, safe_mcms, - config, - signer_addresses, - signer_groups, - group_quorums, - group_parents, - clear_root, + _, + _, + _, + _, + _, + _, root, valid_until, metadata, metadata_proof, signatures, - ops, - ops_proof + _, + _, ) = setup_mcms_deploy_set_config_and_set_root(); @@ -287,7 +295,7 @@ fn test_set_root_signatures_wrong_order() { Result::Ok(_) => panic!("expect 'signer address must increase'"), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'signer address must increase', *panic_data.at(0)); - } + }, } } @@ -295,23 +303,7 @@ fn test_set_root_signatures_wrong_order() { #[feature("safe_dispatcher")] fn test_set_root_signatures_invalid_signer() { let ( - mut spy, - mcms_address, - mcms, - safe_mcms, - config, - signer_addresses, - signer_groups, - group_quorums, - group_parents, - clear_root, - root, - valid_until, - metadata, - metadata_proof, - signatures, - ops, - ops_proof + _, _, _, safe_mcms, _, _, _, _, _, _, root, valid_until, metadata, metadata_proof, _, _, _, ) = setup_mcms_deploy_set_config_and_set_root(); @@ -322,7 +314,7 @@ fn test_set_root_signatures_invalid_signer() { high: 0x9e8df5d64fb9d2ae155b435ac37519fd, low: 0x6d1ffddf225cde953f6c97f8b3a7531d, }, s: u256 { - high: 0x21f13cc6eb1d14f6ebdc497411c57589, low: 0xea109b402fcde2cfe8f3d1b6d2bb8948 + high: 0x21f13cc6eb1d14f6ebdc497411c57589, low: 0xea109b402fcde2cfe8f3d1b6d2bb8948, }, ), signature_from_vrs( @@ -331,9 +323,9 @@ fn test_set_root_signatures_invalid_signer() { high: 0x7a5d64ca9b1814e15eb8df73b3c79ac2, low: 0x9b9080ac6546e07b1118b16e5651e19d, }, s: u256 { - high: 0x62794369d5bb5f5a02d2eb6805951990, low: 0xdfcd8563639dcc6668e235e1bea93303 + high: 0x62794369d5bb5f5a02d2eb6805951990, low: 0xdfcd8563639dcc6668e235e1bea93303, }, - ) + ), ]; let result = safe_mcms @@ -343,7 +335,7 @@ fn test_set_root_signatures_invalid_signer() { Result::Ok(_) => panic!("expect 'invalid signer'"), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'invalid signer', *panic_data.at(0)); - } + }, } } @@ -351,23 +343,23 @@ fn test_set_root_signatures_invalid_signer() { #[feature("safe_dispatcher")] fn test_insufficient_signers() { let ( - mut spy, - mcms_address, - mcms, + _, + _, + _, safe_mcms, - config, - signer_addresses, - signer_groups, - group_quorums, - group_parents, - clear_root, + _, + _, + _, + _, + _, + _, root, valid_until, metadata, metadata_proof, signatures, - ops, - ops_proof + _, + _, ) = setup_mcms_deploy_set_config_and_set_root(); @@ -380,7 +372,7 @@ fn test_insufficient_signers() { Result::Ok(_) => panic!("expect 'insufficient signers'"), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'insufficient signers', *panic_data.at(0)); - } + }, } } @@ -388,23 +380,23 @@ fn test_insufficient_signers() { #[feature("safe_dispatcher")] fn test_valid_until_expired() { let ( - mut spy, - mcms_address, - mcms, + _, + _, + _, safe_mcms, - config, - signer_addresses, - signer_groups, - group_quorums, - group_parents, - clear_root, + _, + _, + _, + _, + _, + _, root, valid_until, metadata, metadata_proof, signatures, - ops, - ops_proof + _, + _, ) = setup_mcms_deploy_set_config_and_set_root(); @@ -417,7 +409,7 @@ fn test_valid_until_expired() { Result::Ok(_) => panic!("expect 'valid until has passed'"), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'valid until has passed', *panic_data.at(0)); - } + }, } } @@ -425,23 +417,23 @@ fn test_valid_until_expired() { #[feature("safe_dispatcher")] fn test_invalid_metadata_proof() { let ( - mut spy, - mcms_address, - mcms, + _, + _, + _, safe_mcms, - config, - signer_addresses, - signer_groups, - group_quorums, - group_parents, - clear_root, + _, + _, + _, + _, + _, + _, root, valid_until, metadata, metadata_proof, signatures, - ops, - ops_proof + _, + _, ) = setup_mcms_deploy_set_config_and_set_root(); @@ -454,7 +446,7 @@ fn test_invalid_metadata_proof() { Result::Ok(_) => panic!("expect 'proof verification failed'"), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'proof verification failed', *panic_data.at(0)); - } + }, } } @@ -462,23 +454,23 @@ fn test_invalid_metadata_proof() { #[feature("safe_dispatcher")] fn test_invalid_chain_id() { let ( - mut spy, - mcms_address, - mcms, + _, + _, + _, safe_mcms, - config, - signer_addresses, - signer_groups, - group_quorums, - group_parents, - clear_root, + _, + _, + _, + _, + _, + _, root, valid_until, metadata, metadata_proof, signatures, - ops, - ops_proof + _, + _, ) = setup_mcms_deploy_set_config_and_set_root(); @@ -490,7 +482,7 @@ fn test_invalid_chain_id() { Result::Ok(_) => panic!("expect 'wrong chain id'"), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'wrong chain id', *panic_data.at(0)); - } + }, } } @@ -498,23 +490,23 @@ fn test_invalid_chain_id() { #[feature("safe_dispatcher")] fn test_invalid_multisig_address() { let ( - mut spy, - mcms_address, - mcms, + _, + _, + _, safe_mcms, - config, - signer_addresses, - signer_groups, - group_quorums, - group_parents, - clear_root, + _, + _, + _, + _, + _, + _, root, valid_until, metadata, metadata_proof, signatures, - ops, - ops_proof + _, + _, ) = setup_mcms_deploy_set_config_and_set_root_WRONG_MULTISIG(); @@ -524,7 +516,7 @@ fn test_invalid_multisig_address() { Result::Ok(_) => panic!("expect 'wrong multisig address'"), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'wrong multisig address', *panic_data.at(0)); - } + }, } } @@ -532,23 +524,23 @@ fn test_invalid_multisig_address() { #[feature("safe_dispatcher")] fn test_pending_ops_remain() { let ( - mut spy, + _, mcms_address, mcms, safe_mcms, - config, - signer_addresses, - signer_groups, - group_quorums, - group_parents, - clear_root, + _, + _, + _, + _, + _, + _, root, valid_until, metadata, metadata_proof, signatures, - ops, - ops_proof + _, + _, ) = setup_mcms_deploy_set_config_and_set_root(); @@ -556,10 +548,9 @@ fn test_pending_ops_remain() { mcms.set_root(root, valid_until, metadata, metadata_proof, signatures.clone()); // sign a different set of operations with same signers - let (signer_address_1, private_key_1, signer_address_2, private_key_2, signer_metadata) = - setup_signers(); + let (_, _, _, _, signer_metadata) = setup_signers(); let (root, valid_until, metadata, metadata_proof, signatures, ops, ops_proof) = set_root_args( - mcms_address, contract_address_const::<123123>(), signer_metadata, 0, 2 + mcms_address, contract_address_const::<123123>(), signer_metadata, 0, 2, ); // second time fails @@ -569,7 +560,7 @@ fn test_pending_ops_remain() { Result::Ok(_) => panic!("expect 'pending operations remain'"), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'pending operations remain', *panic_data.at(0)); - } + }, } } @@ -577,37 +568,18 @@ fn test_pending_ops_remain() { #[test] #[feature("safe_dispatcher")] fn test_wrong_pre_op_count() { - let ( - mut spy, - mcms_address, - mcms, - safe_mcms, - config, - signer_addresses, - signer_groups, - group_quorums, - group_parents, - clear_root, - root, - valid_until, - metadata, - metadata_proof, - signatures, - ops, - _ - ) = + let (_, mcms_address, _, safe_mcms, _, _, _, _, _, _, _, _, _, _, _, _, _) = setup_mcms_deploy_set_config_and_set_root(); // sign a different set of operations with same signers - let (signer_address_1, private_key_1, signer_address_2, private_key_2, signer_metadata) = - setup_signers(); + let (_, _, _, _, signer_metadata) = setup_signers(); let wrong_pre_op_count = 1; let (root, valid_until, metadata, metadata_proof, signatures, _, _) = set_root_args( mcms_address, contract_address_const::<123123>(), signer_metadata, wrong_pre_op_count, - wrong_pre_op_count + 2 + wrong_pre_op_count + 2, ); // first time passes @@ -618,7 +590,7 @@ fn test_wrong_pre_op_count() { Result::Ok(_) => panic!("expect 'wrong pre-operation count'"), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'wrong pre-operation count', *panic_data.at(0)); - } + }, } } @@ -626,23 +598,23 @@ fn test_wrong_pre_op_count() { #[feature("safe_dispatcher")] fn test_wrong_post_ops_count() { let ( - mut spy, + _, mcms_address, mcms, safe_mcms, - config, - signer_addresses, - signer_groups, - group_quorums, - group_parents, - clear_root, + _, + _, + _, + _, + _, + _, root, valid_until, metadata, metadata_proof, signatures, ops, - ops_proof + ops_proof, ) = setup_mcms_deploy_set_config_and_set_root(); @@ -650,8 +622,7 @@ fn test_wrong_post_ops_count() { // sign a different set of operations with same signers - let (signer_address_1, private_key_1, signer_address_2, private_key_2, signer_metadata) = - setup_signers(); + let (_, _, _, _, signer_metadata) = setup_signers(); let op1 = *ops.at(0); let op1_proof = *ops_proof.at(0); @@ -662,7 +633,7 @@ fn test_wrong_post_ops_count() { mcms.execute(op1, op1_proof); mcms.execute(op2, op2_proof); - let (root, valid_until, metadata, metadata_proof, signatures, ops, ops_proof) = set_root_args( + let (root, valid_until, metadata, metadata_proof, signatures, _, _) = set_root_args( mcms_address, contract_address_const::<123123>(), signer_metadata, @@ -675,6 +646,6 @@ fn test_wrong_post_ops_count() { Result::Ok(_) => panic!("expect 'wrong post-operation count'"), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'wrong post-operation count', *panic_data.at(0)); - } + }, } } diff --git a/contracts/src/tests/test_mcms/utils.cairo b/contracts/src/tests/test_mcms/utils.cairo index 10854aa4a..a87bb121d 100644 --- a/contracts/src/tests/test_mcms/utils.cairo +++ b/contracts/src/tests/test_mcms/utils.cairo @@ -1,40 +1,37 @@ -use core::integer::{u512, u256_wide_mul}; +use core::num::traits::WideMul; +use core::integer::{u512, u256}; use alexandria_bytes::{Bytes, BytesTrait}; use alexandria_encoding::sol_abi::sol_bytes::SolBytesTrait; use alexandria_encoding::sol_abi::encode::SolAbiEncodeTrait; use alexandria_math::u512_arithmetics; use core::math::{u256_mul_mod_n, u256_div_mod_n}; use core::zeroable::{IsZeroResult, NonZero, zero_based}; -use alexandria_math::u512_arithmetics::{u512_add, u512_sub, U512Intou256X2,}; - +use alexandria_math::u512_arithmetics::{u512_add, u512_sub, U512Intou256X2}; +use chainlink::utils::{keccak}; use starknet::{ ContractAddress, EthAddress, EthAddressIntoFelt252, EthAddressZeroable, contract_address_const, eth_signature::public_key_point_to_eth_address, secp256_trait::{ Secp256Trait, Secp256PointTrait, recover_public_key, is_signature_entry_valid, Signature, - signature_from_vrs + signature_from_vrs, }, - secp256k1::{Secp256k1Point, Secp256k1Impl}, SyscallResult, SyscallResultTrait + secp256k1::{Secp256k1Point, Secp256k1Impl}, SyscallResult, SyscallResultTrait, }; use chainlink::mcms::{ recover_eth_ecdsa, hash_pair, hash_op, hash_metadata, ExpiringRootAndOpCount, RootMetadata, Config, Signer, eip_191_message_hash, ManyChainMultiSig, Op, - ManyChainMultiSig::{ - NewRoot, InternalFunctionsTrait, contract_state_for_testing, - s_signersContractMemberStateTrait, s_expiring_root_and_op_countContractMemberStateTrait, - s_root_metadataContractMemberStateTrait - }, + ManyChainMultiSig::{NewRoot, InternalFunctionsTrait, contract_state_for_testing}, IManyChainMultiSigDispatcher, IManyChainMultiSigDispatcherTrait, IManyChainMultiSigSafeDispatcher, IManyChainMultiSigSafeDispatcherTrait, IManyChainMultiSig, ManyChainMultiSig::{MAX_NUM_SIGNERS}, }; use snforge_std::{ - declare, ContractClassTrait, start_cheat_caller_address_global, start_cheat_caller_address, - stop_cheat_caller_address, stop_cheat_caller_address_global, spy_events, - EventSpyAssertionsTrait, // Add for assertions on the EventSpy + DeclareResultTrait, declare, ContractClassTrait, start_cheat_caller_address_global, + start_cheat_caller_address, stop_cheat_caller_address, stop_cheat_caller_address_global, + spy_events, EventSpyAssertionsTrait, // Add for assertions on the EventSpy test_address, // the contract being tested, - start_cheat_chain_id, start_cheat_chain_id_global, - start_cheat_block_timestamp_global, cheatcodes::{events::{EventSpy}} + start_cheat_chain_id, start_cheat_chain_id_global, + start_cheat_block_timestamp_global, cheatcodes::{events::{EventSpy}}, }; // @@ -43,9 +40,9 @@ use snforge_std::{ // returns a length 32 array // give (index, value) tuples to fill array with -// +// // ex: fill_array(array!(0, 1)) will fill the 0th index with value 1 -// +// // assumes that values array is sorted in ascending order of the index fn fill_array(mut values: Array<(u32, u8)>) -> Array { let mut result: Array = ArrayTrait::new(); @@ -82,7 +79,7 @@ fn ZERO_ARRAY() -> Array { #[derive(Copy, Drop, Serde)] struct SignerMetadata { address: EthAddress, - private_key: u256 + private_key: u256, } fn setup_signers() -> (EthAddress, u256, EthAddress, u256, Array) { @@ -98,7 +95,7 @@ fn setup_signers() -> (EthAddress, u256, EthAddress, u256, Array let signer_metadata = array![ SignerMetadata { address: signer_address_1, private_key: private_key_1 }, - SignerMetadata { address: signer_address_2, private_key: private_key_2 } + SignerMetadata { address: signer_address_2, private_key: private_key_2 }, ]; (signer_address_1, private_key_1, signer_address_2, private_key_2, signer_metadata) } @@ -142,14 +139,14 @@ impl U512PartialOrd of PartialOrd { // therefore this method is only meant to be used for tests // arg z: message hash, arg e: private key fn insecure_sign(z: u256, e: u256) -> (u256, u256, bool) { - let z_u512: u512 = u256_wide_mul(z, (0x1).into()); + let z_u512: u512 = z.wide_mul((0x1).into()); // order of the finite group - let N = Secp256k1Impl::get_curve_size().try_into().unwrap(); - let n_u512: u512 = u256_wide_mul(N, (0x1).into()); + let N: u256 = Secp256k1Impl::get_curve_size().try_into().unwrap(); + let n_u512: u512 = N.wide_mul((0x1).into()); // "random" number k would be generated by a pseudo-random number generator - // in secure applications it's important that k is random, or else the private key can + // in secure applications it's important that k is random, or else the private key can // be derived from r and s let k = 777; @@ -160,7 +157,7 @@ fn insecure_sign(z: u256, e: u256) -> (u256, u256, bool) { // calculate s = ( z + r*e ) / k (finite element operations) // where product = r*e and sum = z + r*re let product = u256_mul_mod_n(r_x, e, N.try_into().unwrap()); - let product_u512: u512 = u256_wide_mul(product, (0x1).into()); + let product_u512: u512 = product.wide_mul((0x1).into()); // sum = z + product (finite element operations) // avoid u256 overflow by casting to u512 @@ -199,17 +196,15 @@ fn merkle_root(leafs: Array) -> (u256, Span, Span>) { let proof2 = array![*level.at(0), metadata]; // level length is always even (except when it's 1) - while level - .len() > 1 { - let mut i = 0; - let mut new_level: Array = ArrayTrait::new(); - while i < level - .len() { - new_level.append(hash_pair(*(level.at(i)), *level.at(i + 1))); - i += 2 - }; - level = new_level.span(); + while level.len() > 1 { + let mut i = 0; + let mut new_level: Array = ArrayTrait::new(); + while i < level.len() { + new_level.append(hash_pair(*(level.at(i)), *level.at(i + 1))); + i += 2 }; + level = new_level.span(); + }; let mut metadata_proof = *level.at(0); @@ -224,7 +219,7 @@ fn set_root_args( target_address: ContractAddress, mut signers_metadata: Array, pre_op_count: u64, - post_op_count: u64 + post_op_count: u64, ) -> (u256, u32, RootMetadata, Span, Array, Array, Span>) { let mock_chain_id = 732; @@ -237,7 +232,7 @@ fn set_root_args( nonce: 0, to: target_address, selector: selector1, - data: calldata1.span() + data: calldata1.span(), }; // second operation @@ -249,7 +244,7 @@ fn set_root_args( nonce: 1, to: target_address, selector: selector2, - data: calldata2.span() + data: calldata2.span(), }; let metadata = RootMetadata { @@ -270,21 +265,20 @@ fn set_root_args( let (root, metadata_proof, ops_proof) = merkle_root(array![op1_hash, op2_hash, metadata_hash]); let encoded_root = BytesTrait::new_empty().encode(root).encode(valid_until); - let message_hash = eip_191_message_hash(encoded_root.keccak()); + let message_hash = eip_191_message_hash(keccak(@encoded_root.into())); let mut signatures: Array = ArrayTrait::new(); - while let Option::Some(signer_metadata) = signers_metadata - .pop_front() { - let (r, s, y_parity) = insecure_sign(message_hash, signer_metadata.private_key); - let signature = Signature { r: r, s: s, y_parity: y_parity }; - let address = recover_eth_ecdsa(message_hash, signature).unwrap(); + while let Option::Some(signer_metadata) = signers_metadata.pop_front() { + let (r, s, y_parity) = insecure_sign(message_hash, signer_metadata.private_key); + let signature = Signature { r: r, s: s, y_parity: y_parity }; + let address = recover_eth_ecdsa(message_hash, signature).unwrap(); - // sanity check - assert(address == signer_metadata.address, 'signer not equal'); + // sanity check + assert(address == signer_metadata.address, 'signer not equal'); - signatures.append(signature); - }; + signatures.append(signature); + }; let ops = array![op1.clone(), op2.clone()]; @@ -296,24 +290,28 @@ fn set_root_args( // fn setup_mcms_deploy() -> ( - ContractAddress, IManyChainMultiSigDispatcher, IManyChainMultiSigSafeDispatcher + ContractAddress, IManyChainMultiSigDispatcher, IManyChainMultiSigSafeDispatcher, ) { let owner = contract_address_const::<213123123>(); start_cheat_caller_address_global(owner); let calldata = array![owner.into()]; - let (mcms_address, _) = declare("ManyChainMultiSig").unwrap().deploy(@calldata).unwrap(); + let (mcms_address, _) = declare("ManyChainMultiSig") + .unwrap() + .contract_class() + .deploy(@calldata) + .unwrap(); ( mcms_address, IManyChainMultiSigDispatcher { contract_address: mcms_address }, - IManyChainMultiSigSafeDispatcher { contract_address: mcms_address } + IManyChainMultiSigSafeDispatcher { contract_address: mcms_address }, ) } fn setup_mcms_deploy_and_set_config_2_of_2( - signer_address_1: EthAddress, signer_address_2: EthAddress + signer_address_1: EthAddress, signer_address_2: EthAddress, ) -> ( EventSpy, ContractAddress, @@ -324,7 +322,7 @@ fn setup_mcms_deploy_and_set_config_2_of_2( Array, Array, Array, - bool + bool, ) { let (mcms_address, mcms, safe_mcms) = setup_mcms_deploy(); @@ -343,7 +341,7 @@ fn setup_mcms_deploy_and_set_config_2_of_2( signer_groups.span(), group_quorums.span(), group_parents.span(), - clear_root + clear_root, ); let config = mcms.get_config(); @@ -358,7 +356,7 @@ fn setup_mcms_deploy_and_set_config_2_of_2( signer_groups, group_quorums, group_parents, - clear_root + clear_root, ) } @@ -380,10 +378,9 @@ fn setup_mcms_deploy_set_config_and_set_root() -> ( Span, Array, Array, - Span> + Span>, ) { - let (signer_address_1, private_key_1, signer_address_2, private_key_2, signer_metadata) = - setup_signers(); + let (signer_address_1, _, signer_address_2, _, signer_metadata) = setup_signers(); let ( mut spy, @@ -395,18 +392,18 @@ fn setup_mcms_deploy_set_config_and_set_root() -> ( signer_groups, group_quorums, group_parents, - clear_root + clear_root, ) = setup_mcms_deploy_and_set_config_2_of_2( - signer_address_1, signer_address_2 + signer_address_1, signer_address_2, ); let calldata = ArrayTrait::new(); - let mock_target_contract = declare("MockMultisigTarget").unwrap(); + let mock_target_contract = declare("MockMultisigTarget").unwrap().contract_class(); let (target_address, _) = mock_target_contract.deploy(@calldata).unwrap(); let (root, valid_until, metadata, metadata_proof, signatures, ops, ops_proof) = set_root_args( - mcms_address, target_address, signer_metadata, 0, 2 + mcms_address, target_address, signer_metadata, 0, 2, ); // mock chain id & timestamp @@ -432,6 +429,6 @@ fn setup_mcms_deploy_set_config_and_set_root() -> ( metadata_proof, signatures, ops, - ops_proof + ops_proof, ) } diff --git a/contracts/src/tests/test_mock_aggregator.cairo b/contracts/src/tests/test_mock_aggregator.cairo index af3387415..31995d434 100644 --- a/contracts/src/tests/test_mock_aggregator.cairo +++ b/contracts/src/tests/test_mock_aggregator.cairo @@ -5,7 +5,8 @@ use starknet::contract_address_const; use chainlink::ocr2::aggregator::Round; use snforge_std::{ - declare, ContractClassTrait, start_cheat_caller_address_global, stop_cheat_caller_address_global + declare, ContractClassTrait, start_cheat_caller_address_global, + stop_cheat_caller_address_global, }; fn STATE() -> MockAggregator::ContractState { @@ -32,14 +33,14 @@ fn test_deploy() { let latest_round = MockAggregator::Aggregator::latest_round_data(@state); let _ = Round { - round_id: 0, answer: 0_u128, block_num: 0_u64, started_at: 0_u64, updated_at: 0_u64 + round_id: 0, answer: 0_u128, block_num: 0_u64, started_at: 0_u64, updated_at: 0_u64, }; assert( latest_round == Round { - round_id: 0, answer: 0_u128, block_num: 0_u64, started_at: 0_u64, updated_at: 0_u64 + round_id: 0, answer: 0_u128, block_num: 0_u64, started_at: 0_u64, updated_at: 0_u64, }, - 'rounds' + 'rounds', ); } @@ -54,16 +55,16 @@ fn test_set_latest_round() { MockAggregator::MockImpl::set_latest_round_data(ref state, 777_u128, 777_u64, 777_u64, 777_u64); let expected_round = Round { - round_id: 1, answer: 777_u128, block_num: 777_u64, started_at: 777_u64, updated_at: 777_u64 + round_id: 1, answer: 777_u128, block_num: 777_u64, started_at: 777_u64, updated_at: 777_u64, }; assert( - MockAggregator::Aggregator::latest_round_data(@state) == expected_round, 'round not equal' + MockAggregator::Aggregator::latest_round_data(@state) == expected_round, 'round not equal', ); assert( MockAggregator::Aggregator::latest_answer(@state) == expected_round.answer, - 'latest answer not equal' + 'latest answer not equal', ); } diff --git a/contracts/src/tests/test_multisig.cairo b/contracts/src/tests/test_multisig.cairo index 77eeb17b7..50f1a103c 100644 --- a/contracts/src/tests/test_multisig.cairo +++ b/contracts/src/tests/test_multisig.cairo @@ -19,7 +19,7 @@ use chainlink::multisig::{IMultisigDispatcher}; use snforge_std::{ declare, ContractClassTrait, start_cheat_caller_address_global, - stop_cheat_caller_address_global, cheat_caller_address, CheatSpan + stop_cheat_caller_address_global, cheat_caller_address, CheatSpan, DeclareResultTrait, }; fn STATE() -> Multisig::ContractState { @@ -114,7 +114,7 @@ fn test_submit_transaction() { let to = contract_address_const::<42>(); let function_selector = 10; MultisigImpl::submit_transaction( - ref state, :to, :function_selector, calldata: sample_calldata() + ref state, :to, :function_selector, calldata: sample_calldata(), ); let (transaction, _) = MultisigImpl::get_transaction(@state, 0); @@ -123,7 +123,7 @@ fn test_submit_transaction() { assert(transaction.calldata_len == sample_calldata().len(), 'should match calldata length'); assert(!transaction.executed, 'should not be executed'); assert(transaction.confirmations == 0, 'should not have confirmations'); -// TODO: compare calldata when loops are supported + // TODO: compare calldata when loops are supported } #[test] @@ -162,7 +162,7 @@ fn test_confirm_transaction() { assert(MultisigImpl::is_confirmed(@state, nonce: 0, signer: signer1), 'should be confirmed'); assert( - !MultisigImpl::is_confirmed(@state, nonce: 0, signer: signer2), 'should not be confirmed' + !MultisigImpl::is_confirmed(@state, nonce: 0, signer: signer2), 'should not be confirmed', ); let (transaction, _) = MultisigImpl::get_transaction(@state, 0); assert(transaction.confirmations == 1, 'should have confirmation'); @@ -206,10 +206,10 @@ fn test_revoke_confirmation() { MultisigImpl::revoke_confirmation(ref state, nonce: 0); assert( - !MultisigImpl::is_confirmed(@state, nonce: 0, signer: signer1), 'should not be confirmed' + !MultisigImpl::is_confirmed(@state, nonce: 0, signer: signer1), 'should not be confirmed', ); assert( - !MultisigImpl::is_confirmed(@state, nonce: 0, signer: signer2), 'should not be confirmed' + !MultisigImpl::is_confirmed(@state, nonce: 0, signer: signer2), 'should not be confirmed', ); let (transaction, _) = MultisigImpl::get_transaction(@state, 0); assert(transaction.confirmations == 0, 'should not have confirmation'); @@ -274,7 +274,11 @@ fn test_execute() { let calldata = ArrayTrait::new(); - let (test_address, _) = declare("MockMultisigTarget").unwrap().deploy(@calldata).unwrap(); + let (test_address, _) = declare("MockMultisigTarget") + .unwrap() + .contract_class() + .deploy(@calldata) + .unwrap(); start_cheat_caller_address_global(signer1); let increment_calldata = array![42, 100]; @@ -331,7 +335,11 @@ fn test_execute_after_set_signers() { Serde::serialize(@signers, ref deploy_calldata); Serde::serialize(@init_threshold, ref deploy_calldata); - let (multisig_address, _) = declare("Multisig").unwrap().deploy(@deploy_calldata).unwrap(); + let (multisig_address, _) = declare("Multisig") + .unwrap() + .contract_class() + .deploy(@deploy_calldata) + .unwrap(); let multisig = IMultisigDispatcher { contract_address: multisig_address }; @@ -363,7 +371,11 @@ fn test_execute_after_set_signers_and_threshold() { Serde::serialize(@signers, ref deploy_calldata); Serde::serialize(@init_threshold, ref deploy_calldata); - let (multisig_address, _) = declare("Multisig").unwrap().deploy(@deploy_calldata).unwrap(); + let (multisig_address, _) = declare("Multisig") + .unwrap() + .contract_class() + .deploy(@deploy_calldata) + .unwrap(); let multisig = IMultisigDispatcher { contract_address: multisig_address }; @@ -395,7 +407,11 @@ fn test_execute_after_set_threshold() { Serde::serialize(@signers, ref deploy_calldata); Serde::serialize(@init_threshold, ref deploy_calldata); - let (multisig_address, _) = declare("Multisig").unwrap().deploy(@deploy_calldata).unwrap(); + let (multisig_address, _) = declare("Multisig") + .unwrap() + .contract_class() + .deploy(@deploy_calldata) + .unwrap(); let multisig = IMultisigDispatcher { contract_address: multisig_address }; @@ -427,7 +443,11 @@ fn test_set_threshold() { Serde::serialize(@signers, ref deploy_calldata); Serde::serialize(@init_threshold, ref deploy_calldata); - let (multisig_address, _) = declare("Multisig").unwrap().deploy(@deploy_calldata).unwrap(); + let (multisig_address, _) = declare("Multisig") + .unwrap() + .contract_class() + .deploy(@deploy_calldata) + .unwrap(); let multisig = IMultisigDispatcher { contract_address: multisig_address }; assert(multisig.get_threshold() == init_threshold, 'invalid init threshold'); @@ -451,16 +471,20 @@ fn test_recursive_set_threshold() { Serde::serialize(@signers, ref deploy_calldata); Serde::serialize(@init_threshold, ref deploy_calldata); - let (multisig_address, _) = declare("Multisig").unwrap().deploy(@deploy_calldata).unwrap(); + let (multisig_address, _) = declare("Multisig") + .unwrap() + .contract_class() + .deploy(@deploy_calldata) + .unwrap(); // Gets a dispatcher (so we can call methods on the deployed contract) let multisig = IMultisigDispatcher { contract_address: multisig_address }; // Checks that the threshold was correctly initialized on deployment assert(multisig.get_threshold() == init_threshold, 'invalid init threshold'); - // Recursive call occurs here - this code proposes a transaction to the - // multisig contract that calls the set_threshold function on the multisig - // contract. + // Recursive call occurs here - this code proposes a transaction to the + // multisig contract that calls the set_threshold function on the multisig + // contract. let mut set_threshold_calldata = ArrayTrait::new(); Serde::serialize(@new_threshold, ref set_threshold_calldata); start_cheat_caller_address_global(s1); @@ -497,7 +521,11 @@ fn test_set_signers() { Serde::serialize(@init_signers, ref deploy_calldata); Serde::serialize(@threshold, ref deploy_calldata); - let (multisig_address, _) = declare("Multisig").unwrap().deploy(@deploy_calldata).unwrap(); + let (multisig_address, _) = declare("Multisig") + .unwrap() + .contract_class() + .deploy(@deploy_calldata) + .unwrap(); let multisig = IMultisigDispatcher { contract_address: multisig_address }; @@ -531,7 +559,11 @@ fn test_recursive_set_signers() { Serde::serialize(@init_signers, ref deploy_calldata); Serde::serialize(@init_threshold, ref deploy_calldata); - let (multisig_address, _) = declare("Multisig").unwrap().deploy(@deploy_calldata).unwrap(); + let (multisig_address, _) = declare("Multisig") + .unwrap() + .contract_class() + .deploy(@deploy_calldata) + .unwrap(); // Gets a dispatcher (so we can call methods on the deployed contract) let multisig = IMultisigDispatcher { contract_address: multisig_address }; @@ -543,9 +575,9 @@ fn test_recursive_set_signers() { assert(*returned_signers.at(1) == s2, 'should match signer 2'); assert(multisig.get_threshold() == 2, 'wrong init threshold'); - // Recursive call occurs here - this code proposes a transaction to the - // multisig contract that calls the set_signers function on the multisig - // contract. + // Recursive call occurs here - this code proposes a transaction to the + // multisig contract that calls the set_signers function on the multisig + // contract. let mut set_signers_calldata = ArrayTrait::new(); Serde::serialize(@new_signers, ref set_signers_calldata); start_cheat_caller_address_global(s1); @@ -585,7 +617,11 @@ fn test_set_signers_and_threshold() { Serde::serialize(@init_signers, ref deploy_calldata); Serde::serialize(@init_threshold, ref deploy_calldata); - let (multisig_address, _) = declare("Multisig").unwrap().deploy(@deploy_calldata).unwrap(); + let (multisig_address, _) = declare("Multisig") + .unwrap() + .contract_class() + .deploy(@deploy_calldata) + .unwrap(); let multisig = IMultisigDispatcher { contract_address: multisig_address }; @@ -623,7 +659,11 @@ fn test_recursive_set_signers_and_threshold() { Serde::serialize(@init_signers, ref deploy_calldata); Serde::serialize(@init_threshold, ref deploy_calldata); - let (multisig_address, _) = declare("Multisig").unwrap().deploy(@deploy_calldata).unwrap(); + let (multisig_address, _) = declare("Multisig") + .unwrap() + .contract_class() + .deploy(@deploy_calldata) + .unwrap(); // Gets a dispatcher (so we can call methods on the deployed contract) let multisig = IMultisigDispatcher { contract_address: multisig_address }; @@ -636,8 +676,8 @@ fn test_recursive_set_signers_and_threshold() { assert(*returned_signers.at(2) == s3, 'should match signer 3'); assert(multisig.get_threshold() == 3, 'wrong init threshold'); - // Recursive call occurs here - this code proposes a transaction to the - // multisig contract that calls the set_signers_and_threshold function + // Recursive call occurs here - this code proposes a transaction to the + // multisig contract that calls the set_signers_and_threshold function // on the multisig contract. let mut set_signers_and_threshold_calldata = ArrayTrait::new(); Serde::serialize(@new_signers, ref set_signers_and_threshold_calldata); @@ -647,7 +687,7 @@ fn test_recursive_set_signers_and_threshold() { .submit_transaction( multisig_address, selector!("set_signers_and_threshold"), - set_signers_and_threshold_calldata + set_signers_and_threshold_calldata, ); // Signer 1 confirms the transaction diff --git a/contracts/src/tests/test_ownable.cairo b/contracts/src/tests/test_ownable.cairo index d898bbb73..3ed1dc0d4 100644 --- a/contracts/src/tests/test_ownable.cairo +++ b/contracts/src/tests/test_ownable.cairo @@ -5,11 +5,12 @@ use starknet::testing::set_contract_address; use zeroable::Zeroable; use openzeppelin::access::ownable::interface::{ - IOwnableTwoStep, IOwnableTwoStepDispatcher, IOwnableTwoStepDispatcherTrait + IOwnableTwoStep, IOwnableTwoStepDispatcher, IOwnableTwoStepDispatcherTrait, }; use snforge_std::{ - declare, ContractClassTrait, start_cheat_caller_address_global, stop_cheat_caller_address_global + declare, ContractClassTrait, start_cheat_caller_address_global, + stop_cheat_caller_address_global, }; // diff --git a/contracts/src/tests/test_owner_upgradeable.cairo b/contracts/src/tests/test_owner_upgradeable.cairo new file mode 100644 index 000000000..b814d01ea --- /dev/null +++ b/contracts/src/tests/test_owner_upgradeable.cairo @@ -0,0 +1,95 @@ +use traits::Into; +use zeroable::Zeroable; + +use starknet::testing::set_caller_address; +use starknet::ContractAddress; +use starknet::contract_address_const; +use starknet::class_hash::class_hash_const; +use starknet::syscalls::deploy_syscall; + +use openzeppelin::upgrades::interface::{ + IUpgradeable, IUpgradeableDispatcher, IUpgradeableDispatcherTrait, +}; + +use chainlink::libraries::upgrades::v2::owner_upgradeable::OwnerUpgradeableComponent::OwnerUpgradeableImpl; +use chainlink::libraries::upgrades::v2::owner_upgradeable::OwnerUpgradeableComponent; +use chainlink::libraries::mocks::mock_owner_upgradeable::{ + MockOwnerUpgradeable, IFoo, IFooDispatcher, IFooDispatcherTrait, +}; +use chainlink::libraries::mocks::mock_non_upgradeable::{ + MockNonUpgradeable, IMockNonUpgradeableDispatcher, IMockNonUpgradeableDispatcherTrait, + IMockNonUpgradeableDispatcherImpl, +}; + +use snforge_std::{ + declare, ContractClassTrait, start_cheat_caller_address_global, + stop_cheat_caller_address_global, DeclareResultTrait, +}; + +fn setup() -> ContractAddress { + let account: ContractAddress = contract_address_const::<777>(); + start_cheat_caller_address_global(account); + account +} + +fn STATE() -> MockOwnerUpgradeable::ContractState { + MockOwnerUpgradeable::contract_state_for_testing() +} + +#[test] +fn test_upgrade_and_call() { + let account = setup(); + + let calldata = array![account.into()]; + + let (contractAddr, _) = declare("MockOwnerUpgradeable") + .unwrap() + .contract_class() + .deploy(@calldata) + .unwrap(); + + let mockUpgradeable = IFooDispatcher { contract_address: contractAddr }; + assert(mockUpgradeable.foo() == true, 'should call foo'); + + let contract = declare("MockNonUpgradeable").unwrap().contract_class(); + + let mockUpgradeable = IUpgradeableDispatcher { contract_address: contractAddr }; + + mockUpgradeable.upgrade(*(contract.class_hash)); + + // now, contract should be different + let mockNonUpgradeable = IMockNonUpgradeableDispatcher { contract_address: contractAddr }; + assert(mockNonUpgradeable.bar() == true, 'should call bar'); +} + +#[test] +#[should_panic(expected: ('Caller is not the owner',))] +fn test_upgrade_non_owner() { + let _ = setup(); + + let mut state = STATE(); + + OwnerUpgradeableImpl::upgrade(ref state, class_hash_const::<0>()); +} + +#[test] +#[should_panic(expected: ('Class hash cannot be zero',))] +fn test_upgrade_zero() { + let account = setup(); + + let calldata = array![account.into()]; + + let (contractAddr, _) = declare("MockOwnerUpgradeable") + .unwrap() + .contract_class() + .deploy(@calldata) + .unwrap(); + + let mockUpgradeable = IFooDispatcher { contract_address: contractAddr }; + assert(mockUpgradeable.foo() == true, 'should call foo'); + + let mockUpgradeable = IUpgradeableDispatcher { contract_address: contractAddr }; + + mockUpgradeable.upgrade(Zeroable::zero()); +} + diff --git a/contracts/src/tests/test_rbac_timelock.cairo b/contracts/src/tests/test_rbac_timelock.cairo index dd29ccc62..b1865e4a0 100644 --- a/contracts/src/tests/test_rbac_timelock.cairo +++ b/contracts/src/tests/test_rbac_timelock.cairo @@ -4,31 +4,31 @@ use chainlink::{ RBACTimelock, IRBACTimelock, IRBACTimelockDispatcher, IRBACTimelockDispatcherTrait, IRBACTimelockSafeDispatcher, IRBACTimelockSafeDispatcherTrait, RBACTimelock::{ADMIN_ROLE, PROPOSER_ROLE, EXECUTOR_ROLE, CANCELLER_ROLE, BYPASSER_ROLE}, - Call + Call, }, libraries::mocks::mock_multisig_target::{ - IMockMultisigTarget, IMockMultisigTargetDispatcherTrait, IMockMultisigTargetDispatcher - } + IMockMultisigTarget, IMockMultisigTargetDispatcherTrait, IMockMultisigTargetDispatcher, + }, }; use openzeppelin::{ introspection::interface::{ISRC5, ISRC5Dispatcher, ISRC5DispatcherTrait, ISRC5_ID}, access::accesscontrol::{ interface::{ IACCESSCONTROL_ID, IAccessControl, IAccessControlDispatcher, - IAccessControlDispatcherTrait + IAccessControlDispatcherTrait, }, - accesscontrol::AccessControlComponent::Errors + accesscontrol::AccessControlComponent::Errors, }, - token::{erc1155::interface::{IERC1155_RECEIVER_ID}, erc721::interface::{IERC721_RECEIVER_ID}} + token::{erc1155::interface::{IERC1155_RECEIVER_ID}, erc721::interface::{IERC721_RECEIVER_ID}}, }; use chainlink::tests::test_enumerable_set::{expect_out_of_bounds, expect_set_is_1_indexed}; use snforge_std::{ declare, ContractClassTrait, spy_events, EventSpyAssertionsTrait, - start_cheat_caller_address_global, start_cheat_block_timestamp_global + start_cheat_caller_address_global, start_cheat_block_timestamp_global, DeclareResultTrait, }; fn deploy_args() -> ( - u256, ContractAddress, ContractAddress, ContractAddress, ContractAddress, ContractAddress + u256, ContractAddress, ContractAddress, ContractAddress, ContractAddress, ContractAddress, ) { let min_delay: u256 = 0x9; let admin = contract_address_const::<1>(); @@ -41,7 +41,7 @@ fn deploy_args() -> ( fn setup_mock_target() -> (ContractAddress, IMockMultisigTargetDispatcher) { let calldata = ArrayTrait::new(); - let mock_target_contract = declare("MockMultisigTarget").unwrap(); + let mock_target_contract = declare("MockMultisigTarget").unwrap().contract_class(); let (target_address, _) = mock_target_contract.deploy(@calldata).unwrap(); (target_address, IMockMultisigTargetDispatcher { contract_address: target_address }) } @@ -61,12 +61,16 @@ fn setup_timelock() -> (ContractAddress, IRBACTimelockDispatcher, IRBACTimelockS Serde::serialize(@cancellers, ref calldata); Serde::serialize(@bypassers, ref calldata); - let (timelock_address, _) = declare("RBACTimelock").unwrap().deploy(@calldata).unwrap(); + let (timelock_address, _) = declare("RBACTimelock") + .unwrap() + .contract_class() + .deploy(@calldata) + .unwrap(); ( timelock_address, IRBACTimelockDispatcher { contract_address: timelock_address }, - IRBACTimelockSafeDispatcher { contract_address: timelock_address } + IRBACTimelockSafeDispatcher { contract_address: timelock_address }, ) } @@ -101,7 +105,7 @@ fn test_roles() { && timelock.get_role_admin(EXECUTOR_ROLE) == ADMIN_ROLE && timelock.get_role_admin(CANCELLER_ROLE) == ADMIN_ROLE && timelock.get_role_admin(BYPASSER_ROLE) == ADMIN_ROLE, - 'admin role controls all roles' + 'admin role controls all roles', ); // admin address @@ -126,10 +130,10 @@ fn test_deploy() { ( timelock_address, RBACTimelock::Event::MinDelayChange( - RBACTimelock::MinDelayChange { old_duration: 0, new_duration: min_delay } - ) - ) - ] + RBACTimelock::MinDelayChange { old_duration: 0, new_duration: min_delay }, + ), + ), + ], ); } @@ -156,7 +160,7 @@ fn expect_missing_role(result: Result<(), Array>) { Result::Ok(_) => panic!("expect 'Caller is missing role'"), Result::Err(panic_data) => { assert(*panic_data.at(0) == Errors::MISSING_ROLE, *panic_data.at(0)); - } + }, } } @@ -165,7 +169,7 @@ fn expect_operation_not_ready(result: Result<(), Array>) { Result::Ok(_) => panic!("expect 'rbact: operation not ready'"), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'rbact: operation not ready', *panic_data.at(0)); - } + }, } } @@ -182,7 +186,7 @@ fn test_schedule_delay_too_small() { Result::Ok(_) => panic!("expect 'insufficient delay'"), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'insufficient delay', *panic_data.at(0)); - } + }, } } @@ -200,7 +204,7 @@ fn test_schedule_success() { let call = Call { target: contract_address_const::<100>(), selector: selector!("doesnt_exist"), - data: array![0x123].span() + data: array![0x123].span(), }; let calls = array![call].span(); let predecessor = 0; @@ -228,11 +232,11 @@ fn test_schedule_success() { data: call.data, predecessor: predecessor, salt: salt, - delay: min_delay - } - ) - ) - ] + delay: min_delay, + }, + ), + ), + ], ); assert(timelock.is_operation(id), 'should exist'); @@ -258,8 +262,8 @@ fn test_schedule_twice() { Call { target: contract_address_const::<100>(), selector: selector!("doesnt_exist"), - data: array![0x123].span() - } + data: array![0x123].span(), + }, ] .span(); let predecessor = 0; @@ -272,7 +276,7 @@ fn test_schedule_twice() { Result::Ok(_) => panic!("expect 'operation already scheduled'"), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'operation already scheduled', *panic_data.at(0)); - } + }, } } @@ -293,8 +297,8 @@ fn test_schedule_blocked() { let calls = array![ Call { - target: contract_address_const::<100>(), selector: selector, data: array![0x123].span() - } + target: contract_address_const::<100>(), selector: selector, data: array![0x123].span(), + }, ] .span(); let predecessor = 0; @@ -305,7 +309,7 @@ fn test_schedule_blocked() { Result::Ok(_) => panic!("expect 'selector is blocked'"), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'selector is blocked', *panic_data.at(0)); - } + }, } } @@ -320,11 +324,11 @@ fn test_cancel_id_not_pending() { let mock_ready_time = mock_time + min_delay.try_into().unwrap(); let call_1 = Call { - target: target_address, selector: selector!("set_value"), data: array![0x56162].span() + target: target_address, selector: selector!("set_value"), data: array![0x56162].span(), }; let call_2 = Call { - target: target_address, selector: selector!("flip_toggle"), data: array![].span() + target: target_address, selector: selector!("flip_toggle"), data: array![].span(), }; let calls = array![call_1, call_2].span(); @@ -340,7 +344,7 @@ fn test_cancel_id_not_pending() { Result::Ok(_) => panic!("expect 'rbact: cant cancel operation'"), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'rbact: cant cancel operation', *panic_data.at(0)); - } + }, } // test that after a batch has been executed, you can't cancel it @@ -366,7 +370,7 @@ fn test_cancel_id_not_pending() { Result::Ok(_) => panic!("expect 'rbact: cant cancel operation'"), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'rbact: cant cancel operation', *panic_data.at(0)); - } + }, } } @@ -383,7 +387,7 @@ fn test_cancel_success() { let call = Call { target: contract_address_const::<100>(), selector: selector!("doesnt_exist"), - data: array![0x123].span() + data: array![0x123].span(), }; let calls = array![call].span(); let predecessor = 0; @@ -403,9 +407,9 @@ fn test_cancel_success() { @array![ ( timelock_address, - RBACTimelock::Event::Cancelled(RBACTimelock::Cancelled { id: id }) - ) - ] + RBACTimelock::Event::Cancelled(RBACTimelock::Cancelled { id: id }), + ), + ], ); assert(!timelock.is_operation(id), 'not operation'); @@ -424,11 +428,11 @@ fn test_execute_op_not_ready() { start_cheat_block_timestamp_global(mock_time); let call_1 = Call { - target: target_address, selector: selector!("set_value"), data: array![0x56162].span() + target: target_address, selector: selector!("set_value"), data: array![0x56162].span(), }; let call_2 = Call { - target: target_address, selector: selector!("flip_toggle"), data: array![].span() + target: target_address, selector: selector!("flip_toggle"), data: array![].span(), }; let calls = array![call_1, call_2].span(); @@ -471,7 +475,7 @@ fn test_execute_predecessor_invalid() { let call = Call { target: contract_address_const::<100>(), selector: selector!("doesnt_exist"), - data: array![0x123].span() + data: array![0x123].span(), }; let calls = array![call].span(); let predecessor = 4; @@ -487,7 +491,7 @@ fn test_execute_predecessor_invalid() { Result::Ok(_) => panic!("expect 'rbact: missing dependency'"), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'rbact: missing dependency', *panic_data.at(0)); - } + }, } } @@ -530,11 +534,11 @@ fn test_execute_successful() { let mock_ready_time = mock_time + min_delay.try_into().unwrap(); let call_1 = Call { - target: target_address, selector: selector!("set_value"), data: array![0x56162].span() + target: target_address, selector: selector!("set_value"), data: array![0x56162].span(), }; let call_2 = Call { - target: target_address, selector: selector!("flip_toggle"), data: array![].span() + target: target_address, selector: selector!("flip_toggle"), data: array![].span(), }; let calls = array![call_1, call_2].span(); @@ -566,9 +570,9 @@ fn test_execute_successful() { index: 0, target: call_1.target, selector: call_1.selector, - data: call_1.data - } - ) + data: call_1.data, + }, + ), ), ( timelock_address, @@ -578,11 +582,11 @@ fn test_execute_successful() { index: 1, target: call_2.target, selector: call_2.selector, - data: call_2.data - } - ) - ) - ] + data: call_2.data, + }, + ), + ), + ], ); let (actual_value, actual_toggle) = target.read(); @@ -597,7 +601,7 @@ fn test_execute_successful() { let mock_ready_time = mock_time + min_delay.try_into().unwrap(); let call_3 = Call { - target: target_address, selector: selector!("flip_toggle"), data: array![].span() + target: target_address, selector: selector!("flip_toggle"), data: array![].span(), }; let calls = array![call_3].span(); let predecessor = id; @@ -628,11 +632,11 @@ fn test_execute_successful() { index: 0, target: call_3.target, selector: call_3.selector, - data: call_3.data - } - ) - ) - ] + data: call_3.data, + }, + ), + ), + ], ); let (_, actual_toggle) = target.read(); @@ -658,11 +662,11 @@ fn test_update_delay_success() { timelock_address, RBACTimelock::Event::MinDelayChange( RBACTimelock::MinDelayChange { - old_duration: min_delay, new_duration: 0x92289 - } - ) - ) - ] + old_duration: min_delay, new_duration: 0x92289, + }, + ), + ), + ], ); assert(timelock.get_min_delay() == 0x92289, 'new min delay'); @@ -680,7 +684,7 @@ fn test_update_delay_no_affect_op_readiness() { let call = Call { target: contract_address_const::<100>(), selector: selector!("doesnt_exist"), - data: array![0x123].span() + data: array![0x123].span(), }; let calls = array![call].span(); let predecessor = 0; @@ -710,11 +714,11 @@ fn test_bypasser_execute_success() { let mock_time = 3; let call_1 = Call { - target: target_address, selector: selector!("set_value"), data: array![0x56162].span() + target: target_address, selector: selector!("set_value"), data: array![0x56162].span(), }; let call_2 = Call { - target: target_address, selector: selector!("flip_toggle"), data: array![].span() + target: target_address, selector: selector!("flip_toggle"), data: array![].span(), }; let calls = array![call_1, call_2].span(); @@ -736,9 +740,9 @@ fn test_bypasser_execute_success() { index: 0, target: call_1.target, selector: call_1.selector, - data: call_1.data - } - ) + data: call_1.data, + }, + ), ), ( timelock_address, @@ -747,11 +751,11 @@ fn test_bypasser_execute_success() { index: 1, target: call_2.target, selector: call_2.selector, - data: call_2.data - } - ) - ) - ] + data: call_2.data, + }, + ), + ), + ], ); let (actual_value, actual_toggle) = target.read(); @@ -779,10 +783,10 @@ fn test_unblock_selector() { ( timelock_address, RBACTimelock::Event::FunctionSelectorUnblocked( - RBACTimelock::FunctionSelectorUnblocked { selector: selector, } - ) - ) - ] + RBACTimelock::FunctionSelectorUnblocked { selector: selector }, + ), + ), + ], ); assert(timelock.get_blocked_function_selector_count() == 0, 'count is 0'); @@ -800,10 +804,10 @@ fn test_unblock_selector() { ( timelock_address, RBACTimelock::Event::FunctionSelectorUnblocked( - RBACTimelock::FunctionSelectorUnblocked { selector: selector, } - ) - ) - ] + RBACTimelock::FunctionSelectorUnblocked { selector: selector }, + ), + ), + ], ); assert(timelock.get_blocked_function_selector_count() == 0, 'count is 0'); @@ -868,11 +872,11 @@ fn test_lifecycle_of_id() { let mock_ready_time = mock_time + min_delay.try_into().unwrap(); let call_1 = Call { - target: target_address, selector: selector!("set_value"), data: array![0x56162].span() + target: target_address, selector: selector!("set_value"), data: array![0x56162].span(), }; let call_2 = Call { - target: target_address, selector: selector!("flip_toggle"), data: array![].span() + target: target_address, selector: selector!("flip_toggle"), data: array![].span(), }; let calls = array![call_1, call_2].span(); diff --git a/contracts/src/tests/test_sequencer_uptime_feed.cairo b/contracts/src/tests/test_sequencer_uptime_feed.cairo index a7b44ff24..51845f63f 100644 --- a/contracts/src/tests/test_sequencer_uptime_feed.cairo +++ b/contracts/src/tests/test_sequencer_uptime_feed.cairo @@ -15,10 +15,10 @@ use core::result::ResultTrait; use chainlink::emergency::sequencer_uptime_feed::SequencerUptimeFeed; use chainlink::libraries::access_control::{ - IAccessController, IAccessControllerDispatcher, IAccessControllerDispatcherTrait + IAccessController, IAccessControllerDispatcher, IAccessControllerDispatcherTrait, }; use chainlink::ocr2::aggregator_proxy::{ - IAggregatorProxy, IAggregatorProxyDispatcher, IAggregatorProxyDispatcherTrait + IAggregatorProxy, IAggregatorProxyDispatcher, IAggregatorProxyDispatcherTrait, }; use chainlink::ocr2::aggregator_proxy::AggregatorProxy; use chainlink::ocr2::aggregator_proxy::AggregatorProxy::AggregatorProxyImpl; @@ -26,11 +26,12 @@ use chainlink::tests::test_ownable::should_implement_ownable; use chainlink::tests::test_access_controller::should_implement_access_control; use chainlink::emergency::sequencer_uptime_feed::{ - ISequencerUptimeFeed, ISequencerUptimeFeedDispatcher, ISequencerUptimeFeedDispatcherTrait + ISequencerUptimeFeed, ISequencerUptimeFeedDispatcher, ISequencerUptimeFeedDispatcherTrait, }; use snforge_std::{ - declare, ContractClassTrait, start_cheat_caller_address_global, stop_cheat_caller_address_global + declare, ContractClassTrait, start_cheat_caller_address_global, + stop_cheat_caller_address_global, DeclareResultTrait, }; @@ -49,13 +50,17 @@ fn setup() -> (ContractAddress, ContractAddress, ISequencerUptimeFeedDispatcher) // Deploy seqeuencer uptime feed let calldata = array![0, // initial status - account.into() // owner + account.into() // owner ]; - let (sequencerFeedAddr, _) = declare("SequencerUptimeFeed").unwrap().deploy(@calldata).unwrap(); + let (sequencerFeedAddr, _) = declare("SequencerUptimeFeed") + .unwrap() + .contract_class() + .deploy(@calldata) + .unwrap(); let sequencerUptimeFeed = ISequencerUptimeFeedDispatcher { - contract_address: sequencerFeedAddr + contract_address: sequencerFeedAddr, }; (account, sequencerFeedAddr, sequencerUptimeFeed) @@ -74,7 +79,7 @@ fn test_access_control() { } #[test] -#[should_panic()] +#[should_panic] fn test_set_l1_sender_not_owner() { let (_, _, sequencerUptimeFeed) = setup(); start_cheat_caller_address_global(contract_address_const::<111>()); @@ -120,7 +125,7 @@ fn test_aggregator_proxy_response() { let latest_round_data = proxy.latest_round_data(); assert(latest_round_data.answer == 0, 'latest_round_data should be 0'); - // latest answer + // latest answer let latest_answer = proxy.latest_answer(); assert(latest_answer == 0, 'latest_answer should be 0'); diff --git a/contracts/src/tests/test_upgradeable.cairo b/contracts/src/tests/test_upgradeable.cairo index fee927616..9006e5223 100644 --- a/contracts/src/tests/test_upgradeable.cairo +++ b/contracts/src/tests/test_upgradeable.cairo @@ -6,18 +6,19 @@ use starknet::contract_address_const; use starknet::class_hash::class_hash_const; use starknet::syscalls::deploy_syscall; -use chainlink::libraries::upgradeable::Upgradeable; +use chainlink::libraries::upgrades::v1::upgradeable::Upgradeable; use chainlink::libraries::mocks::mock_upgradeable::{ MockUpgradeable, IMockUpgradeableDispatcher, IMockUpgradeableDispatcherTrait, - IMockUpgradeableDispatcherImpl + IMockUpgradeableDispatcherImpl, }; use chainlink::libraries::mocks::mock_non_upgradeable::{ MockNonUpgradeable, IMockNonUpgradeableDispatcher, IMockNonUpgradeableDispatcherTrait, - IMockNonUpgradeableDispatcherImpl + IMockNonUpgradeableDispatcherImpl, }; use snforge_std::{ - declare, ContractClassTrait, start_cheat_caller_address_global, stop_cheat_caller_address_global + declare, ContractClassTrait, start_cheat_caller_address_global, + stop_cheat_caller_address_global, DeclareResultTrait, }; @@ -33,14 +34,18 @@ fn test_upgrade_and_call() { let calldata = array![]; - let (contractAddr, _) = declare("MockUpgradeable").unwrap().deploy(@calldata).unwrap(); + let (contractAddr, _) = declare("MockUpgradeable") + .unwrap() + .contract_class() + .deploy(@calldata) + .unwrap(); let mockUpgradeable = IMockUpgradeableDispatcher { contract_address: contractAddr }; assert(mockUpgradeable.foo() == true, 'should call foo'); - let contract_class = declare("MockNonUpgradeable").unwrap(); + let contract = declare("MockNonUpgradeable").unwrap().contract_class(); - mockUpgradeable.upgrade(contract_class.class_hash); + mockUpgradeable.upgrade(*(contract.class_hash)); // now, contract should be different let mockNonUpgradeable = IMockNonUpgradeableDispatcher { contract_address: contractAddr }; diff --git a/contracts/src/token/mock/invalid_erc667_receiver.cairo b/contracts/src/token/mock/invalid_erc667_receiver.cairo index 34a48bf47..1ffce926a 100644 --- a/contracts/src/token/mock/invalid_erc667_receiver.cairo +++ b/contracts/src/token/mock/invalid_erc667_receiver.cairo @@ -2,7 +2,7 @@ mod InvalidReceiver { #[storage] struct Storage { - _supports: bool + _supports: bool, } #[constructor] diff --git a/contracts/src/token/mock/valid_erc667_receiver.cairo b/contracts/src/token/mock/valid_erc667_receiver.cairo index 285c8532c..09e2e5b13 100644 --- a/contracts/src/token/mock/valid_erc667_receiver.cairo +++ b/contracts/src/token/mock/valid_erc667_receiver.cairo @@ -10,7 +10,7 @@ mod ValidReceiver { use array::ArrayTrait; use openzeppelin::introspection::src5::SRC5Component; use chainlink::libraries::token::v2::erc677_receiver::{ - ERC677ReceiverComponent, IERC677Receiver + ERC677ReceiverComponent, IERC677Receiver, }; component!(path: SRC5Component, storage: src5, event: SRC5Event); @@ -29,7 +29,7 @@ mod ValidReceiver { #[substorage(v0)] src5: SRC5Component::Storage, #[substorage(v0)] - erc677_receiver: ERC677ReceiverComponent::Storage + erc677_receiver: ERC677ReceiverComponent::Storage, } #[event] @@ -38,7 +38,7 @@ mod ValidReceiver { #[flat] SRC5Event: SRC5Component::Event, #[flat] - ERC677ReceiverEvent: ERC677ReceiverComponent::Event + ERC677ReceiverEvent: ERC677ReceiverComponent::Event, } #[constructor] @@ -49,7 +49,7 @@ mod ValidReceiver { #[abi(embed_v0)] impl ERC677ReceiverImpl of IERC677Receiver { fn on_token_transfer( - ref self: ContractState, sender: ContractAddress, value: u256, data: Array + ref self: ContractState, sender: ContractAddress, value: u256, data: Array, ) { self._sender.write(sender); } diff --git a/contracts/src/token/v1/link_token.cairo b/contracts/src/token/v1/link_token.cairo index 6a6df4a18..94460cd2c 100644 --- a/contracts/src/token/v1/link_token.cairo +++ b/contracts/src/token/v1/link_token.cairo @@ -1,6 +1,6 @@ use starknet::ContractAddress; -// https://github.com/starknet-io/starkgate-contracts/blob/v2.0/src/cairo/mintable_token_interface.cairo +// https://github.com/starknet-io/starkgate-contracts/blob/eedee8304e8c407c2e0e03c83187dbc5dcc6787e/src/cairo/mintable_token_interface.cairo #[starknet::interface] trait IMintableToken { fn permissioned_mint(ref self: TContractState, account: ContractAddress, amount: u256); @@ -21,7 +21,7 @@ mod LinkToken { use openzeppelin::token::erc20::interface::{IERC20, IERC20Dispatcher, IERC20DispatcherTrait}; use chainlink::libraries::token::v1::erc677::ERC677Component; use chainlink::libraries::type_and_version::ITypeAndVersion; - use chainlink::libraries::upgradeable::{Upgradeable, IUpgradeable}; + use chainlink::libraries::upgrades::v1::upgradeable::{Upgradeable, IUpgradeable}; component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); component!(path: ERC20Component, storage: erc20, event: ERC20Event); @@ -48,7 +48,7 @@ mod LinkToken { #[substorage(v0)] erc20: ERC20Component::Storage, #[substorage(v0)] - erc677: ERC677Component::Storage + erc677: ERC677Component::Storage, } #[event] @@ -59,7 +59,7 @@ mod LinkToken { #[flat] ERC20Event: ERC20Component::Event, #[flat] - ERC677Event: ERC677Component::Event + ERC677Event: ERC677Component::Event, } // @@ -69,15 +69,31 @@ mod LinkToken { impl MintableToken of IMintableToken { fn permissioned_mint(ref self: ContractState, account: ContractAddress, amount: u256) { only_minter(@self); - self.erc20._mint(account, amount); + self.erc20.mint(account, amount); } fn permissioned_burn(ref self: ContractState, account: ContractAddress, amount: u256) { only_minter(@self); - self.erc20._burn(account, amount); + self.erc20.burn(account, amount); } } + impl HooksImpl of ERC20Component::ERC20HooksTrait { + fn before_update( + ref self: ERC20Component::ComponentState::, + from: ContractAddress, + recipient: ContractAddress, + amount: u256, + ) {} + + fn after_update( + ref self: ERC20Component::ComponentState::, + from: ContractAddress, + recipient: ContractAddress, + amount: u256, + ) {} + } + #[constructor] fn constructor(ref self: ContractState, minter: ContractAddress, owner: ContractAddress) { @@ -109,12 +125,14 @@ mod LinkToken { } } - // fn increase_allowance(ref self: ContractState, spender: ContractAddress, added_value: u256) -> bool { + // fn increase_allowance(ref self: ContractState, spender: ContractAddress, added_value: u256) + // -> bool { // let mut state = ERC20::unsafe_new_contract_state(); // ERC20::ERC20Impl::increase_allowance(ref state, spender, added_value) // } - // fn decrease_allowance(ref self: ContractState, spender: ContractAddress, subtracted_value: u256) -> bool { + // fn decrease_allowance(ref self: ContractState, spender: ContractAddress, subtracted_value: + // u256) -> bool { // let mut state = ERC20::unsafe_new_contract_state(); // ERC20::ERC20Impl::decrease_allowance(ref state, spender, subtracted_value) // } diff --git a/contracts/src/token/v2/link_token.cairo b/contracts/src/token/v2/link_token.cairo index 638d96bbd..566399e4a 100644 --- a/contracts/src/token/v2/link_token.cairo +++ b/contracts/src/token/v2/link_token.cairo @@ -2,7 +2,7 @@ use starknet::ContractAddress; // This token is deployed by the StarkGate bridge -// https://github.com/starknet-io/starkgate-contracts/blob/v2.0/src/cairo/mintable_token_interface.cairo +// https://github.com/starknet-io/starkgate-contracts/blob/eedee8304e8c407c2e0e03c83187dbc5dcc6787e/src/cairo/mintable_token_interface.cairo #[starknet::interface] trait IMintableToken { fn permissioned_mint(ref self: TContractState, account: ContractAddress, amount: u256); @@ -22,23 +22,28 @@ mod LinkToken { use zeroable::Zeroable; use openzeppelin::{ token::erc20::{ - ERC20Component, interface::{IERC20, IERC20Dispatcher, IERC20DispatcherTrait} + ERC20Component, interface::{IERC20, IERC20Dispatcher, IERC20DispatcherTrait}, }, - access::ownable::OwnableComponent + access::ownable::OwnableComponent, upgrades::UpgradeableComponent, }; use super::{IMintableToken, IMinter}; use chainlink::libraries::{ token::v2::erc677::ERC677Component, type_and_version::ITypeAndVersion, - upgradeable::{Upgradeable, IUpgradeable} + upgrades::v1::upgradeable::{Upgradeable, IUpgradeable}, + upgrades::v2::owner_upgradeable::OwnerUpgradeableComponent, }; component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); component!(path: ERC20Component, storage: erc20, event: ERC20Event); component!(path: ERC677Component, storage: erc677, event: ERC677Event); + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent); + component!( + path: OwnerUpgradeableComponent, storage: owner_upgradeable, event: OwnerUpgradeableEvent, + ); #[abi(embed_v0)] impl OwnableImpl = OwnableComponent::OwnableTwoStepImpl; - impl InternalImpl = OwnableComponent::InternalImpl; + impl OwnableInternalImpl = OwnableComponent::InternalImpl; #[abi(embed_v0)] impl ERC20Impl = ERC20Component::ERC20Impl; @@ -49,6 +54,12 @@ mod LinkToken { #[abi(embed_v0)] impl ERC677Impl = ERC677Component::ERC677Impl; + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl; + + #[abi(embed_v0)] + impl OwnerUpgradeableImpl = + OwnerUpgradeableComponent::OwnerUpgradeableImpl; + #[storage] struct Storage { LinkTokenV2_minter: ContractAddress, @@ -58,15 +69,18 @@ mod LinkToken { erc20: ERC20Component::Storage, #[substorage(v0)] erc677: ERC677Component::Storage, + #[substorage(v0)] + upgradeable: UpgradeableComponent::Storage, + #[substorage(v0)] + owner_upgradeable: OwnerUpgradeableComponent::Storage, } #[derive(Drop, starknet::Event)] struct LinkTokenV2NewMinter { old_minter: ContractAddress, - new_minter: ContractAddress + new_minter: ContractAddress, } - #[event] #[derive(Drop, starknet::Event)] enum Event { @@ -76,7 +90,11 @@ mod LinkToken { #[flat] ERC20Event: ERC20Component::Event, #[flat] - ERC677Event: ERC677Component::Event + ERC677Event: ERC677Component::Event, + #[flat] + UpgradeableEvent: UpgradeableComponent::Event, + #[flat] + OwnerUpgradeableEvent: OwnerUpgradeableComponent::Event, } #[constructor] @@ -89,7 +107,7 @@ mod LinkToken { _initial_recipient_ignore: ContractAddress, initial_minter: ContractAddress, owner: ContractAddress, - _upgrade_delay_ignore: u64 + _upgrade_delay_ignore: u64, ) { let name = "ChainLink Token"; let symbol = "LINK"; @@ -104,23 +122,38 @@ mod LinkToken { .emit( Event::LinkTokenV2NewMinter( LinkTokenV2NewMinter { - old_minter: contract_address_const::<0>(), new_minter: initial_minter - } - ) + old_minter: contract_address_const::<0>(), new_minter: initial_minter, + }, + ), ); } + impl HooksImpl of ERC20Component::ERC20HooksTrait { + fn before_update( + ref self: ERC20Component::ComponentState::, + from: ContractAddress, + recipient: ContractAddress, + amount: u256, + ) {} + + fn after_update( + ref self: ERC20Component::ComponentState::, + from: ContractAddress, + recipient: ContractAddress, + amount: u256, + ) {} + } #[abi(embed_v0)] impl MintableToken of IMintableToken { fn permissioned_mint(ref self: ContractState, account: ContractAddress, amount: u256) { self._only_minter(); - self.erc20._mint(account, amount); + self.erc20.mint(account, amount); } fn permissioned_burn(ref self: ContractState, account: ContractAddress, amount: u256) { self._only_minter(); - self.erc20._burn(account, amount); + self.erc20.burn(account, amount); } } @@ -131,14 +164,15 @@ mod LinkToken { let prev_minter = self.LinkTokenV2_minter.read(); assert(new_minter != prev_minter, 'is minter already'); + assert(!new_minter.is_zero(), 'minter is 0'); self.LinkTokenV2_minter.write(new_minter); self .emit( Event::LinkTokenV2NewMinter( - LinkTokenV2NewMinter { old_minter: prev_minter, new_minter: new_minter } - ) + LinkTokenV2NewMinter { old_minter: prev_minter, new_minter: new_minter }, + ), ); } @@ -154,14 +188,6 @@ mod LinkToken { } } - #[abi(embed_v0)] - impl UpgradeableImpl of IUpgradeable { - fn upgrade(ref self: ContractState, new_impl: ClassHash) { - self.ownable.assert_only_owner(); - Upgradeable::upgrade(new_impl) - } - } - // // Internal // diff --git a/contracts/src/utils.cairo b/contracts/src/utils.cairo index 248917648..08cd9850e 100644 --- a/contracts/src/utils.cairo +++ b/contracts/src/utils.cairo @@ -1,9 +1,26 @@ use integer::U128IntoFelt252; use integer::u128s_from_felt252; use integer::U128sFromFelt252Result; +use core::integer::u128_byte_reverse; +use core::keccak::compute_keccak_byte_array; +use alexandria_bytes::{Bytes, BytesTrait}; + fn split_felt(felt: felt252) -> (u128, u128) { match u128s_from_felt252(felt) { U128sFromFelt252Result::Narrow(low) => (0_u128, low), U128sFromFelt252Result::Wide((high, low)) => (high, low), } } + + +pub fn u256_reverse_endian(input: u256) -> u256 { + let low = u128_byte_reverse(input.high); + let high = u128_byte_reverse(input.low); + u256 { low, high } +} + +// never use compute_keccak_byte_array directly because it +// returns little-endian while evm implementations use big-endian +pub fn keccak(input: @ByteArray) -> u256 { + u256_reverse_endian(compute_keccak_byte_array(input)) +} diff --git a/examples/contracts/aggregator_consumer/Scarb.lock b/examples/contracts/aggregator_consumer/Scarb.lock index 5bdff8546..e7dec7b77 100644 --- a/examples/contracts/aggregator_consumer/Scarb.lock +++ b/examples/contracts/aggregator_consumer/Scarb.lock @@ -12,7 +12,7 @@ dependencies = [ [[package]] name = "alexandria_bytes" version = "0.1.0" -source = "git+https://github.com/keep-starknet-strange/alexandria.git?rev=bcdca70afdf59c9976148e95cebad5cf63d75a7f#bcdca70afdf59c9976148e95cebad5cf63d75a7f" +source = "git+https://github.com/keep-starknet-strange/alexandria.git?rev=e1b080577aaa6889116fc8be5dde72b2fd21e397#e1b080577aaa6889116fc8be5dde72b2fd21e397" dependencies = [ "alexandria_data_structures", "alexandria_math", @@ -21,7 +21,7 @@ dependencies = [ [[package]] name = "alexandria_data_structures" version = "0.2.0" -source = "git+https://github.com/keep-starknet-strange/alexandria.git?rev=bcdca70afdf59c9976148e95cebad5cf63d75a7f#bcdca70afdf59c9976148e95cebad5cf63d75a7f" +source = "git+https://github.com/keep-starknet-strange/alexandria.git?rev=e1b080577aaa6889116fc8be5dde72b2fd21e397#e1b080577aaa6889116fc8be5dde72b2fd21e397" dependencies = [ "alexandria_encoding", ] @@ -29,7 +29,7 @@ dependencies = [ [[package]] name = "alexandria_encoding" version = "0.1.0" -source = "git+https://github.com/keep-starknet-strange/alexandria.git?rev=bcdca70afdf59c9976148e95cebad5cf63d75a7f#bcdca70afdf59c9976148e95cebad5cf63d75a7f" +source = "git+https://github.com/keep-starknet-strange/alexandria.git?rev=e1b080577aaa6889116fc8be5dde72b2fd21e397#e1b080577aaa6889116fc8be5dde72b2fd21e397" dependencies = [ "alexandria_bytes", "alexandria_math", @@ -39,7 +39,7 @@ dependencies = [ [[package]] name = "alexandria_math" version = "0.2.0" -source = "git+https://github.com/keep-starknet-strange/alexandria.git?rev=bcdca70afdf59c9976148e95cebad5cf63d75a7f#bcdca70afdf59c9976148e95cebad5cf63d75a7f" +source = "git+https://github.com/keep-starknet-strange/alexandria.git?rev=e1b080577aaa6889116fc8be5dde72b2fd21e397#e1b080577aaa6889116fc8be5dde72b2fd21e397" dependencies = [ "alexandria_data_structures", ] @@ -47,7 +47,7 @@ dependencies = [ [[package]] name = "alexandria_numeric" version = "0.1.0" -source = "git+https://github.com/keep-starknet-strange/alexandria.git?rev=bcdca70afdf59c9976148e95cebad5cf63d75a7f#bcdca70afdf59c9976148e95cebad5cf63d75a7f" +source = "git+https://github.com/keep-starknet-strange/alexandria.git?rev=e1b080577aaa6889116fc8be5dde72b2fd21e397#e1b080577aaa6889116fc8be5dde72b2fd21e397" dependencies = [ "alexandria_math", "alexandria_searching", @@ -56,7 +56,7 @@ dependencies = [ [[package]] name = "alexandria_searching" version = "0.1.0" -source = "git+https://github.com/keep-starknet-strange/alexandria.git?rev=bcdca70afdf59c9976148e95cebad5cf63d75a7f#bcdca70afdf59c9976148e95cebad5cf63d75a7f" +source = "git+https://github.com/keep-starknet-strange/alexandria.git?rev=e1b080577aaa6889116fc8be5dde72b2fd21e397#e1b080577aaa6889116fc8be5dde72b2fd21e397" dependencies = [ "alexandria_data_structures", ] @@ -68,15 +68,119 @@ dependencies = [ "alexandria_bytes", "alexandria_encoding", "openzeppelin", - "snforge_std", ] [[package]] name = "openzeppelin" -version = "0.10.0" -source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.10.0#d77082732daab2690ba50742ea41080eb23299d3" +version = "0.17.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.17.0#bf5d02c25c989ccc24f3ab42ec649617d3f21289" +dependencies = [ + "openzeppelin_access", + "openzeppelin_account", + "openzeppelin_finance", + "openzeppelin_governance", + "openzeppelin_introspection", + "openzeppelin_merkle_tree", + "openzeppelin_presets", + "openzeppelin_security", + "openzeppelin_token", + "openzeppelin_upgrades", + "openzeppelin_utils", +] + +[[package]] +name = "openzeppelin_access" +version = "0.17.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.17.0#bf5d02c25c989ccc24f3ab42ec649617d3f21289" +dependencies = [ + "openzeppelin_introspection", + "openzeppelin_utils", +] + +[[package]] +name = "openzeppelin_account" +version = "0.17.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.17.0#bf5d02c25c989ccc24f3ab42ec649617d3f21289" +dependencies = [ + "openzeppelin_introspection", + "openzeppelin_utils", +] + +[[package]] +name = "openzeppelin_finance" +version = "0.17.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.17.0#bf5d02c25c989ccc24f3ab42ec649617d3f21289" +dependencies = [ + "openzeppelin_access", + "openzeppelin_token", +] + +[[package]] +name = "openzeppelin_governance" +version = "0.17.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.17.0#bf5d02c25c989ccc24f3ab42ec649617d3f21289" +dependencies = [ + "openzeppelin_access", + "openzeppelin_introspection", +] + +[[package]] +name = "openzeppelin_introspection" +version = "0.17.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.17.0#bf5d02c25c989ccc24f3ab42ec649617d3f21289" + +[[package]] +name = "openzeppelin_merkle_tree" +version = "0.17.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.17.0#bf5d02c25c989ccc24f3ab42ec649617d3f21289" + +[[package]] +name = "openzeppelin_presets" +version = "0.17.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.17.0#bf5d02c25c989ccc24f3ab42ec649617d3f21289" +dependencies = [ + "openzeppelin_access", + "openzeppelin_account", + "openzeppelin_finance", + "openzeppelin_introspection", + "openzeppelin_token", + "openzeppelin_upgrades", +] + +[[package]] +name = "openzeppelin_security" +version = "0.17.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.17.0#bf5d02c25c989ccc24f3ab42ec649617d3f21289" + +[[package]] +name = "openzeppelin_token" +version = "0.17.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.17.0#bf5d02c25c989ccc24f3ab42ec649617d3f21289" +dependencies = [ + "openzeppelin_account", + "openzeppelin_governance", + "openzeppelin_introspection", +] + +[[package]] +name = "openzeppelin_upgrades" +version = "0.17.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.17.0#bf5d02c25c989ccc24f3ab42ec649617d3f21289" + +[[package]] +name = "openzeppelin_utils" +version = "0.17.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.17.0#bf5d02c25c989ccc24f3ab42ec649617d3f21289" + +[[package]] +name = "snforge_scarb_plugin" +version = "0.31.0" +source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.31.0#72ea785ca354e9e506de3e5d687da9fb2c1b3c67" [[package]] name = "snforge_std" -version = "0.27.0" -source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.27.0#2d99b7c00678ef0363881ee0273550c44a9263de" +version = "0.31.0" +source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.31.0#72ea785ca354e9e506de3e5d687da9fb2c1b3c67" +dependencies = [ + "snforge_scarb_plugin", +] diff --git a/examples/contracts/aggregator_consumer/Scarb.toml b/examples/contracts/aggregator_consumer/Scarb.toml index 8da76d784..0ef5f6432 100644 --- a/examples/contracts/aggregator_consumer/Scarb.toml +++ b/examples/contracts/aggregator_consumer/Scarb.toml @@ -6,7 +6,13 @@ [package] name = "aggregator_consumer" version = "0.1.0" -cairo-version = "2.6.3" +cairo-version = "2.9.2" + +[scripts] +test = "snforge test" + +# [scripts] +# test = "snforge test" # [scripts] # test = "snforge test" @@ -14,9 +20,9 @@ cairo-version = "2.6.3" # See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html [dependencies] -snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.27.0" } chainlink = { path = "../../../contracts" } -starknet = ">=2.6.3" +starknet = "2.9.2" +snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.31.0" } [lib] diff --git a/examples/contracts/aggregator_consumer/scripts/Scarb.toml b/examples/contracts/aggregator_consumer/scripts/Scarb.toml index bf06f1d83..dc7f2f831 100644 --- a/examples/contracts/aggregator_consumer/scripts/Scarb.toml +++ b/examples/contracts/aggregator_consumer/scripts/Scarb.toml @@ -1,15 +1,15 @@ [package] name = "src" version = "0.1.0" -cairo-version = "2.6.3" +cairo-version = "2.8.2" # See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html [dependencies] -sncast_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.21.0" } +sncast_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.28.0" } chainlink = { path = "../../../../contracts" } aggregator_consumer = { path = "../" } -starknet = ">=2.6.3" +starknet = "2.8.2" [lib] casm = true @@ -19,6 +19,5 @@ casm = true build-external-contracts = [ "chainlink::emergency::sequencer_uptime_feed::SequencerUptimeFeed", "chainlink::ocr2::mocks::mock_aggregator::MockAggregator", - "aggregator_consumer::ocr2::consumer::AggregatorConsumer" + "aggregator_consumer::ocr2::consumer::AggregatorConsumer", ] - diff --git a/examples/contracts/aggregator_consumer/scripts/src/consumer/deploy_aggregator_consumer.cairo b/examples/contracts/aggregator_consumer/scripts/src/consumer/deploy_aggregator_consumer.cairo index 4d55a13db..583653189 100644 --- a/examples/contracts/aggregator_consumer/scripts/src/consumer/deploy_aggregator_consumer.cairo +++ b/examples/contracts/aggregator_consumer/scripts/src/consumer/deploy_aggregator_consumer.cairo @@ -1,12 +1,12 @@ use sncast_std::{ declare, deploy, DeclareResult, DeployResult, get_nonce, DisplayContractAddress, - DisplayClassHash + DisplayClassHash, }; use starknet::{ContractAddress, ClassHash}; fn declare_and_deploy( - contract_name: ByteArray, constructor_calldata: Array + contract_name: ByteArray, constructor_calldata: Array, ) -> DeployResult { let mut class_hash: ClassHash = 0x6d1dd0e5fa4e0284dcf341997f1d781bc2fb7d76ada684da7a2a33c38031df5 @@ -31,7 +31,7 @@ fn declare_and_deploy( Option::Some(salt), true, Option::None, - Option::Some(nonce) + Option::Some(nonce), ); if deploy_result.is_err() { println!("{:?}", deploy_result.unwrap_err()); diff --git a/examples/contracts/aggregator_consumer/scripts/src/consumer/set_answer.cairo b/examples/contracts/aggregator_consumer/scripts/src/consumer/set_answer.cairo index d53d266a7..e39e386a3 100644 --- a/examples/contracts/aggregator_consumer/scripts/src/consumer/set_answer.cairo +++ b/examples/contracts/aggregator_consumer/scripts/src/consumer/set_answer.cairo @@ -21,7 +21,7 @@ fn main() { // Queries the aggregator for the latest round data let mut read_ocr_address_data = read_ocr_address.unwrap().data.span(); let aggregator_address = Serde::< - starknet::ContractAddress + starknet::ContractAddress, >::deserialize(ref read_ocr_address_data) .unwrap(); let latest_round = call(aggregator_address, selector!("latest_round_data"), array![]); @@ -41,7 +41,7 @@ fn main() { selector!("set_answer"), array![round.answer.into()], Option::None, - Option::Some(get_nonce('pending')) + Option::Some(get_nonce('pending')), ); if result.is_err() { println!("{:?}", result.unwrap_err()); diff --git a/examples/contracts/aggregator_consumer/scripts/src/mock_aggregator/deploy_mock_aggregator.cairo b/examples/contracts/aggregator_consumer/scripts/src/mock_aggregator/deploy_mock_aggregator.cairo index e3ab4e9a6..d56e03e1f 100644 --- a/examples/contracts/aggregator_consumer/scripts/src/mock_aggregator/deploy_mock_aggregator.cairo +++ b/examples/contracts/aggregator_consumer/scripts/src/mock_aggregator/deploy_mock_aggregator.cairo @@ -1,12 +1,12 @@ use sncast_std::{ declare, deploy, DeclareResult, DeployResult, get_nonce, DisplayContractAddress, - DisplayClassHash + DisplayClassHash, }; use starknet::{ContractAddress, ClassHash}; fn declare_and_deploy( - contract_name: ByteArray, constructor_calldata: Array + contract_name: ByteArray, constructor_calldata: Array, ) -> DeployResult { let mut class_hash: ClassHash = 0x728d8a221e2204c88df0642b7c6dcee60f7c3d3b3d5c190cac1ceba5baf15e8 @@ -31,7 +31,7 @@ fn declare_and_deploy( Option::Some(salt), true, Option::None, - Option::Some(nonce) + Option::Some(nonce), ); if deploy_result.is_err() { println!("{:?}", deploy_result.unwrap_err()); diff --git a/examples/contracts/aggregator_consumer/scripts/src/mock_aggregator/set_latest_round.cairo b/examples/contracts/aggregator_consumer/scripts/src/mock_aggregator/set_latest_round.cairo index d5a2a0150..8b457871c 100644 --- a/examples/contracts/aggregator_consumer/scripts/src/mock_aggregator/set_latest_round.cairo +++ b/examples/contracts/aggregator_consumer/scripts/src/mock_aggregator/set_latest_round.cairo @@ -9,7 +9,7 @@ fn main() { .try_into() .unwrap(); - // Feel free to modify these + // Feel free to modify these let answer = 1; let block_num = 12345; let observation_timestamp = 1711716556; @@ -20,7 +20,7 @@ fn main() { selector!("set_latest_round_data"), array![answer, block_num, observation_timestamp, transmission_timestamp], Option::None, - Option::Some(get_nonce('pending')) + Option::Some(get_nonce('pending')), ); if result.is_err() { diff --git a/examples/contracts/aggregator_consumer/src/ocr2/price_consumer.cairo b/examples/contracts/aggregator_consumer/src/ocr2/price_consumer.cairo index 9f2d2e381..d38463ddf 100644 --- a/examples/contracts/aggregator_consumer/src/ocr2/price_consumer.cairo +++ b/examples/contracts/aggregator_consumer/src/ocr2/price_consumer.cairo @@ -28,7 +28,7 @@ mod AggregatorPriceConsumer { fn constructor( ref self: ContractState, uptime_feed_address: ContractAddress, - aggregator_address: ContractAddress + aggregator_address: ContractAddress, ) { assert(!uptime_feed_address.is_zero(), 'uptime feed is 0'); assert(!aggregator_address.is_zero(), 'aggregator is 0'); @@ -61,7 +61,7 @@ mod AggregatorPriceConsumer { _ => { assert(!report_stale, 'L2 seq down & report stale'); assert(false, 'L2 seq down & report ok'); - } + }, } } } diff --git a/examples/contracts/aggregator_consumer/tests/test_consumer.cairo b/examples/contracts/aggregator_consumer/tests/test_consumer.cairo index b7afc0b70..ee95950be 100644 --- a/examples/contracts/aggregator_consumer/tests/test_consumer.cairo +++ b/examples/contracts/aggregator_consumer/tests/test_consumer.cairo @@ -1,4 +1,4 @@ -use snforge_std::{declare, ContractClassTrait}; +use starknet::ContractAddress; use chainlink::ocr2::mocks::mock_aggregator::IMockAggregatorDispatcherTrait; use chainlink::ocr2::mocks::mock_aggregator::IMockAggregatorDispatcher; @@ -8,13 +8,14 @@ use chainlink::ocr2::aggregator_proxy::IAggregatorDispatcher; use aggregator_consumer::ocr2::consumer::IAggregatorConsumerDispatcherTrait; use aggregator_consumer::ocr2::consumer::IAggregatorConsumerDispatcher; -use starknet::ContractAddress; +use snforge_std::{declare, ContractClassTrait, DeclareResultTrait}; + fn deploy_mock_aggregator(decimals: u8) -> ContractAddress { let mut calldata = ArrayTrait::new(); calldata.append(decimals.into()); - let contract = declare("MockAggregator").unwrap(); + let contract = declare("MockAggregator").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@calldata).unwrap(); @@ -25,7 +26,7 @@ fn deploy_consumer(aggregator_address: ContractAddress) -> ContractAddress { let mut calldata = ArrayTrait::new(); calldata.append(aggregator_address.into()); - let contract = declare("AggregatorConsumer").unwrap(); + let contract = declare("AggregatorConsumer").unwrap().contract_class(); let (contract_address, _) = contract.deploy(@calldata).unwrap(); @@ -47,7 +48,7 @@ fn test_set_and_read_latest_round() { // Deploys the mock aggregator let mock_aggregator_address = deploy_mock_aggregator(16); let mock_aggregator_dispatcher = IMockAggregatorDispatcher { - contract_address: mock_aggregator_address + contract_address: mock_aggregator_address, }; let aggregator_dispatcher = IAggregatorDispatcher { contract_address: mock_aggregator_address }; @@ -81,7 +82,7 @@ fn test_set_and_read_answer() { // Deploys the mock aggregator let mock_aggregator_address = deploy_mock_aggregator(16); let mock_aggregator_dispatcher = IMockAggregatorDispatcher { - contract_address: mock_aggregator_address + contract_address: mock_aggregator_address, }; // Deploys the consumer @@ -90,7 +91,7 @@ fn test_set_and_read_answer() { // Let's make sure the AggregatorConsumer was initialized correctly assert( - consumer_dispatcher.read_ocr_address() == mock_aggregator_address, 'Invalid OCR address' + consumer_dispatcher.read_ocr_address() == mock_aggregator_address, 'Invalid OCR address', ); assert(consumer_dispatcher.read_answer() == 0, 'Invalid initial answer'); @@ -118,7 +119,7 @@ fn test_set_and_read_answer() { assert(latest_round.started_at == observation_timestamp, 'bad started_at'); assert(latest_round.updated_at == transmission_timestamp, 'bad updated_at'); - // Now let's test that we can set the answer + // Now let's test that we can set the answer consumer_dispatcher.set_answer(latest_round.answer); assert(answer == consumer_dispatcher.read_answer(), 'Invalid answer'); } diff --git a/examples/contracts/aggregator_consumer/tests/test_price_consumer_with_sequencer.cairo b/examples/contracts/aggregator_consumer/tests/test_price_consumer_with_sequencer.cairo index 91874ba2e..d5aa33a72 100644 --- a/examples/contracts/aggregator_consumer/tests/test_price_consumer_with_sequencer.cairo +++ b/examples/contracts/aggregator_consumer/tests/test_price_consumer_with_sequencer.cairo @@ -13,13 +13,18 @@ use starknet::get_caller_address; use starknet::ContractAddress; use snforge_std::{ - declare, ContractClassTrait, start_cheat_caller_address_global, stop_cheat_caller_address_global + declare, ContractClassTrait, start_cheat_caller_address_global, + stop_cheat_caller_address_global, DeclareResultTrait, }; fn deploy_mock_aggregator(decimals: u8) -> ContractAddress { let mut calldata = ArrayTrait::new(); calldata.append(decimals.into()); - let (contract_address, _) = declare("MockAggregator").unwrap().deploy(@calldata).unwrap(); + let (contract_address, _) = declare("MockAggregator") + .unwrap() + .contract_class() + .deploy(@calldata) + .unwrap(); contract_address } @@ -27,18 +32,23 @@ fn deploy_uptime_feed(initial_status: u128, owner_address: ContractAddress) -> C let mut calldata = ArrayTrait::new(); calldata.append(initial_status.into()); calldata.append(owner_address.into()); - let (contract_address, _) = declare("SequencerUptimeFeed").unwrap().deploy(@calldata).unwrap(); + let (contract_address, _) = declare("SequencerUptimeFeed") + .unwrap() + .contract_class() + .deploy(@calldata) + .unwrap(); contract_address } fn deploy_price_consumer( - uptime_feed_address: ContractAddress, aggregator_address: ContractAddress + uptime_feed_address: ContractAddress, aggregator_address: ContractAddress, ) -> ContractAddress { let mut calldata = ArrayTrait::new(); calldata.append(uptime_feed_address.into()); calldata.append(aggregator_address.into()); let (contract_address, _) = declare("AggregatorPriceConsumer") .unwrap() + .contract_class() .deploy(@calldata) .unwrap(); contract_address @@ -55,7 +65,7 @@ fn test_get_latest_price() { let mock_aggregator_address = deploy_mock_aggregator(decimals); let uptime_feed_address = deploy_uptime_feed(init_status, owner); let price_consumer_address = deploy_price_consumer( - uptime_feed_address, mock_aggregator_address + uptime_feed_address, mock_aggregator_address, ); // Adds the price consumer contract to the sequencer uptime feed access control list @@ -65,16 +75,16 @@ fn test_get_latest_price() { IAccessControllerDispatcher { contract_address: uptime_feed_address } .add_access(price_consumer_address); - // The get_latest_price function returns the mock aggregator's latest round answer. At - // this point in the test, there is only one round that is initialized and that is the - // one that the sequencer uptime feed creates when it is deployed. In its constructor, - // a new round is initialized using its initial status as the round's answer, so the - // latest price should be the initial status that was passed into the sequencer uptime + // The get_latest_price function returns the mock aggregator's latest round answer. At + // this point in the test, there is only one round that is initialized and that is the + // one that the sequencer uptime feed creates when it is deployed. In its constructor, + // a new round is initialized using its initial status as the round's answer, so the + // latest price should be the initial status that was passed into the sequencer uptime // feed's constructor. start_cheat_caller_address_global(price_consumer_address); // start_prank(CheatTarget::All, price_consumer_address); let latest_price = IAggregatorPriceConsumerDispatcher { - contract_address: price_consumer_address + contract_address: price_consumer_address, } .get_latest_price(); assert(latest_price == init_status, 'latest price is incorrect'); @@ -91,7 +101,7 @@ fn test_get_latest_price() { // This should now return the updated answer start_cheat_caller_address_global(price_consumer_address); let updated_latest_price = IAggregatorPriceConsumerDispatcher { - contract_address: price_consumer_address + contract_address: price_consumer_address, } .get_latest_price(); assert(updated_latest_price == answer, 'updated price is incorrect'); diff --git a/flake.lock b/flake.lock index 1a5787221..c96b1af61 100644 --- a/flake.lock +++ b/flake.lock @@ -5,11 +5,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1726560853, - "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=", + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", "owner": "numtide", "repo": "flake-utils", - "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", "type": "github" }, "original": { @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1728492678, - "narHash": "sha256-9UTxR8eukdg+XZeHgxW5hQA9fIKHsKCdOIUycTryeVw=", + "lastModified": 1736344531, + "narHash": "sha256-8YVQ9ZbSfuUk2bUf2KRj60NRraLPKPS0Q4QFTbc+c2c=", "owner": "nixos", "repo": "nixpkgs", - "rev": "5633bcff0c6162b9e4b5f1264264611e950c8ec7", + "rev": "bffc22eb12172e6db3c5dde9e3e5628f8e3e7912", "type": "github" }, "original": { @@ -36,11 +36,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1718428119, - "narHash": "sha256-WdWDpNaq6u1IPtxtYHHWpl5BmabtpmLnMAx0RdJ/vo8=", + "lastModified": 1728538411, + "narHash": "sha256-f0SBJz1eZ2yOuKUr5CA9BHULGXVSn6miBuUWdTyhUhU=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "e6cea36f83499eb4e9cd184c8a8e823296b50ad5", + "rev": "b69de56fac8c2b6f8fd27f2eca01dcda8e0a4221", "type": "github" }, "original": { @@ -62,11 +62,11 @@ "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1728527353, - "narHash": "sha256-GY755PX8CbGH3O9iKqauhkFTdP9WSKcOfOkZBe3SOqw=", + "lastModified": 1736476219, + "narHash": "sha256-+qyv3QqdZCdZ3cSO/cbpEY6tntyYjfe1bB12mdpNFaY=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "94749eee5a2b351b6893d5bddb0a18f7f01251ac", + "rev": "de30cc5963da22e9742bbbbb9a3344570ed237b9", "type": "github" }, "original": { diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go index d186383f1..b60057441 100644 --- a/integration-tests/smoke/ocr2_test.go +++ b/integration-tests/smoke/ocr2_test.go @@ -48,7 +48,6 @@ func TestOCRBasic(t *testing.T) { require.NoError(t, err, "Could not set CHAINLINK_ENV_USER") err = os.Setenv("INTERNAL_DOCKER_REPO", *config.Common.InternalDockerRepo) require.NoError(t, err, "Could not set INTERNAL_DOCKER_REPO") - test := test t.Run(test.name, func(t *testing.T) { t.Parallel() logging.Init() diff --git a/integration-tests/soak/ocr2_test.go b/integration-tests/soak/ocr2_test.go index 426a5aa95..6ee0fe613 100644 --- a/integration-tests/soak/ocr2_test.go +++ b/integration-tests/soak/ocr2_test.go @@ -48,7 +48,6 @@ func TestOCRBasicSoak(t *testing.T) { require.NoError(t, err, "Could not set CHAINLINK_ENV_USER") err = os.Setenv("INTERNAL_DOCKER_REPO", *config.Common.InternalDockerRepo) require.NoError(t, err, "Could not set INTERNAL_DOCKER_REPO") - test := test t.Run(test.name, func(t *testing.T) { t.Parallel() logging.Init() diff --git a/integration-tests/testconfig/default.toml b/integration-tests/testconfig/default.toml index 8f9a16810..6fd855453 100644 --- a/integration-tests/testconfig/default.toml +++ b/integration-tests/testconfig/default.toml @@ -1,24 +1,30 @@ # This is the default configuration so OCR2 tests can run without issues [ChainlinkImage] -image="public.ecr.aws/chainlink/chainlink" -version="2.9.0" +image = "public.ecr.aws/chainlink/chainlink" +version = "2.9.0" [Logging] -test_log_collect=false +test_log_collect = false [Logging.LogStream] -log_targets=["file"] -log_producer_timeout="10s" -log_producer_retry_limit=10 +log_targets = ["file"] +log_producer_timeout = "10s" +log_producer_retry_limit = 10 [Network] -selected_networks=["SIMULATED"] # Not needed for Starknet but mandatory from CTF (do not change) +selected_networks = [ + "SIMULATED", +] # Not needed for Starknet but mandatory from CTF (do not change) [Network.RpcHttpUrls] -simulated = ["http://127.0.0.1"] # Not needed for Starknet but mandatory from CTF (do not change) +simulated = [ + "http://127.0.0.1", +] # Not needed for Starknet but mandatory from CTF (do not change) [Network.RpcWsUrls] -simulated = ["wss://127.0.0.1"] # Not needed for Starknet but mandatory from CTF (do not change) +simulated = [ + "wss://127.0.0.1", +] # Not needed for Starknet but mandatory from CTF (do not change) [Common] internal_docker_repo = "public.ecr.aws/chainlink" @@ -26,7 +32,7 @@ inside_k8 = false network = "localnet" user = "satoshi" stateful_db = false -devnet_image = "shardlabs/starknet-devnet-rs:a147b4cd72f9ce9d1fa665d871231370db0f51c7" +devnet_image = "shardlabs/starknet-devnet-rs:a7e193d41833d221550e8ba7246566f50f507e27" postgres_version = "15.7" gauntlet_plus_plus_port = "5234" gauntlet_plus_plus_image = "804282218731.dkr.ecr.us-west-2.amazonaws.com/gauntlet-plus-plus:v2.5.0" diff --git a/ops/charts/devnet/templates/deployment.yaml b/ops/charts/devnet/templates/deployment.yaml index f3c841bb6..ef1565df5 100644 --- a/ops/charts/devnet/templates/deployment.yaml +++ b/ops/charts/devnet/templates/deployment.yaml @@ -29,7 +29,7 @@ spec: {{- if eq .Values.real_node true }} image: "{{ .Values.repository | default "eqlabs/pathfinder"}}:{{ .Values.tag | default "v0.1.8-alpha"}}" {{- else }} - image: "{{ .Values.repository | default "shardlabs/starknet-devnet-rs"}}:{{ .Values.tag | default "a147b4cd72f9ce9d1fa665d871231370db0f51c7"}}" + image: "{{ .Values.repository | default "shardlabs/starknet-devnet-rs"}}:{{ .Values.tag | default "a7e193d41833d221550e8ba7246566f50f507e27"}}" args: ["--port", {{ .Values.service.internalPort | quote}}, "--seed", {{ .Values.seed | quote}}, "--account-class", "cairo1", "--gas-price", "1", "--data-gas-price", "1"] {{- end }} imagePullPolicy: IfNotPresent diff --git a/ops/devnet/environment.go b/ops/devnet/environment.go index cc375c3ca..9692f41cf 100644 --- a/ops/devnet/environment.go +++ b/ops/devnet/environment.go @@ -79,7 +79,7 @@ func defaultProps() map[string]any { "starknet-dev": map[string]any{ "image": map[string]any{ "image": "shardlabs/starknet-devnet-rs", - "version": "a147b4cd72f9ce9d1fa665d871231370db0f51c7", + "version": "a7e193d41833d221550e8ba7246566f50f507e27", }, "resources": map[string]any{ "requests": map[string]any{ diff --git a/ops/scripts/devnet-hardhat.sh b/ops/scripts/devnet-hardhat.sh index c56d67880..d125fd55b 100755 --- a/ops/scripts/devnet-hardhat.sh +++ b/ops/scripts/devnet-hardhat.sh @@ -13,10 +13,10 @@ bash "$(dirname -- "$0")/devnet-hardhat-down.sh" echo "Checking CPU structure..." if [[ $cpu_struct == *"arm"* ]]; then echo "Starting arm devnet container..." - container_version="${CONTAINER_VERSION:-a147b4cd72f9ce9d1fa665d871231370db0f51c7}-arm" + container_version="${CONTAINER_VERSION:-a7e193d41833d221550e8ba7246566f50f507e27}-arm" else echo "Starting i386 devnet container..." - container_version="${CONTAINER_VERSION:-a147b4cd72f9ce9d1fa665d871231370db0f51c7}" + container_version="${CONTAINER_VERSION:-a7e193d41833d221550e8ba7246566f50f507e27}" fi echo "Starting starknet-devnet" diff --git a/relayer/pkg/chainlink/ocr2/medianreport/onchainconfig.go b/relayer/pkg/chainlink/ocr2/medianreport/onchainconfig.go index 47a5b0256..fb6b60388 100644 --- a/relayer/pkg/chainlink/ocr2/medianreport/onchainconfig.go +++ b/relayer/pkg/chainlink/ocr2/medianreport/onchainconfig.go @@ -36,10 +36,10 @@ func (codec OnchainConfigCodec) DecodeToFelts(b []byte) ([]*big.Int, error) { return []*big.Int{}, fmt.Errorf("unexpected version of OnchainConfig, expected %v, got %v", OnchainConfigVersion, configVersion.Int64()) } - min := new(big.Int).SetBytes(b[byteWidth : 2*byteWidth]) - max := new(big.Int).SetBytes(b[2*byteWidth:]) + minVal := new(big.Int).SetBytes(b[byteWidth : 2*byteWidth]) + maxVal := new(big.Int).SetBytes(b[2*byteWidth:]) - return []*big.Int{configVersion, min, max}, nil + return []*big.Int{configVersion, minVal, maxVal}, nil } // Decode converts the onchainconfig via the outputs of DecodeToFelts into unsigned big.Ints that libocr expects @@ -49,25 +49,25 @@ func (codec OnchainConfigCodec) Decode(ctx context.Context, b []byte) (median.On return median.OnchainConfig{}, err } - min := felts[1] - max := felts[2] + minVal := felts[1] + maxVal := felts[2] - if !(min.Cmp(max) <= 0) { - return median.OnchainConfig{}, fmt.Errorf("OnchainConfig min (%v) should not be greater than max(%v)", min, max) + if !(minVal.Cmp(maxVal) <= 0) { + return median.OnchainConfig{}, fmt.Errorf("OnchainConfig min (%v) should not be greater than max(%v)", minVal, maxVal) } - return median.OnchainConfig{Min: min, Max: max}, nil + return median.OnchainConfig{Min: minVal, Max: maxVal}, nil } // EncodeFromFelt encodes the config where min & max are big.Int representations of a felt // Cairo has no notion of signed values: min and max values will be non-negative -func (codec OnchainConfigCodec) EncodeFromFelt(version, min, max *big.Int) ([]byte, error) { +func (codec OnchainConfigCodec) EncodeFromFelt(version, minVal, maxVal *big.Int) ([]byte, error) { if version.Uint64() != OnchainConfigVersion { return nil, fmt.Errorf("unexpected version of OnchainConfig, expected %v, got %v", OnchainConfigVersion, version.Int64()) } - if min.Sign() == -1 || max.Sign() == -1 { - return nil, fmt.Errorf("starknet does not support negative values: min = (%v) and max = (%v)", min, max) + if minVal.Sign() == -1 || maxVal.Sign() == -1 { + return nil, fmt.Errorf("starknet does not support negative values: min = (%v) and max = (%v)", minVal, maxVal) } result := []byte{} @@ -75,8 +75,8 @@ func (codec OnchainConfigCodec) EncodeFromFelt(version, min, max *big.Int) ([]by minBytes := make([]byte, byteWidth) maxBytes := make([]byte, byteWidth) result = append(result, version.FillBytes(versionBytes)...) - result = append(result, min.FillBytes(minBytes)...) - result = append(result, max.FillBytes(maxBytes)...) + result = append(result, minVal.FillBytes(minBytes)...) + result = append(result, maxVal.FillBytes(maxBytes)...) return result, nil } diff --git a/relayer/pkg/chainlink/ocr2/medianreport/report_test.go b/relayer/pkg/chainlink/ocr2/medianreport/report_test.go index a55c94430..e369f86d0 100644 --- a/relayer/pkg/chainlink/ocr2/medianreport/report_test.go +++ b/relayer/pkg/chainlink/ocr2/medianreport/report_test.go @@ -214,7 +214,6 @@ func TestMedianFromReport(t *testing.T) { } for _, tc := range tt { - tc := tc t.Run(tc.name, func(t *testing.T) { ctx := tests.Context(t) var pos []median.ParsedAttributedObservation @@ -229,9 +228,9 @@ func TestMedianFromReport(t *testing.T) { } report, err := cdc.BuildReport(ctx, pos) require.NoError(t, err) - max, err := cdc.MaxReportLength(ctx, len(tc.obs)) + maxReportLength, err := cdc.MaxReportLength(ctx, len(tc.obs)) require.NoError(t, err) - assert.Equal(t, len(report), max) + assert.Equal(t, len(report), maxReportLength) med, err := cdc.MedianFromReport(ctx, report) require.NoError(t, err) assert.Equal(t, tc.expectedMedian.String(), med.String()) diff --git a/scripts/devnet.sh b/scripts/devnet.sh index 70fb3a3a2..c10c5f01b 100755 --- a/scripts/devnet.sh +++ b/scripts/devnet.sh @@ -13,10 +13,10 @@ echo "Checking CPU structure..." if [[ $cpu_struct == *"arm"* ]] then echo "Starting arm devnet container..." - container_version="a147b4cd72f9ce9d1fa665d871231370db0f51c7-arm" + container_version="a7e193d41833d221550e8ba7246566f50f507e27-arm" else echo "Starting i386 devnet container..." - container_version="a147b4cd72f9ce9d1fa665d871231370db0f51c7" + container_version="a7e193d41833d221550e8ba7246566f50f507e27" fi echo "Starting starknet-devnet" diff --git a/shell.nix b/shell.nix index a852720eb..2b8a194a1 100644 --- a/shell.nix +++ b/shell.nix @@ -13,6 +13,10 @@ with pkgs; let }; in mkShell' { + buildInputs = [ + zizmor + ]; + nativeBuildInputs = [ stdenv.cc.cc.lib diff --git a/zizmor.yml b/zizmor.yml new file mode 100644 index 000000000..dfa4151e8 --- /dev/null +++ b/zizmor.yml @@ -0,0 +1,7 @@ +rules: + cache-poisoning: + ignore: + - monitoring-build-push-ecr.yml:49 + github-env: + ignore: + - action.yml:35