Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[submodule "lib/chainlink-evm"]
path = lib/chainlink-evm
url = https://github.com/smartcontractkit/chainlink-evm
[submodule "lib/save"]
path = lib/save
url = https://github.com/llama-risk/save
Empty file added contracts-save-cre/.gitkeep
Empty file.
11 changes: 11 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Used by `@nomicfoundation/hardhat-foundry` + `forge config`. Must match
# `paths.sources` in `hardhat.config.ts`. On the save-cre deploy throwaway
# branch this is a dummy directory; real Solidity is compiled from
# `lib/save/...` via `hardhat-dependency-compiler` and flat remappings in
# `remappings.txt` (global `@openzeppelin` → OZ v5 in lib/).

[profile.default]
src = "contracts-save-cre"
libs = ["lib", "node_modules"]
cache_path = "cache_forge"
solc_version = "0.8.33"
27 changes: 27 additions & 0 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import 'solidity-docgen';
import './tasks';
import '@layerzerolabs/toolbox-hardhat';
import 'hardhat-tracer';
import '@nomicfoundation/hardhat-foundry';
import 'hardhat-dependency-compiler';
import {
chainIds,
ENV,
Expand Down Expand Up @@ -48,6 +50,24 @@ const config: HardhatUserConfig = {
},
},
},
{
// PLACEHOLDER optimizer settings — must be reconciled against
// upstream `llama-risk/save`'s `foundry.toml` (`optimizer`,
// `optimizer_runs`, `via_ir`) before mainnet deploy. Tracked in
// docs/superpowers/specs/2026-04-23-save-cre-receiver-proxy-mainnet-deploy-design.md §10.
version: '0.8.33',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Solidity version 0.8.33 is not a released version of the compiler. This will cause Hardhat to fail when attempting to download the compiler. Please use a valid version such as 0.8.23 or 0.8.28.

settings: {
optimizer: {
enabled: true,
runs: 200,
},
},
},
],
},
dependencyCompiler: {
paths: [
'save/contracts/src/save-cre-receiver-proxy/SaveCreReceiverProxy.sol',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The path for SaveCreReceiverProxy.sol appears to be missing the lib/ prefix. Since the submodule is located in lib/save, the path should reflect this to ensure the hardhat-dependency-compiler plugin can locate the file.

Suggested change
'save/contracts/src/save-cre-receiver-proxy/SaveCreReceiverProxy.sol',
'lib/save/contracts/src/save-cre-receiver-proxy/SaveCreReceiverProxy.sol',

],
},
namedAccounts: {
Expand Down Expand Up @@ -97,6 +117,13 @@ const config: HardhatUserConfig = {
runOnCompile: OPTIMIZER,
},
paths: {
// Throwaway `save-cre` deploy branch only. Flat remapped deps (OZ v5 in
// `lib/openzeppelin-contracts`) are incompatible with the main `contracts/`
// tree (OZ 4.8.3 in node_modules), so we point `sources` at an empty
// directory and let `hardhat-dependency-compiler` pull
// `save/.../SaveCreReceiverProxy.sol` (forge remapping) in as the only graph.
// Main branch must NOT merge this; restore `sources: 'contracts'`.
sources: 'contracts-save-cre',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The sources path has been changed to contracts-save-cre, which disables the main project contracts. To make this configuration safe for merging and avoid breaking the build for other developers, consider using an environment variable to toggle the source directory.

Suggested change
sources: 'contracts-save-cre',
sources: process.env.DEPLOY_SAVE_CRE ? 'contracts-save-cre' : 'contracts',

deploy: 'deploy/',
deployments: 'deployments/',
},
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
"deploy:axelar:post:set:flowlimit": "yarn hh:run:script scripts/deploy/misc/axelar/set_FlowLimit.ts",
"deploy:axelar:post:grant:roles": "yarn hh:run:script scripts/deploy/misc/axelar/grant_Roles.ts",
"deploy:axelar:post:revoke:roles": "yarn hh:run:script scripts/deploy/misc/axelar/revoke_Roles.ts",
"deploy:save-cre": "yarn hh:run:script scripts/deploy/misc/save-cre/deploy_SaveCreReceiverProxy.ts",
"deploy:post:set:sanctionsList": "yarn hh:run:script scripts/deploy/post-deploy/set_SanctionsList.ts",
"deploy:post:set:aaveconfig": "yarn hh:run:script scripts/deploy/post-deploy/set_AaveConfig.ts",
"verify:proxy": "yarn hardhat verify-transparent-proxy",
Expand All @@ -116,6 +117,7 @@
"@layerzerolabs/oft-evm": "3.2.1",
"@layerzerolabs/toolbox-hardhat": "0.6.12",
"@nomicfoundation/hardhat-chai-matchers": "1.0.4",
"@nomicfoundation/hardhat-foundry": "hh2",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The version alias "hh2" for @nomicfoundation/hardhat-foundry is non-standard and potentially confusing. It is recommended to use a standard semver range.

Suggested change
"@nomicfoundation/hardhat-foundry": "hh2",
"@nomicfoundation/hardhat-foundry": "^1.2.1",

"@nomicfoundation/hardhat-network-helpers": "1.0.6",
"@nomicfoundation/hardhat-toolbox": "1.0.2",
"@nomicfoundation/hardhat-verify": "2.0.14",
Expand Down Expand Up @@ -145,6 +147,7 @@
"globals": "16.5.0",
"hardhat": "2.24.0",
"hardhat-contract-sizer": "2.6.1",
"hardhat-dependency-compiler": "1.2.1",
"hardhat-deploy": "0.11.19",
"hardhat-docgen": "1.3.0",
"hardhat-gas-reporter": "1.0.9",
Expand Down
2 changes: 2 additions & 0 deletions remappings.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@chainlink/contracts/=lib/chainlink-evm/contracts/
@openzeppelin/contracts@5.0.2/=lib/save/lib/openzeppelin-contracts/contracts/
3 changes: 2 additions & 1 deletion scripts/deploy/common/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,8 @@ export const sendAndWaitForCustomTxSign = async (
| 'update-lz-oapp-config'
| 'axelar-wire-tokens'
| 'axelar-update-config'
| 'deployer';
| 'deployer'
| 'cre-receiver-owner';
subAction?:
| 'add-payment-token'
| 'grant-token-roles'
Expand Down
90 changes: 90 additions & 0 deletions scripts/deploy/misc/save-cre/deploy_SaveCreReceiverProxy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { HardhatRuntimeEnvironment } from 'hardhat/types';

import { etherscanVerify, logDeploy } from '../../../../helpers/utils';
import { DeployFunction } from '../../common/types';
import {
getDeployer,
getWalletAddressForAction,
sendAndWaitForCustomTxSign,
} from '../../common/utils';

const REGISTRY = '0x2D6e9F608807436DE5D9603B00Abe3FEd1Bc809d';
const EXPECTED_FORWARDER = '0x0b93082D9b3C7C97fAcd250082899BAcf3af3885';
const EXPECTED_AUTHOR = '0xFD1ebE54d435bDd9a81A4BF204756a20AeB7Ee71';
Comment on lines +11 to +13

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Hardcoding addresses for REGISTRY, EXPECTED_FORWARDER, and EXPECTED_AUTHOR limits the script's usability across different environments. It is recommended to move these values to a configuration file or environment variables.


// bytes10(0x62396534366635623834) — ASCII-hex of `"b9e46f5b84"` =
// sha256("midas_por_attestation_prod")[0:5] re-encoded as 10 ASCII hex chars.
const ATTESTATION_WORKFLOW_NAME = '0x62396534366635623834';

// bytes10(0x34346265396532306639) — ASCII-hex of `"44be9e20f9"` =
// sha256("midas_por_verification_prod")[0:5] re-encoded as 10 ASCII hex chars.
const VERIFICATION_WORKFLOW_NAME = '0x34346265396532306639';

// Both attestation and verification workflows share `bytes32(0)` per the
// task spec. The constructor seeds this slot with the attestation name;
// the post-deploy `setWorkflowConfig` call below overwrites it with the
// verification name. The DON-side report selector
// (`setAttestation` vs `setVerification`) is what differentiates which
// registry call is made — not the workflow-config name check.
const WORKFLOW_ID = '0x' + '00'.repeat(32);
const IS_REPORT_WRITE_SECURED = true;

const func: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
const deployer = await getDeployer(hre);
const initialOwner = await getWalletAddressForAction(
hre,
'cre-receiver-owner',
);

console.log('Deployer :', await deployer.getAddress());
console.log('Initial owner :', initialOwner);
console.log('Registry :', REGISTRY);
console.log('Forwarder :', EXPECTED_FORWARDER);
console.log('Author :', EXPECTED_AUTHOR);
console.log('Att. workflow :', ATTESTATION_WORKFLOW_NAME);
console.log('Ver. workflow :', VERIFICATION_WORKFLOW_NAME);

const factory = await hre.ethers.getContractFactory(
'SaveCreReceiverProxy',
deployer,
);

const constructorArgs = [
REGISTRY,
WORKFLOW_ID,
EXPECTED_FORWARDER,
EXPECTED_AUTHOR,
ATTESTATION_WORKFLOW_NAME,
IS_REPORT_WRITE_SECURED,
initialOwner,
] as const;

const proxy = await factory.deploy(...constructorArgs);
logDeploy('SaveCreReceiverProxy', undefined, proxy.address);
await proxy.deployTransaction.wait(2);

await etherscanVerify(hre, proxy.address, ...constructorArgs).catch((e) => {
console.error('Unable to verify SaveCreReceiverProxy. Error: ', e);
});

const setWorkflowTx = await proxy.populateTransaction.setWorkflowConfig(
WORKFLOW_ID,
EXPECTED_FORWARDER,
EXPECTED_AUTHOR,
VERIFICATION_WORKFLOW_NAME,
true,
);

await sendAndWaitForCustomTxSign(
hre,
{ ...setWorkflowTx, to: proxy.address },
{
action: 'cre-receiver-owner',
comment: 'SaveCreReceiverProxy: register verification workflow',
},
);

console.log('Done. Proxy:', proxy.address);
};

export default func;
76 changes: 76 additions & 0 deletions scripts/setup-foundry-submodules.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#!/usr/bin/env bash
# Resolves the two root-level submodules and nested Foundry deps, then applies
# a small import patch in `lib/save` for Hardhat (HH415).
#
# Why two top-level submodules:
# - `lib/save` — llama-risk/save; OZ v5, forge-std, and a nested chainlink
# copy are pulled with `git submodule update --init --recursive`.
# - `lib/chainlink-evm` — smartcontractkit/chainlink-evm. Required at repo
# root: `remappings.txt` maps `@chainlink/contracts/` to
# `lib/chainlink-evm/contracts/` (IReceiver, etc.); that path is *not* the
# same as `lib/save/lib/chainlink-evm/...` used by save's own build.
# OpenZeppelin for Hardhat: `remappings.txt` points at
# `lib/save/lib/openzeppelin-contracts/contracts/`
# so a separate root `lib/openzeppelin-contracts` is not used.
#
# Run:
# ./scripts/setup-foundry-submodules.sh
# yarn compile
#
# `forge` on PATH is not required to run this script; it is needed for
# `forge remappings` / @nomicfoundation/hardhat-foundry when you compile.
#
# Ref: docs/superpowers/specs/2026-04-23-save-cre-receiver-proxy-mainnet-deploy-design.md
#
# HH415: `IReceiver.sol` imports `@openzeppelin/contracts@5.0.2/...` while
# upstream save uses plain `@openzeppelin/contracts/...`. If both prefixes
# map to the same on-disk file, Hardhat errors with
# "Two different source names ... resolve to the same file". We unify the
# Save .sol files to the `@5.0.2` import style to match IReceiver.

set -euo pipefail

cd "$(git rev-parse --show-toplevel)"

if ! command -v forge >/dev/null 2>&1; then
echo "warning: 'forge' not on PATH. Install Foundry for Hardhat compiles: https://getfoundry.sh" >&2
fi

mkdir -p lib

# Register in .gitmodules only if missing. After a normal clone, entries exist
# and we must NOT run `submodule add` again (use update instead).
if ! git config --file .gitmodules --get 'submodule.lib/save.path' &>/dev/null; then
git submodule add --depth=1 \
https://github.com/llama-risk/save \
lib/save
fi
if ! git config --file .gitmodules --get 'submodule.lib/chainlink-evm.path' &>/dev/null; then
git submodule add --depth=1 \
https://github.com/smartcontractkit/chainlink-evm \
lib/chainlink-evm
fi

git submodule update --init --recursive

patch_save_for_hardhat() {
local proxy="lib/save/contracts/src/save-cre-receiver-proxy/SaveCreReceiverProxy.sol"
local abs="lib/save/contracts/src/abstracts/AbstractCreReceiver.sol"
if ! grep -q '@openzeppelin/contracts@5.0.2/access/Ownable2Step' "$proxy" 2>/dev/null; then
if sed --version 2>&1 | grep -q GNU; then
sed -i 's|@openzeppelin/contracts/access/|@openzeppelin/contracts@5.0.2/access/|' "$proxy"
sed -i 's|@openzeppelin/contracts/utils/|@openzeppelin/contracts@5.0.2/utils/|' "$abs"
Comment on lines +61 to +62

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Modifying submodule files with sed is a brittle workaround for import conflicts. These changes are not persistent across submodule updates or resets. It is better to resolve these conflicts using Hardhat's remapping capabilities or by updating the upstream repository.

else
sed -i '' 's|@openzeppelin/contracts/access/|@openzeppelin/contracts@5.0.2/access/|' "$proxy"
sed -i '' 's|@openzeppelin/contracts/utils/|@openzeppelin/contracts@5.0.2/utils/|' "$abs"
fi
echo "Applied Hardhat HH415 import unification patch to lib/save/contracts/..."
else
echo "Patch already applied (import paths use @5.0.2)."
fi
}

patch_save_for_hardhat

echo
echo "Done. Verify with: yarn compile"
22 changes: 22 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2584,6 +2584,17 @@ __metadata:
languageName: node
linkType: hard

"@nomicfoundation/hardhat-foundry@npm:hh2":
version: 1.2.1
resolution: "@nomicfoundation/hardhat-foundry@npm:1.2.1"
dependencies:
picocolors: "npm:^1.1.0"
peerDependencies:
hardhat: ^2.26.0
checksum: 10c0/0cab1137f5aae2f58f61e963445fd300b026fa808b04c172f0e7c6508a880be743d4aca9aca21de90d52350dbb559665369706c8f3fe3f673bfdb7b9c301f322
languageName: node
linkType: hard

"@nomicfoundation/hardhat-network-helpers@npm:1.0.6":
version: 1.0.6
resolution: "@nomicfoundation/hardhat-network-helpers@npm:1.0.6"
Expand Down Expand Up @@ -9583,6 +9594,15 @@ __metadata:
languageName: node
linkType: hard

"hardhat-dependency-compiler@npm:1.2.1":
version: 1.2.1
resolution: "hardhat-dependency-compiler@npm:1.2.1"
peerDependencies:
hardhat: ^2.0.0
checksum: 10c0/d4373422849fe8851d06f1151f51fe17d00fb9f3425cc77cce53bd2d0c9f325d4f1520f2aa8eae305b3a0d012b9841b398d57c3f2d5e7a3d29b6dd4aaebc1f6e
languageName: node
linkType: hard

"hardhat-deploy@npm:0.11.19":
version: 0.11.19
resolution: "hardhat-deploy@npm:0.11.19"
Expand Down Expand Up @@ -11769,6 +11789,7 @@ __metadata:
"@layerzerolabs/oft-evm": "npm:3.2.1"
"@layerzerolabs/toolbox-hardhat": "npm:0.6.12"
"@nomicfoundation/hardhat-chai-matchers": "npm:1.0.4"
"@nomicfoundation/hardhat-foundry": "npm:hh2"
"@nomicfoundation/hardhat-network-helpers": "npm:1.0.6"
"@nomicfoundation/hardhat-toolbox": "npm:1.0.2"
"@nomicfoundation/hardhat-verify": "npm:2.0.14"
Expand Down Expand Up @@ -11798,6 +11819,7 @@ __metadata:
globals: "npm:16.5.0"
hardhat: "npm:2.24.0"
hardhat-contract-sizer: "npm:2.6.1"
hardhat-dependency-compiler: "npm:1.2.1"
hardhat-deploy: "npm:0.11.19"
hardhat-docgen: "npm:1.3.0"
hardhat-gas-reporter: "npm:1.0.9"
Expand Down
Loading