Implement recommendations from 5.0 audit Phase 1A (#4398)

Co-authored-by: Francisco Giordano <fg@frang.io>
Co-authored-by: Hadrien Croubois <hadrien.croubois@gmail.com>
This commit is contained in:
Ernesto García
2023-07-03 12:02:06 -06:00
committed by GitHub
parent 06861dce54
commit bb64458928
38 changed files with 779 additions and 666 deletions

View File

@ -427,8 +427,9 @@ contract TimelockController is AccessControl, ERC721Holder, ERC1155Holder {
* an operation where the timelock is the target and the data is the ABI-encoded call to this function.
*/
function updateDelay(uint256 newDelay) external virtual {
if (msg.sender != address(this)) {
revert TimelockUnauthorizedCaller(msg.sender);
address sender = _msgSender();
if (sender != address(this)) {
revert TimelockUnauthorizedCaller(sender);
}
emit MinDelayChange(_minDelay, newDelay);
_minDelay = newDelay;

View File

@ -19,9 +19,9 @@ interface IVotes {
event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);
/**
* @dev Emitted when a token transfer or delegate change results in changes to a delegate's number of votes.
* @dev Emitted when a token transfer or delegate change results in changes to a delegate's number of voting units.
*/
event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance);
event DelegateVotesChanged(address indexed delegate, uint256 previousVotes, uint256 newVotes);
/**
* @dev Returns the current amount of votes that `account` has.

View File

@ -118,6 +118,7 @@ interface IERC1155Errors {
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
* @param tokenId Identifier number of a token.
*/
error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);

View File

@ -58,16 +58,12 @@ abstract contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI, IER
* Clients calling this function must replace the `\{id\}` substring with the
* actual token type ID.
*/
function uri(uint256) public view virtual returns (string memory) {
function uri(uint256 /* id */) public view virtual returns (string memory) {
return _uri;
}
/**
* @dev See {IERC1155-balanceOf}.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function balanceOf(address account, uint256 id) public view virtual returns (uint256) {
return _balances[id][account];
@ -114,11 +110,12 @@ abstract contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI, IER
/**
* @dev See {IERC1155-safeTransferFrom}.
*/
function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes memory data) public virtual {
if (from != _msgSender() && !isApprovedForAll(from, _msgSender())) {
revert ERC1155MissingApprovalForAll(_msgSender(), from);
function safeTransferFrom(address from, address to, uint256 id, uint256 value, bytes memory data) public virtual {
address sender = _msgSender();
if (from != sender && !isApprovedForAll(from, sender)) {
revert ERC1155MissingApprovalForAll(sender, from);
}
_safeTransferFrom(from, to, id, amount, data);
_safeTransferFrom(from, to, id, value, data);
}
/**
@ -128,17 +125,18 @@ abstract contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI, IER
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
uint256[] memory values,
bytes memory data
) public virtual {
if (from != _msgSender() && !isApprovedForAll(from, _msgSender())) {
revert ERC1155MissingApprovalForAll(_msgSender(), from);
address sender = _msgSender();
if (from != sender && !isApprovedForAll(from, sender)) {
revert ERC1155MissingApprovalForAll(sender, from);
}
_safeBatchTransferFrom(from, to, ids, amounts, data);
_safeBatchTransferFrom(from, to, ids, values, data);
}
/**
* @dev Transfers `amount` tokens of token type `id` from `from` to `to`. Will mint (or burn) if `from` (or `to`) is the zero address.
* @dev Transfers a `value` amount of tokens of type `id` from `from` to `to`. Will mint (or burn) if `from` (or `to`) is the zero address.
*
* Emits a {TransferSingle} event if the arrays contain one element, and {TransferBatch} otherwise.
*
@ -146,75 +144,96 @@ abstract contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI, IER
*
* - If `to` refers to a smart contract, it must implement either {IERC1155Receiver-onERC1155Received}
* or {IERC1155Receiver-onERC1155BatchReceived} and return the acceptance magic value.
* - `ids` and `values` must have the same length.
*
* NOTE: The ERC-1155 acceptance check is not performed in this function. See {_updateWithAcceptanceCheck} instead.
*/
function _update(
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) internal virtual {
if (ids.length != amounts.length) {
revert ERC1155InvalidArrayLength(ids.length, amounts.length);
function _update(address from, address to, uint256[] memory ids, uint256[] memory values) internal virtual {
if (ids.length != values.length) {
revert ERC1155InvalidArrayLength(ids.length, values.length);
}
address operator = _msgSender();
for (uint256 i = 0; i < ids.length; ++i) {
uint256 id = ids.unsafeMemoryAccess(i);
uint256 amount = amounts.unsafeMemoryAccess(i);
uint256 value = values.unsafeMemoryAccess(i);
if (from != address(0)) {
uint256 fromBalance = _balances[id][from];
if (fromBalance < amount) {
revert ERC1155InsufficientBalance(from, fromBalance, amount, id);
if (fromBalance < value) {
revert ERC1155InsufficientBalance(from, fromBalance, value, id);
}
unchecked {
_balances[id][from] = fromBalance - amount;
// Overflow not possible: value <= fromBalance
_balances[id][from] = fromBalance - value;
}
}
if (to != address(0)) {
_balances[id][to] += amount;
_balances[id][to] += value;
}
}
if (ids.length == 1) {
uint256 id = ids.unsafeMemoryAccess(0);
uint256 amount = amounts.unsafeMemoryAccess(0);
emit TransferSingle(operator, from, to, id, amount);
if (to != address(0)) {
_doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data);
}
uint256 value = values.unsafeMemoryAccess(0);
emit TransferSingle(operator, from, to, id, value);
} else {
emit TransferBatch(operator, from, to, ids, amounts);
if (to != address(0)) {
_doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data);
emit TransferBatch(operator, from, to, ids, values);
}
}
/**
* @dev Version of {_update} that performs the token acceptance check by calling {IERC1155Receiver-onERC1155Received}
* or {IERC1155Receiver-onERC1155BatchReceived} on the receiver address if it contains code (eg. is a smart contract
* at the moment of execution).
*
* IMPORTANT: Overriding this function is discouraged because it poses a reentrancy risk from the receiver. So any
* update to the contract state after this function would break the check-effect-interaction pattern. Consider
* overriding {_update} instead.
*/
function _updateWithAcceptanceCheck(
address from,
address to,
uint256[] memory ids,
uint256[] memory values,
bytes memory data
) internal virtual {
_update(from, to, ids, values);
if (to != address(0)) {
address operator = _msgSender();
if (ids.length == 1) {
uint256 id = ids.unsafeMemoryAccess(0);
uint256 value = values.unsafeMemoryAccess(0);
_doSafeTransferAcceptanceCheck(operator, from, to, id, value, data);
} else {
_doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, values, data);
}
}
}
/**
* @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
* @dev Transfers a `value` tokens of token type `id` from `from` to `to`.
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - `from` must have a balance of tokens of type `id` of at least `amount`.
* - `from` must have a balance of tokens of type `id` of at least `value` amount.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
* acceptance magic value.
*/
function _safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes memory data) internal {
function _safeTransferFrom(address from, address to, uint256 id, uint256 value, bytes memory data) internal {
if (to == address(0)) {
revert ERC1155InvalidReceiver(address(0));
}
if (from == address(0)) {
revert ERC1155InvalidSender(address(0));
}
(uint256[] memory ids, uint256[] memory amounts) = _asSingletonArrays(id, amount);
_update(from, to, ids, amounts, data);
(uint256[] memory ids, uint256[] memory values) = _asSingletonArrays(id, value);
_updateWithAcceptanceCheck(from, to, ids, values, data);
}
/**
@ -226,12 +245,13 @@ abstract contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI, IER
*
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
* acceptance magic value.
* - `ids` and `values` must have the same length.
*/
function _safeBatchTransferFrom(
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
uint256[] memory values,
bytes memory data
) internal {
if (to == address(0)) {
@ -240,7 +260,7 @@ abstract contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI, IER
if (from == address(0)) {
revert ERC1155InvalidSender(address(0));
}
_update(from, to, ids, amounts, data);
_updateWithAcceptanceCheck(from, to, ids, values, data);
}
/**
@ -249,7 +269,7 @@ abstract contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI, IER
* https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
*
* By this mechanism, any occurrence of the `\{id\}` substring in either the
* URI or any of the amounts in the JSON file at said URI will be replaced by
* URI or any of the values in the JSON file at said URI will be replaced by
* clients with the token type ID.
*
* For example, the `https://token-cdn-domain/\{id\}.json` URI would be
@ -267,7 +287,7 @@ abstract contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI, IER
}
/**
* @dev Creates `amount` tokens of token type `id`, and assigns them to `to`.
* @dev Creates a `value` amount of tokens of type `id`, and assigns them to `to`.
*
* Emits a {TransferSingle} event.
*
@ -277,12 +297,12 @@ abstract contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI, IER
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
* acceptance magic value.
*/
function _mint(address to, uint256 id, uint256 amount, bytes memory data) internal {
function _mint(address to, uint256 id, uint256 value, bytes memory data) internal {
if (to == address(0)) {
revert ERC1155InvalidReceiver(address(0));
}
(uint256[] memory ids, uint256[] memory amounts) = _asSingletonArrays(id, amount);
_update(address(0), to, ids, amounts, data);
(uint256[] memory ids, uint256[] memory values) = _asSingletonArrays(id, value);
_updateWithAcceptanceCheck(address(0), to, ids, values, data);
}
/**
@ -292,33 +312,34 @@ abstract contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI, IER
*
* Requirements:
*
* - `ids` and `amounts` must have the same length.
* - `ids` and `values` must have the same length.
* - `to` cannot be the zero address.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
* acceptance magic value.
*/
function _mintBatch(address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data) internal {
function _mintBatch(address to, uint256[] memory ids, uint256[] memory values, bytes memory data) internal {
if (to == address(0)) {
revert ERC1155InvalidReceiver(address(0));
}
_update(address(0), to, ids, amounts, data);
_updateWithAcceptanceCheck(address(0), to, ids, values, data);
}
/**
* @dev Destroys `amount` tokens of token type `id` from `from`
* @dev Destroys a `value` amount of tokens of type `id` from `from`
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `from` must have at least `amount` tokens of token type `id`.
* - `from` must have at least `value` amount of tokens of type `id`.
*/
function _burn(address from, uint256 id, uint256 amount) internal {
function _burn(address from, uint256 id, uint256 value) internal {
if (from == address(0)) {
revert ERC1155InvalidSender(address(0));
}
(uint256[] memory ids, uint256[] memory amounts) = _asSingletonArrays(id, amount);
_update(from, address(0), ids, amounts, "");
(uint256[] memory ids, uint256[] memory values) = _asSingletonArrays(id, value);
_updateWithAcceptanceCheck(from, address(0), ids, values, "");
}
/**
@ -328,38 +349,48 @@ abstract contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI, IER
*
* Requirements:
*
* - `ids` and `amounts` must have the same length.
* - `from` cannot be the zero address.
* - `from` must have at least `value` amount of tokens of type `id`.
* - `ids` and `values` must have the same length.
*/
function _burnBatch(address from, uint256[] memory ids, uint256[] memory amounts) internal {
function _burnBatch(address from, uint256[] memory ids, uint256[] memory values) internal {
if (from == address(0)) {
revert ERC1155InvalidSender(address(0));
}
_update(from, address(0), ids, amounts, "");
_updateWithAcceptanceCheck(from, address(0), ids, values, "");
}
/**
* @dev Approve `operator` to operate on all of `owner` tokens
*
* Emits an {ApprovalForAll} event.
*
* Requirements:
*
* - `operator` cannot be the zero address.
*/
function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {
if (owner == operator) {
revert ERC1155InvalidOperator(operator);
if (operator == address(0)) {
revert ERC1155InvalidOperator(address(0));
}
_operatorApprovals[owner][operator] = approved;
emit ApprovalForAll(owner, operator, approved);
}
/**
* @dev Performs an acceptance check by calling {IERC1155-onERC1155Received} on the `to` address
* if it contains code at the moment of execution.
*/
function _doSafeTransferAcceptanceCheck(
address operator,
address from,
address to,
uint256 id,
uint256 amount,
uint256 value,
bytes memory data
) private {
if (to.code.length > 0) {
try IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) {
try IERC1155Receiver(to).onERC1155Received(operator, from, id, value, data) returns (bytes4 response) {
if (response != IERC1155Receiver.onERC1155Received.selector) {
// Tokens rejected
revert ERC1155InvalidReceiver(to);
@ -378,16 +409,20 @@ abstract contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI, IER
}
}
/**
* @dev Performs a batch acceptance check by calling {IERC1155-onERC1155BatchReceived} on the `to` address
* if it contains code at the moment of execution.
*/
function _doSafeBatchTransferAcceptanceCheck(
address operator,
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
uint256[] memory values,
bytes memory data
) private {
if (to.code.length > 0) {
try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns (
try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, values, data) returns (
bytes4 response
) {
if (response != IERC1155Receiver.onERC1155BatchReceived.selector) {
@ -408,20 +443,28 @@ abstract contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI, IER
}
}
/**
* @dev Creates an array in memory with only one value for each of the elements provided.
*/
function _asSingletonArrays(
uint256 element1,
uint256 element2
) private pure returns (uint256[] memory array1, uint256[] memory array2) {
/// @solidity memory-safe-assembly
assembly {
// Load the free memory pointer
array1 := mload(0x40)
// Set array length to 1
mstore(array1, 1)
// Store the single element at the next word after the length (where content starts)
mstore(add(array1, 0x20), element1)
// Repeat for next array locating it right after the first array
array2 := add(array1, 0x40)
mstore(array2, 1)
mstore(add(array2, 0x20), element2)
// Update the free memory pointer by pointing after the second array
mstore(0x40, add(array2, 0x40))
}
}

View File

@ -13,7 +13,7 @@ import {IERC165} from "../../utils/introspection/IERC165.sol";
*/
interface IERC1155 is IERC165 {
/**
* @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
* @dev Emitted when `value` amount of tokens of type `id` are transferred from `from` to `to` by `operator`.
*/
event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
@ -45,7 +45,7 @@ interface IERC1155 is IERC165 {
event URI(string value, uint256 indexed id);
/**
* @dev Returns the amount of tokens of token type `id` owned by `account`.
* @dev Returns the value of tokens of token type `id` owned by `account`.
*
* Requirements:
*
@ -84,7 +84,7 @@ interface IERC1155 is IERC165 {
function isApprovedForAll(address account, address operator) external view returns (bool);
/**
* @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
* @dev Transfers a `value` amount of tokens of type `id` from `from` to `to`.
*
* WARNING: This function can potentially allow a reentrancy attack when transferring tokens
* to an untrusted contract, when invoking {onERC1155Received} on the receiver.
@ -97,11 +97,11 @@ interface IERC1155 is IERC165 {
*
* - `to` cannot be the zero address.
* - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}.
* - `from` must have a balance of tokens of type `id` of at least `amount`.
* - `from` must have a balance of tokens of type `id` of at least `value` amount.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
* acceptance magic value.
*/
function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external;
function safeTransferFrom(address from, address to, uint256 id, uint256 value, bytes calldata data) external;
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
@ -116,7 +116,7 @@ interface IERC1155 is IERC165 {
*
* Requirements:
*
* - `ids` and `amounts` must have the same length.
* - `ids` and `values` must have the same length.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
* acceptance magic value.
*/
@ -124,7 +124,7 @@ interface IERC1155 is IERC165 {
address from,
address to,
uint256[] calldata ids,
uint256[] calldata amounts,
uint256[] calldata values,
bytes calldata data
) external;
}

View File

@ -17,7 +17,7 @@ import {Pausable} from "../../../security/Pausable.sol";
* addition to inheriting this contract, you must define both functions, invoking the
* {Pausable-_pause} and {Pausable-_unpause} internal functions, with appropriate
* access control, e.g. using {AccessControl} or {Ownable}. Not doing so will
* make the contract unpausable.
* make the contract pause mechanism of the contract unreachable, and thus unusable.
*
* _Available since v3.1._
*/
@ -33,9 +33,8 @@ abstract contract ERC1155Pausable is ERC1155, Pausable {
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
uint256[] memory values
) internal virtual override whenNotPaused {
super._update(from, to, ids, amounts, data);
super._update(from, to, ids, values);
}
}

View File

@ -15,20 +15,22 @@ import {ERC1155} from "../ERC1155.sol";
*
* NOTE: This contract implies a global limit of 2**256 - 1 to the number of tokens
* that can be minted.
*
* CAUTION: This extension should not be added in an upgrade to an already deployed contract.
*/
abstract contract ERC1155Supply is ERC1155 {
mapping(uint256 => uint256) private _totalSupply;
uint256 private _totalSupplyAll;
/**
* @dev Total amount of tokens in with a given id.
* @dev Total value of tokens in with a given id.
*/
function totalSupply(uint256 id) public view virtual returns (uint256) {
return _totalSupply[id];
}
/**
* @dev Total amount of tokens.
* @dev Total value of tokens.
*/
function totalSupply() public view virtual returns (uint256) {
return _totalSupplyAll;
@ -48,35 +50,38 @@ abstract contract ERC1155Supply is ERC1155 {
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
uint256[] memory values
) internal virtual override {
super._update(from, to, ids, values);
if (from == address(0)) {
uint256 totalMintAmount = 0;
uint256 totalMintValue = 0;
for (uint256 i = 0; i < ids.length; ++i) {
uint256 amount = amounts[i];
_totalSupply[ids[i]] += amount;
totalMintAmount += amount;
uint256 value = values[i];
// Overflow check required: The rest of the code assumes that totalSupply never overflows
_totalSupply[ids[i]] += value;
totalMintValue += value;
}
_totalSupplyAll += totalMintAmount;
// Overflow check required: The rest of the code assumes that totalSupplyAll never overflows
_totalSupplyAll += totalMintValue;
}
if (to == address(0)) {
uint256 totalBurnAmount = 0;
uint256 totalBurnValue = 0;
for (uint256 i = 0; i < ids.length; ++i) {
uint256 id = ids[i];
uint256 amount = amounts[i];
_totalSupply[id] -= amount;
uint256 value = values[i];
unchecked {
// Overflow not possible: sum(amounts[i]) <= sum(totalSupply(i)) <= totalSupplyAll
totalBurnAmount += amount;
// Overflow not possible: values[i] <= balanceOf(from, ids[i]) <= totalSupply(ids[i])
_totalSupply[ids[i]] -= value;
// Overflow not possible: sum_i(values[i]) <= sum_i(totalSupply(ids[i])) <= totalSupplyAll
totalBurnValue += value;
}
}
unchecked {
// Overflow not possible: totalBurnAmount = sum(amounts[i]) <= sum(totalSupply(i)) <= totalSupplyAll
_totalSupplyAll -= totalBurnAmount;
// Overflow not possible: totalBurnValue = sum_i(values[i]) <= sum_i(totalSupply(ids[i])) <= totalSupplyAll
_totalSupplyAll -= totalBurnValue;
}
}
super._update(from, to, ids, amounts, data);
}
}

View File

@ -113,11 +113,11 @@ abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
* - the caller must have a balance of at least `value`.
*/
function transfer(address to, uint256 amount) public virtual returns (bool) {
function transfer(address to, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
_transfer(owner, to, value);
return true;
}
@ -131,16 +131,16 @@ abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
* NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public virtual returns (bool) {
function approve(address spender, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
_approve(owner, spender, value);
return true;
}
@ -156,14 +156,14 @@ abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
* - `from` must have a balance of at least `value`.
* - the caller must have allowance for ``from``'s tokens of at least
* `amount`.
* `value`.
*/
function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) {
function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
_spendAllowance(from, spender, value);
_transfer(from, to, value);
return true;
}
@ -198,6 +198,9 @@ abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `requestedDecrease`.
*
* NOTE: Although this function is designed to avoid double spending with {approval},
* it can still be frontrunned, preventing any attempt of allowance reduction.
*/
function decreaseAllowance(address spender, uint256 requestedDecrease) public virtual returns (bool) {
address owner = _msgSender();
@ -213,7 +216,7 @@ abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
}
/**
* @dev Moves `amount` of tokens from `from` to `to`.
* @dev Moves a `value` amount of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
@ -222,83 +225,84 @@ abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _transfer(address from, address to, uint256 amount) internal {
function _transfer(address from, address to, uint256 value) internal {
if (from == address(0)) {
revert ERC20InvalidSender(address(0));
}
if (to == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(from, to, amount);
_update(from, to, value);
}
/**
* @dev Transfers `amount` of tokens from `from` to `to`, or alternatively mints (or burns) if `from` (or `to`) is
* @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from` (or `to`) is
* the zero address. All customizations to transfers, mints, and burns should be done by overriding this function.
*
* Emits a {Transfer} event.
*/
function _update(address from, address to, uint256 amount) internal virtual {
function _update(address from, address to, uint256 value) internal virtual {
if (from == address(0)) {
_totalSupply += amount;
// Overflow check required: The rest of the code assumes that totalSupply never overflows
_totalSupply += value;
} else {
uint256 fromBalance = _balances[from];
if (fromBalance < amount) {
revert ERC20InsufficientBalance(from, fromBalance, amount);
if (fromBalance < value) {
revert ERC20InsufficientBalance(from, fromBalance, value);
}
unchecked {
// Overflow not possible: amount <= fromBalance <= totalSupply.
_balances[from] = fromBalance - amount;
// Overflow not possible: value <= fromBalance <= totalSupply.
_balances[from] = fromBalance - value;
}
}
if (to == address(0)) {
unchecked {
// Overflow not possible: amount <= totalSupply or amount <= fromBalance <= totalSupply.
_totalSupply -= amount;
// Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
_totalSupply -= value;
}
} else {
unchecked {
// Overflow not possible: balance + amount is at most totalSupply, which we know fits into a uint256.
_balances[to] += amount;
// Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
_balances[to] += value;
}
}
emit Transfer(from, to, amount);
emit Transfer(from, to, value);
}
/**
* @dev Creates `amount` tokens and assigns them to `account`, by transferring it from address(0).
* @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
* Relies on the `_update` mechanism
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _mint(address account, uint256 amount) internal {
function _mint(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(address(0), account, amount);
_update(address(0), account, value);
}
/**
* @dev Destroys `amount` tokens from `account`, by transferring it to address(0).
* @dev Destroys a `value` amount of tokens from `account`, by transferring it to address(0).
* Relies on the `_update` mechanism.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead
*/
function _burn(address account, uint256 amount) internal {
function _burn(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidSender(address(0));
}
_update(account, address(0), amount);
_update(account, address(0), value);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
* @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
@ -310,8 +314,8 @@ abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(address owner, address spender, uint256 amount) internal virtual {
_approve(owner, spender, amount, true);
function _approve(address owner, address spender, uint256 value) internal virtual {
_approve(owner, spender, value, true);
}
/**
@ -324,42 +328,42 @@ abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
* Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to true
* using the following override:
* ```
* function _approve(address owner, address spender, uint256 amount, bool) internal virtual override {
* super._approve(owner, spender, amount, true);
* function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
* super._approve(owner, spender, value, true);
* }
* ```
*
* Requirements are the same as {_approve}.
*/
function _approve(address owner, address spender, uint256 amount, bool emitEvent) internal virtual {
function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
if (owner == address(0)) {
revert ERC20InvalidApprover(address(0));
}
if (spender == address(0)) {
revert ERC20InvalidSpender(address(0));
}
_allowances[owner][spender] = amount;
_allowances[owner][spender] = value;
if (emitEvent) {
emit Approval(owner, spender, amount);
emit Approval(owner, spender, value);
}
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `amount`.
* @dev Updates `owner` s allowance for `spender` based on spent `value`.
*
* Does not update the allowance amount in case of infinite allowance.
* Does not update the allowance value in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Might emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
if (currentAllowance < amount) {
revert ERC20InsufficientAllowance(spender, currentAllowance, amount);
if (currentAllowance < value) {
revert ERC20InsufficientAllowance(spender, currentAllowance, value);
}
unchecked {
_approve(owner, spender, currentAllowance - amount, false);
_approve(owner, spender, currentAllowance - value, false);
}
}
}

View File

@ -22,23 +22,23 @@ interface IERC20 {
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
@ -50,7 +50,8 @@ interface IERC20 {
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
@ -63,16 +64,16 @@ interface IERC20 {
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
}

View File

@ -13,27 +13,27 @@ import {Context} from "../../../utils/Context.sol";
*/
abstract contract ERC20Burnable is Context, ERC20 {
/**
* @dev Destroys `amount` tokens from the caller.
* @dev Destroys a `value` amount of tokens from the caller.
*
* See {ERC20-_burn}.
*/
function burn(uint256 amount) public virtual {
_burn(_msgSender(), amount);
function burn(uint256 value) public virtual {
_burn(_msgSender(), value);
}
/**
* @dev Destroys `amount` tokens from `account`, deducting from the caller's
* allowance.
* @dev Destroys a `value` amount of tokens from `account`, deducting from
* the caller's allowance.
*
* See {ERC20-_burn} and {ERC20-allowance}.
*
* Requirements:
*
* - the caller must have allowance for ``accounts``'s tokens of at least
* `amount`.
* `value`.
*/
function burnFrom(address account, uint256 amount) public virtual {
_spendAllowance(account, _msgSender(), amount);
_burn(account, amount);
function burnFrom(address account, uint256 value) public virtual {
_spendAllowance(account, _msgSender(), value);
_burn(account, value);
}
}

View File

@ -42,8 +42,8 @@ abstract contract ERC20Capped is ERC20 {
/**
* @dev See {ERC20-_update}.
*/
function _update(address from, address to, uint256 amount) internal virtual override {
super._update(from, to, amount);
function _update(address from, address to, uint256 value) internal virtual override {
super._update(from, to, value);
if (from == address(0)) {
uint256 maxSupply = cap();

View File

@ -14,6 +14,10 @@ import {ERC20} from "../ERC20.sol";
* Adds the {flashLoan} method, which provides flash loan support at the token
* level. By default there is no fee, but this can be changed by overriding {flashFee}.
*
* NOTE: When this extension is used along with the {ERC20Capped} or {ERC20Votes} extensions,
* {maxFlashLoan} will not correctly reflect the maximum that can be flash minted. We recommend
* overriding {maxFlashLoan} so that it correctly reflects the supply cap.
*
* _Available since v4.1._
*/
abstract contract ERC20FlashMint is ERC20, IERC3156FlashLender {
@ -25,7 +29,7 @@ abstract contract ERC20FlashMint is ERC20, IERC3156FlashLender {
error ERC3156UnsupportedToken(address token);
/**
* @dev The requested loan exceeds the max loan amount for `token`.
* @dev The requested loan exceeds the max loan value for `token`.
*/
error ERC3156ExceededMaxLoan(uint256 maxLoan);
@ -38,6 +42,10 @@ abstract contract ERC20FlashMint is ERC20, IERC3156FlashLender {
* @dev Returns the maximum amount of tokens available for loan.
* @param token The address of the token that is requested.
* @return The amount of token that can be loaned.
*
* NOTE: This function does not consider any form of supply cap, so in case
* it's used in a token with a cap like {ERC20Capped}, make sure to override this
* function to integrate the cap instead of `type(uint256).max`.
*/
function maxFlashLoan(address token) public view virtual returns (uint256) {
return token == address(this) ? type(uint256).max - totalSupply() : 0;
@ -48,14 +56,14 @@ abstract contract ERC20FlashMint is ERC20, IERC3156FlashLender {
* the {_flashFee} function which returns the fee applied when doing flash
* loans.
* @param token The token to be flash loaned.
* @param amount The amount of tokens to be loaned.
* @param value The amount of tokens to be loaned.
* @return The fees applied to the corresponding flash loan.
*/
function flashFee(address token, uint256 amount) public view virtual returns (uint256) {
function flashFee(address token, uint256 value) public view virtual returns (uint256) {
if (token != address(this)) {
revert ERC3156UnsupportedToken(token);
}
return _flashFee(token, amount);
return _flashFee(token, value);
}
/**
@ -63,13 +71,13 @@ abstract contract ERC20FlashMint is ERC20, IERC3156FlashLender {
* implementation has 0 fees. This function can be overloaded to make
* the flash loan mechanism deflationary.
* @param token The token to be flash loaned.
* @param amount The amount of tokens to be loaned.
* @param value The amount of tokens to be loaned.
* @return The fees applied to the corresponding flash loan.
*/
function _flashFee(address token, uint256 amount) internal view virtual returns (uint256) {
function _flashFee(address token, uint256 value) internal view virtual returns (uint256) {
// silence warning about unused variable without the addition of bytecode.
token;
amount;
value;
return 0;
}
@ -87,13 +95,13 @@ abstract contract ERC20FlashMint is ERC20, IERC3156FlashLender {
* @dev Performs a flash loan. New tokens are minted and sent to the
* `receiver`, who is required to implement the {IERC3156FlashBorrower}
* interface. By the end of the flash loan, the receiver is expected to own
* amount + fee tokens and have them approved back to the token contract itself so
* value + fee tokens and have them approved back to the token contract itself so
* they can be burned.
* @param receiver The receiver of the flash loan. Should implement the
* {IERC3156FlashBorrower-onFlashLoan} interface.
* @param token The token to be flash loaned. Only `address(this)` is
* supported.
* @param amount The amount of tokens to be loaned.
* @param value The amount of tokens to be loaned.
* @param data An arbitrary datafield that is passed to the receiver.
* @return `true` if the flash loan was successful.
*/
@ -103,24 +111,24 @@ abstract contract ERC20FlashMint is ERC20, IERC3156FlashLender {
function flashLoan(
IERC3156FlashBorrower receiver,
address token,
uint256 amount,
uint256 value,
bytes calldata data
) public virtual returns (bool) {
uint256 maxLoan = maxFlashLoan(token);
if (amount > maxLoan) {
if (value > maxLoan) {
revert ERC3156ExceededMaxLoan(maxLoan);
}
uint256 fee = flashFee(token, amount);
_mint(address(receiver), amount);
if (receiver.onFlashLoan(msg.sender, token, amount, fee, data) != _RETURN_VALUE) {
uint256 fee = flashFee(token, value);
_mint(address(receiver), value);
if (receiver.onFlashLoan(_msgSender(), token, value, fee, data) != _RETURN_VALUE) {
revert ERC3156InvalidReceiver(address(receiver));
}
address flashFeeReceiver = _flashFeeReceiver();
_spendAllowance(address(receiver), address(this), amount + fee);
_spendAllowance(address(receiver), address(this), value + fee);
if (fee == 0 || flashFeeReceiver == address(0)) {
_burn(address(receiver), amount + fee);
_burn(address(receiver), value + fee);
} else {
_burn(address(receiver), amount);
_burn(address(receiver), value);
_transfer(address(receiver), flashFeeReceiver, fee);
}
return true;

View File

@ -17,7 +17,7 @@ import {Pausable} from "../../../security/Pausable.sol";
* addition to inheriting this contract, you must define both functions, invoking the
* {Pausable-_pause} and {Pausable-_unpause} internal functions, with appropriate
* access control, e.g. using {AccessControl} or {Ownable}. Not doing so will
* make the contract unpausable.
* make the contract pause mechanism of the contract unreachable, and thus unusable.
*/
abstract contract ERC20Pausable is ERC20, Pausable {
/**
@ -27,7 +27,7 @@ abstract contract ERC20Pausable is ERC20, Pausable {
*
* - the contract must not be paused.
*/
function _update(address from, address to, uint256 amount) internal virtual override whenNotPaused {
super._update(from, to, amount);
function _update(address from, address to, uint256 value) internal virtual override whenNotPaused {
super._update(from, to, value);
}
}

View File

@ -41,8 +41,8 @@ abstract contract ERC20Votes is ERC20, Votes {
*
* Emits a {IVotes-DelegateVotesChanged} event.
*/
function _update(address from, address to, uint256 amount) internal virtual override {
super._update(from, to, amount);
function _update(address from, address to, uint256 value) internal virtual override {
super._update(from, to, value);
if (from == address(0)) {
uint256 supply = totalSupply();
uint256 cap = _maxSupply();
@ -50,11 +50,14 @@ abstract contract ERC20Votes is ERC20, Votes {
revert ERC20ExceededSafeSupply(supply, cap);
}
}
_transferVotingUnits(from, to, amount);
_transferVotingUnits(from, to, value);
}
/**
* @dev Returns the balance of `account`.
* @dev Returns the voting units of an `account`.
*
* WARNING: Overriding this function may compromise the internal vote accounting.
* `ERC20Votes` assumes tokens map to voting units 1:1 and this is not easy to change.
*/
function _getVotingUnits(address account) internal view virtual override returns (uint256) {
return balanceOf(account);

View File

@ -51,22 +51,28 @@ abstract contract ERC20Wrapper is ERC20 {
/**
* @dev Allow a user to deposit underlying tokens and mint the corresponding number of wrapped tokens.
*/
function depositFor(address account, uint256 amount) public virtual returns (bool) {
function depositFor(address account, uint256 value) public virtual returns (bool) {
address sender = _msgSender();
if (sender == address(this)) {
revert ERC20InvalidSender(address(this));
}
SafeERC20.safeTransferFrom(_underlying, sender, address(this), amount);
_mint(account, amount);
if (account == address(this)) {
revert ERC20InvalidReceiver(account);
}
SafeERC20.safeTransferFrom(_underlying, sender, address(this), value);
_mint(account, value);
return true;
}
/**
* @dev Allow a user to burn a number of wrapped tokens and withdraw the corresponding number of underlying tokens.
*/
function withdrawTo(address account, uint256 amount) public virtual returns (bool) {
_burn(_msgSender(), amount);
SafeERC20.safeTransfer(_underlying, account, amount);
function withdrawTo(address account, uint256 value) public virtual returns (bool) {
if (account == address(this)) {
revert ERC20InvalidReceiver(account);
}
_burn(_msgSender(), value);
SafeERC20.safeTransfer(_underlying, account, value);
return true;
}

View File

@ -486,7 +486,7 @@ abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Er
* that `ownerOf(tokenId)` is `a`.
*/
// solhint-disable-next-line func-name-mixedcase
function __unsafe_increaseBalance(address account, uint256 amount) internal {
_balances[account] += amount;
function __unsafe_increaseBalance(address account, uint256 value) internal {
_balances[account] += value;
}
}

View File

@ -17,7 +17,7 @@ import {Pausable} from "../../../security/Pausable.sol";
* addition to inheriting this contract, you must define both functions, invoking the
* {Pausable-_pause} and {Pausable-_unpause} internal functions, with appropriate
* access control, e.g. using {AccessControl} or {Ownable}. Not doing so will
* make the contract unpausable.
* make the contract pause mechanism of the contract unreachable, and thus unusable.
*/
abstract contract ERC721Pausable is ERC721, Pausable {
/**

View File

@ -13,7 +13,7 @@ abstract contract Nonces {
mapping(address => uint256) private _nonces;
/**
* @dev Returns an address nonce.
* @dev Returns an the next unused nonce for an address.
*/
function nonces(address owner) public view virtual returns (uint256) {
return _nonces[owner];

View File

@ -5,13 +5,12 @@
pragma solidity ^0.8.19;
import {Math} from "../math/Math.sol";
import {SafeCast} from "../math/SafeCast.sol";
/**
* @dev This library defines the `History` struct, for checkpointing values as they change at different points in
* @dev This library defines the `Trace*` struct, for checkpointing values as they change at different points in
* time, and later looking up past values by block number. See {Votes} as an example.
*
* To create a history of checkpoints define a variable type `Checkpoints.History` in your contract, and store a new
* To create a history of checkpoints define a variable type `Checkpoints.Trace*` in your contract, and store a new
* checkpoint for the current transaction block using the {push} function.
*
* _Available since v4.5._
@ -35,6 +34,8 @@ library Checkpoints {
* @dev Pushes a (`key`, `value`) pair into a Trace224 so that it is stored as the checkpoint.
*
* Returns previous value and new value.
*
* IMPORTANT: Never accept `key` as a user input, since an arbitrary `type(uint32).max` key set will disable the library.
*/
function push(Trace224 storage self, uint32 key, uint224 value) internal returns (uint224, uint224) {
return _insert(self._checkpoints, key, value);
@ -220,6 +221,8 @@ library Checkpoints {
* @dev Pushes a (`key`, `value`) pair into a Trace160 so that it is stored as the checkpoint.
*
* Returns previous value and new value.
*
* IMPORTANT: Never accept `key` as a user input, since an arbitrary `type(uint96).max` key set will disable the library.
*/
function push(Trace160 storage self, uint96 key, uint160 value) internal returns (uint160, uint160) {
return _insert(self._checkpoints, key, value);