diff --git a/Projects/ERC1155.sol b/Projects/ERC1155.sol new file mode 100644 index 0000000..81b734e --- /dev/null +++ b/Projects/ERC1155.sol @@ -0,0 +1,21 @@ +// contracts/GameItems.sol +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {ERC1155} from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; + +contract GameItems is ERC1155 { + uint256 public constant GOLD = 0; + uint256 public constant SILVER = 1; + uint256 public constant THORS_HAMMER = 2; + uint256 public constant SWORD = 3; + uint256 public constant SHIELD = 4; + + constructor() ERC1155("https://game.example/api/item/{id}.json") { + _mint(msg.sender, GOLD, 10 ** 18, ""); + _mint(msg.sender, SILVER, 10 ** 27, ""); + _mint(msg.sender, THORS_HAMMER, 1, ""); + _mint(msg.sender, SWORD, 10 ** 9, ""); + _mint(msg.sender, SHIELD, 10 ** 9, ""); + } +} \ No newline at end of file diff --git a/Projects/ERC20.sol b/Projects/ERC20.sol new file mode 100644 index 0000000..e8e228c --- /dev/null +++ b/Projects/ERC20.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT +// Compatible with OpenZeppelin Contracts ^5.6.0 +pragma solidity ^0.8.27; + +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol"; + +contract MyToken is ERC20, Ownable, ERC20Permit { + constructor(address recipient, address initialOwner) + ERC20("MyToken", "MTK") + Ownable(initialOwner) + ERC20Permit("MyToken") + { + _mint(recipient, 100000 * 10 ** decimals()); + } + + function mint(address to, uint256 amount) public onlyOwner { + _mint(to, amount); + } +} diff --git a/Projects/ERC20Staking b/Projects/ERC20Staking new file mode 100644 index 0000000..2fa11ba --- /dev/null +++ b/Projects/ERC20Staking @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: MIT +// Compatible with OpenZeppelin Contracts ^5.6.0 +pragma solidity ^0.8.27; + +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol"; + +разделить логику + – initialize() — базовая инициализация + – startVesting() — запуск вестинга (только после пополнения контракта + +contract ERC20Staking is Ownable { + + function initialize(address[] memory _beneficiary, uint256 _totalAmount, uint256 _cliff, uint256 _startTime, uint256 _endTime, uint256 _minimalAmountToClaim) public { + struct Staking { + address beneficiary; + uint256 totalAmount; + uint256 cliff; + uint256 startTime; + uint256 endTime; + uint256 minimalAmountToClaim; + } + beneficiary = _beneficiary; + totalAmount = _totalAmount; + cliff = _cliff; + startTime = _startTime; + endTime = _endTime; + minimalAmountToClaim = _minimalAmountToClaim; + + modifier onlyOwner() { + require(msg.sender == owner, "Not owner"); + _; + } + + } + function startVesting(address[] memory _beneficiary, uint256 _totalAmount, uint256 _cliff, uint256 _startTime, uint256 _endTime, uint256 _minimalAmountToClaim) external, onlyOwner() { + struct Staking { + address beneficiary = _beneficiary; + uint256 totalAmount = _totalAmount; + uint256 cliff = _cliff; + uint256 startTime = _startTime; + uint256 endTime = _endTime; + uint256 minimalAmountToClaim = _minimalAmountToClaim; + } + } + constructor(address _beneficiary, uint256 _totalAmount, uint256 _cliff, uint256 _startTime, uint256 _endTime, uint256 _minimalAmountToClaim) Ownable(msg.sender) { + beneficiary = _beneficiary; + totalAmount = _totalAmount; + cliff = _cliff; + startTime = _startTime; + endTime = _endTime; + minimalAmountToClaim = _minimalAmountToClaim; + } + + function claim(uint256 _amount) public { + + если юыло заклеймлено то пояаляетмя таймер до возможности заклеймить еще + таймер возможности заклеймить в днях + + Добавить поддержку нескольких получателей + + маппинги стракты + + mapping (address(benefeciary) => struct ) + + + + + + + uint256 timer = block.timestamp + 1 days + if CoolDownTimer > 0 ,error timerNotEnded + + if msg.sender != beneficiary, error not beneficiary + if block.timestamp < startTime, error rano start ne nastupil + if block.timestamp < cliff, error rano cliff ne nastupil + if block.timestamp >= endTime ,error claimTimeEnded + + uint256 amountToClaim = (totalAmount * (block.timestamp - (startTime + cliff))) / Duration; + if amountToClaim < minimalAmountToClaim, error not enough amount to claim + if amountToClaim > amount , error overmnoga + amount -= amountToClaim + claimed += amountToClaim + require(ERC20(token).transfer(benefeciary, amountToClaim)) + CoolDownTimer = block.timestamp + 1 day + + + emit Claimed(amountToClaim) + emit CoolDownTimerSet(CoolDownTimer) + + + } + + + + + +} \ No newline at end of file diff --git a/Projects/ERC20stakingg b/Projects/ERC20stakingg new file mode 100644 index 0000000..775cde2 --- /dev/null +++ b/Projects/ERC20stakingg @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: MIT +// Compatible with OpenZeppelin Contracts ^5.6.0 +pragma solidity ^0.8.27; + +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +contract ERC20Staking is Ownable { + struct Staking { + uint256 totalAmount; + uint256 claimed; + uint64 startTime; + uint64 endTime; + uint64 cliff; + uint64 minimalAmountToClaim; + uint64 cooldownEndsAt; + } + + /// @notice Vesting data per beneficiary + mapping(address => Staking) public stakes; + + /// @notice ERC20 token used for payouts + IERC20 public token; + + /// @notice Global vesting schedule parameters (shared by all beneficiaries) + uint64 public globalStartTime; + uint64 public globalEndTime; + uint64 public globalCliff; + uint64 public globalMinimalAmountToClaim; + + /// @notice Cooldown duration (in seconds) after each claim + uint64 public cooldownDuration; + + /// @notice Total amount allocated across all beneficiaries + uint256 public totalAllocated; + + event VestingStarted(address indexed beneficiary, uint256 amount); + event Claimed(address indexed beneficiary, uint256 amount); + event CooldownSet(address indexed beneficiary, uint64 until); + + constructor( + address token_, + uint64 startTime_, + uint64 endTime_, + uint64 cliff_, + uint64 minimalAmountToClaim_, + uint64 cooldownDuration_ + ) Ownable(msg.sender) { + require(token_ != address(0), "Token is zero"); + require(startTime_ < endTime_, "Invalid time range"); + require(cliff_ >= startTime_, "Cliff before start"); + + token = IERC20(token_); + globalStartTime = startTime_; + globalEndTime = endTime_; + globalCliff = cliff_; + globalMinimalAmountToClaim = minimalAmountToClaim_; + cooldownDuration = cooldownDuration_; + } + + /// @notice Fill or extend vesting stakes for multiple beneficiaries. + /// @dev Assumes the contract is already funded with enough tokens. + function startVesting( + address[] calldata beneficiaries, + uint256[] calldata amounts + ) external onlyOwner { + uint256 length = beneficiaries.length; + require(length == amounts.length && length > 0, "Invalid input"); + + uint256 addedTotal; + + for (uint256 i = 0; i < length; i++) { + address beneficiary = beneficiaries[i]; + uint256 amount = amounts[i]; + + require(beneficiary != address(0), "Zero beneficiary"); + require(amount > 0, "Zero amount"); + + Staking storage s = stakes[beneficiary]; + + // First time configuration for this beneficiary + if (s.totalAmount == 0 && s.claimed == 0) { + s.startTime = globalStartTime; + s.endTime = globalEndTime; + s.cliff = globalCliff; + s.minimalAmountToClaim = globalMinimalAmountToClaim; + } else { + // Ensure that all schedules remain aligned with the global one + require( + s.startTime == globalStartTime && + s.endTime == globalEndTime && + s.cliff == globalCliff && + s.minimalAmountToClaim == globalMinimalAmountToClaim, + "Schedule mismatch" + ); + } + + s.totalAmount += amount; + addedTotal += amount; + + emit VestingStarted(beneficiary, amount); + } + + totalAllocated += addedTotal; + require(totalAllocated <= token.balanceOf(address(this)), "Insufficient pool"); + } + + /// @notice Claim vested tokens for the caller. + /// @param requestedAmount If 0, claims full available amount, otherwise + /// claims the minimum of requestedAmount and the currently claimable amount. + function claim(uint256 requestedAmount) external { + Staking storage s = stakes[msg.sender]; + + require(s.totalAmount > 0, "No vesting"); + require(block.timestamp >= s.startTime, "Vesting not started"); + require(block.timestamp >= s.cliff, "Cliff not reached"); + require(block.timestamp < s.endTime, "Vesting ended"); + require(block.timestamp >= s.cooldownEndsAt, "Cooldown active"); + + uint256 duration = uint256(s.endTime) - uint256(s.startTime); + require(duration > 0, "Invalid duration"); + + uint256 timePassed = block.timestamp - uint256(s.startTime); + if (timePassed > duration) { + timePassed = duration; + } + + uint256 vestedTotal = (s.totalAmount * timePassed) / duration; + + if (vestedTotal <= s.claimed) { + revert("Nothing to claim"); + } + + uint256 claimable = vestedTotal - s.claimed; + require(claimable >= s.minimalAmountToClaim, "Too small"); + + uint256 claimAmount = claimable; + if (requestedAmount > 0 && requestedAmount < claimable) { + claimAmount = requestedAmount; + } + + s.claimed += claimAmount; + s.cooldownEndsAt = uint64(block.timestamp + cooldownDuration); + + require(token.transfer(msg.sender, claimAmount), "Transfer failed"); + + emit Claimed(msg.sender, claimAmount); + emit CooldownSet(msg.sender, s.cooldownEndsAt); + } +} \ No newline at end of file diff --git a/Projects/ERC721.sol b/Projects/ERC721.sol new file mode 100644 index 0000000..d237e8d --- /dev/null +++ b/Projects/ERC721.sol @@ -0,0 +1,19 @@ +// contracts/GameItem.sol +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import {ERC721URIStorage, ERC721} from "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; + +contract Frameitem is ERC721URIStorage { + uint256 private _nextTokenId; + + constructor() ERC721("Frameitem", "FRI") {} + + function awardItem(address player, string memory tokenURI) public returns (uint256) { + uint256 tokenId = _nextTokenId++; + _mint(player, tokenId); + _setTokenURI(tokenId, tokenURI); + + return tokenId; + } +} \ No newline at end of file