_redeemTokensOf
Contract: JBPayoutRedemptionPaymentTerminal
- Step by step
 - Code
 - Errors
 - Events
 - Bug bounty
 
Holders can redeem their tokens to claim the project's overflowed tokens, or to trigger rules determined by the project's current funding cycle's data source.
Only a token holder or a designated operator can redeem its tokens.
Definition
function _redeemTokensOf(
  address _holder,
  uint256 _projectId,
  uint256 _tokenCount,
  uint256 _minReturnedTokens,
  address payable _beneficiary,
  string memory _memo,
  bytes memory _metadata
) private returns (uint256 reclaimAmount) { ... }
- Arguments:
_holderis the account to redeem tokens for._projectIdis the ID of the project to which the tokens being redeemed belong._tokenCountis the number of project tokens to redeem, as a fixed point number with 18 decimals._minReturnedTokensis the minimum amount of terminal tokens expected in return, as a fixed point number with the same amount of decimals as this terminal._beneficiaryis the address to send the terminal tokens to._memois a memo to pass along to the emitted event._metadataare bytes to send along to the data source, delegate, and emitted event, if provided.
 - The function is private to this contract.
 - The function returns the amount of terminal tokens that the tokens were redeemed for, as a fixed point number with the same amount of decimals as this terminal.
 
Body
- 
Make sure the provided beneficiary of the claimed funds isn't the zero address.
// Can't send reclaimed funds to the zero address.
if (_beneficiary == address(0)) revert REDEEM_TO_ZERO_ADDRESS(); - 
Define a reference to the project's funding cycle during which the redemption is being made.
// Define variables that will be needed outside the scoped section below.
// Keep a reference to the funding cycle during which the redemption is being made.
JBFundingCycle memory _fundingCycle; - 
The following scoped block is a bit of a hack to prevent a "Stack too deep" error.
// Scoped section prevents stack too deep. `_delegate` only used within scope.
{ ... }- 
Get a reference to the redemption delegate that.
IJBRedemptionDelegate _delegate; - 
Record the redemption and get a reference to the funding cycle during which the redemption was made, the terminal token amount that should be reclaimed, a delegate to callback to, and an updated memo.
// Record the redemption.
(_fundingCycle, reclaimAmount, _delegate, _memo) = store.recordRedemptionFor(
_holder,
_projectId,
_tokenCount,
_memo,
_metadata
);Internal references:
External references:
 - 
Make sure the amount of terminal tokens being reclaimed is at least as much as the specified minimum.
// The amount being reclaimed must be at least as much as was expected.
if (reclaimAmount < _minReturnedTokens) revert INADEQUATE_RECLAIM_AMOUNT(); - 
Burn the project's tokens if needed.
// Burn the project tokens.
if (_tokenCount > 0)
IJBController(directory.controllerOf(_projectId)).burnTokensOf(
_holder,
_projectId,
_tokenCount,
'',
false
);Internal references:
External references:
 - 
If a delegate was provided, callback to its
didRedeemfunction, and emit an event with the relevant parameters..// If a delegate was returned by the data source, issue a callback to it.
if (_delegate != IJBRedemptionDelegate(address(0))) {
JBDidRedeemData memory _data = JBDidRedeemData(
_holder,
_projectId,
_tokenCount,
_fundingCycle.configuration,
JBTokenAmount(token, reclaimAmount, decimals, currency),
_beneficiary,
_memo,
_metadata
);
_delegate.didRedeem(_data);
emit DelegateDidRedeem(_delegate, _data, msg.sender);
}Internal references:
External references:
Event references:
 
 - 
 - 
If an amount is being reclaimed, send the funds to the beneficiary.
// Send the reclaimed funds to the beneficiary.
if (reclaimAmount > 0) _transferFrom(address(this), _beneficiary, reclaimAmount);Internal references:
 - 
Emit a
RedeemTokensevent with the relevant parameters.emit RedeemTokens(
_fundingCycle.configuration,
_fundingCycle.number,
_projectId,
_holder,
_beneficiary,
_tokenCount,
reclaimAmount,
_memo,
msg.sender
);Event references:
 
