Skip to content
Draft
Show file tree
Hide file tree
Changes from 6 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
Expand Up @@ -4,3 +4,9 @@
[submodule "lib/create3-deploy"]
path = lib/create3-deploy
url = https://github.com/darwinia-network/create3-deploy
[submodule "lib/xapi-contracts"]
path = lib/xapi-contracts
url = https://github.com/ringecosystem/xapi-contracts
[submodule "lib/chainlink"]
path = lib/chainlink
url = https://github.com/smartcontractkit/chainlink
1 change: 1 addition & 0 deletions lib/chainlink
Submodule chainlink added at fb7d6e
1 change: 1 addition & 0 deletions lib/xapi-contracts
Submodule xapi-contracts added at 92466b
3 changes: 3 additions & 0 deletions remappings.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
forge-std/=lib/forge-std/src/
ds-test/=lib/forge-std/lib/ds-test/src/
create3-deploy/=lib/create3-deploy/
xapi/=lib/xapi-contracts/xapi/
xapi-consumer/=lib/xapi-contracts/xapi-consumer/contracts/
@chainlink/=lib/chainlink/
153 changes: 153 additions & 0 deletions src/eco/XAPIOracle.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "../Verifier.sol";
import "../interfaces/IORMP.sol";
import "xapi-consumer/interfaces/IXAPIConsumer.sol";
import "xapi/contracts/lib/XAPIBuilder.sol";

contract XAPIOracle is Verifier, IXAPIConsumer {
using XAPIBuilder for XAPIBuilder.Request;

event SetFee(uint256 indexed chainId, uint256 fee);
event SetApproved(address operator, bool approve);
event Withdrawal(address indexed to, uint256 amt);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
event XAPIRequestMade(uint256 indexed requestId, XAPIBuilder.Request requestData);
event XAPIConsumeResult(uint256 indexed requestId, bytes responseData, uint16 errorCode);
event XAPIConsumeError(uint256 indexed requestId, uint16 errorCode);

address public immutable PROTOCOL;
address public immutable XAPI;
address public immutable EXAGGREGATOR;

address public owner;
// chainId => price
mapping(uint256 => uint256) public feeOf;
// operator => isApproved
mapping(address => bool) public approvedOf;

struct DataSource {
string name;
string url;
string method;
string resultPath;
}

uint256 requestId;

modifier onlyOwner() {
require(msg.sender == owner, "!owner");
_;
}

modifier onlyXAPI() {
require(msg.sender == XAPI, "!xapi");
_;
}

modifier onlyApproved() {
require(isApproved(msg.sender), "!approve");
_;
}

constructor(address dao, address ormp, address xapi, address exagg) {
PROTOCOL = ormp;
owner = dao;
XAPI = xapi;
EXAGGREGATOR = exagg;
}

receive() external payable {}

function version() public pure returns (string memory) {
return "2.1.0";
}

function _buildRequest(uint256 chainId, address channel, uint256 msgIndex)
internal
view
returns (XAPIBuilder.Request memory)
{
XAPIBuilder.Request memory requestData;
requestData._initialize(EXAGGREGATOR, this.xapiCallback.selector);
requestData._addParamUint("_dataSources", chainId);
requestData._startNestedParam("*");
{
requestData._startNestedParam("variables");
{
requestData._addParamUint("chainId", chainId);
requestData._addParamBytes("channel", abi.encodePacked(channel));
requestData._addParamUint("msgIndex", msgIndex);
}
requestData._endNestedParam();
}
requestData._endNestedParam();
return requestData;
}

/// @dev Only could be called by approved address.
/// @param chainId The request source chain id.
/// @param channel The request message channel.
/// @param msgIndex The request message index.
function makeRequestForMessageHash(uint256 chainId, address channel, uint256 msgIndex)
external
payable
onlyApproved
{
XAPIBuilder.Request memory requestData = _buildRequest(chainId, channel, msgIndex);
uint256 fee_ = IXAPI(XAPI).fee(EXAGGREGATOR);
require(msg.value >= fee_, "!fee");
Comment thread
hujw77 marked this conversation as resolved.
Outdated
requestId = IXAPI(XAPI).makeRequest{value: fee_}(requestData);
emit XAPIRequestMade(requestId, requestData);
payable(msg.sender).transfer(msg.value - fee_);
}

function xapiCallback(uint256 requestId_, ResponseData memory response) external onlyXAPI {
require(requestId_ == requestId, "requestId");
Comment thread
hujw77 marked this conversation as resolved.
Outdated
if (response.errorCode != 0) {
(uint256 chainId, address channel, uint256 msgIndex, bytes32 msgHash) =
abi.decode(response.result, (uint256, address, uint256, bytes32));
IORMP(PROTOCOL).importHash(chainId, channel, msgIndex, msgHash);
emit XAPIConsumeResult(requestId, response.result, response.errorCode);
} else {
emit XAPIConsumeError(requestId, response.errorCode);
}
}

function hashOf(uint256 chainId, address channel, uint256 msgIndex) public view override returns (bytes32) {
return IORMP(PROTOCOL).hashLookup(address(this), keccak256(abi.encode(chainId, channel, msgIndex)));
}

function changeOwner(address newOwner) external onlyOwner {
address oldOwner = owner;
owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}

function setApproved(address operator, bool approve) external onlyOwner {
approvedOf[operator] = approve;
emit SetApproved(operator, approve);
}

function isApproved(address operator) public view returns (bool) {
return approvedOf[operator];
}

function withdraw(address to, uint256 amount) external onlyApproved {
(bool success,) = to.call{value: amount}("");
require(success, "!withdraw");
emit Withdrawal(to, amount);
}

function setFee(uint256 chainId, uint256 fee_) external onlyApproved {
feeOf[chainId] = fee_;
emit SetFee(chainId, fee_);
}

function fee(uint256 toChainId, address /*ua*/ ) public view returns (uint256) {
uint256 f = feeOf[toChainId];
require(f != 0, "!fee");
return f;
}
}