Last active
March 3, 2026 20:12
-
-
Save pkoch/a690b142b78e588cd188483aaac3742d to your computer and use it in GitHub Desktop.
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
| diff --git a/src/CCADisbursementTracker.sol b/src/CCADisbursementTracker.sol | |
| index 84c97ba..3522370 100644 | |
| --- a/src/CCADisbursementTracker.sol | |
| +++ b/src/CCADisbursementTracker.sol | |
| @@ -7,7 +7,7 @@ import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; | |
| // @dev As per CCA's own tests, the CCA can leave up to 1e18 wei of dust in the | |
| // contract after sweep+claim. | |
| -uint256 constant MAX_ALLOWABLE_DUST_WEI = 1e18; | |
| +uint256 constant MAX_ALLOWABLE_DUST_WEI = 1; | |
| /** | |
| * @title CCADisbursementTracker | |
| @@ -44,9 +44,9 @@ uint256 constant MAX_ALLOWABLE_DUST_WEI = 1e18; | |
| * Note well: `claimTokensBatch` makes things harder to track 1:1 with this contract, since that can make a single | |
| * transfer that contains bids from different phases of the sale. | |
| * | |
| - * The disburser should call the `recordDisbursement` function to record each disbursement, providing the txHash where | |
| - * the actual disbursement took place and the amount disbursed, so that the original token distribution can be verified. | |
| - * A disburser might choose to split disbursements in tranches, either because the allocation was done in different | |
| + * The disburser should call the `recordDisbursements` function to record disbursements, providing the txHash where the | |
| + * actual disbursement took place and the amount disbursed, so that the original token distribution can be verified. A | |
| + * disburser might choose to split disbursements in tranches, either because the allocation was done in different | |
| * phases of the sales, or for any other reason. The uniqueness and non-zero-ness of txHash isn't enforced | |
| * by design to allow for flexibility in the disburser's strategy. | |
| */ | |
| @@ -149,7 +149,7 @@ contract CCADisbursementTracker is ERC20 { | |
| } | |
| /// @notice Returns true when the sale is fully disbursed (total supply is zero and all missing disbursements recorded). | |
| - /// @dev Expects sweepUnsoldTokens to have been called, all bid tokens claimed, and all disbursements recorded via recordDisbursement. | |
| + /// @dev Expects sweepUnsoldTokens to have been called, all bid tokens claimed, and all disbursements recorded via recordDisbursements. | |
| function saleFullyDisbursed() public view returns (bool) { | |
| return saleFullyClaimed() && _totalMissingDisbursements == 0; | |
| } | |
| @@ -174,6 +174,7 @@ contract CCADisbursementTracker is ERC20 { | |
| error OnlyDisburserCanRecordDisbursements(); | |
| error OverdisbursementDetected(); | |
| error SaleNotFullyClaimed(); | |
| + error ArrayLengthMismatch(); | |
| event MissingDisbursementRecorded(address indexed to, uint256 value); | |
| event DisbursementCompleted(address indexed to, uint256 value, bytes32 txHash); | |
| @@ -241,26 +242,39 @@ contract CCADisbursementTracker is ERC20 { | |
| return result; | |
| } | |
| - /// @notice Records a single disbursement made off-chain, reducing the missing disbursement balance. | |
| + /// @notice Records disbursements made off-chain, reducing the missing disbursement balances. | |
| /// @dev Only callable by the disburser after the sale is fully claimed. Reverts on overdisbursement or zero amounts. | |
| - /// @param to Address the disbursement was made to | |
| - /// @param value Amount disbursed | |
| - /// @param txHash Transaction hash where the on-chain disbursement occurred | |
| - function recordDisbursement(address to, uint256 value, bytes32 txHash) external { | |
| - if (msg.sender != _DISBURSER) revert OnlyDisburserCanRecordDisbursements(); | |
| + /// @param recipients Addresses to record disbursements for | |
| + /// @param values Amounts disbursed to each recipient | |
| + /// @param txHashes Transaction hashes where the on-chain disbursements occurred | |
| + function recordDisbursements(address[] calldata recipients, uint256[] calldata values, bytes32[] calldata txHashes) | |
| + external | |
| + { | |
| if (!saleFullyClaimed()) revert SaleNotFullyClaimed(); | |
| - if (to == address(0)) revert NoZeroAddressRecipientAllowed(); | |
| - if (value == 0) revert NoZeroDisbursementsAllowed(); | |
| - if (_missingDisbursements[to] < value) revert OverdisbursementDetected(); | |
| - // txHash is intentionally not checked for 0x0 or uniqueness to allow for flexibility. | |
| + if (msg.sender != _DISBURSER) revert OnlyDisburserCanRecordDisbursements(); | |
| + uint256 len = recipients.length; | |
| + if (len != values.length || len != txHashes.length) revert ArrayLengthMismatch(); | |
| - unchecked { | |
| - _missingDisbursements[to] -= value; | |
| + for (uint256 i; i < len;) { | |
| + address to = recipients[i]; | |
| + uint256 value = values[i]; | |
| + if (to == address(0)) revert NoZeroAddressRecipientAllowed(); | |
| + if (value == 0) revert NoZeroDisbursementsAllowed(); | |
| + if (_missingDisbursements[to] < value) revert OverdisbursementDetected(); | |
| + // txHash is intentionally not checked for 0x0 or uniqueness to allow for flexibility. | |
| + | |
| + unchecked { | |
| + _missingDisbursements[to] -= value; | |
| + } | |
| _totalMissingDisbursements -= value; | |
| - } | |
| - _disbursements[to].push(Disbursement({value: value, txHash: txHash})); | |
| + _disbursements[to].push(Disbursement({value: value, txHash: txHashes[i]})); | |
| - emit DisbursementCompleted(to, value, txHash); | |
| + emit DisbursementCompleted(to, value, txHashes[i]); | |
| + | |
| + unchecked { | |
| + ++i; | |
| + } | |
| + } | |
| } | |
| /// @dev Reverts; this contract does not accept ETH. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment