From bb1a3343e68e847feaf743a761e656851bb3a084 Mon Sep 17 00:00:00 2001 From: Guantong Date: Thu, 10 Oct 2024 15:31:30 +0800 Subject: [PATCH] XAPI Audit#1 (#5) * audit1 * sync_publish_config_to_remote --- .../src/abstract/aggregator.abstract.ts | 194 +++++++++++++++--- aggregator/src/ormp.aggregator.ts | 20 +- xapi-consumer/contracts/ConsumerExample.sol | 2 +- xapi/contracts/XAPI.sol | 48 +++-- xapi/contracts/interfaces/IXAPI.sol | 27 ++- xapi/test/XAPI.js | 20 +- 6 files changed, 221 insertions(+), 90 deletions(-) diff --git a/aggregator/src/abstract/aggregator.abstract.ts b/aggregator/src/abstract/aggregator.abstract.ts index b42b2d6..5628775 100644 --- a/aggregator/src/abstract/aggregator.abstract.ts +++ b/aggregator/src/abstract/aggregator.abstract.ts @@ -22,10 +22,18 @@ export enum RequestMethod { export class PublishChainConfig { chain_id: ChainId; xapi_address: string; + reporters_fee: string; + publish_fee: string; + reward_address: string; + version: string; - constructor({ chain_id, xapi_address }: { chain_id: ChainId, xapi_address: string }) { + constructor({ chain_id, xapi_address, reporters_fee, publish_fee, reward_address }: { chain_id: ChainId, xapi_address: string, reporters_fee: string, publish_fee: string, reward_address: string }) { this.chain_id = chain_id; this.xapi_address = xapi_address; + this.reporters_fee = reporters_fee; + this.publish_fee = publish_fee; + this.reward_address = reward_address; + this.version = near.blockTimestamp().toString(); } } @@ -87,21 +95,14 @@ class PublishData { response: Response; chain_config: PublishChainConfig; signature: string; + mpc_options: MpcOptions; - nonce: string; - gas_limit: string; - max_fee_per_gas: string; - max_priority_fee_per_gas: string; - - constructor({ request_id, response, chain_config, signature, nonce, gas_limit, max_fee_per_gas, max_priority_fee_per_gas }: { request_id: RequestId, response: Response, chain_config: PublishChainConfig, signature: string, nonce: string, gas_limit: string, max_fee_per_gas: string, max_priority_fee_per_gas: string }) { + constructor({ request_id, response, chain_config, signature, mpc_options }: { request_id: RequestId, response: Response, chain_config: PublishChainConfig, signature: string, mpc_options: MpcOptions }) { this.request_id = request_id; this.response = response; this.chain_config = chain_config; this.signature = signature; - this.nonce = nonce; - this.gas_limit = gas_limit; - this.max_fee_per_gas = max_fee_per_gas; - this.max_priority_fee_per_gas = max_priority_fee_per_gas; + this.mpc_options = mpc_options; } } @@ -117,6 +118,35 @@ class AggregatedEvent extends Nep297Event { } } +class SetPublishChainConfigEvent extends Nep297Event { + constructor(data: PublishChainConfig) { + super("SetPublishChainConfig", data); + } +} + +class SyncPublishChainConfigData { + chain_id: ChainId; + xapi_address: string; + version: string; + call_data: Uint8Array; + signature: string; + mpc_options: MpcOptions; + constructor({ chain_id, xapi_address, version, call_data, signature, mpc_options }: { chain_id: ChainId, xapi_address: string, version: string, call_data: Uint8Array, signature: string, mpc_options: MpcOptions }) { + this.chain_id = chain_id; + this.xapi_address = xapi_address; + this.version = version; + this.call_data = call_data; + this.signature = signature; + this.mpc_options = mpc_options; + } +} + +class SyncPublishChainConfigEvent extends Nep297Event { + constructor(data: SyncPublishChainConfigData) { + super("SyncPublishChainConfig", data); + } +} + export class ReporterRequired { quorum: number; threshold: number; @@ -175,6 +205,14 @@ export class MpcConfig { } } +export class MpcOptions { + nonce: string + gas_limit: string + max_fee_per_gas: string + max_priority_fee_per_gas: string + key_version: number +} + export class Staked { amount: string account_id: AccountId @@ -183,6 +221,9 @@ export class Staked { // Default timeout: 2 hours const DEFAULT_TIME_OUT = "18000000000000"; +// Derivation path prefix for mpc +const DERIVATION_PATH_PREFIX = "XAPI"; + export abstract class Aggregator extends ContractBase { description: string; latest_request_id: RequestId; @@ -268,17 +309,102 @@ export abstract class Aggregator extends ContractBase { return this.staking_contract; } - abstract set_publish_chain_config(publis_chain_config: PublishChainConfig): void; + abstract set_publish_chain_config(publish_chain_config: PublishChainConfig): void; _set_publish_chain_config(publish_chain_config: PublishChainConfig): void { this._assert_operator(); assert(publish_chain_config.chain_id != null, "chain_id can't be null."); assert(publish_chain_config.xapi_address != null, "xapi_address can't be null."); - this.publish_chain_config_lookup.set(publish_chain_config.chain_id.toString(), publish_chain_config); + assert(publish_chain_config.reporters_fee != null, "reporters_fee can't be null."); + assert(publish_chain_config.publish_fee != null, "publish_fee can't be null."); + assert(publish_chain_config.reward_address != null, "reward_address can't be null."); + const _publish_config = new PublishChainConfig({ ...publish_chain_config }); + this.publish_chain_config_lookup.set(publish_chain_config.chain_id, _publish_config); + new SetPublishChainConfigEvent(_publish_config).emit(); + } + + abstract sync_publish_config_to_remote({ chain_id, mpc_options }: { chain_id: ChainId, mpc_options: MpcOptions }): NearPromise; + _sync_publish_config_to_remote({ chain_id, mpc_options }: { chain_id: ChainId, mpc_options: MpcOptions }): NearPromise { + this._check_mpc_options(mpc_options); + const _latest_config = this.publish_chain_config_lookup.get(chain_id); + assert(_latest_config != null, `No publish chain config for ${chain_id}`); + + const function_call_data = encodeFunctionCall({ + functionSignature: "setAggregatorConfig(string,uint256,uint256,address)", + params: [ + near.currentAccountId, + BigInt(_latest_config.reporters_fee), + BigInt(_latest_config.publish_fee), + _latest_config.xapi_address + ] + }) + near.log("functionCallData", function_call_data); + + const function_call_data_bytes = hexToBytes(function_call_data); + near.log("bytes functionCallData", Array.from(function_call_data_bytes)); + + const payload = ethereumTransaction({ + chainId: BigInt(chain_id), + nonce: BigInt(mpc_options.nonce), + maxPriorityFeePerGas: BigInt(mpc_options.max_priority_fee_per_gas), + maxFeePerGas: BigInt(mpc_options.max_fee_per_gas), + gasLimit: BigInt(mpc_options.gas_limit), + to: _latest_config.xapi_address, + value: BigInt(0), + data: function_call_data_bytes, + accessList: [] + }); + const payload_arr = Array.from(payload); + near.log("payload_arr", payload_arr); + + const mpc_args = { + "request": { + "key_version": mpc_options.key_version || 0, + "payload": payload_arr, + "path": `${DERIVATION_PATH_PREFIX}-${chain_id}` + } + } + near.log(`before request signature, prepaidGas: ${near.prepaidGas()}, leftGas: ${near.prepaidGas() - near.usedGas()}`) + const promise = NearPromise.new(this.mpc_config.mpc_contract) + // 1 NEAR to request signature, the surplus will be refunded + .functionCall("sign", JSON.stringify(mpc_args), BigInt(this.mpc_config.attached_balance), ONE_TERA_GAS * BigInt(250)) + .then( + NearPromise.new(near.currentAccountId()) + .functionCall( + "sync_publish_config_to_remote_callback", + JSON.stringify({ chain_id, mpc_options, call_data: function_call_data_bytes, version: _latest_config.version }), + BigInt(0), + // Beware of the 300T cap with mpc gas + BigInt(ONE_TERA_GAS * BigInt(25)) + ) + ); + return promise.asReturn(); + } + + abstract sync_publish_config_to_remote_callback({ chain_id, mpc_options, call_data, version }: { chain_id: ChainId, mpc_options: MpcOptions, call_data: Uint8Array, version: string }): void; + _sync_publish_config_to_remote_callback({ chain_id, mpc_options, call_data, version }: { chain_id: ChainId, mpc_options: MpcOptions, call_data: Uint8Array, version: string }): void { + const _result = this._promise_result({ promise_index: 0 }); + near.log(`sync_publish_config_to_remote_callback ${chain_id}, ${_result.success}, ${_result.result}, version: ${version}`); + const _latest_config = this.publish_chain_config_lookup.get(chain_id); + if (_result.success) { + if (_latest_config.version != version) { + near.log(`Config is out of date, latest: ${_latest_config.version}, want to sync: ${version}`) + return; + } + + new SyncPublishChainConfigEvent(new SyncPublishChainConfigData({ + chain_id, + mpc_options, + call_data, + version, + signature: _result.result, + xapi_address: _latest_config.xapi_address + })).emit(); + } } abstract get_publish_chain_config({ chain_id }: { chain_id: ChainId }): PublishChainConfig; _get_publish_chain_config({ chain_id }: { chain_id: ChainId }): PublishChainConfig { - return this.publish_chain_config_lookup.get(chain_id.toString()); + return this.publish_chain_config_lookup.get(chain_id); } abstract set_timeout({ timeout }: { timeout: Timestamp }): void; @@ -450,12 +576,9 @@ export abstract class Aggregator extends ContractBase { } } - abstract publish_external({ request_id, nonce, gas_limit, max_fee_per_gas, max_priority_fee_per_gas }: { request_id: RequestId, nonce: string, gas_limit: string, max_fee_per_gas: string, max_priority_fee_per_gas: string }): NearPromise; - _publish({ request_id, nonce, gas_limit, max_fee_per_gas, max_priority_fee_per_gas }: { request_id: RequestId, nonce: string, gas_limit: string, max_fee_per_gas: string, max_priority_fee_per_gas: string }): NearPromise { - assert(nonce != null, "nonce can't be null."); - assert(gas_limit != null, "gas_limit can't be null."); - assert(max_fee_per_gas != null, "max_fee_per_gas can't be null."); - assert(max_priority_fee_per_gas != null, "max_priority_fee_per_gas can't be null."); + abstract publish_external({ request_id, mpc_options }: { request_id: RequestId, mpc_options: MpcOptions }): NearPromise; + _publish({ request_id, mpc_options }: { request_id: RequestId, mpc_options: MpcOptions }): NearPromise { + this._check_mpc_options(mpc_options); const _response = this.response_lookup.get(request_id); assert(_response != null, `Response for ${request_id} does not exist`); @@ -472,7 +595,7 @@ export abstract class Aggregator extends ContractBase { } _response.chain_id = (BigInt(request_id) >> BigInt(192)).toString(); - const _chain_config = this.publish_chain_config_lookup.get(_response.chain_id.toString()); + const _chain_config = this.publish_chain_config_lookup.get(_response.chain_id); assert(_chain_config != null, `Chain config for ${_response.chain_id} does not exist`); // Relay it https://sepolia.etherscan.io/tx/0xfe2e2e0018f609b5d10250a823f191942fc42d597ad1cccfb4842f43f1d9196e @@ -494,10 +617,10 @@ export abstract class Aggregator extends ContractBase { const payload = ethereumTransaction({ chainId: BigInt(_response.chain_id), - nonce: BigInt(nonce), - maxPriorityFeePerGas: BigInt(max_priority_fee_per_gas), - maxFeePerGas: BigInt(max_fee_per_gas), - gasLimit: BigInt(gas_limit), + nonce: BigInt(mpc_options.nonce), + maxPriorityFeePerGas: BigInt(mpc_options.max_priority_fee_per_gas), + maxFeePerGas: BigInt(mpc_options.max_fee_per_gas), + gasLimit: BigInt(mpc_options.gas_limit), to: _chain_config.xapi_address, value: BigInt(0), data: function_call_data_bytes, @@ -509,10 +632,10 @@ export abstract class Aggregator extends ContractBase { const mpc_args = { "request": { - "key_version": 0, + "key_version": mpc_options.key_version || 0, "payload": payload_arr, // 0x4dd0A89Cb15D953Fc738362066b412fd303BCe17 - "path": "test" + "path": `${DERIVATION_PATH_PREFIX}-${_response.chain_id}` } } this.response_lookup.set(request_id, _response); @@ -524,7 +647,7 @@ export abstract class Aggregator extends ContractBase { NearPromise.new(near.currentAccountId()) .functionCall( "publish_callback", - JSON.stringify({ request_id, nonce, gas_limit, max_fee_per_gas, max_priority_fee_per_gas }), + JSON.stringify({ request_id, mpc_options }), BigInt(0), // Beware of the 300T cap with mpc gas BigInt(ONE_TERA_GAS * BigInt(25)) @@ -533,22 +656,29 @@ export abstract class Aggregator extends ContractBase { return promise.asReturn(); } - abstract publish_callback({ request_id, nonce, gas_limit, max_fee_per_gas, max_priority_fee_per_gas }: { request_id: RequestId, nonce: string, gas_limit: string, max_fee_per_gas: string, max_priority_fee_per_gas: string }): void - _publish_callback({ request_id, nonce, gas_limit, max_fee_per_gas, max_priority_fee_per_gas }: { request_id: RequestId, nonce: string, gas_limit: string, max_fee_per_gas: string, max_priority_fee_per_gas: string }): void { + abstract publish_callback({ request_id, mpc_options }: { request_id: RequestId, mpc_options: MpcOptions }): void + _publish_callback({ request_id, mpc_options }: { request_id: RequestId, mpc_options: MpcOptions }): void { const _result = this._promise_result({ promise_index: 0 }); near.log(`publish_callback ${request_id}, ${_result.success}, ${_result.result}`); const _response = this.response_lookup.get(request_id); if (_result.success) { _response.status = RequestStatus[RequestStatus.PUBLISHED]; - const _chain_config = this.publish_chain_config_lookup.get(_response.chain_id.toString()); + const _chain_config = this.publish_chain_config_lookup.get(_response.chain_id); new PublishEvent(new PublishData({ request_id, response: _response, chain_config: _chain_config, signature: _result.result, - nonce, gas_limit, max_fee_per_gas, max_priority_fee_per_gas + mpc_options })).emit(); this.response_lookup.set(request_id, _response); } } + private _check_mpc_options(mpc_options: MpcOptions): void { + assert(mpc_options.nonce != null, "nonce can't be null."); + assert(mpc_options.gas_limit != null, "gas_limit can't be null."); + assert(mpc_options.max_fee_per_gas != null, "max_fee_per_gas can't be null."); + assert(mpc_options.max_priority_fee_per_gas != null, "max_priority_fee_per_gas can't be null."); + } + private _report_deposit(report: Report): bigint { const _bytes = BigInt(sizeOf(report)); // 100KB == 1Near == 10^24 yoctoNear diff --git a/aggregator/src/ormp.aggregator.ts b/aggregator/src/ormp.aggregator.ts index eb05a39..6932f3a 100644 --- a/aggregator/src/ormp.aggregator.ts +++ b/aggregator/src/ormp.aggregator.ts @@ -1,6 +1,6 @@ // Find all our documentation at https://docs.near.org import { NearBindgen, near, call, view, migrate, assert, NearPromise, AccountId } from "near-sdk-js"; -import { Aggregator, Answer, ChainId, DataSource, MpcConfig, PublishChainConfig, Report, ReporterRequired, RequestId, Response, Staked, Timestamp } from "./abstract/aggregator.abstract"; +import { Aggregator, Answer, ChainId, DataSource, MpcConfig, MpcOptions, PublishChainConfig, Report, ReporterRequired, RequestId, Response, Staked, Timestamp } from "./abstract/aggregator.abstract"; import { ContractSourceMetadata, Standard } from "../../common/src/standard.abstract"; @NearBindgen({}) @@ -104,13 +104,13 @@ class OrmpAggregator extends Aggregator { /// Calls @call({}) - publish_external({ request_id, nonce, gas_limit, max_fee_per_gas, max_priority_fee_per_gas }: { request_id: RequestId; nonce: string; gas_limit: string; max_fee_per_gas: string; max_priority_fee_per_gas: string; }): NearPromise { - return super._publish({ request_id, nonce, gas_limit, max_fee_per_gas, max_priority_fee_per_gas }); + publish_external({ request_id, mpc_options }: { request_id: RequestId; mpc_options: MpcOptions }): NearPromise { + return super._publish({ request_id, mpc_options }); } @call({ privateFunction: true }) - publish_callback({ request_id, nonce, gas_limit, max_fee_per_gas, max_priority_fee_per_gas }: { request_id: RequestId; nonce: string; gas_limit: string; max_fee_per_gas: string; max_priority_fee_per_gas: string; }): void { - super._publish_callback({ request_id, nonce, gas_limit, max_fee_per_gas, max_priority_fee_per_gas }); + publish_callback({ request_id, mpc_options }: { request_id: RequestId; mpc_options: MpcOptions }): void { + super._publish_callback({ request_id, mpc_options }); } @call({ privateFunction: true }) @@ -123,6 +123,16 @@ class OrmpAggregator extends Aggregator { return super._report({ request_id, answers, reward_address }); } + @call({}) + sync_publish_config_to_remote({ chain_id, mpc_options }: { chain_id: ChainId; mpc_options: MpcOptions; }): NearPromise { + return super._sync_publish_config_to_remote({ chain_id, mpc_options }); + } + + @call({ privateFunction: true }) + sync_publish_config_to_remote_callback({ chain_id, mpc_options, call_data, version }: { chain_id: ChainId; mpc_options: MpcOptions; call_data: Uint8Array; version: string; }): void { + return super._sync_publish_config_to_remote_callback({ chain_id, mpc_options, call_data, version }); + } + @call({ payableFunction: true }) add_data_source(data_source: DataSource): void { super._add_data_source(data_source); diff --git a/xapi-consumer/contracts/ConsumerExample.sol b/xapi-consumer/contracts/ConsumerExample.sol index 6e72b2f..6c6fd98 100644 --- a/xapi-consumer/contracts/ConsumerExample.sol +++ b/xapi-consumer/contracts/ConsumerExample.sol @@ -17,7 +17,7 @@ contract ConsumerExample { xapi = IXAPI(xapiAddress); } - function makeRequest(string memory aggregator) external payable { + function makeRequest(address aggregator) external payable { string memory requestData = "{'hello':'world'}"; uint256 requestId = xapi.makeRequest{value: msg.value}(requestData, this.fulfillCallback.selector, aggregator); emit RequestMade(requestId, requestData); diff --git a/xapi/contracts/XAPI.sol b/xapi/contracts/XAPI.sol index e6988b1..624dc1e 100644 --- a/xapi/contracts/XAPI.sol +++ b/xapi/contracts/XAPI.sol @@ -7,22 +7,22 @@ import "./interfaces/IXAPI.sol"; contract XAPI is IXAPI, Ownable2Step { uint256 public requestCount; mapping(uint256 => Request) public requests; - mapping(string => AggregatorConfig) public aggregatorConfigs; + mapping(address => AggregatorConfig) public aggregatorConfigs; mapping(address => uint256) public rewards; constructor() Ownable(msg.sender) {} - function makeRequest(string memory requestData, bytes4 callbackFunction, string memory aggregator) + function makeRequest(string memory requestData, bytes4 callbackFunction, address exAggregator) external payable returns (uint256) { require(msg.sender != address(this), "CANT call self"); - AggregatorConfig memory aggregatorConfig = aggregatorConfigs[aggregator]; - require(aggregatorConfig.fulfillAddress != address(0), "!Aggregator"); + AggregatorConfig memory aggregatorConfig = aggregatorConfigs[exAggregator]; + require(aggregatorConfig.rewardAddress != address(0), "!Aggregator"); require(!aggregatorConfig.suspended, "Suspended"); - uint256 feeRequired = aggregatorConfig.perReporterFee * aggregatorConfig.quorum + aggregatorConfig.publishFee; + uint256 feeRequired = aggregatorConfig.reportersFee + aggregatorConfig.publishFee; require(msg.value >= feeRequired, "Insufficient fees"); requestCount++; @@ -33,31 +33,31 @@ contract XAPI is IXAPI, Ownable2Step { callbackFunction: callbackFunction, status: RequestStatus.Pending, payment: msg.value, - aggregator: aggregator, - fulfillAddress: aggregatorConfig.fulfillAddress, + aggregator: aggregatorConfig.aggregator, + exAggregator: exAggregator, response: ResponseData({reporters: new address[](0), result: new bytes(0)}), requestData: requestData }); - emit RequestMade(requestId, aggregator, requestData, msg.sender); + emit RequestMade(requestId, aggregatorConfig.aggregator, requestData, msg.sender); return requestId; } function fulfill(uint256 requestId, ResponseData memory response) external { Request storage request = requests[requestId]; require(decodeChainId(requestId) == block.chainid, "!chainId"); - require(msg.sender == request.fulfillAddress, "!Fulfill address"); + require(msg.sender == request.exAggregator, "!exAggregator address"); require(request.status == RequestStatus.Pending, "!Pending"); request.response = response; - AggregatorConfig memory aggregatorConfig = aggregatorConfigs[request.aggregator]; + AggregatorConfig memory aggregatorConfig = aggregatorConfigs[msg.sender]; // Avoid changing the reward configuration after the request but before the response to obtain the contract balance require( - aggregatorConfig.publishFee + aggregatorConfig.perReporterFee * response.reporters.length <= request.payment, + aggregatorConfig.publishFee + aggregatorConfig.reportersFee <= request.payment, "Insufficient rewards" ); for (uint256 i = 0; i < response.reporters.length; i++) { - rewards[response.reporters[i]] += aggregatorConfig.perReporterFee; + rewards[response.reporters[i]] += aggregatorConfig.reportersFee / response.reporters.length; } rewards[aggregatorConfig.rewardAddress] += aggregatorConfig.publishFee; @@ -94,28 +94,26 @@ contract XAPI is IXAPI, Ownable2Step { function setAggregatorConfig( string memory aggregator, - uint256 perReporterFee, + uint256 reportersFee, uint256 publishFee, - address fulfillAddress, - address rewardAddress, - uint8 quorum - ) external onlyOwner { - aggregatorConfigs[aggregator] = AggregatorConfig({ - fulfillAddress: fulfillAddress, - perReporterFee: perReporterFee, + address rewardAddress + ) external { + aggregatorConfigs[msg.sender] = AggregatorConfig({ + aggregator: aggregator, + reportersFee: reportersFee, publishFee: publishFee, rewardAddress: rewardAddress, - quorum: quorum, suspended: false }); - emit AggregatorConfigSet(aggregator, perReporterFee, publishFee, fulfillAddress, rewardAddress); + emit AggregatorConfigSet(msg.sender, reportersFee, publishFee, aggregator, rewardAddress); } - function suspendAggregator(string memory aggregator) external onlyOwner { - aggregatorConfigs[aggregator].suspended = true; + function suspendAggregator(address exAggregator) external onlyOwner{ + require(aggregatorConfigs[exAggregator].rewardAddress != address(0), "!Aggregator"); + aggregatorConfigs[exAggregator].suspended = true; - emit AggregatorSuspended(aggregator); + emit AggregatorSuspended(exAggregator, aggregatorConfigs[exAggregator].aggregator); } function encodeRequestId(uint256 count) internal view returns (uint256) { diff --git a/xapi/contracts/interfaces/IXAPI.sol b/xapi/contracts/interfaces/IXAPI.sol index 6e5267c..068650e 100644 --- a/xapi/contracts/interfaces/IXAPI.sol +++ b/xapi/contracts/interfaces/IXAPI.sol @@ -10,7 +10,7 @@ struct Request { address callbackContract; bytes4 callbackFunction; RequestStatus status; - address fulfillAddress; + address exAggregator; ResponseData response; uint256 payment; } @@ -27,11 +27,11 @@ struct ResponseData { } struct AggregatorConfig { - address fulfillAddress; + // Aggregator account on near + string aggregator; address rewardAddress; - uint256 perReporterFee; + uint256 reportersFee; uint256 publishFee; - uint8 quorum; bool suspended; } @@ -40,15 +40,15 @@ interface IXAPI { event Fulfilled(uint256 indexed requestId, ResponseData response, RequestStatus indexed status); event RewardsWithdrawn(address indexed withdrawer, uint256 amount); event AggregatorConfigSet( - string indexed aggregator, - uint256 perReporterFee, + address indexed exAggregator, + uint256 reportersFee, uint256 publishFee, - address fulfillAddress, + string aggregator, address rewardAddress ); - event AggregatorSuspended(string indexed aggregator); + event AggregatorSuspended(address indexed exAggregator, string indexed aggregator); - function makeRequest(string memory requestData, bytes4 callbackFunction, string memory aggregator) + function makeRequest(string memory requestData, bytes4 callbackFunction, address exAggregator) external payable returns (uint256); @@ -59,14 +59,13 @@ interface IXAPI { function withdrawRewards() external; + // Should be called by Aggregator mpc function setAggregatorConfig( string memory aggregator, - uint256 perReporterFee, + uint256 reportersFee, uint256 publishFee, - address fulfillAddress, - address rewardAddress, - uint8 quorum + address rewardAddress ) external; - function suspendAggregator(string memory aggregator) external; + function suspendAggregator(address exAggregator) external; } diff --git a/xapi/test/XAPI.js b/xapi/test/XAPI.js index 0ae4d10..64109b5 100644 --- a/xapi/test/XAPI.js +++ b/xapi/test/XAPI.js @@ -29,30 +29,24 @@ describe("XAPI", function () { describe("Aggregator Configuration", function () { it("Should set aggregator config correctly", async function () { const { xapi, owner } = await loadFixture(deployXAPIFixture); - await xapi.setAggregatorConfig("testAggregator", 100, 1000, owner.address, owner.address, 5); - const config = await xapi.aggregatorConfigs("testAggregator"); - expect(config.fulfillAddress).to.equal(owner.address); + await xapi.setAggregatorConfig("testAggregator", 100, 1000, owner.address, 5); + const config = await xapi.aggregatorConfigs(owner.address); + expect(config.aggregator).to.equal("testAggregator"); expect(config.perReporterFee).to.equal(100); expect(config.publishFee).to.equal(1000); expect(config.suspended).to.be.false; }); - - it("Should only allow owner to set aggregator config", async function () { - const { xapi, otherAccount } = await loadFixture(deployXAPIFixture); - await expect(xapi.connect(otherAccount).setAggregatorConfig("testAggregator", 100, 1000, owner.address, owner.address, 5)) - .to.be.reverted; - }); }); describe("Make Request", function () { it("Should create a new request", async function () { const { xapi, owner } = await loadFixture(deployXAPIFixture); - await xapi.setAggregatorConfig("testAggregator", 100, 1000, owner.address, owner.address, 5); + await xapi.setAggregatorConfig("testAggregator", 100, 1000, owner.address, 5); const requestData = "test data"; const callbackFunction = "0x12345678"; const requestFee = 1500; // 5 * 100 + 1000 - await expect(xapi.makeRequest(requestData, callbackFunction, "testAggregator", { value: requestFee })) + await expect(xapi.makeRequest(requestData, callbackFunction, owner.address, { value: requestFee })) .to.emit(xapi, "RequestMade") .withArgs(anyValue, "testAggregator", requestData, owner.address); }); @@ -62,11 +56,11 @@ describe("XAPI", function () { it("Should fulfill a request", async function () { const { xapi, owner } = await loadFixture(deployXAPIFixture); // Setup and make a request first - await xapi.setAggregatorConfig("testAggregator", 100, 1000, owner.address, owner.address, 5); + await xapi.setAggregatorConfig("testAggregator", 100, 1000, owner.address, 5); const requestData = "test data"; const callbackFunction = "0x12345678"; const requestFee = 1500; - await xapi.makeRequest(requestData, callbackFunction, "testAggregator", { value: requestFee }); + await xapi.makeRequest(requestData, callbackFunction, owner.address, { value: requestFee }); const chainId = await ethers.provider.getNetwork().then(n => n.chainId); const requestId = encodeRequestId(chainId, 1);