// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)pragmasolidity ^0.8.0;/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/interfaceIERC20{
/**
* @dev Returns the amount of tokens in existence.
*/functiontotalSupply() externalviewreturns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/functionbalanceOf(address account) externalviewreturns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/functiontransfer(address recipient, uint256 amount) externalreturns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/functionallowance(address owner, address spender) externalviewreturns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/functionapprove(address spender, uint256 amount) externalreturns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/functiontransferFrom(address sender,
address recipient,
uint256 amount
) externalreturns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/eventTransfer(addressindexedfrom, addressindexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/eventApproval(addressindexed owner, addressindexed spender, uint256 value);
}
Contract Source Code
File 4 of 4: gALCX.sol
// SPDX-License-Identifier: UNLICENSEDpragmasolidity 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 stakingcontractgALCXisERC20{
IERC20 public alcx = IERC20(0xdBdb4d16EdA451D0503b854CF79D55697F90c8DF);
IALCXSource public pools = IALCXSource(0xAB8e74017a8Cc7c15FFcCd726603790d26d7DeCa);
uintpublic poolId =1;
uintpublicconstant exchangeRatePrecision =1e18;
uintpublic exchangeRate = exchangeRatePrecision;
addresspublic owner;
eventExchangeRateChange(uint _exchangeRate);
eventStake(address _from, uint _gAmount, uint _amount);
eventUnstake(address _from, uint _gAmount, uint _amount);
/// @param _name The token name/// @param _symbol The token symbolconstructor(stringmemory _name, stringmemory _symbol) ERC20(_name, _symbol, 18) {
owner =msg.sender;
reApprove();
}
// OWNERSHIPmodifieronlyOwner{
require(msg.sender== owner, "Not owner");
_;
}
/// @notice Transfer contract ownership/// @param _owner The new owner addressfunctiontransferOwnership(address _owner) externalonlyOwner{
owner = _owner;
}
/// @notice Set a new staking pool address and migrate funds there/// @param _pools The new pool address/// @param _poolId The new pool idfunctionmigrateSource(address _pools, uint _poolId) externalonlyOwner{
// 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 ALCXuint 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 anyonefunctionreApprove() public{
bool success = alcx.approve(address(pools), type(uint).max);
}
// PUBLIC FUNCTIONS/// @notice Claim and autocompound rewardsfunctionbumpExchangeRate() public{
// Claim from pool
pools.claim(poolId);
// Bump exchange rateuint 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 depositfunctionstake(uint amount) external{
// Get current exchange rate between ALCX and gALCX
bumpExchangeRate();
// Then receive new depositsbool success = alcx.transferFrom(msg.sender, address(this), amount);
require(success, "Transfer failed");
pools.deposit(poolId, amount);
// gAmount always <= amountuint 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 withdrawfunctionunstake(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 hurtrequire(success, "Transfer failed");
emit Unstake(msg.sender, gAmount, amount);
}
}