Skip to content

Commit

Permalink
Implement upgraded version for price feed smart contract (#218)
Browse files Browse the repository at this point in the history
* Implement upgraded version for price feed smart contract

* Implement deployment scripts

* Fix RedStone main demo constant value

* Add two new RedStone data service URLs

* Apply review suggestions
  • Loading branch information
matjazv authored Dec 10, 2024
1 parent 259848c commit 12311ae
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 35 deletions.
46 changes: 46 additions & 0 deletions script/contracts/L2/upgraded/L2PriceFeedWithoutRoundsV2.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.23;

import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import { Upgrades } from "openzeppelin-foundry-upgrades/Upgrades.sol";
import { Options } from "openzeppelin-foundry-upgrades/Options.sol";
import { Script, console2 } from "forge-std/Script.sol";
import { L2PriceFeedWithoutRoundsV2 } from "src/L2/upgraded/L2PriceFeedWithoutRoundsV2.sol";

/// @title L2PriceFeedWithoutRoundsV2Script - L2PriceFeedWithoutRoundsV2 contract deployment script
/// @notice This contract is used to deploy L2PriceFeedWithoutRoundsV2 contract which is upgraded version of
/// L2PriceFeedWithoutRounds contract.
contract L2PriceFeedWithoutRoundsV2Script is Script {
function setUp() public { }

/// @notice This function deploys L2PriceFeedWithoutRoundsV2 contract.
function run() public {
// Deployer's private key. This key is used to deploy the contract.
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");

// Validate L2PriceFeedWithoutRoundsV2 contract if it is implemented correctly so that it may be used as new
// implementation for the proxy contract.
Options memory opts;
opts.referenceContract = "L2PriceFeedWithoutRounds.sol";
opts.unsafeAllow = "constructor";
Upgrades.validateUpgrade("L2PriceFeedWithoutRoundsV2.sol", opts);

console2.log("Deploying L2 PriceFeedWithoutRoundsV2 contract...");

// deploy L2PriceFeedWithoutRoundsV2 contract
vm.startBroadcast(deployerPrivateKey);
L2PriceFeedWithoutRoundsV2 l2PriceFeedWithoutRoundsV2 = new L2PriceFeedWithoutRoundsV2();
vm.stopBroadcast();

assert(address(l2PriceFeedWithoutRoundsV2) != address(0));

// ERC1967Utils: keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1.
assert(
l2PriceFeedWithoutRoundsV2.proxiableUUID()
== bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1)
);

console2.log("L2 PriceFeedWithoutRoundsV2 contract successfully deployed!");
console2.log("L2 PriceFeedWithoutRoundsV2 address: %s", address(l2PriceFeedWithoutRoundsV2));
}
}
48 changes: 48 additions & 0 deletions script/upgraded/deployPriceFeedWithoutRoundsV2.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/usr/bin/env bash

echo "Instructing the shell to exit immediately if any command returns a non-zero exit status..."
set -e
echo "Done."

echo "Navigating to the root directory of the project..."
cd ../../
echo "Done."

echo "Setting environment variables..."
source .env
echo "Done."

echo "Creating $NETWORK directory inside deployment/artifacts/contracts directory..."
if [ -z "$NETWORK" ]
then
echo "NETWORK variable inside .env file is not set. Please set NETWORK environment variable."
exit 1
else
if [ -d "deployment/artifacts/contracts/$NETWORK" ]
then
echo "Directory deployment/artifacts/contracts/$NETWORK already exists."
else
mkdir deployment/artifacts/contracts/$NETWORK
fi
fi
echo "Done."

echo "Cleaning up the build artifacts to be able to deploy the contract..."
forge clean
echo "Done."

echo "Deploying and if enabled verifying L2PriceFeedWithoutRoundsV2 smart contract..."
if [ -z "$CONTRACT_VERIFIER" ]
then
forge script --rpc-url="$L2_RPC_URL" --broadcast -vvvv script/contracts/L2/upgraded/L2PriceFeedWithoutRoundsV2.s.sol:L2PriceFeedWithoutRoundsV2Script
else
if [ $CONTRACT_VERIFIER = "blockscout" ]
then
forge script --rpc-url="$L2_RPC_URL" --broadcast --verify --verifier blockscout --verifier-url $L2_VERIFIER_URL -vvvv script/contracts/L2/upgraded/L2PriceFeedWithoutRoundsV2.s.sol:L2PriceFeedWithoutRoundsV2Script
fi
if [ $CONTRACT_VERIFIER = "etherscan" ]
then
forge script --rpc-url="$L2_RPC_URL" --broadcast --verify --verifier etherscan --etherscan-api-key="$L2_ETHERSCAN_API_KEY" -vvvv script/contracts/L2/upgraded/L2PriceFeedWithoutRoundsV2.s.sol:L2PriceFeedWithoutRoundsV2Script
fi
fi
echo "Done."
17 changes: 17 additions & 0 deletions src/L2/upgraded/L2PriceFeedWithoutRoundsV2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.23;

