Skip to content

Commit

Permalink
feat: WitnetPriceFeesBypassV20
Browse files Browse the repository at this point in the history
  • Loading branch information
guidiaz committed Mar 20, 2024
1 parent 56728c0 commit 639b9e6
Show file tree
Hide file tree
Showing 5 changed files with 362 additions and 1 deletion.
334 changes: 334 additions & 0 deletions contracts/impls/apps/WitnetPriceFeedsBypassV20.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,334 @@
// SPDX-License-Identifier: MIT

pragma solidity >=0.8.0 <0.9.0;

import "../../impls/WitnetUpgradableBase.sol";
import "../../interfaces/V2/IWitnetPriceFeeds.sol";

abstract contract WitnetPriceFeedsV07 {
function supportedFeeds() virtual external view returns (bytes4[] memory, string[] memory, bytes32[] memory);
}

abstract contract WitnetPriceFeedsV20 {
function isUpgradableFrom(address) virtual external view returns (bool);
function latestUpdateResponseStatus(bytes4) virtual external view returns (WitnetV2.ResponseStatus);
function owner() virtual external view returns (address);
function requestUpdate(bytes4) virtual external payable returns (uint256);
function specs() virtual external view returns (bytes4);
}

/// @title Witnet Price Feeds surrogate bypass implementation to V2.0
/// @author The Witnet Foundation
contract WitnetPriceFeedsBypassV20
is
WitnetUpgradableBase
{
using Witnet for bytes4;
WitnetPriceFeedsV20 immutable public surrogate;

constructor (
WitnetPriceFeedsV20 _surrogate,
bool _upgradable,
bytes32 _versionTag
)
WitnetUpgradableBase(
_upgradable,
_versionTag,
"io.witnet.proxiable.router"
)
{
_require(
address(_surrogate).code.length > 0
&& _surrogate.specs() == type(IWitnetPriceFeeds).interfaceId,
"uncompliant surrogate"
);
surrogate = _surrogate;
}

// solhint-disable-next-line payable-fallback
fallback() virtual override external { /* solhint-disable no-complex-fallback */
address _surrogate = address(surrogate);
assembly { /* solhint-disable avoid-low-level-calls */
// Gas optimized surrogate call to the 'surrogate' immutable contract.
// Note: `msg.data`, `msg.sender` and `msg.value` will be passed over
// to actual implementation of `msg.sig` within `implementation` contract.
let ptr := mload(0x40)
calldatacopy(ptr, 0, calldatasize())
let result := call(gas(), _surrogate, 0, ptr, calldatasize(), 0, 0)
let size := returndatasize()
returndatacopy(ptr, 0, size)
switch result
case 0 {
// pass back revert message:
revert(ptr, size)
}
default {
// pass back same data as returned by 'implementation' contract:
return(ptr, size)
}
}
}

function class() public pure returns (string memory) {
return type(WitnetPriceFeedsBypassV20).name;
}


// ================================================================================================================
// --- Overrides 'Upgradeable' -------------------------------------------------------------------------------------

function owner() public view override returns (address) {
return surrogate.owner();
}


// ================================================================================================================
// --- Overrides 'Upgradeable' ------------------------------------------------------------------------------------

/// @notice Re-initialize contract's storage context upon a new upgrade from a proxy.
/// @dev Must fail when trying to upgrade to same logic contract more than once.
function initialize(bytes memory)
public override
onlyDelegateCalls // => we don't want the logic base contract to be ever initialized
{
if (
__proxiable().proxy == address(0)
&& __proxiable().implementation == address(0)
) {
// a proxy is being initialized for the first time...
__proxiable().proxy = address(this);
_transferOwnership(msg.sender);
} else {
// only the owner can initialize:
if (msg.sender != owner()) {
_revert("not the owner");
}
}

// Check that new implentation hasn't actually been initialized already:
_require(
__proxiable().implementation != base(),
"already initialized"
);

(bytes4[] memory _id4s,,) = WitnetPriceFeedsV07(address(this)).supportedFeeds();
for (uint _ix = 0; _ix < _id4s.length; _ix ++) {
// Check that all supported price feeds under current implementation
// are actually supported under the surrogate immutable instance,
// and that none of them is currently in pending status:
WitnetV2.ResponseStatus _status = surrogate.latestUpdateResponseStatus(_id4s[_ix]);
_require(
_status == WitnetV2.ResponseStatus.Ready
|| _status == WitnetV2.ResponseStatus.Error
|| _status == WitnetV2.ResponseStatus.Delivered,
string(abi.encodePacked(
"unconsolidated feed: 0x",
_id4s[_ix].toHexString()
))
);
}

// Set new implementation as initialized:
__proxiable().implementation = base();

// Emit event:
emit Upgraded(msg.sender, base(), codehash(), version());
}

function isUpgradableFrom(address _from) external view override returns (bool) {
return surrogate.isUpgradableFrom(_from);
}


// ================================================================================================================
// --- Partial interception of 'IWitnetFeeds' ---------------------------------------------------------------------

function estimateUpdateBaseFee(bytes4, uint256 _gasPrice, uint256) public view returns (uint256) {
return abi.decode(
_staticcall(abi.encodeWithSignature(
"estimateUpdateBaseFee(uint256)",
_gasPrice
)),
(uint256)
);
}

function estimateUpdateBaseFee(bytes4, uint256 _gasPrice, uint256, bytes32) external view returns (uint256) {
return abi.decode(
_staticcall(abi.encodeWithSignature(
"estimateUpdateBaseFee(uint256)",
_gasPrice
)),
(uint256)
);
}

function latestResponse(bytes4 _feedId) public view returns (Witnet.Response memory) {
WitnetV2.Response memory _responseV2 = abi.decode(
_staticcall(abi.encodeWithSignature(
"lastValidResponse(bytes4)",
_feedId
)),
(WitnetV2.Response)
);
return Witnet.Response({
reporter: _responseV2.reporter,
timestamp: uint256(_responseV2.resultTimestamp),
drTxHash: _responseV2.resultTallyHash,
cborBytes: _responseV2.resultCborBytes
});
}

function latestResult(bytes4 _feedId) public view returns (Witnet.Result memory) {
return Witnet.resultFromCborBytes(
latestResponse(_feedId).cborBytes
);
}

function latestUpdateRequest(bytes4 _feedId) external view returns (Witnet.Request memory) {
WitnetV2.Request memory _requestV2 = abi.decode(
_staticcall(abi.encodeWithSignature(
"latestUpdateRequest(bytes4)",
_feedId
)),
(WitnetV2.Request)
);
return Witnet.Request({
addr: address(0),
slaHash: abi.decode(abi.encode(_requestV2.witnetSLA), (bytes32)),
radHash: _requestV2.witnetRAD,
gasprice: tx.gasprice,
reward: uint256(_requestV2.evmReward)
});
}

function latestUpdateResponse(bytes4 _feedId) external view returns (Witnet.Response memory) {
WitnetV2.Response memory _responseV2 = abi.decode(
_staticcall(abi.encodeWithSignature(
"latestUpdateResponse(bytes4)",
_feedId
)),
(WitnetV2.Response)
);
return Witnet.Response({
reporter: _responseV2.reporter,
timestamp: uint256(_responseV2.resultTimestamp),
drTxHash: bytes32(_responseV2.resultTallyHash),
cborBytes: _responseV2.resultCborBytes
});
}

function latestUpdateResultStatus(bytes4 _feedId) external view returns (Witnet.ResultStatus) {
WitnetV2.ResponseStatus _status = abi.decode(
_staticcall(abi.encodeWithSignature(
"latestUpdateResponseStatus(bytes4)",
_feedId
)),
(WitnetV2.ResponseStatus)
);
if (_status == WitnetV2.ResponseStatus.Finalizing) {
return Witnet.ResultStatus.Awaiting;
} else {
return Witnet.ResultStatus(uint8(_status));
}
}

function lookupBytecode(bytes4 _feedId) external view returns (bytes memory) {
return abi.decode(
_staticcall(abi.encodeWithSignature(
"lookupWitnetBytecode(bytes4)",
_feedId
)),
((bytes))
);
}

function lookupRadHash(bytes4 _feedId) external view returns (bytes32) {
return abi.decode(
_staticcall(abi.encodeWithSignature(
"lookupWitnetRadHash(bytes4)",
_feedId
)),
(bytes32)
);
}

function requestUpdate(bytes4 _feedId) external payable returns (uint256 _usedFunds) {
_usedFunds = surrogate.requestUpdate{
value: msg.value
}(
_feedId
);
if (_usedFunds < msg.value) {
// transfer back unused funds:
payable(msg.sender).transfer(msg.value - _usedFunds);
}
}

function requestUpdate(bytes4, bytes32) external payable returns (uint256) {
_revert("deprecated");
}


// ================================================================================================================
// --- Partial interception of 'IWitnetFeedsAdmin' ----------------------------------------------------------------

function settleDefaultRadonSLA(Witnet.RadonSLA calldata _slaV1) external {
_require(
Witnet.isValid(_slaV1),
"invalid SLA"
);
__call(abi.encodeWithSignature(
"settleDefaultRadonSLA((uint8,uint64))",
WitnetV2.RadonSLA({
committeeSize: _slaV1.numWitnesses,
witnessingFeeNanoWit: _slaV1.witnessCollateral / _slaV1.numWitnesses
})
));
}


// ================================================================================================================
// --- Internal methods -------------------------------------------------------------------------------------------

function _require(bool _condition, string memory _message) internal pure {
if (!_condition) {
_revert(_message);
}
}

function _revert(string memory _reason) internal pure {
revert(
string(abi.encodePacked(
class(),
": ",
_reason
))
);
}

function _staticcall(bytes memory _encodedCall) internal view returns (bytes memory _returnData) {
bool _success;
(_success, _returnData) = address(surrogate).staticcall(_encodedCall);
_require(
_success,
string(abi.encodePacked(
"cannot surrogate static call: 0x",
msg.sig.toHexString()
))
);
}

function __call(bytes memory _encodedCall) internal returns (bytes memory _returnData) {
bool _success;
(_success, _returnData) = address(surrogate).call(_encodedCall);
_require(
_success,
string(abi.encodePacked(
"cannot surrogate call: 0x",
msg.sig.toHexString()
))
);
}

}
1 change: 1 addition & 0 deletions contracts/interfaces/V2/IWitnetPriceFeeds.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pragma solidity >=0.8.0 <0.9.0;
import "./IWitnetPriceSolver.sol";

