Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More flexible getAvailableFunds #698

Merged
merged 9 commits into from
Jul 7, 2023
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
2 changes: 1 addition & 1 deletion contracts/domain/BosonConstants.sol
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ string constant BUNDLE_REQUIRES_AT_LEAST_ONE_TWIN_AND_ONE_OFFER = "Bundle must h
// Revert Reasons: Funds related
string constant NATIVE_WRONG_ADDRESS = "Native token address must be 0";
string constant NATIVE_WRONG_AMOUNT = "Transferred value must match amount";
string constant TOKEN_NAME_UNSPECIFIED = "Token name unspecified";
string constant TOKEN_NAME_UNSPECIFIED = "Token name unavailable";
string constant NATIVE_CURRENCY = "Native currency";
string constant TOKEN_AMOUNT_MISMATCH = "Number of amounts should match number of tokens";
string constant NOTHING_TO_WITHDRAW = "Nothing to withdraw";
Expand Down
36 changes: 35 additions & 1 deletion contracts/interfaces/handlers/IBosonFundsHandler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { IBosonFundsLibEvents } from "../events/IBosonFundsEvents.sol";
*
* @notice Handles custody and withdrawal of buyer and seller funds within the protocol.
*
* The ERC-165 identifier for this interface is: 0xb5850c2a
* The ERC-165 identifier for this interface is: 0x2f4a64d7
*/
interface IBosonFundsHandler is IBosonFundsEvents, IBosonFundsLibEvents {
/**
Expand Down Expand Up @@ -72,8 +72,42 @@ interface IBosonFundsHandler is IBosonFundsEvents, IBosonFundsLibEvents {
*/
function withdrawProtocolFees(address[] calldata _tokenList, uint256[] calldata _tokenAmounts) external;

/**
* @notice Returns list of addresses for which the entity has funds available.
* If the list is too long, it can be retrieved in chunks by using `getTokenListPaginated` and specifying _limit and _offset.
*
* @param _entityId - id of entity for which availability of funds should be checked
* @return tokenList - list of token addresses
*/
function getTokenList(uint256 _entityId) external view returns (address[] memory tokenList);

/**
* @notice Returns list of addresses for which the entity has funds available.
*
* @param _entityId - id of entity for which availability of funds should be checked
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add _limit and _offset parameters here

* @param _limit - the maximum number of token addresses that should be returned starting from the index defined by `_offset`. If `_offset` + `_limit` exceeds total tokens, `_limit` is adjusted to return all remaining tokens.
* @param _offset - the starting index from which to return token addresses. If `_offset` is greater than or equal to total tokens, an empty list is returned.
* @return tokenList - list of token addresses
*/
function getTokenListPaginated(
uint256 _entityId,
uint256 _limit,
uint256 _offset
) external view returns (address[] memory tokenList);

/**
* @notice Returns the information about the funds that an entity can use as a sellerDeposit and/or withdraw from the protocol.
* It tries to get information about all tokens that the entity has in availableFunds storage.
* If the token list is too long, this call may run out of gas. In this case, the caller should use the function `getAvailableFunds` and pass the token list.
*
* @param _entityId - id of entity for which availability of funds should be checked
* @return availableFunds - list of token addresses, token names and amount that can be used as a seller deposit or be withdrawn
*/
function getAllAvailableFunds(uint256 _entityId) external view returns (BosonTypes.Funds[] memory availableFunds);

/**
* @notice Returns the information about the funds that an entity can use as a sellerDeposit and/or withdraw from the protocol.
* To get a list of tokens that the entity has in availableFunds storage, use the function `getTokenList`.
*
* @param _entityId - id of entity for which availability of funds should be checked
* @param _tokenList - list of tokens addresses to get available funds
Expand Down
20 changes: 20 additions & 0 deletions contracts/mock/Foreign20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,25 @@ contract Foreign20Malicious2 is Foreign20 {
}
}

/**
* @title Foreign20 that consumes all gas when name called
*
*
* @notice Mock ERC-(20) for Unit Testing
*/
contract Foreign20MaliciousName is Foreign20 {
function name() public pure override returns (string memory) {
// name consumes all gas
unchecked {
uint256 i = 0;
while (true) {
i++;
}
}
return "nothing";
}
}

/**
* @title Foreign20 that takes a fee during the transfer
*
Expand Down Expand Up @@ -224,5 +243,6 @@ contract Foreign20GasTheft is Foreign20 {
while (true) {
// consume all gas
}
return false;
}
}
69 changes: 61 additions & 8 deletions contracts/protocol/facets/FundsHandlerFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -149,24 +149,77 @@ contract FundsHandlerFacet is IBosonFundsHandler, ProtocolBase {
withdrawFundsInternal(protocolAddresses().treasury, 0, _tokenList, _tokenAmounts);
}

/**
* @notice Returns list of addresses for which the entity has funds available.
* If the list is too long, it can be retrieved in chunks by using `getTokenListPaginated` and specifying _limit and _offset.
*
* @param _entityId - id of entity for which availability of funds should be checked
* @return tokenList - list of token addresses
*/
function getTokenList(uint256 _entityId) external view override returns (address[] memory tokenList) {
return protocolLookups().tokenList[_entityId];
}

/**
* @notice Returns list of addresses for which the entity has funds available.
*
* @param _entityId - id of entity for which availability of funds should be checked
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add _limit and _offset parameters here

* @param _limit - the maximum number of token addresses that should be returned starting from the index defined by `_offset`. If `_offset` + `_limit` exceeds total tokens, `_limit` is adjusted to return all remaining tokens.
* @param _offset - the starting index from which to return token addresses. If `_offset` is greater than or equal to total tokens, an empty list is returned.
* @return tokenList - list of token addresses
*/
function getTokenListPaginated(
uint256 _entityId,
uint256 _limit,
uint256 _offset
) external view override returns (address[] memory tokenList) {
address[] storage tokens = protocolLookups().tokenList[_entityId];

if (_offset >= tokens.length) {
return new address[](0);
} else if (_offset + _limit > tokens.length) {
_limit = tokens.length - _offset;
}

tokenList = new address[](_limit);

for (uint i = 0; i < _limit; i++) {
tokenList[i] = tokens[_offset++];
}

return tokenList;
}

/**
* @notice Returns the information about the funds that an entity can use as a sellerDeposit and/or withdraw from the protocol.
* It tries to get information about all tokens that the entity has in availableFunds storage.
* If the token list is too long, this call may run out of gas. In this case, the caller should use the function `getAvailableFunds` and pass the token list.
*
* @param _entityId - id of entity for which availability of funds should be checked
* @return availableFunds - list of token addresses, token names and amount that can be used as a seller deposit or be withdrawn
*/
function getAllAvailableFunds(uint256 _entityId) external view override returns (Funds[] memory availableFunds) {
// get list of token addresses for the entity
address[] memory tokenList = protocolLookups().tokenList[_entityId];
return getAvailableFunds(_entityId, tokenList);
}

/**
* @notice Returns the information about the funds that an entity can use as a sellerDeposit and/or withdraw from the protocol.
* To get a list of tokens that the entity has in availableFunds storage, use the function `getTokenList`.
*
* @param _entityId - id of entity for which availability of funds should be checked
* @param _tokenList - list of tokens addresses to get available funds
* @return availableFunds - list of token addresses, token names and amount that can be used as a seller deposit or be withdrawn
*/
function getAvailableFunds(
uint256 _entityId,
address[] calldata _tokenList
) external view override returns (Funds[] memory availableFunds) {
// Cache protocol lookups for reference
ProtocolLib.ProtocolLookups storage lookups = protocolLookups();

address[] memory _tokenList
) public view override returns (Funds[] memory availableFunds) {
availableFunds = new Funds[](_tokenList.length);

// Get entity's availableFunds storage pointer
mapping(address => uint256) storage entityFunds = lookups.availableFunds[_entityId];
mapping(address => uint256) storage entityFunds = protocolLookups().availableFunds[_entityId];

for (uint256 i = 0; i < _tokenList.length; i++) {
address tokenAddress = _tokenList[i];
Expand All @@ -176,8 +229,8 @@ contract FundsHandlerFacet is IBosonFundsHandler, ProtocolBase {
// If tokenAddress is 0, it represents the native currency
tokenName = NATIVE_CURRENCY;
} else {
// Try to get token name
try IERC20Metadata(tokenAddress).name() returns (string memory name) {
// Try to get token name. Typically, name consumes less than 30,000 gas, but we leave some extra gas just in case
try IERC20Metadata(tokenAddress).name{ gas: 40000 }() returns (string memory name) {
tokenName = name;
} catch {
tokenName = TOKEN_NAME_UNSPECIFIED;
Expand Down
Loading