[eth] contract improvement (#348)
* Make Pyth.initialize private * Make contract upgrade more resillient + add fail test * Remove deployCommitHash The deployCommitHash process is error-prone and it's alternatives require changing many parts of the code. And as it is not used anywhere. I believe it is not worth the effort. * Improve price not found log
This commit is contained in:
parent
62ef9d7d1f
commit
c47199d6cb
|
@ -14,11 +14,11 @@ import "./PythInternalStructs.sol";
|
||||||
abstract contract Pyth is PythGetters, PythSetters, AbstractPyth {
|
abstract contract Pyth is PythGetters, PythSetters, AbstractPyth {
|
||||||
using BytesLib for bytes;
|
using BytesLib for bytes;
|
||||||
|
|
||||||
function initialize(
|
function _initialize(
|
||||||
address wormhole,
|
address wormhole,
|
||||||
uint16 pyth2WormholeChainId,
|
uint16 pyth2WormholeChainId,
|
||||||
bytes32 pyth2WormholeEmitter
|
bytes32 pyth2WormholeEmitter
|
||||||
) virtual public {
|
) internal {
|
||||||
setWormhole(wormhole);
|
setWormhole(wormhole);
|
||||||
setPyth2WormholeChainId(pyth2WormholeChainId);
|
setPyth2WormholeChainId(pyth2WormholeChainId);
|
||||||
setPyth2WormholeEmitter(pyth2WormholeEmitter);
|
setPyth2WormholeEmitter(pyth2WormholeEmitter);
|
||||||
|
@ -222,7 +222,7 @@ abstract contract Pyth is PythGetters, PythSetters, AbstractPyth {
|
||||||
function queryPriceFeed(bytes32 id) public view override returns (PythStructs.PriceFeed memory priceFeed){
|
function queryPriceFeed(bytes32 id) public view override returns (PythStructs.PriceFeed memory priceFeed){
|
||||||
// Look up the latest price info for the given ID
|
// Look up the latest price info for the given ID
|
||||||
PythInternalStructs.PriceInfo memory info = latestPriceInfo(id);
|
PythInternalStructs.PriceInfo memory info = latestPriceInfo(id);
|
||||||
require(info.priceFeed.id != 0, "no price feed found for the given price id");
|
require(info.priceFeed.id != 0, "price feed for the given id is not pushed or does not exist");
|
||||||
|
|
||||||
return info.priceFeed;
|
return info.priceFeed;
|
||||||
}
|
}
|
||||||
|
@ -236,17 +236,7 @@ abstract contract Pyth is PythGetters, PythSetters, AbstractPyth {
|
||||||
return validTimePeriodSeconds();
|
return validTimePeriodSeconds();
|
||||||
}
|
}
|
||||||
|
|
||||||
function isPyth() internal pure returns (bool) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function version() public pure returns (string memory) {
|
function version() public pure returns (string memory) {
|
||||||
return "1.1.0";
|
return "1.1.0";
|
||||||
}
|
}
|
||||||
|
|
||||||
function deployCommitHash() public pure returns (string memory) {
|
|
||||||
// This is a place holder for the commit hash and will be replaced
|
|
||||||
// with the commit hash upon deployment.
|
|
||||||
return "__DEPLOY_COMMIT_HASH_PLACEHOLER__";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,11 +18,11 @@ contract PythUpgradable is Initializable, OwnableUpgradeable, UUPSUpgradeable, P
|
||||||
address wormhole,
|
address wormhole,
|
||||||
uint16 pyth2WormholeChainId,
|
uint16 pyth2WormholeChainId,
|
||||||
bytes32 pyth2WormholeEmitter
|
bytes32 pyth2WormholeEmitter
|
||||||
) initializer override public {
|
) initializer public {
|
||||||
__Ownable_init();
|
__Ownable_init();
|
||||||
__UUPSUpgradeable_init();
|
__UUPSUpgradeable_init();
|
||||||
|
|
||||||
Pyth.initialize(wormhole, pyth2WormholeChainId, pyth2WormholeEmitter);
|
Pyth._initialize(wormhole, pyth2WormholeChainId, pyth2WormholeEmitter);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Privileged function to specify additional data sources in the contract
|
/// Privileged function to specify additional data sources in the contract
|
||||||
|
@ -81,11 +81,18 @@ contract PythUpgradable is Initializable, OwnableUpgradeable, UUPSUpgradeable, P
|
||||||
// Only allow the owner to upgrade the proxy to a new implementation.
|
// Only allow the owner to upgrade the proxy to a new implementation.
|
||||||
function _authorizeUpgrade(address) internal override onlyOwner {}
|
function _authorizeUpgrade(address) internal override onlyOwner {}
|
||||||
|
|
||||||
|
function pythUpgradableMagic() public pure returns (uint32) {
|
||||||
|
return 0x97a6f304;
|
||||||
|
}
|
||||||
|
|
||||||
// Execute a UpgradeContract governance message
|
// Execute a UpgradeContract governance message
|
||||||
function upgradeUpgradableContract(UpgradeContractPayload memory payload) override internal {
|
function upgradeUpgradableContract(UpgradeContractPayload memory payload) override internal {
|
||||||
address oldImplementation = _getImplementation();
|
address oldImplementation = _getImplementation();
|
||||||
_upgradeToAndCallUUPS(payload.newImplementation, new bytes(0), false);
|
_upgradeToAndCallUUPS(payload.newImplementation, new bytes(0), false);
|
||||||
require(isPyth(), "the new implementation is not a Pyth contract");
|
|
||||||
|
// Calling a method using `this.<method>` will cause a contract call that will use
|
||||||
|
// the new contract.
|
||||||
|
require(this.pythUpgradableMagic() == 0x97a6f304, "the new implementation is not a Pyth contract");
|
||||||
|
|
||||||
emit ContractUpgraded(oldImplementation, _getImplementation());
|
emit ContractUpgraded(oldImplementation, _getImplementation());
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
// contracts/Implementation.sol
|
||||||
|
// SPDX-License-Identifier: Apache 2
|
||||||
|
|
||||||
|
pragma solidity ^0.8.0;
|
||||||
|
|
||||||
|
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
|
||||||
|
|
||||||
|
contract MockUpgradeableProxy is UUPSUpgradeable {
|
||||||
|
function isUpgradeActive() external pure returns (bool) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _authorizeUpgrade(address) internal override {}
|
||||||
|
}
|
|
@ -13,10 +13,6 @@ set -euo pipefail
|
||||||
|
|
||||||
echo "=========== Compiling ==========="
|
echo "=========== Compiling ==========="
|
||||||
|
|
||||||
echo "Replacing the deploy commit hash..."
|
|
||||||
# Set the deploy commit hash in the contract (used for debugging purposes)
|
|
||||||
sed -i "s/__DEPLOY_COMMIT_HASH_PLACEHOLER__/$(git rev-parse HEAD)/g" ./contracts/pyth/Pyth.sol
|
|
||||||
|
|
||||||
echo "Building the contract..."
|
echo "Building the contract..."
|
||||||
# Ensure that we deploy a fresh build with up-to-date dependencies.
|
# Ensure that we deploy a fresh build with up-to-date dependencies.
|
||||||
rm -rf build && npx truffle compile --all
|
rm -rf build && npx truffle compile --all
|
||||||
|
@ -41,6 +37,4 @@ while [[ $# -ne 0 ]]; do
|
||||||
echo "Deployment to $NETWORK finished successfully"
|
echo "Deployment to $NETWORK finished successfully"
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "=========== Cleaning up ==========="
|
echo "=========== Finished ==========="
|
||||||
echo "Reverting back the deploy commit hash..."
|
|
||||||
sed -i "s/$(git rev-parse HEAD)/__DEPLOY_COMMIT_HASH_PLACEHOLER__/g" ./contracts/pyth/Pyth.sol
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ const Wormhole = artifacts.require("Wormhole");
|
||||||
|
|
||||||
const PythUpgradable = artifacts.require("PythUpgradable");
|
const PythUpgradable = artifacts.require("PythUpgradable");
|
||||||
const MockPythUpgrade = artifacts.require("MockPythUpgrade");
|
const MockPythUpgrade = artifacts.require("MockPythUpgrade");
|
||||||
|
const MockUpgradeableProxy = artifacts.require("MockUpgradeableProxy");
|
||||||
|
|
||||||
const testSigner1PK =
|
const testSigner1PK =
|
||||||
"cfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0";
|
"cfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0";
|
||||||
|
@ -594,7 +595,7 @@ contract("Pyth", function () {
|
||||||
this.pythProxy.queryPriceFeed(
|
this.pythProxy.queryPriceFeed(
|
||||||
"0xdeadfeeddeadfeeddeadfeeddeadfeeddeadfeeddeadfeeddeadfeeddeadfeed"
|
"0xdeadfeeddeadfeeddeadfeeddeadfeeddeadfeeddeadfeeddeadfeeddeadfeed"
|
||||||
),
|
),
|
||||||
"no price feed found for the given price id"
|
"price feed for the given id is not pushed or does not exist"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -982,6 +983,27 @@ contract("Pyth", function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("Upgrading the contract to a non-pyth contract won't work", async function () {
|
||||||
|
const newImplementation = await MockUpgradeableProxy.new();
|
||||||
|
|
||||||
|
const data = new governance.EthereumUpgradeContractInstruction(
|
||||||
|
governance.CHAINS.ethereum,
|
||||||
|
new governance.HexString20Bytes(newImplementation.address),
|
||||||
|
).serialize();
|
||||||
|
|
||||||
|
const vaa = await createVAAFromUint8Array(data,
|
||||||
|
testGovernanceChainId,
|
||||||
|
testGovernanceEmitter,
|
||||||
|
1
|
||||||
|
);
|
||||||
|
|
||||||
|
// Calling a non-existing method will cause a revert with no explanation.
|
||||||
|
await expectRevert(
|
||||||
|
this.pythProxy.executeGovernanceInstruction(vaa),
|
||||||
|
"revert"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it("Setting governance data source should work", async function () {
|
it("Setting governance data source should work", async function () {
|
||||||
const data = new governance.SetGovernanceDataSourceInstruction(
|
const data = new governance.SetGovernanceDataSourceInstruction(
|
||||||
governance.CHAINS.ethereum,
|
governance.CHAINS.ethereum,
|
||||||
|
|
Loading…
Reference in New Issue