interface IWitnetPriceFeeds {

/// ======================================================================================================
/// --- IFeeds extension ---------------------------------------------------------------------------------

Expand Down
24 changes: 24 additions & 0 deletions contracts/libs/Witnet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,17 @@ library Witnet {
&& a.minerCommitRevealFee >= b.minerCommitRevealFee
);
}

function isValid(RadonSLA calldata sla) internal pure returns (bool) {
return (
sla.numWitnesses > 0
&& sla.numWitnesses <= 127
&& sla.minConsensusPercentage >= 51
&& sla.witnessReward > 0
&& sla.witnessCollateral > 20 * 10 ** 9
&& sla.witnessCollateral / sla.witnessReward <= 125
);
}


/// ===============================================================================================================
Expand Down Expand Up @@ -501,6 +512,19 @@ library Witnet {
}


/// ===============================================================================================================
/// --- 'bytes4' helper methods -----------------------------------------------------------------------------------

function toHexString(bytes4 word) internal pure returns (string memory) {
return string(abi.encodePacked(
toHexString(uint8(bytes1(word))),
toHexString(uint8(bytes1(word << 8))),
toHexString(uint8(bytes1(word << 16))),
toHexString(uint8(bytes1(word << 24)))
));
}


/// ===============================================================================================================
/// --- 'string' helper methods -----------------------------------------------------------------------------------

