Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable mint vtSQToken in Vesting Contract #297

Merged
merged 18 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions contracts/root/VTSQtoken.sol
mzxyz marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (C) 2020-2023 SubQuery Pte Ltd authors & contributors
// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity 0.8.15;

import '@openzeppelin/contracts/token/ERC20/ERC20.sol';
import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import '@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol';
import '@openzeppelin/contracts/access/Ownable.sol';

contract VTSQToken is ERC20, Ownable, ERC20Burnable {
using SafeERC20 for IERC20;
address public minter;

modifier isMinter() {
require(minter == msg.sender, 'Not minter');
_;
}

constructor(address _minter, uint256 totalSupply) ERC20('VTSubQueryToken', 'vtSQT') Ownable() {
minter = _minter;
_mint(msg.sender, totalSupply);
}

function mint(address destination, uint256 amount) external isMinter {
_mint(destination, amount);
}

/// #if_succeeds {:msg "minter should be set"} minter == _minter;
/// #if_succeeds {:msg "owner functionality"} old(msg.sender == address(owner));
function setMinter(address _minter) external onlyOwner {
minter = _minter;
}

function getMinter() external view returns (address) {
return minter;
}
}
53 changes: 47 additions & 6 deletions contracts/Vesting.sol → contracts/root/Vesting.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "../interfaces/ISQToken.sol";

contract Vesting is Ownable {
using SafeERC20 for IERC20;
Expand All @@ -17,6 +18,7 @@ contract Vesting is Ownable {
}

address public token;
address public vtToken;
uint256 public vestingStartDate;
uint256 public totalAllocation;
uint256 public totalClaimed;
Expand All @@ -25,13 +27,18 @@ contract Vesting is Ownable {
mapping(address => uint256) public userPlanId;
mapping(address => uint256) public allocations;
mapping(address => uint256) public claimed;
mapping(address => uint256) public vtSQTAllocations;
mzxyz marked this conversation as resolved.
Show resolved Hide resolved

event VestingPlanAdded(uint256 planId, uint256 lockPeriod, uint256 vestingPeriod, uint256 initialUnlockPercent);
event VestingAllocated(address indexed user, uint256 planId, uint256 allocation);
event VestingClaimed(address indexed user, uint256 amount);
event TokenDeposited(address indexed user, uint256 amount);
mzxyz marked this conversation as resolved.
Show resolved Hide resolved
event TokenWithdrawn(address indexed user, uint256 amount);

constructor(address _token) Ownable() {
constructor(address _token, address _vtToken) Ownable() {
require(_token != address(0x0), "G009");
require(_vtToken != address(0x0), "G009");
vtToken = _vtToken;
token = _token;
}

Expand All @@ -56,6 +63,7 @@ contract Vesting is Ownable {

userPlanId[addr] = planId;
allocations[addr] = allocation;
vtSQTAllocations[addr] = allocation;
totalAllocation += allocation;

emit VestingAllocated(addr, planId, allocation);
Expand All @@ -72,11 +80,13 @@ contract Vesting is Ownable {

function depositByAdmin(uint256 amount) external onlyOwner {
require(amount > 0, "V007");
ISQToken(vtToken).mint(address(this), amount);
mzxyz marked this conversation as resolved.
Show resolved Hide resolved
require(IERC20(token).transferFrom(msg.sender, address(this), amount), "V008");
}

function withdrawAllByAdmin() external onlyOwner {
uint256 amount = IERC20(token).balanceOf(address(this));
ISQToken(vtToken).burn(amount);
require(IERC20(token).transfer(msg.sender, amount), "V008");
}

Expand All @@ -86,23 +96,54 @@ contract Vesting is Ownable {
vestingStartDate = _vestingStartDate;

uint256 amount = IERC20(token).balanceOf(address(this));
uint256 vtTokenAmount = IERC20(vtToken).balanceOf(address(this));
require(amount == vtTokenAmount, "V013");
require(amount == totalAllocation, "V010");

transferOwnership(address(this));
}

function deposit(uint256 amount) external {
require(amount > 0, "V007");

vtSQTAllocations[msg.sender] += amount;
IERC20(vtToken).transferFrom(msg.sender, address(this), amount);

emit TokenDeposited(msg.sender, amount);
}

function withdraw(uint256 amount) external {
require(amount > 0, "V007");
require(vtSQTAllocations[msg.sender] >= amount, "V014");

vtSQTAllocations[msg.sender] -= amount;
IERC20(vtToken).transferFrom(address(this), msg.sender, amount);

emit TokenWithdrawn(msg.sender, amount);
}

function claim() external {
require(allocations[msg.sender] != 0, "V011");

uint256 claimAmount = claimableAmount(msg.sender);
claimed[msg.sender] += claimAmount;
totalClaimed += claimAmount;
uint256 amount = claimableAmount(msg.sender);
require(amount > 0, "V012");

ISQToken(vtToken).burn(amount);
vtSQTAllocations[msg.sender] -= amount;
claimed[msg.sender] += amount;
totalClaimed += amount;

require(IERC20(token).transfer(msg.sender, claimAmount), "V008");
emit VestingClaimed(msg.sender, claimAmount);
require(IERC20(token).transfer(msg.sender, amount), "V008");
emit VestingClaimed(msg.sender, amount);
}

function claimableAmount(address user) public view returns (uint256) {
ianhe8x marked this conversation as resolved.
Show resolved Hide resolved
uint256 amount = unlockedAmount(user);
uint256 vtSQTAmount = vtSQTAllocations[msg.sender];
return vtSQTAmount >= amount ? amount : vtSQTAmount;
}

function unlockedAmount(address user) public view returns (uint256) {
// vesting start date is not set or allocation is empty
if (vestingStartDate == 0 || allocations[user] == 0) {
return 0;
Expand Down
3 changes: 3 additions & 0 deletions publish/revertcode.json
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,9 @@
"V009": "vesting start date must in the future",
"V010": "balance not enough for allocation",
"V011": "vesting is not set on the account",
"V012": "no token available to claim",
"V013": "inconsistent amount between SQT and vtSQT",
"V014": "insufficient vtSQT to widthdraw",
"OR001": "invalid asset price",
"OR002": "not meet the block number limitation",
"OR003": "invalid price size change",
Expand Down
Loading