// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;import"../utils/Context.sol";
import"../utils/introspection/ERC165.sol";
/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/interfaceIAccessControl{
functionhasRole(bytes32 role, address account) externalviewreturns (bool);
functiongetRoleAdmin(bytes32 role) externalviewreturns (bytes32);
functiongrantRole(bytes32 role, address account) external;
functionrevokeRole(bytes32 role, address account) external;
functionrenounceRole(bytes32 role, address account) external;
}
/**
* @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.
*/abstractcontractAccessControlisContext, IAccessControl, ERC165{
structRoleData {
mapping (address=>bool) members;
bytes32 adminRole;
}
mapping (bytes32=> RoleData) private _roles;
bytes32publicconstant DEFAULT_ADMIN_ROLE =0x00;
/**
* @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._
*/eventRoleAdminChanged(bytes32indexed role, bytes32indexed previousAdminRole, bytes32indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, an admin role
* bearer except when using {_setupRole}.
*/eventRoleGranted(bytes32indexed role, addressindexed account, addressindexed 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`)
*/eventRoleRevoked(bytes32indexed role, addressindexed account, addressindexed sender);
/**
* @dev See {IERC165-supportsInterface}.
*/functionsupportsInterface(bytes4 interfaceId) publicviewvirtualoverridereturns (bool) {
return interfaceId ==type(IAccessControl).interfaceId||super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/functionhasRole(bytes32 role, address account) publicviewoverridereturns (bool) {
return _roles[role].members[account];
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/functiongetRoleAdmin(bytes32 role) publicviewoverridereturns (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.
*/functiongrantRole(bytes32 role, address account) publicvirtualoverride{
require(hasRole(getRoleAdmin(role), _msgSender()), "AccessControl: sender must be an admin to grant");
_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.
*/functionrevokeRole(bytes32 role, address account) publicvirtualoverride{
require(hasRole(getRoleAdmin(role), _msgSender()), "AccessControl: sender must be an admin to revoke");
_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 granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/functionrenounceRole(bytes32 role, address account) publicvirtualoverride{
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.
*
* [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}.
* ====
*/function_setupRole(bytes32 role, address account) internalvirtual{
_grantRole(role, account);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/function_setRoleAdmin(bytes32 role, bytes32 adminRole) internalvirtual{
emit RoleAdminChanged(role, getRoleAdmin(role), adminRole);
_roles[role].adminRole = adminRole;
}
function_grantRole(bytes32 role, address account) private{
if (!hasRole(role, account)) {
_roles[role].members[account] =true;
emit RoleGranted(role, account, _msgSender());
}
}
function_revokeRole(bytes32 role, address account) private{
if (hasRole(role, account)) {
_roles[role].members[account] =false;
emit RoleRevoked(role, account, _msgSender());
}
}
}
// SPDX-License-Identifier: MITpragmasolidity ^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.
*/abstractcontractContext{
function_msgSender() internalviewvirtualreturns (address) {
returnmsg.sender;
}
function_msgData() internalviewvirtualreturns (bytescalldata) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691returnmsg.data;
}
}
Contract Source Code
File 4 of 12: Dice.sol
pragmasolidity >=0.8.0 <0.9.0;/**
@notice Used to generate two dice rolls.
These are pseudo-random numbers.
The first dice roll is exploitable as any smart contract can determine its value.
The second dice roll includes the first dice roll, as well as the hash of a future block.
Therefore the second dice roll is not available at the time the first dice roll is committed to.
Inspired by the concept of commit-reveal, but simplified to save gas.
Requires a trusted party to roll the second dice. However anyone can choose to audit/verify the second dice roll.
@dev maxDiceRoll - largest possible dice roll
offset - number of blocks to look into the future for the second dice roll
@author Immutable
*/contractDice{
uint256 maxDiceRoll;
uint8 offset;
eventSecondDiceRoll(uint256indexed _firstDiceRoll,
uint256indexed _commitBlock,
uint256 _secondDiceRoll
);
/// @param _maxDiceRoll largest dice roll possible/// @param _offset how many blocks to look into the future for second dice rollconstructor(uint256 _maxDiceRoll, uint8 _offset) {
maxDiceRoll = _maxDiceRoll;
offset = _offset;
}
/// @notice Take the exploitable 'random' number from a previous block already committed to, and enhance it with the blockhash of a later block/// @param _firstDiceRoll The exploitable 'random' number generated previously/// @param _commitBlock The block that _firstDiceRoll was generated in/// @return A new 'random' number that was not available at the time of _commitBlockfunctiongetSecondDiceRoll(uint256 _firstDiceRoll, uint256 _commitBlock)
publicviewreturns (uint256)
{
return _getSecondDiceRoll(_firstDiceRoll, _commitBlock);
}
/// @notice Take the exploitable 'random' number from a previous block that was already committed to, and enhance it with the blockhash of a later block. Emit this new number as an event./// @param _firstDiceRoll The exploitable 'random' number generated previously/// @param _commitBlock The block that _firstDiceRoll was generated infunctionemitSecondDiceRoll(uint256 _firstDiceRoll, uint256 _commitBlock)
public{
emit SecondDiceRoll(
_firstDiceRoll,
_commitBlock,
_getSecondDiceRoll(_firstDiceRoll, _commitBlock)
);
}
function_getSecondDiceRoll(uint256 _firstDiceRoll, uint256 _commitBlock)
internalviewreturns (uint256)
{
returnuint256(
keccak256(
abi.encodePacked(
_firstDiceRoll,
_getFutureBlockhash(_commitBlock)
)
)
) % maxDiceRoll;
}
function_getFutureBlockhash(uint256 _commitBlock)
internalviewreturns (bytes32)
{
uint256 delta =block.number- _commitBlock;
require(delta < offset +256, "Called too late"); // Only the last 256 blockhashes are accessible to the smart contractrequire(delta >= offset +1, "Called too early"); // The hash of commitBlock + offset isn't available until the following blockbytes32 futureBlockhash =blockhash(_commitBlock + offset);
require(futureBlockhash !=bytes32(0), "Future blockhash empty"); // Sanity check to ensure we have a blockhash, which we will due to previous checksreturn futureBlockhash;
}
/// @notice Return a "random" number by hashing a variety of inputs such as the blockhash of the last block, the timestamp of this block, the buyers address, and a seed provided by the buyer./// @dev This function is exploitable as a smart contract can see what random number would be generated and make a decision based on that. Must be used with getSecondDiceRoll()functiongetFirstDiceRoll(uint256 _userProvidedSeed)
publicviewreturns (uint256 randomNumber)
{
returnuint256(
keccak256(
abi.encodePacked(
blockhash(block.number-1),
block.timestamp,
msg.sender,
_userProvidedSeed
)
)
) % maxDiceRoll;
}
/// @return true '_chance%' of the time where _chance is a percentage to 2 d.p. E.g. 1050 for 10.5%/// @dev _random must be a random number between 0 and _maxDiceRollfunction_diceWin(uint256 _random,
uint256 _chance,
uint256 _maxDiceRoll
) internalpurereturns (bool) {
return _random < (_maxDiceRoll * _chance) / _maxDiceRoll;
}
/// @dev _random must be a random number between 0 and _maxDiceRoll/// @return true when _random falls between _lowerLimit and _upperLimit, where limits are percentages to 2 d.p. E.g. 1050 for 10.5%function_diceWinRanged(uint256 _random,
uint256 _lowerLimit,
uint256 _upperLimit,
uint256 _maxDiceRoll
) internalpurereturns (bool) {
return
_random < (_maxDiceRoll * _upperLimit) / _maxDiceRoll &&
_random >= (_maxDiceRoll * _lowerLimit) / _maxDiceRoll;
}
}
Contract Source Code
File 5 of 12: ERC165.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;import"./IERC165.sol";
/**
* @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.
*/abstractcontractERC165isIERC165{
/**
* @dev See {IERC165-supportsInterface}.
*/functionsupportsInterface(bytes4 interfaceId) publicviewvirtualoverridereturns (bool) {
return interfaceId ==type(IERC165).interfaceId;
}
}
Contract Source Code
File 6 of 12: ExchangeRate.sol
pragmasolidity >=0.8.0 <0.9.0;import"@openzeppelin/contracts/access/AccessControl.sol";
import"./Constants.sol";
import"./interfaces/IUniswapV2Pair.sol";
contractExchangeRateisConstants, AccessControl{
addresspublic usdEthPairAddress;
uint256constant cUsdDecimals =2;
IUniswapV2Pair usdEthPair;
eventUpdateUsdToEthPair(address _usdToEthPairAddress);
constructor(address _usdEthPairAddress) {
usdEthPairAddress = _usdEthPairAddress;
usdEthPair = IUniswapV2Pair(_usdEthPairAddress);
}
/// @notice Set the uniswap liquidity pool used to determine exchange rate/// @param _usdEthPairAddress address of the contractfunctionupdateUsdToEthPair(address _usdEthPairAddress) public{
require(
hasRole(PRODUCT_OWNER_ROLE, msg.sender),
"Caller is not product owner"
);
usdEthPairAddress = _usdEthPairAddress;
usdEthPair = IUniswapV2Pair(usdEthPairAddress);
emit UpdateUsdToEthPair(_usdEthPairAddress);
}
/// @notice Calculate Wei price dynamically based on reserves on Uniswap for ETH / DAI pair/// @param _amountInUsd the amount to convert, in USDx100, e.g. 186355 for $1863.55 USD/// @return amount of wei needed to buy _amountInUsdfunctiongetWeiPrice(uint256 _amountInUsd) publicviewreturns (uint256) {
(uint112 usdReserve, uint112 ethReserve, uint32 blockTimestampLast) =
usdEthPair.getReserves();
return _calcWeiFromUsd(usdReserve, ethReserve, _amountInUsd);
}
function_calcWeiFromUsd(uint112 _usdReserve,
uint112 _ethReserve,
uint256 _amountInUsd
) publicpurereturns (uint256) {
return
(_amountInUsd * _ethReserve * (10**18)) /
(_usdReserve * (10**cUsdDecimals));
}
}
// SPDX-License-Identifier: MITpragmasolidity ^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}.
*/interfaceIERC165{
/**
* @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.
*/functionsupportsInterface(bytes4 interfaceId) externalviewreturns (bool);
}