Ensure oracle fees are paid independently#3899
Open
flopez7 wants to merge 14 commits into
Open
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
2 Skipped Deployments
|
…omplete/Cancel in the same transaction
portuu3
requested changes
May 13, 2026
…anceler address and event amount
portuu3
requested changes
May 14, 2026
…cle fee calculations in Escrow contract
… update event emissions for cancellation refunds and oracle fee transfers
portuu3
reviewed
May 19, 2026
Comment on lines
325
to
384
| function _finalize() private { | ||
| EscrowStatuses _status = status; | ||
| uint256 _remaining = remainingFunds; | ||
| uint256 _remainingFunds = remainingFunds; | ||
|
|
||
| uint256 _reputationOracleFee = (fundAmount * | ||
| reputationOracleFeePercentage) / 100; | ||
| uint256 _recordingOracleFee = (fundAmount * | ||
| recordingOracleFeePercentage) / 100; | ||
| uint256 _exchangeOracleFee = (fundAmount * | ||
| exchangeOracleFeePercentage) / 100; | ||
| uint256 _totalOracleFee = _reputationOracleFee + | ||
| _recordingOracleFee + | ||
| _exchangeOracleFee; | ||
|
|
||
| IERC20 tokenContract = IERC20(token); | ||
|
|
||
| fundAmount = 0; | ||
| remainingFunds = 0; | ||
| reservedFunds = 0; | ||
|
|
||
| if (bytes(intermediateResultsUrl).length != 0) { | ||
| address[] memory oracles = new address[](3); | ||
| uint256[] memory amounts = new uint256[](3); | ||
|
|
||
| oracles[0] = reputationOracle; | ||
| oracles[1] = recordingOracle; | ||
| oracles[2] = exchangeOracle; | ||
| amounts[0] = _reputationOracleFee; | ||
| amounts[1] = _recordingOracleFee; | ||
| amounts[2] = _exchangeOracleFee; | ||
|
|
||
| if (_reputationOracleFee > 0) { | ||
| tokenContract.safeTransfer( | ||
| reputationOracle, | ||
| _reputationOracleFee | ||
| ); | ||
| } | ||
| if (_recordingOracleFee > 0) { | ||
| tokenContract.safeTransfer( | ||
| recordingOracle, | ||
| _recordingOracleFee | ||
| ); | ||
| } | ||
| if (_exchangeOracleFee > 0) { | ||
| tokenContract.safeTransfer(exchangeOracle, _exchangeOracleFee); | ||
| } | ||
|
|
||
| if (_remaining > 0) { | ||
| IERC20 tokenContract = IERC20(token); | ||
| tokenContract.safeTransfer(launcher, _remaining); | ||
| emit OracleFeeTransfer(oracles, amounts); | ||
| } else { | ||
| _remainingFunds += _totalOracleFee; | ||
| } | ||
|
|
||
| if (_remainingFunds > 0) { | ||
| tokenContract.safeTransfer(launcher, _remainingFunds); | ||
| if (_status == EscrowStatuses.ToCancel) { | ||
| emit CancellationRefund(_remaining); | ||
| emit CancellationRefund(_remainingFunds); | ||
| } | ||
| remainingFunds = 0; | ||
| reservedFunds = 0; | ||
| } | ||
|
|
||
| if (_status == EscrowStatuses.ToCancel) { |
Collaborator
There was a problem hiding this comment.
Suggested change
| function _finalize() private { | |
| EscrowStatuses _status = status; | |
| uint256 _remaining = remainingFunds; | |
| uint256 _remainingFunds = remainingFunds; | |
| uint256 _reputationOracleFee = (fundAmount * | |
| reputationOracleFeePercentage) / 100; | |
| uint256 _recordingOracleFee = (fundAmount * | |
| recordingOracleFeePercentage) / 100; | |
| uint256 _exchangeOracleFee = (fundAmount * | |
| exchangeOracleFeePercentage) / 100; | |
| uint256 _totalOracleFee = _reputationOracleFee + | |
| _recordingOracleFee + | |
| _exchangeOracleFee; | |
| IERC20 tokenContract = IERC20(token); | |
| fundAmount = 0; | |
| remainingFunds = 0; | |
| reservedFunds = 0; | |
| if (bytes(intermediateResultsUrl).length != 0) { | |
| address[] memory oracles = new address[](3); | |
| uint256[] memory amounts = new uint256[](3); | |
| oracles[0] = reputationOracle; | |
| oracles[1] = recordingOracle; | |
| oracles[2] = exchangeOracle; | |
| amounts[0] = _reputationOracleFee; | |
| amounts[1] = _recordingOracleFee; | |
| amounts[2] = _exchangeOracleFee; | |
| if (_reputationOracleFee > 0) { | |
| tokenContract.safeTransfer( | |
| reputationOracle, | |
| _reputationOracleFee | |
| ); | |
| } | |
| if (_recordingOracleFee > 0) { | |
| tokenContract.safeTransfer( | |
| recordingOracle, | |
| _recordingOracleFee | |
| ); | |
| } | |
| if (_exchangeOracleFee > 0) { | |
| tokenContract.safeTransfer(exchangeOracle, _exchangeOracleFee); | |
| } | |
| if (_remaining > 0) { | |
| IERC20 tokenContract = IERC20(token); | |
| tokenContract.safeTransfer(launcher, _remaining); | |
| emit OracleFeeTransfer(oracles, amounts); | |
| } else { | |
| _remainingFunds += _totalOracleFee; | |
| } | |
| if (_remainingFunds > 0) { | |
| tokenContract.safeTransfer(launcher, _remainingFunds); | |
| if (_status == EscrowStatuses.ToCancel) { | |
| emit CancellationRefund(_remaining); | |
| emit CancellationRefund(_remainingFunds); | |
| } | |
| remainingFunds = 0; | |
| reservedFunds = 0; | |
| } | |
| if (_status == EscrowStatuses.ToCancel) { | |
| function _finalize() private { | |
| bool isCancellation = status == EscrowStatuses.ToCancel; | |
| uint256 _remainingFunds = remainingFunds; | |
| uint256 _reputationOracleFee = (fundAmount * | |
| reputationOracleFeePercentage) / 100; | |
| uint256 _recordingOracleFee = (fundAmount * | |
| recordingOracleFeePercentage) / 100; | |
| uint256 _exchangeOracleFee = (fundAmount * | |
| exchangeOracleFeePercentage) / 100; | |
| uint256 _totalOracleFee = _reputationOracleFee + | |
| _recordingOracleFee + | |
| _exchangeOracleFee; | |
| IERC20 tokenContract = IERC20(token); | |
| fundAmount = 0; | |
| remainingFunds = 0; | |
| reservedFunds = 0; | |
| status = isCancellation | |
| ? EscrowStatuses.Cancelled | |
| : EscrowStatuses.Complete; | |
| if (bytes(intermediateResultsUrl).length != 0) { | |
| address[] memory oracles = new address[](3); | |
| uint256[] memory amounts = new uint256[](3); | |
| oracles[0] = reputationOracle; | |
| oracles[1] = recordingOracle; | |
| oracles[2] = exchangeOracle; | |
| amounts[0] = _reputationOracleFee; | |
| amounts[1] = _recordingOracleFee; | |
| amounts[2] = _exchangeOracleFee; | |
| if (_reputationOracleFee > 0) { | |
| tokenContract.safeTransfer( | |
| reputationOracle, | |
| _reputationOracleFee | |
| ); | |
| } | |
| if (_recordingOracleFee > 0) { | |
| tokenContract.safeTransfer( | |
| recordingOracle, | |
| _recordingOracleFee | |
| ); | |
| } | |
| if (_exchangeOracleFee > 0) { | |
| tokenContract.safeTransfer(exchangeOracle, _exchangeOracleFee); | |
| } | |
| emit OracleFeeTransfer(oracles, amounts); | |
| } else { | |
| _remainingFunds += _totalOracleFee; | |
| } | |
| if (_remainingFunds > 0) { | |
| tokenContract.safeTransfer(launcher, _remainingFunds); | |
| if (isCancellation) { | |
| emit CancellationRefund(_remainingFunds); | |
| } | |
| } | |
| if (isCancellation) { | |
| emit Cancelled(); | |
| } else { | |
| emit Completed(); | |
| } | |
| } |
…d update related tests for consistency
…lid status checks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Issue tracking
#3880
Context behind the change
Oracle fees were previously affected when worker submissions were rejected and no worker payout was made. This change separates oracle compensation from worker payouts so oracle fees are reserved from the funded amount and paid when the escrow is completed or canceled, while rejected submissions still receive no worker payout. The SDK, oracles, and subgraph were updated to use and track the corrected fund amounts and oracle fee transfers.
How has this been tested?
Deployed locally
Release plan
Deploy new contract version, SDKs and subgraph
Potential risks; What to monitor; Rollback plan
Check bulkpayout balance and fees.