// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.0;
/**
* @title Swap
* @dev Main Swap contract that burns old token and mints new token for given user
*/
contract Owned {
address public owner;
constructor() {
owner = msg.sender;
}
modifier onlyOwner {
assert(msg.sender == owner);
_;
}
/* This function is used to transfer adminship to new owner
* @param _newOwner - address of new admin or owner
*/
function transferOwnership(address _newOwner) onlyOwner public {
assert(_newOwner != address(0));
owner = _newOwner;
}
}
contract Swapper is Owned
{
ERC20 public oldToken;
ERC20 public newToken;
Burner public burner;
event SwapExecuted(address user, uint256 amount);
struct VestingUnit {
uint256 amount;
uint256 timestamp;
}
uint256 public approvalDeadline;
mapping(address => VestingUnit[]) public holdersVestingData;
function claim() public {
VestingUnit[] memory vestingUnits = holdersVestingData[msg.sender];
uint sum = 0;
for(uint i = 0; i < vestingUnits.length; i++) {
uint256 finalClaimableTime = vestingUnits[i].timestamp + findTimeMultipler(i) * 30 days + 2 weeks;
if(finalClaimableTime < block.timestamp){
continue;
}
if(vestingUnits[i].amount > 0 && vestingUnits[i].timestamp < block.timestamp) {
sum += vestingUnits[i].amount;
delete holdersVestingData[msg.sender][i];
}
}
newToken.transfer(msg.sender, sum);
}
function amountClaimable(address holder) public view returns(uint256) {
VestingUnit[] memory vestingUnits = holdersVestingData[holder];
uint sum = 0;
for(uint i = 0; i < vestingUnits.length; i++) {
uint256 finalClaimableTime = vestingUnits[i].timestamp + findTimeMultipler(i) * 30 days + 2 weeks;
if(finalClaimableTime < block.timestamp){
continue;
}
if(vestingUnits[i].amount > 0 && vestingUnits[i].timestamp < block.timestamp) {
sum += vestingUnits[i].amount;
}
}
return sum;
}
constructor(
address _oldToken,
address _newToken,
address _burner,
uint256 _approvalDeadline
) {
approvalDeadline = _approvalDeadline;
oldToken = ERC20(_oldToken);
newToken = ERC20(_newToken);
burner = Burner(_burner);
}
function updateApprovalDeadline(uint256 _approvalDeadline) onlyOwner public {
approvalDeadline = _approvalDeadline;
}
function energencyWithdraw(uint256 _amount) onlyOwner public {
newToken.transfer(msg.sender,_amount);
}
function SwapNow(uint256 _val) public {
require(approvalDeadline > block.timestamp,"deadline reached");
require(oldToken.allowance(msg.sender, address(this)) >= _val,"allowance lower");
oldToken.transferFrom(msg.sender, address(burner), _val);
burner.burn(_val);
newToken.transfer(msg.sender, _val / 10);
setVestingData(_val);
emit SwapExecuted(msg.sender, _val);
}
function calculateCutPerMonth(uint256 totalAmount) private pure returns (uint256){
return totalAmount * 75/1000;
}
function setVestingData(uint256 _val) private {
uint256 amount = calculateCutPerMonth(_val);
uint256 finalChunkAmount = _val - _val/10;
for(uint256 i=0; i < 11; i++){
uint256 vestingTimestamp = block.timestamp + 90 days + 30 days * i;
VestingUnit memory vestingData = VestingUnit({amount:amount,timestamp:vestingTimestamp});
holdersVestingData[msg.sender].push(vestingData);
finalChunkAmount -= amount;
}
holdersVestingData[msg.sender].push(VestingUnit({amount:finalChunkAmount,timestamp:block.timestamp + 90 days + 30 days * 11}));
}
function findTimeMultipler(uint256 i) private pure returns(uint256){
if((i+1)%12 == 0){
return 0 ;
}
else{
return 12 - (i+1)%12;
}
}
function remainingClaim(address _holder) view public returns(uint256) {
VestingUnit[] memory vestingUnits = holdersVestingData[_holder];
uint sum = 0;
for(uint i = 0; i < vestingUnits.length; i++) {
if(vestingUnits[i].amount > 0) {
sum += vestingUnits[i].amount;
}
}
return sum;
}
}
contract Burner is Owned {
ERC20 oldToken;
function returnOwnership(address _newOwner) public onlyOwner {
oldToken.transferOwnership(_newOwner);
}
constructor(address _oldToken) {
oldToken = ERC20(_oldToken);
}
function burn(uint256 _val) public {
oldToken.burn(_val);
}
}
interface ERC20 {
function transferOwnership(address _newOwner) external;
function transferFrom(
address _from,
address _to,
uint256 _amount
) external returns (bool success);
function allowance(address owner, address spender) external view returns (uint256);
function burn(uint256 _value) external;
function transfer(address recipient, uint256 amount) external returns (bool);
}
{
"compilationTarget": {
"Swapper.sol": "Swapper"
},
"evmVersion": "berlin",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_oldToken","type":"address"},{"internalType":"address","name":"_newToken","type":"address"},{"internalType":"address","name":"_burner","type":"address"},{"internalType":"uint256","name":"_approvalDeadline","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"SwapExecuted","type":"event"},{"inputs":[{"internalType":"uint256","name":"_val","type":"uint256"}],"name":"SwapNow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"}],"name":"amountClaimable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"approvalDeadline","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"burner","outputs":[{"internalType":"contract Burner","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"energencyWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"holdersVestingData","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"newToken","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oldToken","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_holder","type":"address"}],"name":"remainingClaim","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_approvalDeadline","type":"uint256"}],"name":"updateApprovalDeadline","outputs":[],"stateMutability":"nonpayable","type":"function"}]