forked from DecentralizedClimateFoundation/DCIPs
329 lines
11 KiB
Solidity
329 lines
11 KiB
Solidity
// SPDX-License-Identifier: CC0-1.0
|
|
pragma solidity ^0.8.0;
|
|
|
|
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
|
import "@openzeppelin/contracts/access/Ownable.sol";
|
|
import "./interfaces/IERC20charity.sol";
|
|
|
|
/**
|
|
*@title ERC720 charity Token
|
|
*@author Aubay
|
|
*@dev Extension of ERC720 Token that can be partially donated to a charity project
|
|
*
|
|
*This extensions keeps track of donations to charity addresses. The owner can chose the charity adresses listed.
|
|
*Users can active the donation option or not and specify a different pourcentage than the default one donate.
|
|
* A pourcentage af the amount of token transfered will be added and send to a charity address.
|
|
*/
|
|
|
|
abstract contract ERC20Charity is IERC20charity, ERC20, Ownable {
|
|
mapping(address => uint256) public whitelistedRate; //Keep track of the rate for each charity address
|
|
mapping(address => uint256) internal indexOfAddresses;
|
|
mapping(address => mapping(address => uint256)) private _donation; //Keep track of the desired rate to donate for each user
|
|
mapping(address => address) private _defaultAddress; //keep track of each user's default charity address
|
|
|
|
address[] whitelistedAddresses; //Addresses whitelisted
|
|
|
|
/**
|
|
* @dev See {IERC165-supportsInterface}.
|
|
*/
|
|
function supportsInterface(
|
|
bytes4 interfaceId
|
|
) public view virtual override(IERC165) returns (bool) {
|
|
return
|
|
interfaceId == type(IERC20charity).interfaceId ||
|
|
interfaceId == type(IERC165).interfaceId;
|
|
}
|
|
|
|
/**
|
|
*@dev The default rate of donation can be override
|
|
*/
|
|
function _defaultRate() internal pure virtual returns (uint256) {
|
|
return 10; // 0.1%
|
|
}
|
|
|
|
/**
|
|
*@dev The denominator to interpret the rate of donation , defaults to 10000 so rate are expressed in basis points, but may be customized by an override.
|
|
* base 10000 , so 10000 =100% , 0 = 0% , 2000 =20%
|
|
*/
|
|
function _feeDenominator() internal pure virtual returns (uint256) {
|
|
return 10000;
|
|
}
|
|
|
|
/**
|
|
*@notice Add address to whitelist and set rate to the default rate.
|
|
* @dev Requirements:
|
|
*
|
|
* - `toAdd` cannot be the zero address.
|
|
*
|
|
* @param toAdd The address to whitelist.
|
|
*/
|
|
function addToWhitelist(address toAdd) external virtual onlyOwner {
|
|
if (indexOfAddresses[toAdd] == 0) {
|
|
whitelistedRate[toAdd] = _defaultRate();
|
|
whitelistedAddresses.push(toAdd);
|
|
indexOfAddresses[toAdd] = whitelistedAddresses.length;
|
|
}
|
|
|
|
emit AddedToWhitelist(toAdd);
|
|
}
|
|
|
|
/**
|
|
*@notice Remove the address from the whitelist and set rate to the default rate.
|
|
* @dev Requirements:
|
|
*
|
|
* - `toRemove` cannot be the zero address.
|
|
*
|
|
* @param toRemove The address to remove from whitelist.
|
|
*/
|
|
function deleteFromWhitelist(address toRemove) external virtual onlyOwner {
|
|
uint256 index1 = indexOfAddresses[toRemove];
|
|
require(index1 > 0, "Invalid index"); //Indexing starts at 1, 0 is not allowed
|
|
// move the last item into the index being vacated
|
|
address lastValue = whitelistedAddresses[
|
|
whitelistedAddresses.length - 1
|
|
];
|
|
whitelistedAddresses[index1 - 1] = lastValue; // adjust for 1-based indexing
|
|
indexOfAddresses[lastValue] = index1;
|
|
whitelistedAddresses.pop();
|
|
indexOfAddresses[toRemove] = 0;
|
|
|
|
delete whitelistedRate[toRemove]; //whitelistedRate[toRemove] =0;
|
|
emit RemovedFromWhitelist(toRemove);
|
|
}
|
|
|
|
/// @notice Get all registered charity addresses
|
|
/// @return List of all registered donations addresses
|
|
function getAllWhitelistedAddresses() external view returns (address[] memory) {
|
|
return whitelistedAddresses;
|
|
}
|
|
|
|
/// @notice Display for a user the rate of the default charity address that will receive donation.
|
|
/// @return The default rate of the registered address for the user.
|
|
function getRate() external view returns (uint256) {
|
|
return _donation[msg.sender][_defaultAddress[msg.sender]];
|
|
}
|
|
|
|
/**
|
|
*@notice Set for a user a default charity address that will receive donation.
|
|
* The default rate specified in {whitelistedRate} will be applied.
|
|
* @dev Requirements:
|
|
*
|
|
* - `whitelistedAddr` cannot be the zero address.
|
|
*
|
|
* @param whitelistedAddr The address to set as default.
|
|
*/
|
|
function setSpecificDefaultAddress(
|
|
address whitelistedAddr
|
|
) external virtual {
|
|
require(
|
|
whitelistedRate[whitelistedAddr] != 0,
|
|
"ERC20Charity: invalid whitelisted rate"
|
|
);
|
|
_defaultAddress[msg.sender] = whitelistedAddr;
|
|
_donation[msg.sender][whitelistedAddr] = whitelistedRate[
|
|
whitelistedAddr
|
|
];
|
|
emit DonnationAddressChanged(whitelistedAddr);
|
|
}
|
|
|
|
/**
|
|
*@notice Set for a user a default charity address that will receive donation.
|
|
* The rate is specified by the user.
|
|
* @dev Requirements:
|
|
*
|
|
* - `whitelistedAddr` cannot be the zero address.
|
|
* - `rate` cannot be inferior to the default rate
|
|
* or to the rate specified by the owner of this contract in {whitelistedRate}.
|
|
*
|
|
* @param whitelistedAddr The address to set as default.
|
|
* @param rate The personalised rate for donation.
|
|
*/
|
|
function setSpecificDefaultAddressAndRate(
|
|
address whitelistedAddr,
|
|
uint256 rate
|
|
) external virtual {
|
|
require(
|
|
rate <= _feeDenominator(),
|
|
"ERC20Charity: rate must be between 0 and _feeDenominator"
|
|
);
|
|
require(
|
|
rate >= _defaultRate(),
|
|
"ERC20Charity: rate fee must exceed default rate"
|
|
);
|
|
require(
|
|
rate >= whitelistedRate[whitelistedAddr],
|
|
"ERC20Charity: rate fee must exceed the fee set by the owner"
|
|
);
|
|
require(
|
|
whitelistedRate[whitelistedAddr] != 0,
|
|
"ERC20Charity: invalid whitelisted address"
|
|
);
|
|
_defaultAddress[msg.sender] = whitelistedAddr;
|
|
_donation[msg.sender][whitelistedAddr] = rate;
|
|
emit DonnationAddressAndRateChanged(whitelistedAddr, rate);
|
|
}
|
|
|
|
/**
|
|
*@notice Set personlised rate for charity address in {whitelistedRate}.
|
|
* @dev Requirements:
|
|
*
|
|
* - `whitelistedAddr` cannot be the zero address.
|
|
* - `rate` cannot be inferior to the default rate.
|
|
*
|
|
* @param whitelistedAddr The address to set as default.
|
|
* @param rate The personalised rate for donation.
|
|
*/
|
|
function setSpecificRate(
|
|
address whitelistedAddr,
|
|
uint256 rate
|
|
) external virtual onlyOwner {
|
|
require(
|
|
rate <= _feeDenominator(),
|
|
"ERC20Charity: rate must be between 0 and _feeDenominator"
|
|
);
|
|
require(
|
|
rate >= _defaultRate(),
|
|
"ERC20Charity: rate fee must exceed default rate"
|
|
);
|
|
require(
|
|
whitelistedRate[whitelistedAddr] != 0,
|
|
"ERC20Charity: invalid whitelisted address"
|
|
);
|
|
whitelistedRate[whitelistedAddr] = rate;
|
|
emit ModifiedCharityRate(whitelistedAddr, rate);
|
|
}
|
|
|
|
/**
|
|
*@notice Display for a user the default charity address that will receive donation.
|
|
* The default rate specified in {whitelistedRate} will be applied.
|
|
*/
|
|
function specificDefaultAddress() external view virtual returns (address) {
|
|
return _defaultAddress[msg.sender];
|
|
}
|
|
|
|
/**
|
|
* inherit IERC20charity
|
|
*/
|
|
function charityInfo(
|
|
address charityAddr
|
|
) external view virtual returns (bool, uint256 rate) {
|
|
rate = whitelistedRate[charityAddr];
|
|
if (rate != 0) {
|
|
return (true, rate);
|
|
} else {
|
|
return (false, rate);
|
|
}
|
|
}
|
|
|
|
/**
|
|
*@notice Delete The Default Address and so deactivate donnations .
|
|
*/
|
|
function deleteDefaultAddress() external virtual {
|
|
_defaultAddress[msg.sender] = address(0);
|
|
emit DonnationAddressChanged(address(0));
|
|
}
|
|
|
|
/**
|
|
*@notice Return the rate to donate.
|
|
* @dev Requirements:
|
|
*
|
|
* - `from` cannot be the zero address
|
|
*
|
|
* @param from The address to get rate of donation.
|
|
*/
|
|
function _returnRate(address from) internal virtual returns (uint256 rate) {
|
|
address whitelistedAddr = _defaultAddress[from];
|
|
rate = _donation[from][whitelistedAddr];
|
|
if (
|
|
whitelistedRate[whitelistedAddr] == 0 ||
|
|
_defaultAddress[from] == address(0)
|
|
) {
|
|
rate = 0;
|
|
}
|
|
return rate;
|
|
}
|
|
|
|
/**
|
|
* @dev See {IERC20-transfer}.
|
|
*
|
|
* Requirements:
|
|
*
|
|
* - `to` cannot be the zero address.
|
|
* - the caller must have a balance of at least `amount`.
|
|
*/
|
|
function transfer(
|
|
address to,
|
|
uint256 amount
|
|
) public virtual override returns (bool) {
|
|
address owner = _msgSender();
|
|
|
|
if (_defaultAddress[msg.sender] != address(0)) {
|
|
address whitelistedAddr = _defaultAddress[msg.sender];
|
|
uint256 rate = _returnRate(msg.sender);
|
|
uint256 donate = (amount * rate) / _feeDenominator();
|
|
_transfer(owner, whitelistedAddr, donate);
|
|
}
|
|
_transfer(owner, to, amount);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @dev See {IERC20-transferFrom}.
|
|
*
|
|
* Emits an {Approval} event indicating the updated allowance. This is not
|
|
* required by the EIP. See the note at the beginning of {ERC20}.
|
|
*
|
|
* NOTE: Does not update the allowance if the current allowance
|
|
* is the maximum `uint256`.
|
|
*
|
|
* Requirements:
|
|
*
|
|
* - `from` and `to` cannot be the zero address.
|
|
* - `from` must have a balance of at least `amount`.
|
|
* - the caller must have allowance for ``from``'s tokens of at least
|
|
* `amount`.
|
|
*/
|
|
function transferFrom(
|
|
address from,
|
|
address to,
|
|
uint256 amount
|
|
) public virtual override returns (bool) {
|
|
address spender = _msgSender();
|
|
_spendAllowance(from, spender, amount);
|
|
|
|
if (_defaultAddress[from] != address(0)) {
|
|
address whitelistedAddr = _defaultAddress[from];
|
|
uint256 rate = _returnRate(from);
|
|
uint256 donate = (amount * rate) / _feeDenominator();
|
|
_spendAllowance(from, spender, donate);
|
|
_transfer(from, whitelistedAddr, donate);
|
|
}
|
|
_transfer(from, to, amount);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @dev See {IERC20-approve}.
|
|
*
|
|
* NOTE: If `amount` 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 override returns (bool) {
|
|
address owner = _msgSender();
|
|
_approve(owner, spender, amount);
|
|
if (_defaultAddress[msg.sender] != address(0)) {
|
|
uint256 rate = _returnRate(msg.sender);
|
|
uint256 donate = (amount * rate) / _feeDenominator();
|
|
_approve(owner, spender, (donate + amount));
|
|
}
|
|
return true;
|
|
}
|
|
}
|