import { L2PriceFeedWithoutRounds } from "../L2PriceFeedWithoutRounds.sol";

/// @title L2PriceFeedWithoutRoundsV2 - L2PriceFeedWithoutRoundsV2 contract
/// @notice This contract represents PriceFeedWithoutRoundsForMultiFeedAdapter contract. It is an upgradeable version of
/// the L2PriceFeedWithoutRounds contract, allowing for updates to the adapter contract address while
/// maintaining the same interface and functionality.
contract L2PriceFeedWithoutRoundsV2 is L2PriceFeedWithoutRounds {
/// @notice Setting global params.
/// @param _newAdapter The address of the new MultiFeedAdapter contract.
function initializeV2(address _newAdapter) public virtual reinitializer(2) {
require(_newAdapter != address(0), "L2PriceFeedWithoutRoundsV2: new adapter contract address can not be zero");
priceFeedAdapter = _newAdapter;
}
}
64 changes: 30 additions & 34 deletions test/L2/L2PriceFeedWithoutRounds.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,17 @@ pragma solidity 0.8.23;

import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import { OwnableUpgradeable } from "@openzeppelin-upgradeable/contracts/access/OwnableUpgradeable.sol";
import { Test } from "forge-std/Test.sol";
import { Test, console } from "forge-std/Test.sol";
import { L2PriceFeedWithoutRoundsFactory } from "src/L2/L2PriceFeedWithoutRoundsFactory.sol";
import { L2PriceFeedWithoutRounds } from "src/L2/L2PriceFeedWithoutRounds.sol";

contract L2PriceFeedWithoutRoundsV2Mock is L2PriceFeedWithoutRounds {
string public testVersion;

function initializeV2(string memory _version) public reinitializer(2) {
testVersion = _version;
}

function onlyV2() public pure returns (string memory) {
return "Hello from V2";
}
}
import { L2PriceFeedWithoutRoundsV2 } from "src/L2/upgraded/L2PriceFeedWithoutRoundsV2.sol";

