// SPDX-License-Identifier: GPL-3.0
// File: node_modules\openzeppelin-solidity\contracts\utils\math\SafeMath.sol
// OpenZeppelin Contracts (last updated v4.6.0) (utils/math/SafeMath.sol)
pragma solidity ^0.8.0;
// CAUTION
// This version of SafeMath should only be used with Solidity 0.8 or later,
// because it relies on the compiler's built in overflow checks.
/**
* @dev Wrappers over Solidity's arithmetic operations.
*
* NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler
* now has built in overflow checking.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
return a + b;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return a - b;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
return a * b;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator.
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return a / b;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return a % b;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {trySub}.
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
unchecked {
require(b <= a, errorMessage);
return a - b;
}
}
/**
* @dev Returns the integer division of two unsigned integers, reverting with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
unchecked {
require(b > 0, errorMessage);
return a / b;
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting with custom message when dividing by zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryMod}.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
unchecked {
require(b > 0, errorMessage);
return a % b;
}
}
}
// File: node_modules\openzeppelin-solidity\contracts\access\IAccessControl.sol
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
pragma solidity ^0.8.0;
/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/
interface IAccessControl {
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*
* _Available since v3.1._
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, an admin role
* bearer except when using {AccessControl-_setupRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/
function renounceRole(bytes32 role, address account) external;
}
// File: node_modules\openzeppelin-solidity\contracts\access\IAccessControlEnumerable.sol
// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol)
pragma solidity ^0.8.0;
/**
* @dev External interface of AccessControlEnumerable declared to support ERC165 detection.
*/
interface IAccessControlEnumerable is IAccessControl {
/**
* @dev Returns one of the accounts that have `role`. `index` must be a
* value between 0 and {getRoleMemberCount}, non-inclusive.
*
* Role bearers are not sorted in any particular way, and their ordering may
* change at any point.
*
* WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
* you perform all queries on the same block. See the following
* https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
* for more information.
*/
function getRoleMember(bytes32 role, uint256 index) external view returns (address);
/**
* @dev Returns the number of accounts that have `role`. Can be used
* together with {getRoleMember} to enumerate all bearers of a role.
*/
function getRoleMemberCount(bytes32 role) external view returns (uint256);
}
// File: node_modules\openzeppelin-solidity\contracts\utils\Context.sol
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
// File: node_modules\openzeppelin-solidity\contracts\utils\math\Math.sol
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1);
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(
uint256 x,
uint256 y,
uint256 denominator,
Rounding rounding
) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10**64) {
value /= 10**64;
result += 64;
}
if (value >= 10**32) {
value /= 10**32;
result += 32;
}
if (value >= 10**16) {
value /= 10**16;
result += 16;
}
if (value >= 10**8) {
value /= 10**8;
result += 8;
}
if (value >= 10**4) {
value /= 10**4;
result += 4;
}
if (value >= 10**2) {
value /= 10**2;
result += 2;
}
if (value >= 10**1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
}
}
}
// File: node_modules\openzeppelin-solidity\contracts\utils\Strings.sol
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)
pragma solidity ^0.8.0;
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant _SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
}
// File: node_modules\openzeppelin-solidity\contracts\utils\introspection\IERC165.sol
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
// File: node_modules\openzeppelin-solidity\contracts\utils\introspection\ERC165.sol
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
pragma solidity ^0.8.0;
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*
* Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
// File: node_modules\openzeppelin-solidity\contracts\access\AccessControl.sol
// OpenZeppelin Contracts (last updated v4.8.0) (access/AccessControl.sol)
pragma solidity ^0.8.0;
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it.
*/
abstract contract AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address => bool) members;
bytes32 adminRole;
}
mapping(bytes32 => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with a standardized message including the required role.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*
* _Available since v4.1._
*/
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
return _roles[role].members[account];
}
/**
* @dev Revert with a standard message if `_msgSender()` is missing `role`.
* Overriding this function changes the behavior of the {onlyRole} modifier.
*
* Format of the revert message is described in {_checkRole}.
*
* _Available since v4.6._
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Revert with a standard message if `account` is missing `role`.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert(
string(
abi.encodePacked(
"AccessControl: account ",
Strings.toHexString(account),
" is missing role ",
Strings.toHexString(uint256(role), 32)
)
)
);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
return _roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleGranted} event.
*/
function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleRevoked} event.
*/
function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address account) public virtual override {
require(account == _msgSender(), "AccessControl: can only renounce roles for self");
_revokeRole(role, account);
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event. Note that unlike {grantRole}, this function doesn't perform any
* checks on the calling account.
*
* May emit a {RoleGranted} event.
*
* [WARNING]
* ====
* This function should only be called from the constructor when setting
* up the initial roles for the system.
*
* Using this function in any other way is effectively circumventing the admin
* system imposed by {AccessControl}.
* ====
*
* NOTE: This function is deprecated in favor of {_grantRole}.
*/
function _setupRole(bytes32 role, address account) internal virtual {
_grantRole(role, account);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Grants `role` to `account`.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual {
if (!hasRole(role, account)) {
_roles[role].members[account] = true;
emit RoleGranted(role, account, _msgSender());
}
}
/**
* @dev Revokes `role` from `account`.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual {
if (hasRole(role, account)) {
_roles[role].members[account] = false;
emit RoleRevoked(role, account, _msgSender());
}
}
}
// File: node_modules\openzeppelin-solidity\contracts\utils\structs\EnumerableSet.sol
// OpenZeppelin Contracts (last updated v4.8.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.0;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping(bytes32 => uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
if (lastIndex != toDeleteIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastValue;
// Update the index for the moved value
set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}
// File: node_modules\openzeppelin-solidity\contracts\access\AccessControlEnumerable.sol
// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol)
pragma solidity ^0.8.0;
/**
* @dev Extension of {AccessControl} that allows enumerating the members of each role.
*/
abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl {
using EnumerableSet for EnumerableSet.AddressSet;
mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers;
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns one of the accounts that have `role`. `index` must be a
* value between 0 and {getRoleMemberCount}, non-inclusive.
*
* Role bearers are not sorted in any particular way, and their ordering may
* change at any point.
*
* WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
* you perform all queries on the same block. See the following
* https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
* for more information.
*/
function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) {
return _roleMembers[role].at(index);
}
/**
* @dev Returns the number of accounts that have `role`. Can be used
* together with {getRoleMember} to enumerate all bearers of a role.
*/
function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) {
return _roleMembers[role].length();
}
/**
* @dev Overload {_grantRole} to track enumerable memberships
*/
function _grantRole(bytes32 role, address account) internal virtual override {
super._grantRole(role, account);
_roleMembers[role].add(account);
}
/**
* @dev Overload {_revokeRole} to track enumerable memberships
*/
function _revokeRole(bytes32 role, address account) internal virtual override {
super._revokeRole(role, account);
_roleMembers[role].remove(account);
}
}
// File: contracts\AkemonaWhitelistBase.sol
pragma solidity ^0.8.19;
//import "./AkemonaCrowdsale.sol";
abstract contract AkemonaWhitelistBase {
function isTransferAuthorized(address _from, address _to, AkemonaCrowdsale _contract) public virtual view returns (bool);
function isPurchaseAuthorized(address _investor, uint256 _amount) public virtual view returns (bool);
function addWhitelistedAddresses(address[] memory _addresses) public virtual returns (bool);
function removeWhitelistedAddresses(address[] memory _addresses) public virtual;
}
// File: contracts\AkemonaWhitelistTracker.sol
pragma solidity ^0.8.19;
contract AkemonaWhitelistTracker is Context, AccessControlEnumerable {
AkemonaWhitelistBase[] public whitelists;
constructor() {
_setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
}
modifier restricted() {
require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "not authorized");
_;
}
function addWhitelist(address _whitelist) public restricted {
whitelists.push(AkemonaWhitelistBase(_whitelist));
}
function removeWhitelist(uint256 index) public restricted {
require(index < whitelists.length);
whitelists[index] = whitelists[whitelists.length-1];
whitelists.pop();
}
}
// File: contracts\AkemonaWhitelist.sol
pragma solidity ^0.8.19;
//import "./AkemonaCrowdsale.sol";
//import "./AkemonaWhitelistBase.sol";
contract AkemonaWhitelist is AkemonaWhitelistBase {
address public owner;
mapping (address => bool) public accredited;
mapping (address => bool) public whitelisted;
mapping (address => mapping(address => bool)) public exceptions;
constructor() public {
owner = msg.sender;
}
modifier restricted() {
if (msg.sender == owner) _;
}
function isPurchaseAuthorized(address _investor, uint256 _amount) public view override returns (bool) {
if (!whitelisted[_investor]) {
return false;
}
return true;
}
function isTransferAuthorized(address _from, address _to, AkemonaCrowdsale _contract) public view override returns (bool) {
if (exceptions[_from][_to]) {
return true;
}
if (!_contract.isDisbursed()) {
return false;
}
if (!whitelisted[_to]) {
return false;
}
if (block.timestamp - _contract.disbursementTime() > 60 * 60 * 24 * 365) {
return true;
}
if (accredited[_from]) {
return true;
}
// TODO
// If the crowdsale contract is in a buyback period, and the toAddress is the borrower, and the fromAddress is an original investor in the crowdsale, return true
return false;
}
function addAccreditedStatus(address[] memory _addresses) public restricted {
for (uint8 i = 0; i < _addresses.length; i++) {
accredited[_addresses[i]] = true;
}
}
function addWhitelistedAddresses(address[] memory _addresses) public override restricted returns (bool) {
for (uint8 i = 0; i < _addresses.length; i++) {
whitelisted[_addresses[i]] = true;
}
return true;
}
function removeAccreditedStatus(address[] memory _addresses) public restricted {
for (uint8 i = 0; i < _addresses.length; i++) {
accredited[_addresses[i]] = false;
}
}
function removeWhitelistedAddresses(address[] memory _addresses) public override restricted {
for (uint8 i = 0; i < _addresses.length; i++) {
accredited[_addresses[i]] = false;
whitelisted[_addresses[i]] = false;
}
}
function addException(address _from, address _to) public restricted {
exceptions[_from][_to] = true;
}
function removeException(address _from, address _to) public restricted {
exceptions[_from][_to] = false;
}
function hasException(address _from, address _to) public view returns (bool) {
return exceptions[_from][_to];
}
}
// File: node_modules\openzeppelin-solidity\contracts\token\ERC20\IERC20.sol
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` 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);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` 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);
}
// File: node_modules\openzeppelin-solidity\contracts\token\ERC20\extensions\IERC20Metadata.sol
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
// File: node_modules\openzeppelin-solidity\contracts\token\ERC20\ERC20.sol
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20PresetMinterPauser}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract ERC20 is Context, IERC20, IERC20Metadata {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* The default value of {decimals} is 18. To select a different value for
* {decimals} you should overload it.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the value {ERC20} uses, unless this function is
* overridden;
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual override returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
/**
* @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();
_transfer(owner, to, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
/**
* @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);
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);
_transfer(from, to, amount);
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, allowance(owner, spender) + addedValue);
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
address owner = _msgSender();
uint256 currentAllowance = allowance(owner, spender);
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
return true;
}
/**
* @dev Moves `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.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
*/
function _transfer(
address from,
address to,
uint256 amount
) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
// Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
// decrementing then incrementing.
_balances[to] += amount;
}
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
unchecked {
// Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
_balances[account] += amount;
}
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
// Overflow not possible: amount <= accountBalance <= totalSupply.
_totalSupply -= amount;
}
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
/**
* @dev Sets `amount` 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.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(
address owner,
address spender,
uint256 amount
) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `amount`.
*
* Does not update the allowance amount 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 {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
/**
* @dev Hook that is called after any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* has been transferred to `to`.
* - when `from` is zero, `amount` tokens have been minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens have been burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _afterTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
}
// File: node_modules\openzeppelin-solidity\contracts\token\ERC20\extensions\ERC20Burnable.sol
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/extensions/ERC20Burnable.sol)
pragma solidity ^0.8.0;
/**
* @dev Extension of {ERC20} that allows token holders to destroy both their own
* tokens and those that they have an allowance for, in a way that can be
* recognized off-chain (via event analysis).
*/
abstract contract ERC20Burnable is Context, ERC20 {
/**
* @dev Destroys `amount` tokens from the caller.
*
* See {ERC20-_burn}.
*/
function burn(uint256 amount) public virtual {
_burn(_msgSender(), amount);
}
/**
* @dev Destroys `amount` 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`.
*/
function burnFrom(address account, uint256 amount) public virtual {
_spendAllowance(account, _msgSender(), amount);
_burn(account, amount);
}
}
// File: node_modules\openzeppelin-solidity\contracts\security\Pausable.sol
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)
pragma solidity ^0.8.0;
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract Pausable is Context {
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
bool private _paused;
/**
* @dev Initializes the contract in unpaused state.
*/
constructor() {
_paused = false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
require(!paused(), "Pausable: paused");
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
require(paused(), "Pausable: not paused");
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}
// File: node_modules\openzeppelin-solidity\contracts\token\ERC20\extensions\ERC20Pausable.sol
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/ERC20Pausable.sol)
pragma solidity ^0.8.0;
/**
* @dev ERC20 token with pausable token transfers, minting and burning.
*
* Useful for scenarios such as preventing trades until the end of an evaluation
* period, or having an emergency switch for freezing all token transfers in the
* event of a large bug.
*/
abstract contract ERC20Pausable is ERC20, Pausable {
/**
* @dev See {ERC20-_beforeTokenTransfer}.
*
* Requirements:
*
* - the contract must not be paused.
*/
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual override {
super._beforeTokenTransfer(from, to, amount);
require(!paused(), "ERC20Pausable: token transfer while paused");
}
}
// File: node_modules\openzeppelin-solidity\contracts\token\ERC20\presets\ERC20PresetMinterPauser.sol
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/presets/ERC20PresetMinterPauser.sol)
pragma solidity ^0.8.0;
/**
* @dev {ERC20} token, including:
*
* - ability for holders to burn (destroy) their tokens
* - a minter role that allows for token minting (creation)
* - a pauser role that allows to stop all token transfers
*
* This contract uses {AccessControl} to lock permissioned functions using the
* different roles - head to its documentation for details.
*
* The account that deploys the contract will be granted the minter and pauser
* roles, as well as the default admin role, which will let it grant both minter
* and pauser roles to other accounts.
*
* _Deprecated in favor of https://wizard.openzeppelin.com/[Contracts Wizard]._
*/
contract ERC20PresetMinterPauser is Context, AccessControlEnumerable, ERC20Burnable, ERC20Pausable {
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
/**
* @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE` and `PAUSER_ROLE` to the
* account that deploys the contract.
*
* See {ERC20-constructor}.
*/
constructor(string memory name, string memory symbol) ERC20(name, symbol) {
_setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
_setupRole(MINTER_ROLE, _msgSender());
_setupRole(PAUSER_ROLE, _msgSender());
}
/**
* @dev Creates `amount` new tokens for `to`.
*
* See {ERC20-_mint}.
*
* Requirements:
*
* - the caller must have the `MINTER_ROLE`.
*/
function mint(address to, uint256 amount) public virtual {
require(hasRole(MINTER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have minter role to mint");
_mint(to, amount);
}
/**
* @dev Pauses all token transfers.
*
* See {ERC20Pausable} and {Pausable-_pause}.
*
* Requirements:
*
* - the caller must have the `PAUSER_ROLE`.
*/
function pause() public virtual {
require(hasRole(PAUSER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have pauser role to pause");
_pause();
}
/**
* @dev Unpauses all token transfers.
*
* See {ERC20Pausable} and {Pausable-_unpause}.
*
* Requirements:
*
* - the caller must have the `PAUSER_ROLE`.
*/
function unpause() public virtual {
require(hasRole(PAUSER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have pauser role to unpause");
_unpause();
}
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual override(ERC20, ERC20Pausable) {
super._beforeTokenTransfer(from, to, amount);
}
}
// File: contracts\AkemonaDisbursement.sol
pragma solidity ^0.8.19;
//import "./AkemonaWhitelist.sol";
//import "./AkemonaCrowdsaleToken.sol";
//import "./Usdc.sol";
/*
1. Administrator sets up partial or full redemption of securities
Contract creation ("AkemonaDisbursement")
2. Investor makes an onchain request for seeking disbursement of funds
Paying agent who is the owner of the disbursement wallet approves the payment to investors
Administrator allocates the disbursement amount to investors
Investor pulls the onchain funds into their own wallet
*/
contract AkemonaDisbursement {
address public owner;
AkemonaCrowdsale crowdsale;
AkemonaCrowdsaleToken token;
Usdc usdc;
address[] disbursementWallets;
bool _isClosed = true;
uint256 _amountDisbursedPerToken;
uint256 _totalDisbursementAmount;
uint256 _openingTime;
bool _isFinal;
uint256 serialNumber;
uint256 public currentRound;
struct RedemptionRequest {
bool onBehalfOf;
bool redemptionAllocated;
bool redemptionClaimed;
uint256 allocatedAmount;
uint256 burnAmount;
uint8 disbursementWalletIndex;
bool offchainRedemption;
address wallet;
bytes32 investorId;
bool exists; // ignore this, it is used as a solidity hack to check for existence
uint256 round;
}
// Maps investorId to RedemptionRequest
mapping(bytes32 => RedemptionRequest) public walletlessRedemptionRequest;
// Maps wallet address to RedemptionRequest
mapping(address => RedemptionRequest) public walletRedemptionRequest;
event DisbursementSetupEvent(uint256 openingTime, bool isFinal, uint256 serialNumber, uint256 amountDisbursedPerToken, uint256 round);
event RedemptionRequestEvent(bool onBehalfOf, bool offchainRedemption, uint256 serialNumber, address wallet, bytes32 investorId, uint256 round);
event DisbursementAllocatedEvent(address wallet, bytes32 investorId, uint256 allocatedAmount, uint256 burnAmount, uint8 disbursementWalletIndex, bool offchainRedemption, uint256 serialNumber, uint256 round);
event DisbursementRedeemedEvent(address wallet, bytes32 investorId, uint256 allocatedAmount, uint256 burnAmount, uint8 disbursementWalletIndex, bool offchainRedemption, uint256 serialNumber, uint256 round);
event DisbursementClosedEvent(uint256 serialNumber, uint256 round);
constructor(
address _crowdsale,
address _crowdsaleToken,
address[] memory _disbursementWallets,
address payable _usdc,
uint256 _serialNumber
) public {
currentRound = 0;
owner = msg.sender;
crowdsale = AkemonaCrowdsale(_crowdsale);
token = AkemonaCrowdsaleToken(_crowdsaleToken);
usdc = Usdc(_usdc);
disbursementWallets = _disbursementWallets;
serialNumber = _serialNumber;
require (_crowdsale == address(token.crowdsale()), "E0");
}
modifier restricted() {
require(msg.sender == owner, "E3");
_;
}
function isOpen() public view returns (bool) {
return _openingTime > 0 && isClosed() == false && block.timestamp > _openingTime;
}
function isClosed() public view returns (bool) {
return _isClosed;
}
function close() public restricted {
_isClosed = true;
emit DisbursementClosedEvent(getSerialNumber(), currentRound);
}
/*
1. Multiple whitelists per token (add/remove)
2. Permissioned whitelist
3. Central tracker for whitelist
TODO: add amountDisbursedPerToken to event
*/
// Wallets must be funded prior to calling this function
function setupDisbursement (
uint256 amountDisbursedPerToken,
uint256 openingTime,
bool isFinal
) public restricted {
require(_isClosed == true, "The active round must bee closed before this can be called.");
//require ((_amountDisbursedPerToken / 1e6) * token.totalSupplyNonWalletless() <= _totalDisbursementAmount, "Not enough disbursementAmount to cover token supply");
currentRound = currentRound + 1;
_isClosed = false;
_amountDisbursedPerToken = amountDisbursedPerToken;
//_totalDisbursementAmount = totalDisbursementAmount;
_openingTime = openingTime;
_isFinal = isFinal;
uint256 _fundedTotal = 0;
for (uint8 i = 0; i < disbursementWallets.length; i++) {
_fundedTotal = _fundedTotal + usdc.balanceOf(disbursementWallets[i]);
}
require (_fundedTotal >= (amountDisbursedPerToken * (token.totalSupplyNonWalletless() / 1e6)), "Not enough funds in wallets to cover disbursementAmount");
emit DisbursementSetupEvent(openingTime, isFinal, getSerialNumber(), amountDisbursedPerToken, currentRound);
}
function getSerialNumber() public view returns (uint256) {
return serialNumber;
}
function requestRedemption(bool offchainRedemption) public {
require(isOpen(), "disbursement is not open");
//for (uint8 i = 0; i < addresses.length; i++) {
require(token.balanceOf(msg.sender) > 0, "no tokens found for this wallet");
require(walletRedemptionRequest[msg.sender].exists == false || walletRedemptionRequest[msg.sender].round < currentRound, "request already exists for this round");
//require
//require(crowdsale.getInvestorForPurchaseIndex(purchaseIndexes[i]) == msg.sender, "must request from the investor address");
walletRedemptionRequest[msg.sender] = RedemptionRequest(false, false, false, 0, 0, 0, offchainRedemption, msg.sender, 0, true, currentRound);
emit RedemptionRequestEvent(false, offchainRedemption, getSerialNumber(), msg.sender, 0, currentRound);
//}
}
function requestRedemptionOnBehalfOfWallet(address[] memory wallets, bool[] memory offchainRedemption) public restricted {
for (uint8 i = 0; i < wallets.length; i++) {
require(walletRedemptionRequest[wallets[i]].exists == false || walletRedemptionRequest[wallets[i]].round < currentRound, "request already exists for this round");
walletRedemptionRequest[wallets[i]] = RedemptionRequest(true, false, false, 0, 0, 0, offchainRedemption[i], msg.sender, 0, true, currentRound);
emit RedemptionRequestEvent(true, offchainRedemption[i], getSerialNumber(), wallets[i], 0, currentRound);
}
}
function requestRedemptionOnBehalfOf(bytes32[] memory _investorIds) public restricted {
for (uint8 i = 0; i < _investorIds.length; i++) {
require(walletlessRedemptionRequest[_investorIds[i]].exists == false || walletlessRedemptionRequest[_investorIds[i]].round < currentRound, "request already exists");
walletlessRedemptionRequest[_investorIds[i]] = RedemptionRequest(true, false, false, 0, 0, 0, true, address(0), _investorIds[i], true, currentRound);
emit RedemptionRequestEvent(true, true, getSerialNumber(), address(0), _investorIds[i], currentRound);
}
}
// emits DisbursementAllocatedEvent
// in the case of on-chain disbursements, the wallet index indicates which disbursementWallet to use (starting with 0)
function allocateDisbursementsByInvestorId(bytes32[] memory _investorIds, uint256[] memory _amounts, uint8[] memory _walletIndexes) public restricted {
for (uint8 i = 0; i < _investorIds.length; i++) {
require(walletlessRedemptionRequest[_investorIds[i]].exists != false, "request does not exist");
//address addr = crowdsale.getInvestorForPurchaseIndex(_investorIds[i]);
//require(token.balanceOf(addr) * (_amountDisbursedPerToken / 1e6) <= _amounts[i], "too much is being allocated for this investor");
require(walletlessRedemptionRequest[_investorIds[i]].redemptionClaimed == false, "request has already been redeemed");
walletlessRedemptionRequest[_investorIds[i]].allocatedAmount = _amounts[i];
walletlessRedemptionRequest[_investorIds[i]].disbursementWalletIndex = _walletIndexes[i];
walletlessRedemptionRequest[_investorIds[i]].redemptionAllocated = true;
emit DisbursementAllocatedEvent(address(0), _investorIds[i], _amounts[i], 0, _walletIndexes[i], walletlessRedemptionRequest[_investorIds[i]].offchainRedemption, getSerialNumber(), currentRound);
}
}
function allocateDisbursementsByWallet(address[] memory _wallets, uint256[] memory _amounts, uint256[] memory _burnAmounts, uint8[] memory _walletIndexes) public restricted {
for (uint8 i = 0; i < _wallets.length; i++) {
require(walletRedemptionRequest[_wallets[i]].exists != false, "request does not exist");
//address addr = crowdsale.getInvestorForPurchaseIndex(_investorIds[i]);
//require(token.balanceOf(addr) * (_amountDisbursedPerToken / 1e6) <= _amounts[i], "too much is being allocated for this investor");
require(walletRedemptionRequest[_wallets[i]].redemptionClaimed == false, "request has already been redeemed");
require(walletRedemptionRequest[_wallets[i]].offchainRedemption == false || _amounts[i] == 0, "cannot allocate non-zero amount to offchain redemption request");
walletRedemptionRequest[_wallets[i]].allocatedAmount = _amounts[i];
walletRedemptionRequest[_wallets[i]].disbursementWalletIndex = _walletIndexes[i];
walletRedemptionRequest[_wallets[i]].redemptionAllocated = true;
walletRedemptionRequest[_wallets[i]].burnAmount = _burnAmounts[i];
emit DisbursementAllocatedEvent(_wallets[i], 0, _amounts[i], _burnAmounts[i], _walletIndexes[i], walletRedemptionRequest[_wallets[i]].offchainRedemption, getSerialNumber(), currentRound);
}
}
/*
function debugAllowance() public view returns (uint256) {
uint256 allowance = usdc.allowance(disbursementWallets[walletRedemptionRequest[msg.sender].disbursementWalletIndex], address(this));
return allowance;
}
function debugAllocated() public view returns (uint256) {
return walletRedemptionRequest[msg.sender].allocatedAmount;
}
function debugIndex() public view returns (uint8) {
return disbursementWallets[walletRedemptionRequest[msg.sender].disbursementWalletIndex
}
*/
function redeemDisbursement() public {
require(isOpen(), "disbursement is not open");
//for (uint8 i = 0; i < purchaseIndexes.length; i++) {
//address addr = crowdsale.getInvestorForPurchaseIndex(purchaseIndexes[i]);
//require(addr == msg.sender, "must redeem from the investor address");
require(walletRedemptionRequest[msg.sender].redemptionAllocated == true, "disbursement is not allocated");
require(walletRedemptionRequest[msg.sender].offchainRedemption == false, "disbursement is designated as offchain");
uint256 allowance = usdc.allowance(disbursementWallets[walletRedemptionRequest[msg.sender].disbursementWalletIndex], address(this));
require(allowance >= walletRedemptionRequest[msg.sender].allocatedAmount, "insufficient approval in wallet");
require(walletRedemptionRequest[msg.sender].redemptionClaimed == false, "disbursement has already been redeemed");
// Ensure this flag gets set before the USDC transfer
walletRedemptionRequest[msg.sender].redemptionClaimed = true;
require(usdc.transferFrom(disbursementWallets[walletRedemptionRequest[msg.sender].disbursementWalletIndex], msg.sender, walletRedemptionRequest[msg.sender].allocatedAmount), "USDC transfer failed.");
if (walletRedemptionRequest[msg.sender].burnAmount == 0) {
if (_isFinal) {
token.burnFromAdmin(msg.sender, token.balanceOf(msg.sender));
}
} else {
token.burnFromAdmin(msg.sender, walletRedemptionRequest[msg.sender].burnAmount);
}
emit DisbursementRedeemedEvent(msg.sender, 0, walletRedemptionRequest[msg.sender].allocatedAmount, walletRedemptionRequest[msg.sender].burnAmount, walletRedemptionRequest[msg.sender].disbursementWalletIndex,walletRedemptionRequest[msg.sender].offchainRedemption, getSerialNumber(), currentRound);
//}
}
function redeemDisbursementOnBehalfOfWallet(address[] memory _wallets) public restricted {
for (uint8 i = 0; i < _wallets.length; i++) {
require(walletRedemptionRequest[_wallets[i]].redemptionAllocated == true, "disbursement is not allocated");
require(walletRedemptionRequest[_wallets[i]].redemptionClaimed == false, "disbursement has already been redeemed");
walletRedemptionRequest[_wallets[i]].redemptionClaimed = true;
if (walletRedemptionRequest[_wallets[i]].offchainRedemption == false) {
require(usdc.transferFrom(disbursementWallets[walletRedemptionRequest[_wallets[i]].disbursementWalletIndex], _wallets[i], walletRedemptionRequest[_wallets[i]].allocatedAmount), "USDC transfer failed.");
}
if (walletRedemptionRequest[_wallets[i]].burnAmount == 0) {
if (_isFinal) {
token.burnFromAdmin(_wallets[i], token.balanceOf(_wallets[i]));
}
} else {
token.burnFromAdmin(_wallets[i], walletRedemptionRequest[_wallets[i]].burnAmount);
}
emit DisbursementRedeemedEvent(_wallets[i], 0, walletRedemptionRequest[_wallets[i]].allocatedAmount, walletRedemptionRequest[_wallets[i]].burnAmount, walletRedemptionRequest[_wallets[i]].disbursementWalletIndex, walletRedemptionRequest[_wallets[i]].offchainRedemption, getSerialNumber(), currentRound);
}
}
function redeemDisbursementsOffchain(bytes32[] memory _investorIds) public restricted {
require(isOpen(), "disbursement is not open");
for (uint8 i = 0; i < _investorIds.length; i++) {
require(walletlessRedemptionRequest[_investorIds[i]].redemptionAllocated == true, "disbursement is not allocated");
require(walletlessRedemptionRequest[_investorIds[i]].offchainRedemption == true, "disbursement is not designated as offchain");
require(walletlessRedemptionRequest[_investorIds[i]].redemptionClaimed == false, "disbursement has already been redeemed");
walletlessRedemptionRequest[_investorIds[i]].redemptionClaimed = true;
//address addr = crowdsale.getInvestorForPurchaseIndex(_investorIds[i]);
//if (_isFinal) {
// token.burnFromAdmin(addr, token.balanceOf(addr));
//}
emit DisbursementRedeemedEvent(address(0), _investorIds[i], walletlessRedemptionRequest[_investorIds[i]].allocatedAmount, walletlessRedemptionRequest[_investorIds[i]].burnAmount, walletlessRedemptionRequest[_investorIds[i]].disbursementWalletIndex, walletlessRedemptionRequest[_investorIds[i]].offchainRedemption, getSerialNumber(), currentRound);
}
/*
for (uint8 i = 0; i < _wallets.length; i++) {
require(walletRedemptionRequest[_wallets[i]].redemptionAllocated == true, "disbursement is not allocated");
require(walletRedemptionRequest[_wallets[i]].offchainRedemption == true, "disbursement is not designated as offchain");
require(walletRedemptionRequest[_wallets[i]].redemptionClaimed == false, "disbursement has already been redeemed");
walletRedemptionRequest[_wallets[i]].redemptionClaimed = true;
if (_burnAmounts[i] == 0) {
if (_isFinal) {
token.burnFromAdmin(_wallets[i], token.balanceOf(_wallets[i]));
}
} else {
token.burnFromAdmin(_wallets[i], _burnAmounts[i]);
}
emit DisbursementRedeemedEvent(_wallets[i], 0, walletRedemptionRequest[_wallets[i]].allocatedAmount, _burnAmounts[i], walletRedemptionRequest[_wallets[i]].disbursementWalletIndex, walletRedemptionRequest[_wallets[i]].offchainRedemption, getSerialNumber(), currentRound);
}*/
}
}
// File: contracts\AkemonaCrowdsaleToken.sol
pragma solidity ^0.8.19;
//import "./AkemonaCrowdsale.sol";
//import "./AkemonaWhitelist.sol";
/**
* @title SampleCrowdsaleToken
* @dev Very simple ERC20 Token that can be minted.
* It is meant to be used in a crowdsale contract.
*/
contract AkemonaCrowdsaleToken is ERC20PresetMinterPauser {
AkemonaCrowdsale public crowdsale;
AkemonaDisbursement public disbursement;
bool _disbursementSet;
constructor (string memory name, string memory symbol, address _crowdsale) public ERC20PresetMinterPauser(name, symbol) {
// solhint-disable-previous-line no-empty-blocks
crowdsale = AkemonaCrowdsale(_crowdsale);
}
modifier restricted() {
require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "E3");
_;
}
function setCrowdsale(address _crowdsale) public restricted {
crowdsale = AkemonaCrowdsale(_crowdsale);
}
function _transfer(address from, address to, uint256 value) internal override {
if (!hasRole(DEFAULT_ADMIN_ROLE, _msgSender())) {
require(crowdsale.isTransferAuthorized(from, to, value), "Transfer is not authorized.");
if (hasDisbursement()) {
require(disbursement.isOpen() == false, "Cannot transfer while disbursement is open.");
}
}
super._transfer(from, to, value);
}
function decimals() public view override returns (uint8) {
return 6;
}
function burnFromAdmin(address account, uint256 amount) public restricted {
require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "AkemonaCrowdsaleToken: must have admin role to call burnFromAdmin");
_burn(account, amount);
}
function burnFromAdminMultiple(address[] memory accounts, uint256[] memory amounts) public restricted {
require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "AkemonaCrowdsaleToken: must have admin role to call burnFromAdmin");
for (uint8 i = 0; i < accounts.length; i++) {
_burn(accounts[i], amounts[i]);
}
}
// function balanceOf(address account) public override view returns (uint256) {
// return super.balanceOf(account);
// }
function adminTransfer(address from, address to, uint256 value) public restricted {
super._transfer(from, to, value);
}
function setDisbursement(address _disbursement) public restricted {
if (hasDisbursement()) {
require(disbursement.isClosed(), "Cannot replace a disbursement that isnt closed");
}
disbursement = AkemonaDisbursement(_disbursement);
grantRole(DEFAULT_ADMIN_ROLE, _disbursement);
_disbursementSet = true;
}
function hasDisbursement() public view returns (bool) {
return _disbursementSet;
}
function addAdmin(address account) public restricted {
grantRole(DEFAULT_ADMIN_ROLE, account);
}
function totalSupplyNonWalletless() public view returns (uint256) {
return totalSupply() - balanceOf(crowdsale.owner());
}
}
// File: contracts\Usdc.sol
pragma solidity ^0.8.19;
library SafeMathMod { // Partial SafeMath Library
function mul(uint256 a, uint256 b) pure internal returns(uint256) {
uint256 c = a * b;
assert(a == 0 || c / a == b);
return c;
}
function div(uint256 a, uint256 b) pure internal returns(uint256) {
assert(b != 0); // Solidity automatically throws when dividing by 0
uint256 c = a / b;
assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
function sub(uint256 a, uint256 b) internal pure returns(uint256 c) {
require((c = a - b) < a);
}
function add(uint256 a, uint256 b) internal pure returns(uint256 c) {
require((c = a + b) > a);
}
}
contract Usdc { //is inherently ERC20
using SafeMathMod
for uint256;
address owner;
string constant public name = "USDC";
string constant public symbol = "USDC";
uint256 constant public decimals = 6;
uint256 constant public totalSupply = 100000000e18;
uint256 constant private MAX_UINT256 = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowed;
event Transfer(address indexed _from, address indexed _to, uint256 _value);
event TransferFrom(address indexed _spender, address indexed _from, address indexed _to, uint256 _value);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
constructor () public {
balanceOf[msg.sender] = totalSupply;
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
/**
* @dev function that sells available tokens
*/
function transfer(address _to, uint256 _value) public returns(bool success) {
/* Ensures that tokens are not sent to address "0x0" */
require(_to != address(0));
/* Prevents sending tokens directly to contracts. */
/* SafeMathMOd.sub will throw if there is not enough balance and if the transfer value is 0. */
balanceOf[msg.sender] = balanceOf[msg.sender].sub(_value);
balanceOf[_to] = balanceOf[_to].add(_value);
emit Transfer(msg.sender, _to, _value);
return true;
}
/**
* @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from`
*
* @param _from The address of the sender
* @param _to The address of the recipient
* @param _value The amount of token to be transferred
*/
function transferFrom(address _from, address _to, uint256 _value) public returns(bool success) {
/* Ensures that tokens are not sent to address "0x0" */
require(_to != address(0));
/* Ensures tokens are not sent to this contract */
uint256 allowance = allowed[_from][msg.sender];
/* Ensures sender has enough available allowance OR sender is balance holder allowing single transsaction send to contracts*/
require(_value <= allowance || _from == msg.sender);
/* Use SafeMathMod to add and subtract from the _to and _from addresses respectively. Prevents under/overflow and 0 transfers */
balanceOf[_to] = balanceOf[_to].add(_value);
balanceOf[_from] = balanceOf[_from].sub(_value);
/* Only reduce allowance if not MAX_UINT256 in order to save gas on unlimited allowance */
/* Balance holder does not need allowance to send from self. */
if (allowed[_from][msg.sender] != MAX_UINT256 && _from != msg.sender) {
allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
}
emit Transfer(_from, _to, _value);
return true;
}
/**
* @dev Transfer the specified amounts of tokens to the specified addresses.
* @dev Be aware that there is no check for duplicate recipients.
*
* @param _toAddresses Receiver addresses.
* @param _amounts Amounts of tokens that will be transferred.
*/
function multiPartyTransfer(address[] memory _toAddresses, uint256[] memory _amounts) public {
/* Ensures _toAddresses array is less than or equal to 255 */
require(_toAddresses.length <= 255);
/* Ensures _toAddress and _amounts have the same number of entries. */
require(_toAddresses.length == _amounts.length);
for (uint8 i = 0; i < _toAddresses.length; i++) {
transfer(_toAddresses[i], _amounts[i]);
}
}
/**
* @dev Transfer the specified amounts of tokens to the specified addresses from authorized balance of sender.
* @dev Be aware that there is no check for duplicate recipients.
*
* @param _from The address of the sender
* @param _toAddresses The addresses of the recipients (MAX 255)
* @param _amounts The amounts of tokens to be transferred
*/
function multiPartyTransferFrom(address _from, address[] memory _toAddresses, uint256[] memory _amounts) public {
/* Ensures _toAddresses array is less than or equal to 255 */
require(_toAddresses.length <= 255);
/* Ensures _toAddress and _amounts have the same number of entries. */
require(_toAddresses.length == _amounts.length);
for (uint8 i = 0; i < _toAddresses.length; i++) {
transferFrom(_from, _toAddresses[i], _amounts[i]);
}
}
/**
* @notice `msg.sender` approves `_spender` to spend `_value` tokens
*
* @param _spender The address of the account able to transfer the tokens
* @param _value The amount of tokens to be approved for transfer
*/
function approve(address _spender, uint256 _value) public returns(bool success) {
/* Ensures address "0x0" is not assigned allowance. */
require(_spender != address(0));
allowed[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
return true;
}
/**
* @param _owner The address of the account owning tokens
* @param _spender The address of the account able to transfer the tokens
*/
function allowance(address _owner, address _spender) public view returns(uint256 remaining) {
remaining = allowed[_owner][_spender];
}
//function isNotContract(address _addr) private view returns(bool) {
// uint length;
// assembly {
/* retrieve the size of the code on target address, this needs assembly */
// length: = extcodesize(_addr)
// }
// return (length == 0);
//}
}
// File: contracts\AkemonaCrowdsale.sol
pragma solidity ^0.8.19;
contract AkemonaCrowdsale {
struct RefundRequest {
uint256 purchaseIndex;
bool isReconfirm;
// TODO: partial refund
}
RefundRequest[] public refundRequests;
struct Purchase {
address investor;
bytes32 investorId;
uint256 paid;
uint256 received;
bool refunded;
bool refundRequested;
bool refundAllocated;
bool requiresReconfirm;
bool isClosed;
bool offChain;
bool noWallet;
bytes32 offchainPurchaseId;
}
/* TODO:
1) issue tokens to 0 address for no wallet
2) covert noWallet to wallet and issue event
*/
Purchase[] public purchases;
address public owner;
AkemonaCrowdsaleToken public token;
Usdc public usdc;
AkemonaWhitelistBase[] public whitelists;
address public escrow;
uint256 public openingTime;
uint256 public closingTime;
uint256 public minimumInvestment;
uint256 public goal;
uint256 public cap;
uint256 public raised;
bool public isDisbursed;
uint256 public disbursementTime;
bool private _closed;
// zero coupon bond
uint256 public maturityTime;
uint256 public effectiveDailyRate;
uint256 public transactionId;
string public securityType;
bool public directRefund;
event PurchaseEvent(address indexed _purchaser, uint256 indexed _paid, uint256 indexed _received, uint256 _purchaseIndex);
event PurchaseClosedEvent(address indexed _purchaser, uint256 indexed _paid, uint256 indexed _received, uint256 _purchaseIndex);
event RefundProcessedEvent(address indexed _purchaser, uint256 indexed _paid, uint256 indexed _received, uint256 _purchaseIndex);
event RefundAllocatedEvent(address indexed _purchaser, uint256 indexed _paid, uint256 indexed _received, uint256 _purchaseIndex);
event RefundRequestedEvent(address indexed _purchaser, uint256 indexed _paid, uint256 indexed _received, uint256 _purchaseIndex);
event ReconfirmEvent(address indexed _purchaser, uint256 indexed _paid, uint256 indexed _received, uint256 _purchaseIndex);
event RecoverTokenEvent(address indexed _oldAddress, address indexed _newAddress, uint256 _purchaseIndex);
event ConvertNoWalletEvent(address indexed _wallet, uint256 _purchaseIndex);
function getPurchase(uint256 i) public view returns (Purchase memory) {
return purchases[i];
}
constructor(
address payable _usdc,
address[] memory _whitelists,
address _escrow,
/*
WAS:
uint256[] memory _openingClosingMaturityTime,
//uint256 _openingTime,
//uint256 _closingTime,
//uint256 _maturityTime,
uint256 _minimumInvestment,
uint256 _goal,
uint256 _cap,
uint256 _effectiveDailyRate,
*/
uint256[] memory _intArguments,
//uint256 _openingTime,
//uint256 _closingTime,
//uint256 _maturityTime,
//uint256 _minimumInvestment,
//uint256 _goal,
//uint256 _cap,
//uint256 _effectiveDailyRate,
string memory name,
string memory symbol,
string memory _securityType,
bool _directRefund
) public {
owner = msg.sender;
raised = 0;
//token = new AkemonaCrowdsaleToken("ake", symbol);
token = new AkemonaCrowdsaleToken(name, symbol, address(this));
token.addAdmin(msg.sender);
usdc = Usdc(_usdc);
for (uint8 i = 0; i < _whitelists.length; i++) {
whitelists.push(AkemonaWhitelistBase(_whitelists[i]));
}
escrow = _escrow;
openingTime = _intArguments[0];
closingTime = _intArguments[1];
maturityTime = _intArguments[2];
minimumInvestment = _intArguments[3];
goal = _intArguments[4];
cap = _intArguments[5];
effectiveDailyRate = _intArguments[6];
require(closingTime > openingTime, "E1");
require(maturityTime > closingTime, "E2");
transactionId = 1;
securityType = _securityType;
directRefund = _directRefund;
}
modifier restricted() {
require(msg.sender == owner, "E3");
_;
}
modifier whenNotClosed() {
require(!_closed, "E4");
_;
}
modifier whenClosed() {
require(_closed, "E5");
_;
}
function addWhitelist(address _whitelist) public restricted {
whitelists.push(AkemonaWhitelistBase(_whitelist));
}
function removeWhitelist(uint256 index) public restricted {
require(index < whitelists.length);
whitelists[index] = whitelists[whitelists.length-1];
whitelists.pop();
}
function getInvestorForPurchaseIndex(uint256 index) public view returns (address) {
return purchases[index].investor;
}
/* TODO:
1) issue tokens to 0 address for no wallet
2) covert noWallet to wallet and issue event
*/
// TODO: offchainInvestmentId
function processNoWalletPurchase(bytes32[] memory _userIds, uint256[] memory _amounts, uint256[] memory amountToIssue, bytes32[] memory _offchainInvestmentIds, uint256 _transactionId) public restricted {
require(transactionId == _transactionId, "E6");
transactionId = transactionId + 1;
for (uint8 i = 0; i < _userIds.length; i++) {
require(!isDisbursed, "E7");
//require(block.timestamp < closingTime, "Contract is past its closing time."); // now a client-side check
require(block.timestamp > openingTime, "E8");
require(raised < cap, "E9");
uint usdcAmount = _amounts[i];
require(raised + usdcAmount <= cap, "E10");
require (usdcAmount >= minimumInvestment, "E11");
//uint256 amountToIssue = getNumTokensPerNumDollarsWithCredit(usdcAmount, _numDays[i]);
token.mint(owner, amountToIssue[i]);
raised = raised + usdcAmount;
purchases.push(Purchase(0x0000000000000000000000000000000000000000, _userIds[i], usdcAmount, amountToIssue[i], false, false, false, false, false, true, true, _offchainInvestmentIds[i]));
emit PurchaseEvent(0x0000000000000000000000000000000000000000, usdcAmount, amountToIssue[i], purchases.length - 1);
}
}
function convertNoWalletToWallet(uint256[] memory purchaseIndices, address wallet) public restricted {
for (uint i = 0; i < purchaseIndices.length; i++) {
Purchase storage currentPurchase = purchases[purchaseIndices[i]];
require(currentPurchase.noWallet, "E12");
currentPurchase.investor = wallet;
currentPurchase.noWallet = false;
//token.transferFrom(owner, wallet, currentPurchase.received);
//token.transfer(wallet, currentPurchase.received);
token.adminTransfer(owner, wallet, currentPurchase.received);
emit ConvertNoWalletEvent(wallet, purchaseIndices[i]);
}
}
// TODO: offchainInvestmentId
function processPurchaseOnBehalfOf(address[] memory _addresses, uint256[] memory _amounts, uint256[] memory amountToIssue, bytes32[] memory _offchainInvestmentIds, uint256 _transactionId) public restricted {
require(transactionId == _transactionId, "E13");
transactionId = transactionId + 1;
for (uint8 i = 0; i < _addresses.length; i++) {
require(!isDisbursed, "E14");
//require(block.timestamp < closingTime, "Contract is past its closing time."); // now a client-side check
require(block.timestamp > openingTime, "E15");
require(raised < cap, "E16");
uint usdcAmount = _amounts[i];
require(raised + usdcAmount <= cap, "E17");
bool authorized = false;
for (uint8 i = 0; i < whitelists.length; i++) {
if (whitelists[i].isPurchaseAuthorized(_addresses[i], usdcAmount)) {
authorized = true;
}
}
require(authorized, "E18");
require (usdcAmount >= minimumInvestment, "E19");
//uint256 amountToIssue = getNumTokensPerNumDollarsWithCredit(usdcAmount, _numDays[i]);
token.mint(_addresses[i], amountToIssue[i]);
raised = raised + usdcAmount;
purchases.push(Purchase(_addresses[i], "", usdcAmount, amountToIssue[i], false, false, false, false, false, true, false, _offchainInvestmentIds[i]));
emit PurchaseEvent(_addresses[i], usdcAmount, amountToIssue[i], purchases.length - 1);
}
}
function processPurchase(address _investor, uint usdcAmount, uint256 amountToIssue) public restricted {
require(!isDisbursed, "E20");
//require(block.timestamp < closingTime, "Contract is past its closing time."); // now a client-side check
require(block.timestamp > openingTime, "E21");
require(raised < cap, "E22");
require(usdc.allowance(_investor, address(this)) > 0, "E23");
//uint usdcAmount = usdc.allowance(_investor, address(this));
require(raised + usdcAmount <= cap, "E24");
bool authorized = false;
for (uint8 i = 0; i < whitelists.length; i++) {
if (whitelists[i].isPurchaseAuthorized(_investor, usdcAmount)) {
authorized = true;
}
}
require(authorized, "E25");
require (usdcAmount >= minimumInvestment, "E26");
require(usdc.transferFrom(_investor, escrow, usdcAmount), "E27");
//uint256 amountToIssue = getNumTokensPerNumDollars(usdcAmount);
token.mint(_investor, amountToIssue);
raised = raised + usdcAmount;
purchases.push(Purchase(_investor, "", usdcAmount, amountToIssue, false, false, false, false, false, false, false, ""));
emit PurchaseEvent(_investor, usdcAmount, amountToIssue, purchases.length - 1);
}
function processPurchases(address[] memory _addresses, uint[] memory usdcAmount, uint256[] memory amountToIssue) public restricted {
for (uint8 i = 0; i < _addresses.length; i++) {
if (usdc.allowance(_addresses[i], address(this)) > 0) {
processPurchase(_addresses[i], usdcAmount[i], amountToIssue[i]);
}
}
}
function setDisbursed(bool _isDisbursed, uint256 _disbursementTime) public restricted {
isDisbursed = _isDisbursed;
disbursementTime = _disbursementTime;
}
function close() public restricted whenNotClosed {
_closed = true;
}
function unclose() public restricted whenClosed {
_closed = false;
}
function closed() public view returns (bool) {
return _closed;
}
function isTransferAuthorized(address _from, address _to, uint256 value) public view returns (bool) {
for (uint8 i = 0; i < whitelists.length; i++) {
if (whitelists[i].isTransferAuthorized(_from, _to, this)) {
return true;
}
}
return false;
}
function rollingClose() public restricted {
require(!isDisbursed, "E28");
for (uint i = 0; i < purchases.length; i++) {
if (!purchases[i].requiresReconfirm && !purchases[i].refunded && !purchases[i].refundRequested && !purchases[i].isClosed) {
purchases[i].isClosed = true;
emit PurchaseClosedEvent(purchases[i].investor, purchases[i].paid, purchases[i].received, i);
}
}
}
function reconfirmPurchase(uint256 i) public whenNotClosed {
require(!isDisbursed, "E29");
if (purchases[i].investor == msg.sender && !purchases[i].refunded && !purchases[i].refundRequested) {
purchases[i].requiresReconfirm = false;
emit ReconfirmEvent(purchases[i].investor, purchases[i].paid, purchases[i].received, i);
}
}
function reconfirmPurchaseOnBehalfOf(uint256 i) public restricted {
require(!isDisbursed, "E30");
if (!purchases[i].refunded && !purchases[i].refundRequested) {
purchases[i].requiresReconfirm = false;
emit ReconfirmEvent(purchases[i].investor, purchases[i].paid, purchases[i].received, i);
}
}
function requireReconfirm() public restricted {
require(!isDisbursed, "E31");
for (uint i = 0; i < purchases.length; i++) {
if (!purchases[i].refunded && !purchases[i].refundRequested && !purchases[i].isClosed) {
purchases[i].requiresReconfirm = true;
}
}
}
function processReconfirm() public restricted {
require(!isDisbursed, "E32");
for (uint i = 0; i < purchases.length; i++) {
if (purchases[i].requiresReconfirm && !purchases[i].refunded && !purchases[i].refundRequested) {
purchases[i].refundRequested = true;
refundRequests.push(RefundRequest(i, true));
}
}
}
function requestRefundForPurchase(uint256 i) public whenNotClosed {
require(!isDisbursed, "E33");
bool refundProcessed = false;
if (purchases[i].investor == msg.sender && !purchases[i].refunded && !purchases[i].refundRequested && !purchases[i].isClosed) {
purchases[i].refundRequested = true;
refundRequests.push(RefundRequest(i, false));
refundProcessed = true;
emit RefundRequestedEvent(purchases[i].investor, purchases[i].paid, purchases[i].received, i);
}
require(refundProcessed, "E34");
}
function requestRefundForPurchaseOnBehalfOf(uint256 i) public restricted {
require(!isDisbursed, "E35");
bool refundProcessed = false;
if (!purchases[i].refunded && !purchases[i].refundRequested && !purchases[i].isClosed) {
purchases[i].refundRequested = true;
refundRequests.push(RefundRequest(i, false));
refundProcessed = true;
emit RefundRequestedEvent(purchases[i].investor, purchases[i].paid, purchases[i].received, i);
}
require(refundProcessed, "E36");
}
// maxLength used as a hack to avoid unestimatable gas price error
function requestRefundAll(uint256 maxLength) public restricted {
require(!isDisbursed, "E37");
for (uint i = 0; i < purchases.length && i < maxLength; i++) {
if (!purchases[i].refunded && !purchases[i].refundRequested && !purchases[i].isClosed) {
purchases[i].refundRequested = true;
refundRequests.push(RefundRequest(i, false));
emit RefundRequestedEvent(purchases[i].investor, purchases[i].paid, purchases[i].received, i);
}
}
}
function requestRefund(bool onlyMostRecent) public whenNotClosed {
require(!isDisbursed, "E38");
bool refundProcessed = false;
bool cont = true;
for (uint i = purchases.length - 1; i >= 0 && cont; i--) {
if (purchases[i].investor == msg.sender && !purchases[i].refunded && !purchases[i].refundRequested && !purchases[i].isClosed) {
purchases[i].refundRequested = true;
refundRequests.push(RefundRequest(i, false));
refundProcessed = true;
emit RefundRequestedEvent(purchases[i].investor, purchases[i].paid, purchases[i].received, i);
if (onlyMostRecent) {
cont = false;
}
}
}
require(refundProcessed, "E39");
}
function processRefund(uint256 purchaseIndex) public whenNotClosed {
require(!directRefund);
require(!isDisbursed, "Contract is already disbursed");
uint256 usdcAuthorized = usdc.allowance(escrow, address(this));
require(usdcAuthorized > 0, "Contract does not have authorization to refund USDC.");
uint256 usdcAvailable = usdc.balanceOf(escrow);
require(usdcAvailable > 0, "Insufficient funds.");
if (usdcAuthorized < usdcAvailable) {
usdcAvailable = usdcAuthorized;
}
Purchase storage currentPurchase = purchases[purchaseIndex];
require(currentPurchase.investor == msg.sender);
require(!currentPurchase.offChain, "This was an offchain purchase and needs to be processed via processOffchainRefund method");
if(!currentPurchase.refunded && currentPurchase.refundRequested && currentPurchase.refundAllocated) {
if (usdcAvailable >= currentPurchase.paid) {
// Process the refund
require(usdc.transferFrom(escrow, currentPurchase.investor, currentPurchase.paid), "USDC transfer failed.");
token.burnFromAdmin(currentPurchase.investor, currentPurchase.received);
currentPurchase.refunded = true;
raised = raised - currentPurchase.paid;
emit RefundProcessedEvent(currentPurchase.investor, currentPurchase.paid, currentPurchase.received, purchaseIndex);
}
}
}
function processOffchainRefund(uint256 purchaseIndex) public restricted {
processManualRefund(purchaseIndex);
}
function processDirectRefundByEscrow(uint256 purchaseIndex) public restricted {
processManualRefund(purchaseIndex);
}
function processManualRefund(uint256 purchaseIndex) public restricted {
require(!isDisbursed, "Contract is already disbursed");
Purchase storage currentPurchase = purchases[purchaseIndex];
require(!currentPurchase.refunded, "This purchase is already marked as refunded");
require(currentPurchase.refundRequested, "This purchase is not marked as refundRequested");
if (!currentPurchase.noWallet) {
// Burn tokens as the refund has been processed offchain
token.burnFromAdmin(currentPurchase.investor, currentPurchase.received);
}
currentPurchase.refunded = true;
raised = raised - currentPurchase.paid;
emit RefundProcessedEvent(currentPurchase.investor, currentPurchase.paid, currentPurchase.received, purchaseIndex);
}
function getPurchasesPendingRefund() public view returns (bool[] memory) {
require(!isDisbursed, "Contract is already disbursed");
uint256 usdcAuthorized = usdc.allowance(escrow, address(this));
require(usdcAuthorized > 0, "Contract does not have authorization to refund USDC.");
uint256 usdcAvailable = usdc.balanceOf(escrow);
require(usdcAvailable > 0, "Insufficient funds.");
if (usdcAuthorized < usdcAvailable) {
usdcAvailable = usdcAuthorized;
}
bool continueProcessing = true;
bool[] memory pendingRefunds = new bool[](purchases.length);
for (uint i = 0; i < refundRequests.length; i++) {
if (continueProcessing) {
Purchase storage currentPurchase = purchases[refundRequests[i].purchaseIndex];
if (!currentPurchase.refunded && currentPurchase.refundRequested && !currentPurchase.offChain) {
if (usdcAvailable >= currentPurchase.paid) {
if (currentPurchase.refundAllocated) {
usdcAvailable = usdcAvailable - currentPurchase.paid;
} else {
// Allocate the refund
pendingRefunds[refundRequests[i].purchaseIndex] = true;
usdcAvailable = usdcAvailable - currentPurchase.paid;
}
} else {
// Insufficient funds to process refund
continueProcessing = false;
}
}
}
}
return pendingRefunds;
}
function allocateRefunds(uint256[] memory purchaseIndices) public restricted {
for (uint i = 0; i < purchaseIndices.length; i++) {
Purchase storage currentPurchase = purchases[purchaseIndices[i]];
require(!currentPurchase.offChain, "One or more purchaseIndices was an offchain purchase and cannot be allocated");
if (!currentPurchase.refunded && currentPurchase.refundRequested) {
currentPurchase.refundAllocated = true;
emit RefundAllocatedEvent(currentPurchase.investor, currentPurchase.paid, currentPurchase.received, purchaseIndices[i]);
}
}
}
function moveSentTokens(address _to) public restricted {
uint usdcAmount = usdc.balanceOf(address(this));
require(usdc.transfer(_to, usdcAmount), "USDC transfer failed.");
}
function getWhitelist(uint256 i) public view returns (AkemonaWhitelistBase) {
return whitelists[i];
}
function getTest() public view returns (uint256) {
return goal;
}
function getToken() public view returns (AkemonaCrowdsaleToken) {
return token;
}
function recoverLostTokens(uint256[] memory purchaseIndices, address _to) public restricted {
//AkemonaWhitelist wl = AkemonaWhitelist(crowdsale.whitelist);
for (uint i = 0; i < purchaseIndices.length; i++) {
Purchase storage currentPurchase = purchases[purchaseIndices[i]];
require (this.getToken().balanceOf(currentPurchase.investor) >= currentPurchase.received, "Account has insufficient tokens");
this.getToken().adminTransfer(currentPurchase.investor, _to, currentPurchase.received);
emit RecoverTokenEvent(currentPurchase.investor, _to, purchaseIndices[i]);
currentPurchase.investor = _to;
}
}
}
{
"compilationTarget": {
"AkemonaCrowdsaleToken.sol": "AkemonaCrowdsaleToken"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"address","name":"_crowdsale","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINTER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PAUSER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"addAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"adminTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burnFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burnFromAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"accounts","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"burnFromAdminMultiple","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"crowdsale","outputs":[{"internalType":"contract AkemonaCrowdsale","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"disbursement","outputs":[{"internalType":"contract AkemonaDisbursement","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getRoleMember","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleMemberCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"hasDisbursement","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_crowdsale","type":"address"}],"name":"setCrowdsale","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_disbursement","type":"address"}],"name":"setDisbursement","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupplyNonWalletless","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"}]