Expand Down
3 changes: 2 additions & 1 deletion migrations/witnet.addresses.json
Original file line number Diff line number Diff line change
Expand Up @@ -780,7 +780,8 @@
"WitnetRequestBoardImplementation": "0x307b1a85dC9BE441Cd7e4931266Dd6a24C4A98d2",
"WitnetRequestBoardTrustableOvm2": "0xDb248F9D510aF57A6f7d009712e45f6cDDEA776d",
"WitnetRequestFactoryImplementation": "0xc228d93908bd3E9E9015f83f125d1514803eDC06",
"WitnetOracleV20": "0x77703aE126B971c9946d562F41Dd47071dA00777"
"WitnetOracleV20": "0x77703aE126B971c9946d562F41Dd47071dA00777",
"WitnetPriceFeedsV20": "0x1111AbA2164AcdC6D291b08DfB374280035E1111"
},
"optimism.mainnet": {
"WitnetLib": "0x1D9c4a8f8B7b5F9B8e2641D81927f8F8Cc7fF079",
Expand Down
1 change: 1 addition & 0 deletions migrations/witnet.settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ module.exports = {
},
optimism: {
WitnetRequestBoard: "WitnetRequestBoardBypassV20:WitnetRequestBoardTrustableOvm2",
WitnetPriceFeeds: "WitnetPriceFeedsBypassV20:",
},
"polygon.zkevm.goerli": {
WitnetBytecodes: "WitnetBytecodesNoSha256",
Expand Down

0 comments on commit 639b9e6

Please sign in to comment.