Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
15 changes: 9 additions & 6 deletions contracts/protocol/payload-delivery/AuctionManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,12 @@ contract AuctionManager is
);
if (!_hasRole(TRANSMITTER_ROLE, transmitter)) revert InvalidTransmitter();

(uint256 watcherFees, uint256 transmitterFees) = getTransmitterMaxFeesRequired(
requestMetadata.fees.token,
requestCount_
);
// check if the bid exceeds the max fees quoted by app gateway subtracting the watcher fees
if (fee > getTransmitterMaxFeesRequired(requestMetadata.fees.token, requestCount_))
revert BidExceedsMaxFees();
if (fee > transmitterFees) revert BidExceedsMaxFees();

// check if the bid is lower than the existing bid
if (
Expand All @@ -141,10 +144,10 @@ contract AuctionManager is

// block the fees
IFeesManager(addressResolver__.feesManager()).blockFees(
requestMetadata.appGateway,
requestMetadata.consumeFrom,
requestMetadata.fees,
newBid,
watcherPrecompile__().getTotalFeesRequired(requestMetadata.fees.token, requestCount_),
watcherFees,
requestCount_
);

Expand Down Expand Up @@ -210,7 +213,7 @@ contract AuctionManager is
function getTransmitterMaxFeesRequired(
address token_,
uint40 requestCount_
) public view returns (uint256) {
) public view returns (uint256, uint256) {
// check if the bid is for this auction manager
if (requestMetadata.auctionManager != address(this)) revert InvalidBid();

Expand All @@ -220,7 +223,7 @@ contract AuctionManager is

// get the total fees required for the watcher precompile ops
uint256 watcherFees = watcherPrecompile__().getTotalFeesRequired(token_, requestCount_);
return requestMetadata.fees.amount - watcherFees;
return (watcherFees, requestMetadata.fees.amount - watcherFees);
}

function _recoverSigner(
Expand Down
54 changes: 36 additions & 18 deletions contracts/protocol/payload-delivery/FeesManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ abstract contract FeesManagerStorage is IFeesManager {
/// @dev signatureNonce => isNonceUsed
mapping(uint256 => bool) public isNonceUsed;

mapping(uint40 => address) public requestCountConsumeFrom;

// slots [57-106] reserved for gap
uint256[50] _gap_after;

Expand Down Expand Up @@ -221,7 +223,7 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol
/// @param requestCount_ The batch identifier
/// @dev Only callable by delivery helper
function blockFees(
address originAppGateway_,
address consumeFrom_,
Fees memory feesGivenByApp_,
Bid memory winningBid_,
uint256 watcherFees_,
Expand All @@ -230,11 +232,10 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol
if (msg.sender != deliveryHelper__().getRequestMetadata(requestCount_).auctionManager)
revert NotAuctionManager();

address appGateway = _getCoreAppGateway(originAppGateway_);
// Block fees
uint256 availableFees = getAvailableFees(
feesGivenByApp_.feePoolChain,
appGateway,
consumeFrom_,
feesGivenByApp_.feePoolToken
);

Expand All @@ -244,7 +245,7 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol
uint256 feesNeeded = winningBid_.fee + watcherFees_;
if (availableFees < feesNeeded) revert InsufficientFeesAvailable();

TokenBalance storage tokenBalance = userFeeBalances[appGateway][
TokenBalance storage tokenBalance = userFeeBalances[consumeFrom_][
feesGivenByApp_.feePoolChain
][feesGivenByApp_.feePoolToken];

Expand All @@ -258,6 +259,7 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol
feePoolToken: feesGivenByApp_.feePoolToken,
amount: feesNeeded
});
requestCountConsumeFrom[requestCount_] = consumeFrom_;

emit FeesBlocked(
requestCount_,
Expand All @@ -272,23 +274,29 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol
/// @param transmitter_ The address of the transmitter who executed the batch
function unblockAndAssignFees(
uint40 requestCount_,
address transmitter_,
address originAppGateway_
address transmitter_
) external override onlyDeliveryHelper {
Fees memory fees = requestCountBlockedFees[requestCount_];
if (fees.amount == 0) return;

address appGateway = _getCoreAppGateway(originAppGateway_);
TokenBalance storage tokenBalance = userFeeBalances[appGateway][fees.feePoolChain][
RequestMetadata memory requestMetadata = deliveryHelper__().getRequestMetadata(
requestCount_
);

TokenBalance storage tokenBalance = userFeeBalances[consumeFrom][fees.feePoolChain][
fees.feePoolToken
];

uint256 transmitterBid = requestMetadata.winningBid.fee;
uint256 remainingFees = fees.amount - transmitterBid;

// Unblock fees from deposit
tokenBalance.blocked -= fees.amount;
tokenBalance.deposited -= fees.amount;
tokenBalance.deposited -= transmitterBid;
tokenBalance.deposited -= remainingFees;

// Assign fees to transmitter
transmitterFees[transmitter_][fees.feePoolChain][fees.feePoolToken] += fees.amount;
transmitterFees[transmitter_][fees.feePoolChain][fees.feePoolToken] += transmitterBid;

// Clean up storage
delete requestCountBlockedFees[requestCount_];
Expand All @@ -299,15 +307,14 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol
uint32 chainSlug_,
address token_,
uint256 amount_,
address consumeFrom_
uint40 requestCount_
) external onlyWatcherPrecompile {
address appGateway = _getCoreAppGateway(consumeFrom_);
TokenBalance storage tokenBalance = userFeeBalances[appGateway][chainSlug_][token_];
if (tokenBalance.deposited < amount_)
revert InsufficientWatcherPrecompileFeesAvailable(chainSlug_, token_, consumeFrom_);
tokenBalance.deposited -= amount_;
Fees storage fees = requestCountBlockedFees[requestCount_];
if (fees.amount == 0) revert NoFeesBlocked();

fees.amount -= amount_;
watcherPrecompileFees[chainSlug_][token_] += amount_;
emit WatcherPrecompileFeesAssigned(chainSlug_, token_, amount_, consumeFrom_);
emit WatcherPrecompileFeesAssigned(chainSlug_, token_, amount_, requestCount_);
}

function unblockFees(uint40 requestCount_) external {
Expand Down Expand Up @@ -412,7 +419,18 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol
readAt: 0,
payload: payload_
});
requestCount = watcherPrecompile__().submitRequest(payloadSubmitParamsArray);

RequestMetadata memory requestMetadata = RequestMetadata({
appGateway: address(this),
auctionManager: address(0),
feesApprovalData: bytes(""),
fees: Fees({token: token_, amount: amount_}),
winningBid: Bid({transmitter: transmitter_, fee: 0, extraData: new bytes(0)})
});
requestCount = watcherPrecompile__().submitRequest(
payloadSubmitParamsArray,
requestMetadata
);

// same transmitter can execute requests without auction
watcherPrecompile__().startProcessingRequest(requestCount, transmitter_);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ contract DeliveryHelper is FeesHelpers {
requestMetadata_.winningBid.transmitter = winningBid_.transmitter;

if (!isRestarted) {
watcherPrecompile__().startProcessingRequest(requestCount_, winningBid_.transmitter);
watcherPrecompile__().startProcessingRequest(requestCount_, winningBid_);
} else {
watcherPrecompile__().updateTransmitter(requestCount_, winningBid_.transmitter);
}
Expand All @@ -52,6 +52,7 @@ contract DeliveryHelper is FeesHelpers {
function finishRequest(uint40 requestCount_) external onlyWatcherPrecompile {
RequestMetadata storage requestMetadata_ = requests[requestCount_];

// todo: move it to watcher precompile
if (requestMetadata_.winningBid.transmitter != address(0))
IFeesManager(addressResolver__.feesManager()).unblockAndAssignFees(
requestCount_,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ abstract contract RequestQueue is DeliveryUtils {
});

// process and submit the queue of payloads to watcher precompile
requestCount = watcherPrecompile__().submitRequest(payloadSubmitParamsArray);
requestCount = watcherPrecompile__().submitRequest(payloadSubmitParamsArray, requestMetadata);
requests[requestCount] = requestMetadata;

// send query directly if request contains only reads
Expand Down
17 changes: 17 additions & 0 deletions contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ contract WatcherPrecompileLimits is
mapping(address => uint256) public queryFees;
mapping(address => uint256) public finalizeFees;
mapping(address => uint256) public scheduleFees;
mapping(address => uint256) public callBackFees;

/// @notice Emitted when the default limit and rate per second are set
event DefaultLimitAndRatePerSecondSet(uint256 defaultLimit, uint256 defaultRatePerSecond);
Expand Down Expand Up @@ -196,6 +197,16 @@ contract WatcherPrecompileLimits is
}
}

function setCallBackFees(
address[] calldata tokens_,
uint256[] calldata amounts_
) external onlyOwner {
require(tokens_.length == amounts_.length, "Length mismatch");
for (uint256 i = 0; i < tokens_.length; i++) {
callBackFees[tokens_[i]] = amounts_[i];
}
}

function getTotalFeesRequired(
address token_,
uint40 requestCount_
Expand All @@ -205,9 +216,15 @@ contract WatcherPrecompileLimits is
revert WatcherFeesNotSetForToken(token_);
}

uint256 totalCallbacks = precompileCount[QUERY][requestCount_] +
precompileCount[FINALIZE][requestCount_] +
precompileCount[SCHEDULE][requestCount_];

totalFees += totalCallbacks * callBackFees[token_];
totalFees += precompileCount[QUERY][requestCount_] * queryFees[token_];
totalFees += precompileCount[FINALIZE][requestCount_] * finalizeFees[token_];
totalFees += precompileCount[SCHEDULE][requestCount_] * scheduleFees[token_];

return totalFees;
}
}
11 changes: 7 additions & 4 deletions contracts/protocol/watcherPrecompile/core/RequestHandler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ abstract contract RequestHandler is WatcherPrecompileCore {
/// @dev This function processes a batch of payload requests and assigns them to batches
/// @dev It also consumes limits for the app gateway based on the number of reads and writes
function submitRequest(
PayloadSubmitParams[] calldata payloadSubmitParams
PayloadSubmitParams[] calldata payloadSubmitParams,
RequestMetadata calldata requestMetadata
) public returns (uint40 requestCount) {
address appGateway = _checkAppGateways(payloadSubmitParams);

Expand Down Expand Up @@ -96,6 +97,8 @@ abstract contract RequestHandler is WatcherPrecompileCore {
requestParams[requestCount].payloadsRemaining = payloadSubmitParams.length;
requestParams[requestCount].middleware = msg.sender;

requestMetadata[requestCount] = requestMetadata;

emit RequestSubmitted(
msg.sender,
requestCount,
Expand Down Expand Up @@ -129,17 +132,17 @@ abstract contract RequestHandler is WatcherPrecompileCore {

/// @notice Starts processing a request with a specified transmitter
/// @param requestCount The request count to start processing
/// @param transmitter The address of the transmitter
/// @param winningBid The winning bid, contains fees, transmitter and extra data
/// @dev This function initiates the processing of a request by a transmitter
/// @dev It verifies that the caller is the middleware and that the request hasn't been started yet
function startProcessingRequest(uint40 requestCount, address transmitter) public {
function startProcessingRequest(uint40 requestCount, Bid memory winningBid) public {
RequestParams storage r = requestParams[requestCount];
if (r.middleware != msg.sender) revert InvalidCaller();
if (r.transmitter != address(0)) revert AlreadyStarted();
if (r.currentBatchPayloadsLeft > 0) revert AlreadyStarted();

uint40 batchCount = r.payloadParamsArray[0].payloadHeader.getBatchCount();
r.transmitter = transmitter;
r.transmitter = winningBid.transmitter;
r.currentBatch = batchCount;

_processBatch(requestCount, batchCount);
Expand Down
24 changes: 7 additions & 17 deletions contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,6 @@ contract WatcherPrecompile is RequestHandler {
PayloadParams memory params_,`
address transmitter_
) external returns (bytes32) {
IFeesManager(addressResolver__.feesManager()).assignWatcherPrecompileFees(
evmxSlug,
params_.payloadHeader.getToken(),
watcherPrecompileLimits__.finalizeFees(params_.payloadHeader.getToken()),
msg.sender
);
return _finalize(params_, transmitter_);
}

Expand Down Expand Up @@ -208,11 +202,14 @@ contract WatcherPrecompile is RequestHandler {
PayloadParams memory payloadParams = payloads[resolvedPromises_[i].payloadId];
address asyncPromise = payloadParams.asyncPromise;

uint40 requestCount = payloadParams.payloadHeader.getRequestCount();

// todo: non trusted call
if (asyncPromise != address(0)) {
// todo: limit the gas used for promise resolution
// Resolve each promise with its corresponding return data
bool success = IPromise(asyncPromise).markResolved(
payloadParams.payloadHeader.getRequestCount(),
requestCount,
resolvedPromises_[i].payloadId,
resolvedPromises_[i].returnData
);
Expand All @@ -224,27 +221,20 @@ contract WatcherPrecompile is RequestHandler {
}

isPromiseExecuted[resolvedPromises_[i].payloadId] = true;
RequestParams storage requestParams_ = requestParams[
payloadParams.payloadHeader.getRequestCount()
];
RequestParams storage requestParams_ = requestParams[requestCount];
requestParams_.currentBatchPayloadsLeft--;
requestParams_.payloadsRemaining--;

// if all payloads of a batch are executed, process the next batch
if (
requestParams_.currentBatchPayloadsLeft == 0 && requestParams_.payloadsRemaining > 0
) {
_processBatch(
payloadParams.payloadHeader.getRequestCount(),
++requestParams_.currentBatch
);
_processBatch(requestCount, ++requestParams_.currentBatch);
}

// if all payloads of a request are executed, finish the request
if (requestParams_.payloadsRemaining == 0) {
IMiddleware(requestParams_.middleware).finishRequest(
payloadParams.payloadHeader.getRequestCount()
);
IMiddleware(requestParams_.middleware).finishRequest(requestCount);
}
emit PromiseResolved(resolvedPromises_[i].payloadId, asyncPromise);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ abstract contract WatcherPrecompileCore is
) internal returns (bytes32 timeoutId) {
if (delayInSeconds_ > maxTimeoutDelayInSeconds) revert TimeoutDelayTooLarge();

_consumeFees(
requestCount_,
watcherPrecompileLimits__.scheduleFees(requestMetadata_.fees.feePoolToken)
);

uint256 executeAt = block.timestamp + delayInSeconds_;
timeoutId = _encodeTimeoutId();

Expand Down Expand Up @@ -74,6 +79,11 @@ abstract contract WatcherPrecompileCore is
requestParams[params_.payloadHeader.getRequestCount()].middleware
);

_consumeFees(
params_.payloadHeader.getRequestCount(),
watcherPrecompileLimits__.finalizeFees(params_.payloadHeader.getChainSlug())
);

uint256 deadline = block.timestamp + expiryTime;
payloads[params_.payloadId].deadline = deadline;
payloads[params_.payloadId].finalizedTransmitter = transmitter_;
Expand Down Expand Up @@ -109,6 +119,11 @@ abstract contract WatcherPrecompileCore is
/// @param params_ The payload parameters for the query
/// @dev This function sets up a query request and emits a QueryRequested event
function _query(PayloadParams memory params_) internal {
_consumeFees(
params_.payloadHeader.getRequestCount(),
watcherPrecompileLimits__.queryFees(params_.payloadHeader.getChainSlug())
);

payloads[params_.payloadId].prevDigestsHash = _getPreviousDigestsHash(
params_.payloadHeader.getBatchCount()
);
Expand Down Expand Up @@ -239,6 +254,19 @@ abstract contract WatcherPrecompileCore is
if (signer != owner()) revert InvalidWatcherSignature();
}

function _consumeFees(uint40 requestCount_, uint256 fees_) internal {
RequestMetadata memory requestMetadata_ = requestMetadata[requestCount_];

// for callbacks in all precompiles
uint256 feesToConsume = fees_ + watcherPrecompileLimits__().callBackFees();
IFeesManager(addressResolver__.feesManager()).assignWatcherPrecompileFees(
requestMetadata_.fees.feePoolChain,
requestMetadata_.fees.feePoolToken,
feesToConsume,
requestCount_
);
}

/// @notice Gets the batch IDs for a request
/// @param requestCount_ The request count to get the batch IDs for
/// @return An array of batch IDs for the given request
Expand Down
Loading