diff --git a/contracts/LSP7DigitalAsset/ILSP7DigitalAsset.sol b/contracts/LSP7DigitalAsset/ILSP7DigitalAsset.sol
index 6a977e7de..a17060c78 100644
--- a/contracts/LSP7DigitalAsset/ILSP7DigitalAsset.sol
+++ b/contracts/LSP7DigitalAsset/ILSP7DigitalAsset.sol
@@ -15,14 +15,13 @@ interface ILSP7DigitalAsset is IERC165, IERC725Y {
// --- Events
/**
- * @dev Emitted when `amount` tokens is transferred from `from` to `to`.
- * @param operator The address of operator sending tokens
- * @param from The address which tokens are sent
- * @param to The receiving address
- * @param amount The amount of tokens transferred
- * @param allowNonLSP1Recipient When set to TRUE, `to` may be any address but
- * when set to FALSE `to` must be a contract that supports LSP1 UniversalReceiver
- * @param data Additional data the caller wants included in the emitted event, and sent in the hooks to `from` and `to` addresses
+ * @dev Emitted when the `from` transferred successfully `amount` of tokens to `to`.
+ * @param operator The address of the operator that executed the transfer.
+ * @param from The address which tokens were sent from (balance decreased by `-amount`).
+ * @param to The address that received the tokens (balance increased by `+amount`).
+ * @param amount The amount of tokens transferred.
+ * @param allowNonLSP1Recipient if the transferred enforced the `to` recipient address to be a contract that implements the LSP1 standard or not.
+ * @param data Any additional data included by the caller during the transfer, and sent in the LSP1 hooks to the `from` and `to` addresses.
*/
event Transfer(
address indexed operator,
@@ -46,7 +45,7 @@ interface ILSP7DigitalAsset is IERC165, IERC725Y {
);
/**
- * @dev Emitted when `tokenOwner` disables `operator` for `amount` tokens.
+ * @dev Emitted when `tokenOwner` disables `operator` for `amount` tokens and set its {`authorizedAmountFor(...)`} to `0`.
* @param operator The address revoked from operating
* @param tokenOwner The token owner
*/
@@ -55,78 +54,83 @@ interface ILSP7DigitalAsset is IERC165, IERC725Y {
// --- Token queries
/**
- * @dev Returns the number of decimals used to get its user representation
- * If the contract represents a NFT then 0 SHOULD be used, otherwise 18 is the common value
+ * @dev Returns the number of decimals used to get its user representation.
+ * If the asset contract has been set to be non-divisible via the `isNonDivisible_` parameter in
+ * the `constructor`, the decimals returned wiil be `0`. Otherwise `18` is the common value.
*
- * NOTE: This information is only used for _display_ purposes: it in
+ * @custom:notice This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {balanceOf} and {transfer}.
+ *
+ * @return the number of decimals. If `0` is returned, the asset is non-divisible.
*/
function decimals() external view returns (uint8);
/**
- * @dev Returns the number of existing tokens.
- * @return The number of existing tokens
+ * @dev Returns the number of existing tokens that have been minted in this contract.
+ * @return The number of existing tokens.
*/
function totalSupply() external view returns (uint256);
// --- Token owner queries
/**
- * @dev Returns the number of tokens owned by `tokenOwner`.
- * @param tokenOwner The address to query
- * @return The number of tokens owned by this address
+ * @dev Get the number of tokens owned by `tokenOwner`.
+ * If the token is divisible (the {decimals} function returns `18`), the amount returned should be divided
+ * by 1e18 to get a better picture of the actual balance of the `tokenOwner`.
+ *
+ * _Example:_
+ *
+ * ```
+ * balanceOf(someAddress) -> 42_000_000_000_000_000_000 / 1e18 = 42 tokens
+ * ```
+ *
+ * @param tokenOwner The address of the token holder to query the balance for.
+ * @return The amount of tokens owned by `tokenOwner`.
*/
function balanceOf(address tokenOwner) external view returns (uint256);
// --- Operator functionality
/**
- * @param operator The address to authorize as an operator.
- * @param amount The amount of tokens operator has access to.
- * @dev Sets `amount` as the amount of tokens `operator` address has access to from callers tokens.
- *
- * To avoid front-running and Allowance Double-Spend Exploit when
- * increasing or decreasing the authorized amount of an operator,
- * it is advised to:
- * 1. call {revokeOperator} first, and
- * 2. then re-call {authorizeOperator} with the new amount
+ * @dev Sets an `amount` of tokens that an `operator` has access from the caller's balance (allowance). See {authorizedAmountFor}.
*
- * for more information, see:
- * https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM/
- *
- *
- * See {authorizedAmountFor}.
- *
- * Requirements
+ * @param operator The address to authorize as an operator.
+ * @param amount The allowance amount of tokens operator has access to.
*
+ * @custom:requirements
* - `operator` cannot be the zero address.
*
- * Emits an {AuthorizedOperator} event.
+ * @custom:events {AuthorizedOperator} when allowance is given to a new operator or
+ * an existing operator's allowance is updated.
*/
function authorizeOperator(address operator, uint256 amount) external;
/**
- * @param operator The address to revoke as an operator.
- * @dev Removes `operator` address as an operator of callers tokens.
- *
- * See {authorizedAmountFor}.
+ * @dev Removes the `operator` address as an operator of callers tokens, disallowing it to send any amount of tokens
+ * on behalf of the token owner (the caller of the function `msg.sender`). See also {authorizedAmountFor}.
*
- * Requirements
+ * @param operator The address to revoke as an operator.
*
+ * @custom:requirements
+ * - `operator` cannot be calling address.
* - `operator` cannot be the zero address.
*
- * Emits a {RevokedOperator} event.
+ * @custom:events {RevokedOperator} event with address of the operator being revoked for the caller (token holder).
*/
function revokeOperator(address operator) external;
/**
- * @param operator The address to query operator status for.
- * @param tokenOwner The token owner.
- * @return The amount of tokens `operator` address has access to from `tokenOwner`.
- * @dev Returns amount of tokens `operator` address has access to from `tokenOwner`.
- * Operators can send and burn tokens on behalf of their owners. The tokenOwner is their own
- * operator.
+ * @dev Get the amount of tokens `operator` address has access to from `tokenOwner`.
+ * Operators can send and burn tokens on behalf of their owners.
+ *
+ * @param operator The operator's address to query the authorized amount for.
+ * @param tokenOwner The token owner that `operator` has allowance on.
+ *
+ * @return The amount of tokens the `operator`'s address has access on the `tokenOwner`'s balance.
+ *
+ * @custom:info If this function is called with the same address for `operator` and `tokenOwner`, it will simply read the `tokenOwner`'s balance
+ * (since a tokenOwner is its own operator).
*/
function authorizedAmountFor(
address operator,
@@ -136,25 +140,35 @@ interface ILSP7DigitalAsset is IERC165, IERC725Y {
// --- Transfer functionality
/**
- * @param from The sending address.
- * @param to The receiving address.
- * @param amount The amount of tokens to transfer.
- * @param allowNonLSP1Recipient When set to TRUE, to may be any address but
- * when set to FALSE to must be a contract that supports LSP1 UniversalReceiver
- * @param data Additional data the caller wants included in the emitted event, and sent in the hooks to `from` and `to` addresses.
- *
- * @dev Transfers `amount` of tokens from `from` to `to`. The `allowNonLSP1Recipient` parameter will be used
- * when notifying the token sender and receiver.
+ * @dev Transfers an `amount` of tokens from the `from` address to the `to` address and notify both sender and recipients via the LSP1 {`universalReceiver(...)`} function.
+ * If the tokens are transferred by an operator on behalf of a token holder, the allowance for the operator will be decreased by `amount` once the token transfer
+ * has been completed (See {authorizedAmountFor}).
*
- * Requirements:
+ * @param from The sender address.
+ * @param to The recipient address.
+ * @param amount The amount of tokens to transfer.
+ * @param allowNonLSP1Recipient When set to `true`, the `to` address CAN be any address. When set to `false`, the `to` address MUST be a contract that supports the LSP1 UniversalReceiver standard.
+ * @param data Any additional data the caller wants included in the emitted event, and sent in the hooks of the `from` and `to` addresses.
*
+ * @custom:requirements
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
- * - `amount` tokens must be owned by `from`.
- * - If the caller is not `from`, it must be an operator for `from` with access to at least
- * `amount` tokens.
+ * - `from` and `to` cannot be the same address (`from` cannot send tokens to itself).
+ * - `from` MUST have a balance of at least `amount` tokens.
+ * - If the caller is not `from`, it must be an operator for `from` with an allowance of at least `amount` of tokens.
+ *
+ * @custom:events
+ * - {Transfer} event when tokens get successfully transferred.
+ * - if the transfer is triggered by an operator, either the {AuthorizedOperator} event will be emitted with the updated allowance or the {RevokedOperator}
+ * event will be emitted if the operator has no more allowance left.
+ *
+ * @custom:hint The `allowNonLSP1Recipient` parameter **MUST be set to `true`** to transfer tokens to Externally Owned Accounts (EOAs)
+ * or contracts that do not implement the LSP1 Universal Receiver Standard. Otherwise the function will revert making the transfer fail.
*
- * Emits a {Transfer} event.
+ * @custom:info if the `to` address is a contract that implements LSP1, it will always be notified via its `universalReceiver(...)` function, regardless if `allowNonLSP1Recipient` is set to `true` or `false`.
+ *
+ * @custom:warning Be aware that when either the sender or the recipient can have logic that revert in their `universalReceiver(...)` function when being notified.
+ * This even if the `allowNonLSP1Recipient` was set to `true`.
*/
function transfer(
address from,
@@ -165,26 +179,24 @@ interface ILSP7DigitalAsset is IERC165, IERC725Y {
) external;
/**
- * @param from The list of sending addresses.
- * @param to The list of receiving addresses.
- * @param amount The amount of tokens to transfer.
- * @param allowNonLSP1Recipient When set to TRUE, to may be any address but
- * when set to FALSE to must be a contract that supports LSP1 UniversalReceiver
- * @param data Additional data the caller wants included in the emitted event, and sent in the hooks to `from` and `to` addresses.
+ * @dev Same as {`transfer(...)`} but transfer multiple tokens based on the arrays of `from`, `to`, `amount`.
*
- * @dev Transfers many tokens based on the list `from`, `to`, `amount`. If any transfer fails
- * the call will revert.
+ * @custom:info If any transfer in the batch fail or revert, the whole call will revert.
*
- * Requirements:
+ * @param from An array of sending addresses.
+ * @param to An array of receiving addresses.
+ * @param amount An array of amount of tokens to transfer for each `from -> to` transfer.
+ * @param allowNonLSP1Recipient For each transfer, when set to `true`, the `to` address CAN be any address. When set to `false`, the `to` address MUST be a contract that supports the LSP1 UniversalReceiver standard.
+ * @param data An array of additional data the caller wants included in the emitted event, and sent in the hooks to `from` and `to` addresses.
*
- * - `from`, `to`, `amount` lists are the same length.
+ * @custom:requirements
+ * - `from`, `to`, `amount` lists MUST be of the same length.
* - no values in `from` can be the zero address.
* - no values in `to` can be the zero address.
- * - each `amount` tokens must be owned by `from`.
- * - If the caller is not `from`, it must be an operator for `from` with access to at least
- * `amount` tokens.
+ * - each `amount` tokens MUST be owned by `from`.
+ * - for each transfer, if the caller is not `from`, it MUST be an operator for `from` with access to at least `amount` tokens.
*
- * Emits {Transfer} events.
+ * @custom:events {Transfer} event **for each token transfer**.
*/
function transferBatch(
address[] memory from,
diff --git a/contracts/LSP7DigitalAsset/LSP7DigitalAsset.sol b/contracts/LSP7DigitalAsset/LSP7DigitalAsset.sol
index 42ff10fcc..abf0ce915 100644
--- a/contracts/LSP7DigitalAsset/LSP7DigitalAsset.sol
+++ b/contracts/LSP7DigitalAsset/LSP7DigitalAsset.sol
@@ -15,10 +15,10 @@ import {LSP7DigitalAssetCore} from "./LSP7DigitalAssetCore.sol";
import {_INTERFACEID_LSP7} from "./LSP7Constants.sol";
/**
- * @title LSP7DigitalAsset contract
+ * @title Implementation of a LSP7 Digital Asset, a contract that represents a fungible token.
* @author Matthew Stevens
- * @dev Implementation of a LSP7 compliant contract.
*
+ * @dev Minting and transferring are supplied with a `uint256` amount.
* This implementation is agnostic to the way tokens are created.
* A supply mechanism has to be added in a derived contract using {_mint}
* For a generic mechanism, see {LSP7Mintable}.
@@ -44,7 +44,7 @@ abstract contract LSP7DigitalAsset is
}
/**
- * @dev See {IERC165-supportsInterface}.
+ * @inheritdoc IERC165
*/
function supportsInterface(
bytes4 interfaceId
diff --git a/contracts/LSP7DigitalAsset/LSP7DigitalAssetCore.sol b/contracts/LSP7DigitalAsset/LSP7DigitalAssetCore.sol
index 205919325..ee8937ba2 100644
--- a/contracts/LSP7DigitalAsset/LSP7DigitalAssetCore.sol
+++ b/contracts/LSP7DigitalAsset/LSP7DigitalAssetCore.sol
@@ -29,8 +29,8 @@ import {
*
* This contract implement the core logic of the functions for the {ILSP7DigitalAsset} interface.
*
- * Similar to ERC20, the non-standard {decreaseAllowance} and {increaseAllowance}
- * functions have been added to mitigate the well-known issues around setting allowances.
+ * Similar to ERC20, the non-standard {increaseAllowance} and {decreaseAllowance} functions
+ * have been added to mitigate the well-known issues around setting allowances.
*/
abstract contract LSP7DigitalAssetCore is ILSP7DigitalAsset {
// --- Storage
@@ -78,15 +78,14 @@ abstract contract LSP7DigitalAssetCore is ILSP7DigitalAsset {
/**
* @inheritdoc ILSP7DigitalAsset
*
- * @dev To avoid front-running and Allowance Double-Spend Exploit when
- * increasing or decreasing the authorized amount of an operator,
- * it is advised to:
- * 1. call {revokeOperator} first, and
- * 2. then re-call {authorizeOperator} with the new amount
+ * @custom:danger To avoid front-running and Allowance Double-Spend Exploit when
+ * increasing or decreasing the authorized amount of an operator, it is advised to:
*
- * for more information, see:
- * https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM/
+ * 1. either call {revokeOperator} first, and then re-call {authorizeOperator} with the new amount.
+ * 2. or use the non-standard functions {increaseAllowance} or {decreaseAllowance}.
*
+ * For more information, see:
+ * https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM/
*/
function authorizeOperator(
address operator,
@@ -185,20 +184,24 @@ abstract contract LSP7DigitalAssetCore is ILSP7DigitalAsset {
}
/**
+ * @custom:info This is a non-standard function, not part of the LSP7 standard interface.
+ * It has been added in the LSP7 contract implementation so that it can be used as a prevention mechanism
+ * against double spending allowance vulnerability.
+ *
* @notice Increase the allowance of `operator` by +`addedAmount`
- * @dev Atomically increases the allowance granted to `operator` by the caller.
*
+ * @dev Atomically increases the allowance granted to `operator` by the caller.
* This is an alternative approach to {authorizeOperator} that can be used as a mitigation
- * for problems described in {ILSP7DigitalAsset}.
- *
- * Emits an {AuthorizedOperator} event indicating the updated allowance.
+ * for the double spending allowance problem.
*
* @param operator the operator to increase the allowance for `msg.sender`
* @param addedAmount the additional amount to add on top of the current operator's allowance
*
- * @dev Requirements:
+ * @custom:requirements
* - `operator` cannot be the same address as `msg.sender`
* - `operator` cannot be the zero address.
+ *
+ * @custom:events {AuthorizedOperator} indicating the updated allowance
*/
function increaseAllowance(
address operator,
@@ -212,22 +215,27 @@ abstract contract LSP7DigitalAssetCore is ILSP7DigitalAsset {
}
/**
+ * @custom:info This is a non-standard function, not part of the LSP7 standard interface.
+ * It has been added in the LSP7 contract implementation so that it can be used as a prevention mechanism
+ * against the double spending allowance vulnerability.
+ *
* @notice Decrease the allowance of `operator` by -`substractedAmount`
+ *
* @dev Atomically decreases the allowance granted to `operator` by the caller.
* This is an alternative approach to {authorizeOperator} that can be used as a mitigation
- * for problems described in {ILSP7DigitalAsset}
+ * for the double spending allowance problem.
*
- * Emits:
- * - an {AuthorizedOperator} event indicating the updated allowance after decreasing it.
- * - a {RevokeOperator} event if `substractedAmount` is the full allowance,
- * indicating `operator` does not have any allowance left for `msg.sender`.
+ * @custom:events
+ * - {AuthorizedOperator} event indicating the updated allowance after decreasing it.
+ * - {RevokeOperator} event if `substractedAmount` is the full allowance,
+ * indicating `operator` does not have any alauthorizedAmountForlowance left for `msg.sender`.
*
* @param operator the operator to decrease allowance for `msg.sender`
* @param substractedAmount the amount to decrease by in the operator's allowance.
*
- * @dev Requirements:
+ * @custom:requirements
* - `operator` cannot be the zero address.
- * - operator` must have allowance for the caller of at least `substractedAmount`.
+ * - `operator` must have allowance for the caller of at least `substractedAmount`.
*/
function decreaseAllowance(
address operator,
@@ -248,17 +256,16 @@ abstract contract LSP7DigitalAssetCore is ILSP7DigitalAsset {
}
/**
- * @dev Changes token `amount` the `operator` has access to from `tokenOwner` tokens. If the
- * amount is zero then the operator is being revoked, otherwise the operator amount is being
- * modified.
+ * @dev Changes token `amount` the `operator` has access to from `tokenOwner` tokens.
+ * If the amount is zero then the operator is being revoked, otherwise the operator amount is being modified.
*
- * See {authorizedAmountFor}.
- *
- * Emits either {AuthorizedOperator} or {RevokedOperator} event.
- *
- * Requirements
+ * @custom:events
+ * - {RevokedOperator} event when operator's allowance is set to `0`.
+ * - {AuthorizedOperator} event when operator's allowance is set to any other amount.
*
+ * @custom:requirements
* - `operator` cannot be the zero address.
+ * - `operator` cannot be the same address as `tokenOwner`.
*/
function _updateOperator(
address tokenOwner,
@@ -283,13 +290,17 @@ abstract contract LSP7DigitalAssetCore is ILSP7DigitalAsset {
}
/**
- * @dev Mints `amount` tokens and transfers it to `to`.
+ * @dev Mints `amount` of tokens and transfers it to `to`.
*
- * Requirements:
+ * @param to the address to mint tokens for.
+ * @param amount the amount of tokens to mint.
+ * @param allowNonLSP1Recipient a boolean that describe if transfer to a `to` address that does not support LSP1 is allowed or not.
+ * @param data Additional data the caller wants included in the emitted {Transfer} event, and sent in the LSP1 hook to the `to` address.
*
+ * @custom:requirements
* - `to` cannot be the zero address.
*
- * Emits a {Transfer} event.
+ * @custom:events {Transfer} event with `address(0)` as `from`.
*/
function _mint(
address to,
@@ -324,16 +335,27 @@ abstract contract LSP7DigitalAssetCore is ILSP7DigitalAsset {
}
/**
- * @dev Destroys `amount` tokens.
+ * @dev Burns (= destroys) `amount` of tokens, decrease the `from` balance. This is done by sending them to the zero address.
+ *
+ * Both the sender and recipient will be notified of the token transfer through the LSP1 {universalReceiver}
+ * function, if they are contracts that support the LSP1 interface. Their `universalReceiver` function will receive
+ * all the parameters in the calldata packed encoded.
*
- * Requirements:
+ * Any logic in the {_beforeTokenTransfer} function will run before updating the balances.
*
+ * @param from the address to burn tokens from its balance.
+ * @param amount the amount of tokens to burn.
+ * @param data Additional data the caller wants included in the emitted event, and sent in the LSP1 hook to the `from` and `to` address.
+ *
+ * @custom:hint In dApps, you can know which address is burning tokens by listening for the `Transfer` event and filter with the zero address as `to`.
+ *
+ * @custom:requirements
* - `from` cannot be the zero address.
* - `from` must have at least `amount` tokens.
* - If the caller is not `from`, it must be an operator for `from` with access to at least
* `amount` tokens.
*
- * Emits a {Transfer} event.
+ * @custom:events {Transfer} event with `address(0)` as the `to` address
*/
function _burn(
address from,
@@ -367,7 +389,7 @@ abstract contract LSP7DigitalAssetCore is ILSP7DigitalAsset {
_beforeTokenTransfer(from, address(0), amount);
- // tokens being burned
+ // tokens being burnt
_existingTokens -= amount;
_tokenOwnerBalances[from] -= amount;
@@ -384,17 +406,27 @@ abstract contract LSP7DigitalAssetCore is ILSP7DigitalAsset {
}
/**
- * @dev Transfers `amount` tokens from `from` to `to`.
+ * @dev Transfer tokens from `from` to `to` by decreasing the balance of `from` by `-amount` and increasing the balance
+ * of `to` by `+amount`.
*
- * Requirements:
+ * Both the sender and recipient will be notified of the token transfer through the LSP1 {universalReceiver}
+ * function, if they are contracts that support the LSP1 interface. Their `universalReceiver` function will receive
+ * all the parameters in the calldata packed encoded.
*
- * - `to` cannot be the zero address.
+ * Any logic in the {_beforeTokenTransfer} function will run before updating the balances.
+ *
+ * @param from the address to decrease the balance.
+ * @param to the address to increase the balance.
+ * @param amount the amount of tokens to transfer from `from` to `to`.
+ * @param allowNonLSP1Recipient a boolean that describe if transfer to a `to` address that does not support LSP1 is allowed or not.
+ * @param data Additional data the caller wants included in the emitted event, and sent in the LSP1 hook to the `from` and `to` address.
+ *
+ * @custom:requirements
* - `from` cannot be the zero address.
- * - `from` must have at least `amount` tokens.
- * - If the caller is not `from`, it must be an operator for `from` with access to at least
- * `amount` tokens.
+ * - `to` cannot be the zero address.
+ * - `from` must have at least `amount` of tokens.
*
- * Emits a {Transfer} event.
+ * @custom:events {Transfer} event.
*/
function _transfer(
address from,
@@ -428,16 +460,12 @@ abstract contract LSP7DigitalAssetCore is ILSP7DigitalAsset {
}
/**
- * @dev Hook that is called before any token transfer. This includes minting
- * and burning.
+ * @dev Hook that is called before any token transfer, including minting and burning.
+ * Allows to run custom logic before updating balances and notifiying sender/recipient by overriding this function.
*
- * Calling conditions:
- *
- * - When `from` and `to` are both non-zero, ``from``'s `amount` tokens will be
- * transferred to `to`.
- * - When `from` is zero, `amount` tokens will be minted for `to`.
- * - When `to` is zero, ``from``'s `amount` tokens will be burned.
- * - `from` and `to` are never both zero.
+ * @param from The sender address
+ * @param to The recipient address
+ * @param amount The amount of token to transfer
*/
function _beforeTokenTransfer(
address from,
@@ -446,8 +474,12 @@ abstract contract LSP7DigitalAssetCore is ILSP7DigitalAsset {
) internal virtual {}
/**
- * @dev An attempt is made to notify the token sender about the `amount` tokens changing owners using
- * LSP1 interface.
+ * @dev Attempt to notify the token sender `from` about the `amount` of tokens being transferred.
+ * This is done by calling its {universalReceiver} function with the `_TYPEID_LSP7_TOKENSSENDER` as typeId, if `from` is a contract that supports the LSP1 interface.
+ * If `from` is an EOA or a contract that does not support the LSP1 interface, nothing will happen and no notification will be sent.
+
+ * @param from The address to call the {universalReceiver} function on.
+ * @param lsp1Data the data to be sent to the `from` address in the `universalReceiver` call.
*/
function _notifyTokenSender(
address from,
@@ -467,10 +499,16 @@ abstract contract LSP7DigitalAssetCore is ILSP7DigitalAsset {
}
/**
- * @dev An attempt is made to notify the token receiver about the `amount` tokens changing owners
- * using LSP1 interface. When allowNonLSP1Recipient is FALSE the token receiver MUST support LSP1.
+ * @dev Attempt to notify the token receiver `to` about the `amount` tokens being received.
+ * This is done by calling its {universalReceiver} function with the `_TYPEID_LSP7_TOKENSRECIPIENT` as typeId, if `to` is a contract that supports the LSP1 interface.
+ *
+ * If `to` is is an EOA or a contract that does not support the LSP1 interface, the behaviour will depend on the `allowNonLSP1Recipient` boolean flag.
+ * - if `allowNonLSP1Recipient` is set to `true`, nothing will happen and no notification will be sent.
+ * - if `allowNonLSP1Recipient` is set to `false, the transaction will revert.
*
- * The receiver may revert when the token being sent is not wanted.
+ * @param to The address to call the {universalReceiver} function on.
+ * @param allowNonLSP1Recipient a boolean that describe if transfer to a `to` address that does not support LSP1 is allowed or not.
+ * @param lsp1Data the data to be sent to the `to` address in the `universalReceiver(...)` call.
*/
function _notifyTokenReceiver(
address to,
diff --git a/contracts/LSP7DigitalAsset/LSP7Errors.sol b/contracts/LSP7DigitalAsset/LSP7Errors.sol
index 308cf4dfc..b01947b81 100644
--- a/contracts/LSP7DigitalAsset/LSP7Errors.sol
+++ b/contracts/LSP7DigitalAsset/LSP7Errors.sol
@@ -29,7 +29,10 @@ error LSP7AmountExceedsAuthorizedAmount(
error LSP7CannotUseAddressZeroAsOperator();
/**
- * @dev reverts when one tries to send tokens to or from the zero address.
+ * @dev reverts when trying to:
+ * - mint tokens to the zero address.
+ * - burn tokens from the zero address.
+ * - transfer tokens from or to the zero address.
*/
error LSP7CannotSendWithAddressZero();
@@ -39,7 +42,7 @@ error LSP7CannotSendWithAddressZero();
error LSP7CannotSendToSelf();
/**
- * @dev reverts when the parameters used for `transferBatch` have different lengths.
+ * @dev reverts when the array parameters used in {transferBatch} have different lengths.
*/
error LSP7InvalidTransferBatch();
@@ -52,8 +55,7 @@ error LSP7NotifyTokenReceiverContractMissingLSP1Interface(
);
/**
- * @dev reverts if the `tokenReceiver` is an EOA
- * when minting or transferring tokens with `bool allowNonLSP1Recipient` set as `false`.
+ * @dev reverts if the `tokenReceiver` is an EOA when minting or transferring tokens with `bool allowNonLSP1Recipient` set as `false`.
*/
error LSP7NotifyTokenReceiverIsEOA(address tokenReceiver);
@@ -63,6 +65,6 @@ error LSP7NotifyTokenReceiverIsEOA(address tokenReceiver);
error LSP7TokenOwnerCannotBeOperator();
/**
- * @dev Reverts when trying to decrease an operator's allowance to more than his current allowance
+ * @dev Reverts when trying to decrease an operator's allowance to more than its current allowance.
*/
error LSP7DecreasedAllowanceBelowZero();
diff --git a/docs/contracts/LSP7DigitalAsset/LSP7DigitalAsset.md b/docs/contracts/LSP7DigitalAsset/LSP7DigitalAsset.md
new file mode 100644
index 000000000..eecfc5fbb
--- /dev/null
+++ b/docs/contracts/LSP7DigitalAsset/LSP7DigitalAsset.md
@@ -0,0 +1,1302 @@
+
+
+
+# LSP7DigitalAsset
+
+:::info Standard Specifications
+
+[`LSP-7-DigitalAsset`](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md)
+
+:::
+:::info Solidity implementation
+
+[`LSP7DigitalAsset.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/LSP7DigitalAsset.sol)
+
+:::
+
+> Implementation of a LSP7 Digital Asset, a contract that represents a fungible token.
+
+Minting and transferring are supplied with a `uint256` amount. This implementation is agnostic to the way tokens are created. A supply mechanism has to be added in a derived contract using [`_mint`](#_mint) For a generic mechanism, see [`LSP7Mintable`](#lsp7mintable).
+
+## Public Methods
+
+Public methods are accessible externally from users, allowing interaction with this function from dApps or other smart contracts.
+When marked as 'public', a method can be called both externally and internally, on the other hand, when marked as 'external', a method can only be called externally.
+
+### authorizeOperator
+
+:::note References
+
+- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#authorizeoperator)
+- Solidity implementation: [`LSP7DigitalAsset.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/LSP7DigitalAsset.sol)
+- Function signature: `authorizeOperator(address,uint256)`
+- Function selector: `0x47980aa3`
+
+:::
+
+:::danger
+
+To avoid front-running and Allowance Double-Spend Exploit when increasing or decreasing the authorized amount of an operator, it is advised to: 1. either call {revokeOperator} first, and then re-call {authorizeOperator} with the new amount. 2. or use the non-standard functions {increaseAllowance} or {decreaseAllowance}. For more information, see: https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM/
+
+:::
+
+```solidity
+function authorizeOperator(
+ address operator,
+ uint256 amount
+) external nonpayable;
+```
+
+Sets an `amount` of tokens that an `operator` has access from the caller's balance (allowance). See [`authorizedAmountFor`](#authorizedamountfor).
+
+#### Parameters
+
+| Name | Type | Description |
+| ---------- | :-------: | ------------------------------------------------------ |
+| `operator` | `address` | The address to authorize as an operator. |
+| `amount` | `uint256` | The allowance amount of tokens operator has access to. |
+
+
+
+### authorizedAmountFor
+
+:::note References
+
+- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#authorizedamountfor)
+- Solidity implementation: [`LSP7DigitalAsset.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/LSP7DigitalAsset.sol)
+- Function signature: `authorizedAmountFor(address,address)`
+- Function selector: `0x65aeaa95`
+
+:::
+
+```solidity
+function authorizedAmountFor(
+ address operator,
+ address tokenOwner
+) external view returns (uint256);
+```
+
+Get the amount of tokens `operator` address has access to from `tokenOwner`. Operators can send and burn tokens on behalf of their owners.
+
+#### Parameters
+
+| Name | Type | Description |
+| ------------ | :-------: | ---------------------------------------------------------- |
+| `operator` | `address` | The operator's address to query the authorized amount for. |
+| `tokenOwner` | `address` | The token owner that `operator` has allowance on. |
+
+#### Returns
+
+| Name | Type | Description |
+| ---- | :-------: | --------------------------------------------------------------------------------------- |
+| `0` | `uint256` | The amount of tokens the `operator`'s address has access on the `tokenOwner`'s balance. |
+
+
+
+### balanceOf
+
+:::note References
+
+- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#balanceof)
+- Solidity implementation: [`LSP7DigitalAsset.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/LSP7DigitalAsset.sol)
+- Function signature: `balanceOf(address)`
+- Function selector: `0x70a08231`
+
+:::
+
+```solidity
+function balanceOf(address tokenOwner) external view returns (uint256);
+```
+
+Get the number of tokens owned by `tokenOwner`. If the token is divisible (the [`decimals`](#decimals) function returns `18`), the amount returned should be divided by 1e18 to get a better picture of the actual balance of the `tokenOwner`. _Example:_ `balanceOf(someAddress) -> 42_000_000_000_000_000_000 / 1e18 = 42 tokens`
+
+#### Parameters
+
+| Name | Type | Description |
+| ------------ | :-------: | --------------------------------------------------------- |
+| `tokenOwner` | `address` | The address of the token holder to query the balance for. |
+
+#### Returns
+
+| Name | Type | Description |
+| ---- | :-------: | ------------------------------------------- |
+| `0` | `uint256` | The amount of tokens owned by `tokenOwner`. |
+
+
+
+### decimals
+
+:::note References
+
+- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#decimals)
+- Solidity implementation: [`LSP7DigitalAsset.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/LSP7DigitalAsset.sol)
+- Function signature: `decimals()`
+- Function selector: `0x313ce567`
+
+:::
+
+```solidity
+function decimals() external view returns (uint8);
+```
+
+Returns the number of decimals used to get its user representation. If the asset contract has been set to be non-divisible via the `isNonDivisible_` parameter in the `constructor`, the decimals returned wiil be `0`. Otherwise `18` is the common value.
+
+#### Returns
+
+| Name | Type | Description |
+| ---- | :-----: | ----------------------------------------------------------------------- |
+| `0` | `uint8` | the number of decimals. If `0` is returned, the asset is non-divisible. |
+
+
+
+### decreaseAllowance
+
+:::note References
+
+- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#decreaseallowance)
+- Solidity implementation: [`LSP7DigitalAsset.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/LSP7DigitalAsset.sol)
+- Function signature: `decreaseAllowance(address,uint256)`
+- Function selector: `0xa457c2d7`
+
+:::
+
+:::info
+
+This is a non-standard function, not part of the LSP7 standard interface. It has been added in the LSP7 contract implementation so that it can be used as a prevention mechanism against the double spending allowance vulnerability.
+
+:::
+
+```solidity
+function decreaseAllowance(
+ address operator,
+ uint256 substractedAmount
+) external nonpayable;
+```
+
+_Decrease the allowance of `operator` by -`substractedAmount`_
+
+Atomically decreases the allowance granted to `operator` by the caller. This is an alternative approach to [`authorizeOperator`](#authorizeoperator) that can be used as a mitigation for the double spending allowance problem.
+
+
+ +**Requirements:** + +- `operator` cannot be the zero address. +- `operator` must have allowance for the caller of at least `substractedAmount`. + ++ +
+ +**Emitted events:** + +- [`AuthorizedOperator`](#authorizedoperator) event indicating the updated allowance after decreasing it. +- [`RevokeOperator`](#revokeoperator) event if `substractedAmount` is the full allowance, indicating `operator` does not have any alauthorizedAmountForlowance left for `msg.sender`. + ++ +#### Parameters + +| Name | Type | Description | +| ------------------- | :-------: | ------------------------------------------------------ | +| `operator` | `address` | the operator to decrease allowance for `msg.sender` | +| `substractedAmount` | `uint256` | the amount to decrease by in the operator's allowance. | + +
+ +**Requirements:** + +- `operator` cannot be the same address as `msg.sender` +- `operator` cannot be the zero address. + ++ +
+ +**Emitted events:** + +- [`AuthorizedOperator`](#authorizedoperator) indicating the updated allowance + ++ +#### Parameters + +| Name | Type | Description | +| ------------- | :-------: | ----------------------------------------------------------------------- | +| `operator` | `address` | the operator to increase the allowance for `msg.sender` | +| `addedAmount` | `uint256` | the additional amount to add on top of the current operator's allowance | + +
+ +**Emitted events:** + +- [`Transfer`](#transfer) event with `address(0)` as `from`. + ++ +#### Parameters + +| Name | Type | Description | +| ----------------------- | :-------: | ------------------------------------------------------------------------------------------------------------------------- | +| `to` | `address` | the address to mint tokens for. | +| `amount` | `uint256` | the amount of tokens to mint. | +| `allowNonLSP1Recipient` | `bool` | a boolean that describe if transfer to a `to` address that does not support LSP1 is allowed or not. | +| `data` | `bytes` | Additional data the caller wants included in the emitted {Transfer} event, and sent in the LSP1 hook to the `to` address. | + +
+ +**Emitted events:** + +- [`Transfer`](#transfer) event with `address(0)` as the `to` address + ++ +#### Parameters + +| Name | Type | Description | +| -------- | :-------: | ------------------------------------------------------------------------------------------------------------------------- | +| `from` | `address` | the address to burn tokens from its balance. | +| `amount` | `uint256` | the amount of tokens to burn. | +| `data` | `bytes` | Additional data the caller wants included in the emitted event, and sent in the LSP1 hook to the `from` and `to` address. | + +
+ +**Emitted events:** + +- [`Transfer`](#transfer) event. + ++ +#### Parameters + +| Name | Type | Description | +| ----------------------- | :-------: | ------------------------------------------------------------------------------------------------------------------------- | +| `from` | `address` | the address to decrease the balance. | +| `to` | `address` | the address to increase the balance. | +| `amount` | `uint256` | the amount of tokens to transfer from `from` to `to`. | +| `allowNonLSP1Recipient` | `bool` | a boolean that describe if transfer to a `to` address that does not support LSP1 is allowed or not. | +| `data` | `bytes` | Additional data the caller wants included in the emitted event, and sent in the LSP1 hook to the `from` and `to` address. | + +