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 17 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
2 changes: 2 additions & 0 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ jobs:
with:
node-version: 18
- run: yarn
- name: clean cache
run: yarn clean
- name: build
run: yarn build
- name: lint
Expand Down
37 changes: 37 additions & 0 deletions contracts/root/VTSQToken.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// 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) ERC20('VTSubQueryToken', 'vtSQT') Ownable() {
minter = _minter;
}

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;
}
}
27 changes: 21 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 @@ -30,8 +32,10 @@ contract Vesting is Ownable {
event VestingAllocated(address indexed user, uint256 planId, uint256 allocation);
event VestingClaimed(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 @@ -58,6 +62,8 @@ contract Vesting is Ownable {
allocations[addr] = allocation;
totalAllocation += allocation;

ISQToken(vtToken).mint(addr, allocation);

emit VestingAllocated(addr, planId, allocation);
}

Expand Down Expand Up @@ -94,15 +100,24 @@ contract Vesting is Ownable {
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).burnFrom(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 = IERC20(vtToken).balanceOf(user);
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
7 changes: 7 additions & 0 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,13 @@ task('publishRoot', "verify and publish contracts on etherscan")
address: deployment.Vesting.address,
constructorArguments: [deployment.SQToken.address],
});
//VTSQToken
console.log(`verify VTSQToken`);
await hre.run("verify:verify", {
address: deployment.VTSQToken.address,
contract: 'contracts/root/VTSQToken.sol:VTSQToken',
constructorArguments: [constants.AddressZero],
});
//Settings
console.log(`verify Settings`);
await hre.run("verify:verify", {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"build:ts": "scripts/build.sh",
"build:abi": "ts-node --transpileOnly scripts/abi.ts",
"build": "yarn build:contract && yarn build:ts && yarn build:abi",
"clean": "rm -rf build artifacts",
"lint": "solhint contracts/**/*.sol --fix",
"test": "hardhat test",
"test:all": "hardhat test ./test/*.test.ts",
Expand Down
Loading
Loading