-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathgALCX.sol
110 lines (93 loc) · 3.85 KB
/
gALCX.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.11;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ERC20} from "@rari-capital/solmate/src/tokens/ERC20.sol";
import {IALCXSource} from "./interfaces/IALCXSource.sol";
/// @title A wrapper for single-sided ALCX staking
contract gALCX is ERC20 {
IERC20 public alcx = IERC20(0xdBdb4d16EdA451D0503b854CF79D55697F90c8DF);
IALCXSource public pools = IALCXSource(0xAB8e74017a8Cc7c15FFcCd726603790d26d7DeCa);
uint public poolId = 1;
uint public constant exchangeRatePrecision = 1e18;
uint public exchangeRate = exchangeRatePrecision;
address public owner;
event ExchangeRateChange(uint _exchangeRate);
event Stake(address _from, uint _gAmount, uint _amount);
event Unstake(address _from, uint _gAmount, uint _amount);
/// @param _name The token name
/// @param _symbol The token symbol
constructor(string memory _name, string memory _symbol) ERC20(_name, _symbol, 18) {
owner = msg.sender;
reApprove();
}
// OWNERSHIP
modifier onlyOwner {
require(msg.sender == owner, "Not owner");
_;
}
/// @notice Transfer contract ownership
/// @param _owner The new owner address
function transferOwnership(address _owner) external onlyOwner {
owner = _owner;
}
/// @notice Set a new staking pool address and migrate funds there
/// @param _pools The new pool address
/// @param _poolId The new pool id
function migrateSource(address _pools, uint _poolId) external onlyOwner {
// Withdraw ALCX
bumpExchangeRate();
uint poolBalance = pools.getStakeTotalDeposited(address(this), poolId);
pools.withdraw(poolId, poolBalance);
// Update staking pool address and id
pools = IALCXSource(_pools);
poolId = _poolId;
// Deposit ALCX
uint balance = alcx.balanceOf(address(this));
reApprove();
pools.deposit(poolId, balance);
}
/// @notice Approve the staking pool to move funds in this address, can be called by anyone
function reApprove() public {
bool success = alcx.approve(address(pools), type(uint).max);
}
// PUBLIC FUNCTIONS
/// @notice Claim and autocompound rewards
function bumpExchangeRate() public {
// Claim from pool
pools.claim(poolId);
// Bump exchange rate
uint balance = alcx.balanceOf(address(this));
if (balance > 0) {
exchangeRate += (balance * exchangeRatePrecision) / totalSupply;
emit ExchangeRateChange(exchangeRate);
// Restake
pools.deposit(poolId, balance);
}
}
/// @notice Deposit new funds into the staking pool
/// @param amount The amount of ALCX to deposit
function stake(uint amount) external {
// Get current exchange rate between ALCX and gALCX
bumpExchangeRate();
// Then receive new deposits
bool success = alcx.transferFrom(msg.sender, address(this), amount);
require(success, "Transfer failed");
pools.deposit(poolId, amount);
// gAmount always <= amount
uint gAmount = amount * exchangeRatePrecision / exchangeRate;
_mint(msg.sender, gAmount);
emit Stake(msg.sender, gAmount, amount);
}
/// @notice Withdraw funds from the staking pool
/// @param gAmount the amount of gALCX to withdraw
function unstake(uint gAmount) external {
bumpExchangeRate();
uint amount = gAmount * exchangeRate / exchangeRatePrecision;
_burn(msg.sender, gAmount);
// Withdraw ALCX and send to user
pools.withdraw(poolId, amount);
bool success = alcx.transfer(msg.sender, amount); // Should return true or revert, but doesn't hurt
require(success, "Transfer failed");
emit Unstake(msg.sender, gAmount, amount);
}
}