mirror of
https://github.com/supleed2/COMP70017-PoDL-CW.git
synced 2024-12-22 05:35:50 +00:00
Initial commit
This commit is contained in:
commit
8a9462b868
14
.gitignore
vendored
Normal file
14
.gitignore
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# Compiler files
|
||||||
|
cache/
|
||||||
|
out/
|
||||||
|
|
||||||
|
# Ignores development broadcast logs
|
||||||
|
!/broadcast
|
||||||
|
/broadcast/*/31337/
|
||||||
|
/broadcast/**/dry-run/
|
||||||
|
|
||||||
|
# Docs
|
||||||
|
docs/
|
||||||
|
|
||||||
|
# Dotenv file
|
||||||
|
.env
|
4
.gitmodules
vendored
Normal file
4
.gitmodules
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
[submodule "lib/forge-std"]
|
||||||
|
path = lib/forge-std
|
||||||
|
url = https://github.com/foundry-rs/forge-std
|
||||||
|
branch = v1.3.0
|
6
README.md
Normal file
6
README.md
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
# Coursework skeleton
|
||||||
|
|
||||||
|
This is the skeleton for the coursework of the Principle of Distributed Ledgers 2023.
|
||||||
|
It contains [the interfaces](./src/interfaces) of the contracts to implement and an [ERC20 implementation](./src/contracts/PurchaseToken.sol).
|
||||||
|
|
||||||
|
The repository uses [Foundry](https://book.getfoundry.sh/projects/working-on-an-existing-project).
|
7
foundry.toml
Normal file
7
foundry.toml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
[profile.default]
|
||||||
|
src = 'src'
|
||||||
|
out = 'out'
|
||||||
|
libs = ['lib']
|
||||||
|
solc = "0.8.10"
|
||||||
|
|
||||||
|
# See more config options https://github.com/foundry-rs/foundry/tree/master/config
|
1
lib/forge-std
Submodule
1
lib/forge-std
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 066ff16c5c03e6f931cd041fd366bc4be1fae82a
|
0
lib/lib.sol
Normal file
0
lib/lib.sol
Normal file
451
src/contracts/PurchaseToken.sol
Normal file
451
src/contracts/PurchaseToken.sol
Normal file
|
@ -0,0 +1,451 @@
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/ERC20.sol)
|
||||||
|
pragma solidity ^0.8.10;
|
||||||
|
|
||||||
|
import "../interfaces/IERC20.sol";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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].
|
||||||
|
*
|
||||||
|
* The default value of {decimals} is 18. To change this, you should override
|
||||||
|
* this function so it returns a different value.
|
||||||
|
*
|
||||||
|
* 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 PurchaseToken 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}.
|
||||||
|
*
|
||||||
|
* All two of these values are immutable: they can only be set once during
|
||||||
|
* construction.
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
_name = "PurchaseToken";
|
||||||
|
_symbol = "PT";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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 `18`, a balance of `5e18` tokens should
|
||||||
|
* be displayed to a user as `5.00` (`5e18 / 10 ** 18`).
|
||||||
|
*
|
||||||
|
* Tokens usually opt for a value of 18, imitating the relationship between
|
||||||
|
* Ether and Wei. This is the default value returned by this function, unless
|
||||||
|
* it's overridden.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
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 This allows users to mint ERC20 tokens by sending ETH to this contract.
|
||||||
|
* The amount of ERC20 tokens minted will be 100 times the amount of ETH sent.
|
||||||
|
*/
|
||||||
|
function mint() external payable {
|
||||||
|
uint256 amountToMint = msg.value * 100;
|
||||||
|
_mint(msg.sender, amountToMint);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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 {}
|
||||||
|
}
|
106
src/interfaces/IERC20.sol
Normal file
106
src/interfaces/IERC20.sol
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/IERC20.sol)
|
||||||
|
pragma solidity ^0.8.10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/extensions/IERC20Metadata.sol)
|
||||||
|
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);
|
||||||
|
}
|
31
src/interfaces/IPrimaryMarket.sol
Normal file
31
src/interfaces/IPrimaryMarket.sol
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
// SPDX-License-Identifier: UNLICENSED
|
||||||
|
pragma solidity ^0.8.10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Required interface for the primary market.
|
||||||
|
* The primary market is the first point of sale for tickets.
|
||||||
|
* It is responsible for minting tickets and transferring them to the purchaser.
|
||||||
|
* In this implementation, the purchase price is fixed at 100e18 purchase tokens
|
||||||
|
* and the maximum number of tickets that can be purchased is 1000.
|
||||||
|
* The purchase token is an ERC20 token that is specified when the contract is deployed.
|
||||||
|
* The NFT to be minted is an implementation of the ITicketNFT interface and should be created (i.e. deployed)
|
||||||
|
* when the contract implementing this interface is deployed.
|
||||||
|
*/
|
||||||
|
interface IPrimaryMarket {
|
||||||
|
/**
|
||||||
|
* @dev Emitted when a purchase by `holder` occurs, with `holderName` specified.
|
||||||
|
*/
|
||||||
|
event Purchase(address indexed holder, string indexed holderName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Returns the administrator of the primary market.
|
||||||
|
* This should be the address that created the contract.
|
||||||
|
*/
|
||||||
|
function admin() external view returns (address);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Takes the initial NFT token holder's name as a string input
|
||||||
|
* and transfers ERC20 tokens from the purchaser to the admin of this contract
|
||||||
|
*/
|
||||||
|
function purchase(string memory holderName) external;
|
||||||
|
}
|
55
src/interfaces/ISecondaryMarket.sol
Normal file
55
src/interfaces/ISecondaryMarket.sol
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
// SPDX-License-Identifier: UNLICENSED
|
||||||
|
pragma solidity ^0.8.10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Required interface for the secondary market.
|
||||||
|
* The secondary market is the point of sale for tickets after they have been initially purchased from the primary market
|
||||||
|
*/
|
||||||
|
interface ISecondaryMarket {
|
||||||
|
/**
|
||||||
|
* @dev Event emitted when a new ticket listing is created
|
||||||
|
*/
|
||||||
|
event Listing(
|
||||||
|
uint256 indexed ticketID,
|
||||||
|
address indexed holder,
|
||||||
|
uint256 price
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Event emitted when an amount of the purchase token is transferred from
|
||||||
|
*/
|
||||||
|
event Purchase(
|
||||||
|
address indexed purchaser,
|
||||||
|
uint256 indexed ticketID,
|
||||||
|
uint256 price,
|
||||||
|
string newName
|
||||||
|
);
|
||||||
|
/**
|
||||||
|
* @dev Event emitted when a ticket is delisted
|
||||||
|
*/
|
||||||
|
event Delisting(uint256 indexed ticketID);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev This method lists a ticket with `ticketID` for sale by transferring the ticket
|
||||||
|
* such that it is held by this contract. Only the current owner of a specific
|
||||||
|
* ticket is able to list that ticket on the secondary market. The purchase
|
||||||
|
* `price` is specified in an amount of `PurchaseToken`.
|
||||||
|
*/
|
||||||
|
function listTicket(uint256 ticketID, uint256 price) external;
|
||||||
|
|
||||||
|
/** @dev This method allows the msg.sender to purchase a listed ticket with `ticketID`
|
||||||
|
* by paying the purchase price that was specified when the ticket was listed.
|
||||||
|
* `name` gives the new name that should be stated on the ticket when it is purchased.
|
||||||
|
* Note: Only non-expired and unused tickets can be purchased and there is a
|
||||||
|
* fee charged every time a purchase is made. The fee is charged on the price.
|
||||||
|
* The final amount that the lister of the ticket receives is the price
|
||||||
|
* minus the fee. The fee should go to the admin of the primary market.
|
||||||
|
*/
|
||||||
|
function purchase(uint256 ticketID, string calldata name) external;
|
||||||
|
|
||||||
|
/** @dev This method delists a previously listed ticket with `ticketID`. Only the account that
|
||||||
|
* listed the ticket may delist the ticket. The ticket should be transferred back
|
||||||
|
* to msg.sender, i.e., the lister.
|
||||||
|
*/
|
||||||
|
function delistTicket(uint256 ticketID) external;
|
||||||
|
}
|
146
src/interfaces/ITicketNFT.sol
Normal file
146
src/interfaces/ITicketNFT.sol
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
// SPDX-License-Identifier: UNLICENSED
|
||||||
|
pragma solidity ^0.8.10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Required interface for the TicketNFT contract.
|
||||||
|
* A ticket NFT is a non-fungible token that represents a single entry to an event.
|
||||||
|
*/
|
||||||
|
interface ITicketNFT {
|
||||||
|
/**
|
||||||
|
* @dev Emitted when `ticketID` ticket is transferred from `from` to `to`.
|
||||||
|
*/
|
||||||
|
event Transfer(
|
||||||
|
address indexed from,
|
||||||
|
address indexed to,
|
||||||
|
uint256 indexed ticketID
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Emitted when `holder` enables `approved` to manage the `ticketID` ticket.
|
||||||
|
*/
|
||||||
|
event Approval(
|
||||||
|
address indexed holder,
|
||||||
|
address indexed approved,
|
||||||
|
uint256 indexed ticketID
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mints a new ticket for `holder` with `holderName`.
|
||||||
|
* The ticket must be assigned the following metadata:
|
||||||
|
* - A unique ticket ID. Once a ticket has been used or expired, its ID should not be reallocated
|
||||||
|
* - An expiry time of 10 days from the time of minting
|
||||||
|
* - A boolean `used` flag set to false
|
||||||
|
* On minting, a `Transfer` event should be emitted with `from` set to the zero address.
|
||||||
|
*
|
||||||
|
* Requirements:
|
||||||
|
*
|
||||||
|
* - The caller must be the primary market
|
||||||
|
*/
|
||||||
|
function mint(address holder, string memory holderName) external;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Returns the number of tickets a `holder` has.
|
||||||
|
*/
|
||||||
|
function balanceOf(address holder) external view returns (uint256 balance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Returns the address of the holder of the `ticketID` ticket.
|
||||||
|
*
|
||||||
|
* Requirements:
|
||||||
|
*
|
||||||
|
* - `ticketID` must exist.
|
||||||
|
*/
|
||||||
|
function holderOf(uint256 ticketID) external view returns (address holder);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Transfers `ticketID` ticket from `from` to `to`.
|
||||||
|
* This should also set the approved address for this ticket to the zero address
|
||||||
|
*
|
||||||
|
* Requirements:
|
||||||
|
*
|
||||||
|
* - `from` cannot be the zero address.
|
||||||
|
* - `to` cannot be the zero address.
|
||||||
|
* - `ticketID` ticket must be either:
|
||||||
|
* - owned by `from`.
|
||||||
|
* - approved to move this ticket by `approve`
|
||||||
|
*
|
||||||
|
* Emits a `Transfer` and an `Approval` event.
|
||||||
|
*/
|
||||||
|
function transferFrom(
|
||||||
|
address from,
|
||||||
|
address to,
|
||||||
|
uint256 ticketID
|
||||||
|
) external;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Gives permission to `to` to transfer `ticketID` ticket to another account.
|
||||||
|
* The approval is cleared when the ticket is transferred.
|
||||||
|
*
|
||||||
|
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
|
||||||
|
*
|
||||||
|
* Requirements:
|
||||||
|
*
|
||||||
|
* - The caller must own the ticket
|
||||||
|
* - `ticketID` must exist.
|
||||||
|
*
|
||||||
|
* Emits an `Approval` event.
|
||||||
|
*/
|
||||||
|
function approve(address to, uint256 ticketID) external;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Returns the account approved for `ticketID` ticket.
|
||||||
|
*
|
||||||
|
* Requirements:
|
||||||
|
*
|
||||||
|
* - `ticketID` must exist.
|
||||||
|
*/
|
||||||
|
function getApproved(uint256 ticketID)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (address operator);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Returns the current `holderName` associated with a `ticketID`.
|
||||||
|
* Requirements:
|
||||||
|
*
|
||||||
|
* - `ticketID` must exist.
|
||||||
|
*/
|
||||||
|
function holderNameOf(uint256 ticketID)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (string memory holderName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Updates the `holderName` associated with a `ticketID`.
|
||||||
|
* Note that this does not update the actual holder of the ticket.
|
||||||
|
*
|
||||||
|
* Requirements:
|
||||||
|
*
|
||||||
|
* - `ticketID` must exists
|
||||||
|
* - Only the current holder can call this function
|
||||||
|
*/
|
||||||
|
function updateHolderName(uint256 ticketID, string calldata newName)
|
||||||
|
external;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Sets the `used` flag associated with a `ticketID` to `true`
|
||||||
|
*
|
||||||
|
* Requirements:
|
||||||
|
*
|
||||||
|
* - `ticketID` must exist
|
||||||
|
* - the ticket must not already be used
|
||||||
|
* - the ticket must not be expired
|
||||||
|
* - Only the administrator of the primary market can call this function
|
||||||
|
*/
|
||||||
|
function setUsed(uint256 ticketID) external;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Returns `true` if the `used` flag associated with a `ticketID` if `true`
|
||||||
|
* or if the ticket has expired, i.e., the current time is greater than the ticket's
|
||||||
|
* `expiryDate`.
|
||||||
|
* Requirements:
|
||||||
|
*
|
||||||
|
* - `ticketID` must exist
|
||||||
|
*/
|
||||||
|
function isExpiredOrUsed(uint256 ticketID) external view returns (bool);
|
||||||
|
}
|
0
test/.keep
Normal file
0
test/.keep
Normal file
Loading…
Reference in a new issue