contract L2PriceFeedWithoutRoundsTest is Test {
L2PriceFeedWithoutRounds public l2PriceFeed;
L2PriceFeedWithoutRounds public l2PriceFeedImplementation;

address public priceFeedAdapter = 0x1038999DCf0A302Cc8Eed72fAeCbf0eEBfC476b0;
address public priceFeedAdapter = vm.addr(uint256(bytes32("priceFeedAdapter")));
address public newPriceFeedAdapter = vm.addr(uint256(bytes32("newPriceFeedAdapter")));

function setUp() public {
// deploy L2PriceFeedWithoutRoundsFactory Implementation contract
Expand All @@ -42,19 +32,19 @@ contract L2PriceFeedWithoutRoundsTest is Test {

// create L2PriceFeedWithoutRounds contract
l2PriceFeed =
L2PriceFeedWithoutRounds(l2PriceFeedFactory.createL2PriceFeedWithoutRounds("USDT", priceFeedAdapter));
L2PriceFeedWithoutRounds(l2PriceFeedFactory.createL2PriceFeedWithoutRounds("LSK", priceFeedAdapter));
assert(address(l2PriceFeed) != address(0));
assertEq(l2PriceFeed.decimals(), 8);
assertEq(keccak256(bytes(l2PriceFeed.description())), keccak256(bytes("Redstone Price Feed")));
assertEq(l2PriceFeed.getDataFeedId(), bytes32("USDT"));
assertEq(l2PriceFeed.getDataFeedId(), bytes32("LSK"));
assertEq(address(l2PriceFeed.getPriceFeedAdapter()), priceFeedAdapter);

// accept ownership
l2PriceFeed.acceptOwnership();

// check L2PriceFeedWithoutRoundsFactory variables
assertEq(l2PriceFeedFactory.l2PriceFeedWithoutRoundsContracts(0), address(l2PriceFeed));
assertEq(l2PriceFeedFactory.l2PriceFeedWithoutRoundsDataFeedIds(address(l2PriceFeed), 0), "USDT");
assertEq(l2PriceFeedFactory.l2PriceFeedWithoutRoundsDataFeedIds(address(l2PriceFeed), 0), "LSK");
}

function test_TransferOwnership() public {
Expand Down Expand Up @@ -105,8 +95,8 @@ contract L2PriceFeedWithoutRoundsTest is Test {
}

function testFuzz_UpgradeToAndCall_RevertWhenNotOwner(uint256 _addressSeed) public {
// deploy L2PriceFeedWithoutRoundsV2Mock implementation contract
L2PriceFeedWithoutRoundsV2Mock l2PriceFeedV2Implementation = new L2PriceFeedWithoutRoundsV2Mock();
// deploy L2PriceFeedWithoutRoundsV2 implementation contract
L2PriceFeedWithoutRoundsV2 l2PriceFeedV2Implementation = new L2PriceFeedWithoutRoundsV2();
_addressSeed = bound(_addressSeed, 1, type(uint160).max);
address nobody = vm.addr(_addressSeed);

Expand All @@ -119,33 +109,39 @@ contract L2PriceFeedWithoutRoundsTest is Test {
l2PriceFeed.upgradeToAndCall(address(l2PriceFeedV2Implementation), "");
}

function test_UpgradeToAndCall_ZeroAdapterAddress() public {
// deploy L2PriceFeedWithoutRoundsV2 implementation contract
L2PriceFeedWithoutRoundsV2 l2PriceFeedV2Implementation = new L2PriceFeedWithoutRoundsV2();

// try to upgrade to L2PriceFeedWithoutRoundsV2 implementation contract with zero adapter address
vm.expectRevert("L2PriceFeedWithoutRoundsV2: new adapter contract address can not be zero");
l2PriceFeed.upgradeToAndCall(
address(l2PriceFeedV2Implementation),
abi.encodeWithSelector(l2PriceFeedV2Implementation.initializeV2.selector, 0x0)
);
}

function test_UpgradeToAndCall_SuccessUpgrade() public {
// deploy L2PriceFeedWithoutRoundsV2Mock implementation contract
L2PriceFeedWithoutRoundsV2Mock l2PriceFeedV2Implementation = new L2PriceFeedWithoutRoundsV2Mock();
// deploy L2PriceFeedWithoutRoundsV2 implementation contract
L2PriceFeedWithoutRoundsV2 l2PriceFeedV2Implementation = new L2PriceFeedWithoutRoundsV2();

// upgrade contract, and also change some variables by reinitialize
// upgrade contract, and also change priceFeedAdapter address
l2PriceFeed.upgradeToAndCall(
address(l2PriceFeedV2Implementation),
abi.encodeWithSelector(l2PriceFeedV2Implementation.initializeV2.selector, "v2.0.0")
abi.encodeWithSelector(l2PriceFeedV2Implementation.initializeV2.selector, newPriceFeedAdapter)
);

// wrap L2PriceFeedWithoutRounds proxy with new contract
L2PriceFeedWithoutRoundsV2Mock l2PriceFeedV2 = L2PriceFeedWithoutRoundsV2Mock(address(l2PriceFeed));
L2PriceFeedWithoutRoundsV2 l2PriceFeedV2 = L2PriceFeedWithoutRoundsV2(address(l2PriceFeed));

// check if the upgrade was successful and the variables are the same
// check if the upgrade was successful and the new priceFeedAdapter address is set
assertEq(l2PriceFeedV2.decimals(), 8);
assertEq(keccak256(bytes(l2PriceFeedV2.description())), keccak256(bytes("Redstone Price Feed")));
assertEq(l2PriceFeedV2.getDataFeedId(), bytes32("USDT"));
assertEq(address(l2PriceFeedV2.getPriceFeedAdapter()), priceFeedAdapter);

// version of L2PriceFeedWithoutRounds set to v2.0.0
assertEq(l2PriceFeedV2.testVersion(), "v2.0.0");

// new function introduced
assertEq(l2PriceFeedV2.onlyV2(), "Hello from V2");
assertEq(l2PriceFeedV2.getDataFeedId(), bytes32("LSK"));
assertEq(address(l2PriceFeedV2.getPriceFeedAdapter()), newPriceFeedAdapter);

// assure cannot re-reinitialize
vm.expectRevert();
l2PriceFeedV2.initializeV2("v3.0.0");
l2PriceFeedV2.initializeV2(newPriceFeedAdapter);
}
}
4 changes: 3 additions & 1 deletion web3-functions/redstone/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ const ORACLE_ABI = [
];

const REDSTONE_PRIMARY_PROD = "redstone-primary-prod";
const REDSTONE_MAIN_DEMO = "redstone-primary-prod";
const REDSTONE_MAIN_DEMO = "redstone-main-demo";
// https://github.com/redstone-finance/redstone-oracles-monorepo/blob/main/packages/sdk/src/data-services-urls.ts
const DEV_GWS = [
"https://oracle-gateway-1.b.redstone.vip",
"https://oracle-gateway-1.b.redstone.finance",
"https://d33trozg86ya9x.cloudfront.net", // https://github.com/gelatodigital/w3f-redstone-poc-v2/blob/main/web3-functions/redstone/index.ts#L32
];
const PROD_GWS = [
"https://oracle-gateway-1.a.redstone.vip",
"https://oracle-gateway-1.a.redstone.finance",
"https://oracle-gateway-2.a.redstone.finance",
];
Expand Down

0 comments on commit 12311ae

Please sign in to comment.