Skip to content

Commit

Permalink
payble oracle
Browse files Browse the repository at this point in the history
  • Loading branch information
killroy192 committed Dec 4, 2023
1 parent 7c09b39 commit 7b26bd4
Show file tree
Hide file tree
Showing 18 changed files with 404 additions and 213 deletions.
51 changes: 50 additions & 1 deletion deployment-lock.json

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion scripts/deployment/example/arbitrum-goerli/config.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
const externals = require("../../../config.externals")["arbitrum-goerli"];

module.exports = [
{
contract: "AutomationEmitter",
},
{
contract: "RequestLib",
},
{
contract: "AutomationEmitter",
contract: "FeeManagerLib",
},
{
contract: "FakedOracle",
Expand All @@ -24,6 +27,7 @@ module.exports = [
deployerOptions: {
libs: {
RequestLib: (deploymentLock) => deploymentLock.RequestLib.addr,
FeeManagerLib: (deploymentLock) => deploymentLock.FeeManagerLib.addr,
},
},
},
Expand Down
8 changes: 6 additions & 2 deletions scripts/deployment/example/arbitrum-sepolia/config.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
const externals = require("../../../config.externals")["arbitrum-sepolia"];

module.exports = [
{
contract: "AutomationEmitter",
},
{
contract: "RequestLib",
},
{
contract: "AutomationEmitter",
contract: "FeeManagerLib",
},
{
contract: "FakedOracle",
Expand All @@ -19,11 +22,12 @@ module.exports = [
// eth/usd data feed
externals.datafeed,
// timeout
60,
5,
],
deployerOptions: {
libs: {
RequestLib: (deploymentLock) => deploymentLock.RequestLib.addr,
FeeManagerLib: (deploymentLock) => deploymentLock.FeeManagerLib.addr,
},
},
},
Expand Down
6 changes: 5 additions & 1 deletion scripts/deployment/example/hardhat/config.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
const externals = require("../../../config.externals").hardhat;

module.exports = [
{
contract: "AutomationEmitter",
},
{
contract: "RequestLib",
},
{
contract: "AutomationEmitter",
contract: "FeeManagerLib",
},
{
contract: "FakedOracle",
Expand All @@ -24,6 +27,7 @@ module.exports = [
deployerOptions: {
libs: {
RequestLib: (deploymentLock) => deploymentLock.RequestLib.addr,
FeeManagerLib: (deploymentLock) => deploymentLock.FeeManagerLib.addr,
},
},
},
Expand Down
16 changes: 14 additions & 2 deletions scripts/e2e/example/fund.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,23 @@ const { getDeploymentLockData } = require("../../common");
async function main() {
const lock = (await getDeploymentLockData())[hre.network.name];

console.log("fund swapApp with minted usdc")

const usdc = await hre.ethers.getContractAt("FUSDC", lock.FUSDC.addr);

usdc.mint(lock.SwapApp.addr, ethers.parseEther("0.0001"));
await usdc.mint(lock.SwapApp.addr, ethers.parseEther("0.0001"));

console.log("done\n");

console.log("fund oracle treasure with eth");

const amountIn = ethers.parseEther("0.001");

const oracle = await hre.ethers.getContractAt("FakedOracleProxy", lock.FakedOracleProxy.addr);

await oracle.handlePayment({ value: ethers.parseEther("100") });

console.log("Successfully fund swapApp with USDC");
console.log("done\n");
}

// We recommend this pattern to be able to use async/await everywhere
Expand Down
7 changes: 6 additions & 1 deletion scripts/e2e/example/trade.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,12 @@ async function main() {
console.log("done\n");
console.log("execute trade");
await consumer.trade(tradeArgs, nonce);
console.log("Successfully traded fWETH tokens for fUSDC");

const usdc = await hre.ethers.getContractAt("FUSDC", lock.FUSDC.addr);

const balance = await usdc.balanceOf(signerAddr);

console.log("Successfully traded fWETH tokens for fUSDC", balance);

console.log("run fallback check logic..");
const oracle = await hre.ethers.getContractAt(
Expand Down
134 changes: 86 additions & 48 deletions src/Oracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,70 +2,87 @@
pragma solidity ^0.8.16;

import {Log} from "@chainlink/contracts/src/v0.8/automation/interfaces/ILogAutomation.sol";
import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";

import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {IAsset} from "./interfaces/IAsset.sol";
import {IVerifierProxy} from "./interfaces/IVerifierProxy.sol";
import {IFeeManager} from "./interfaces/IFeeManager.sol";
import {IOracle} from "./interfaces/IOracle.sol";
import {IOracleConsumerContract, ForwardData, FeedType} from "./interfaces/IOracleCallBackContract.sol";
import {IRequestsManager} from "./interfaces/IRequestsManager.sol";
import {IAutomationEmitter} from "./interfaces/IAutomationEmitter.sol";

import {RequestLib} from "./libs/RequestLib.sol";
import {FeeManagerLib} from "./libs/FeeManagerLib.sol";

import {DataStreamConsumer} from "./DataStreamConsumer.sol";
import {PriceFeedConsumer} from "./PriceFeedConsumer.sol";
import {RequestsManager} from "./RequestsManager.sol";

contract Oracle is IOracle, DataStreamConsumer, PriceFeedConsumer {
error DuplicatedRequestCreation(bytes32 id);
contract Oracle is IOracle, DataStreamConsumer, ReentrancyGuard {
error InvalidRequestsExecution(bytes32 id);
error FailedRequestsConsumption(bytes32 id);
error FeeIsTooSmall();

IAutomationEmitter public immutable emitter;
IVerifierProxy public immutable verifier;
RequestsManager public immutable requestManager;
AggregatorV3Interface public immutable priceFeed;
// blocks from request initialization
uint256 public immutable requestTimeout;

// fee state
using FeeManagerLib for FeeManagerLib.FeeState;
FeeManagerLib.FeeState private _feeState;

using RequestLib for RequestLib.Requests;
RequestLib.Requests private _requests;

// Find a complete list of IDs and verifiers at https://docs.chain.link/data-streams/stream-ids
constructor(
address _emmiter,
address _verifier,
string memory _dataStreamfeedId,
address _priceFeedId,
address _priceFeed,
uint256 _requestTimeout
) DataStreamConsumer(_dataStreamfeedId) PriceFeedConsumer(_priceFeedId) {
) DataStreamConsumer(_dataStreamfeedId) {
emitter = IAutomationEmitter(_emmiter);
verifier = IVerifierProxy(_verifier);
requestManager = new RequestsManager();
priceFeed = AggregatorV3Interface(_priceFeed);
requestTimeout = _requestTimeout;
}

function handlePayment() public payable returns (bool) {
uint256 fee = FeeManagerLib.toDec(msg.value);
if (fee < _feeState.processingFee) {
revert FeeIsTooSmall();
}
_feeState.deposit(fee);
return true;
}

function processingFee() external view returns (uint256) {
return _feeState.processingFee;
}

function processingFeeDecimals() external pure returns (uint256) {
return FeeManagerLib.decimals();
}

function _addRequest(
address callbackContract,
bytes memory callbackArgs,
uint256 nonce,
address sender
) internal {
(
bytes32 id,
IRequestsManager.RequestStats memory reqStats
) = getRequestProps(callbackContract, callbackArgs, nonce, sender);
// prevent duplicated request execution
if (reqStats.status == IRequestsManager.RequestStatus.Pending) {
revert DuplicatedRequestCreation(id);
}
requestManager.addRequest(id);
handlePayment();
_requests.addRequest(callbackContract, callbackArgs, nonce, sender);
}

function addRequest(
address callbackContract,
bytes memory callbackArgs,
uint256 nonce,
address sender
) external returns (bool) {
) external payable returns (bool) {
_addRequest(callbackContract, callbackArgs, nonce, sender);
return
emitter.emitAutomationEvent(
Expand Down Expand Up @@ -93,13 +110,15 @@ contract Oracle is IOracle, DataStreamConsumer, PriceFeedConsumer {
address sender
) = abi.decode(extraData, (address, bytes, uint256, address));

(
bytes32 id,
IRequestsManager.RequestStats memory reqStats
) = getRequestProps(callbackContract, callbackArgs, nonce, sender);
(bytes32 id, RequestLib.RequestStats memory reqStats) = getRequestProps(
callbackContract,
callbackArgs,
nonce,
sender
);

// prevent duplicated request execution
if (reqStats.status != IRequestsManager.RequestStatus.Pending) {
if (reqStats.status != RequestLib.RequestStatus.Pending) {
revert InvalidRequestsExecution(id);
}

Expand All @@ -109,25 +128,23 @@ contract Oracle is IOracle, DataStreamConsumer, PriceFeedConsumer {
// Report verification fees
IFeeManager feeManager = IFeeManager(address(verifier.s_feeManager()));

address feeTokenAddress = feeManager.i_linkAddress();
address feeNativeTokenAddress = feeManager.i_nativeAddress();
(IAsset memory fee, , ) = feeManager.getFeeAndReward(
address(this),
reportData,
feeTokenAddress
feeNativeTokenAddress
);

// Approve rewardManager to spend this contract's balance in fees
IERC20(feeTokenAddress).approve(
address(feeManager.i_rewardManager()),
fee.amount
);
_feeState.updateFee(fee.amount);

// Verify the report
bytes memory verifiedReportData = verifier.verify(
unverifiedReport,
abi.encode(feeTokenAddress)
abi.encode(feeNativeTokenAddress)
);

_feeState.spend(fee.amount);

// Decode verified report data into BasicReport struct
BasicReport memory report = abi.decode(
verifiedReportData,
Expand All @@ -146,16 +163,16 @@ contract Oracle is IOracle, DataStreamConsumer, PriceFeedConsumer {
revert FailedRequestsConsumption(id);
}

requestManager.fulfillRequest(id);
_requests.fulfillRequest(id);
}

function fallbackCall(
address callbackContract,
bytes memory callbackArgs,
uint256 nonce,
address sender
) external returns (bool) {
(bytes32 id, bool executable) = previewFallbackCall(
) external nonReentrant returns (bool) {
(bytes32 id, bool executable, uint256 reward) = previewFallbackCall(
callbackContract,
callbackArgs,
nonce,
Expand All @@ -166,7 +183,7 @@ contract Oracle is IOracle, DataStreamConsumer, PriceFeedConsumer {
revert InvalidRequestsExecution(id);
}

int256 price = getLatestPrice();
(, int256 price, , , ) = priceFeed.latestRoundData();

bool success = IOracleConsumerContract(callbackContract).consume(
ForwardData({
Expand All @@ -180,25 +197,36 @@ contract Oracle is IOracle, DataStreamConsumer, PriceFeedConsumer {
revert FailedRequestsConsumption(id);
}

return requestManager.fulfillRequest(id);
(bool rewardingSuccess, ) = payable(msg.sender).call{value: reward}("");

if (!rewardingSuccess) {
revert FailedRequestsConsumption(id);
}

_requests.fulfillRequest(id);

_feeState.spendReward();

return true;
}

function previewFallbackCall(
address callbackContract,
bytes memory callbackArgs,
uint256 nonce,
address sender
) public view returns (bytes32, bool) {
(
bytes32 id,
IRequestsManager.RequestStats memory reqStats
) = getRequestProps(callbackContract, callbackArgs, nonce, sender);
) public view returns (bytes32, bool, uint256) {
(bytes32 id, RequestLib.RequestStats memory reqStats) = getRequestProps(
callbackContract,
callbackArgs,
nonce,
sender
);

bool executable = reqStats.status ==
IRequestsManager.RequestStatus.Pending &&
reqStats.blockNumber + requestTimeout > block.number;
bool executable = reqStats.status == RequestLib.RequestStatus.Pending &&
reqStats.blockNumber + requestTimeout <= block.number;

return (id, executable);
return (id, executable, _feeState.treasure);
}

// Utils
Expand All @@ -208,14 +236,24 @@ contract Oracle is IOracle, DataStreamConsumer, PriceFeedConsumer {
bytes memory callbackArgs,
uint256 nonce,
address sender
) public view returns (bytes32, RequestsManager.RequestStats memory) {
) public view returns (bytes32, RequestLib.RequestStats memory) {
bytes32 id = RequestLib.generateId(
callbackContract,
callbackArgs,
nonce,
sender
);

return (id, requestManager.getRequest(id));
return (id, _requests.getRequest(id));
}

// fallbacks

fallback() external payable {
handlePayment();
}

receive() external payable {
handlePayment();
}
}
Loading

0 comments on commit 7b26bd4

Please sign in to comment.