/**
  @notice
  Holders can redeem their tokens to claim the project's overflowed tokens, or to trigger rules determined by the project's current funding cycle's data source.
  @dev
  Only a token holder or a designated operator can redeem its tokens.
  @param _holder The account to redeem tokens for.
  @param _projectId The ID of the project to which the tokens being redeemed belong.
  @param _tokenCount The number of project tokens to redeem, as a fixed point number with 18 decimals.
  @param _minReturnedTokens The minimum amount of terminal tokens expected in return, as a fixed point number with the same amount of decimals as this terminal.
  @param _beneficiary The address to send the terminal tokens to.
  @param _memo A memo to pass along to the emitted event.
  @param _metadata Bytes to send along to the data source, delegate, and emitted event, if provided.
  @return reclaimAmount The amount of terminal tokens that the project tokens were redeemed for, as a fixed point number with 18 decimals.
*/
function _redeemTokensOf(
  address _holder,
  uint256 _projectId,
  uint256 _tokenCount,
  uint256 _minReturnedTokens,
  address payable _beneficiary,
  string memory _memo,
  bytes memory _metadata
) private returns (uint256 reclaimAmount) {
  // Can't send reclaimed funds to the zero address.
  if (_beneficiary == address(0)) revert REDEEM_TO_ZERO_ADDRESS();
    // Define variables that will be needed outside the scoped section below.
  // Keep a reference to the funding cycle during which the redemption is being made.
  JBFundingCycle memory _fundingCycle;
  // Scoped section prevents stack too deep. `_delegate` only used within scope.
  {
    IJBRedemptionDelegate _delegate;
    // Record the redemption.
    (_fundingCycle, reclaimAmount, _delegate, _memo) = store.recordRedemptionFor(
      _holder,
      _projectId,
      _tokenCount,
      _memo,
      _metadata
    );
    // The amount being reclaimed must be at least as much as was expected.
    if (reclaimAmount < _minReturnedTokens) revert INADEQUATE_RECLAIM_AMOUNT();
    // Burn the project tokens.
    if (_tokenCount > 0)
      IJBController(directory.controllerOf(_projectId)).burnTokensOf(
        _holder,
        _projectId,
        _tokenCount,
        '',
        false
      );
    // If a delegate was returned by the data source, issue a callback to it.
    if (_delegate != IJBRedemptionDelegate(address(0))) {
      JBDidRedeemData memory _data = JBDidRedeemData(
        _holder,
        _projectId,
        _fundingCycle.configuration,
        _tokenCount,
        JBTokenAmount(token, reclaimAmount, decimals, currency),
        _beneficiary,
        _memo,
        _metadata
      );
      _delegate.didRedeem(_data);
      emit DelegateDidRedeem(_delegate, _data, msg.sender);
    }
  }
  // Send the reclaimed funds to the beneficiary.
  if (reclaimAmount > 0) _transferFrom(address(this), _beneficiary, reclaimAmount);
  emit RedeemTokens(
    _fundingCycle.configuration,
    _fundingCycle.number,
    _projectId,
    _holder,
    _beneficiary,
    _tokenCount,
    reclaimAmount,
    _memo,
    _metadata,
    msg.sender
  );
}
| String | Description | 
|---|---|
REDEEM_TO_ZERO_ADDRESS | Thrown if the zero address was sent as the beneficiary. | 
INADEQUATE_RECLAIM_AMOUNT | Thrown if the amount being reclaimed is less than the specified minimum. | 
| Name | Data | 
|---|---|
RedeemTokens | 
  | 
| Category | Description | Reward | 
|---|---|---|
| Optimization | Help make this operation more efficient. | 0.5ETH | 
| Low severity | Identify a vulnerability in this operation that could lead to an inconvenience for a user of the protocol or for a protocol developer. | 1ETH | 
| High severity | Identify a vulnerability in this operation that could lead to data corruption or loss of funds. | 5+ETH |