Skip to content

Commit

Permalink
add delegate to veJOJO
Browse files Browse the repository at this point in the history
  • Loading branch information
radar bear committed Dec 2, 2024
1 parent d1bfad1 commit b3dafb5
Show file tree
Hide file tree
Showing 7 changed files with 646 additions and 24 deletions.
41 changes: 41 additions & 0 deletions script/deployVeJOJO.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,47 @@ import "../lib/forge-std/src/Script.sol";
import "../src/token/veJOJO.sol";
import "./utils.s.sol";

contract DeployVeJOJOMainnet is Script {
// add this to be excluded from coverage report
function test() public {}

function run() external {
uint256 deployerPrivateKey = vm.envUint("JOJO_DEPLOYER_PK");
vm.startBroadcast(deployerPrivateKey);

// 部署 veJOJO
address jojoToken = 0x0645bC5cDff2376089323Ac20Df4119e48e4BCc4; // JOJO token 地址
address usdc = 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913; // usdc token 地址
address owner = 0xD0cFCf1899A749bf0398fc885DB7ee0479C05eFC; // owner 地址

veJOJO v = new veJOJO(
jojoToken, // JOJO token
usdc
);

v.transferOwnership(owner);
vm.stopBroadcast();

// 验证合约
string memory chainId = vm.envString("CHAIN_ID");
bytes memory arguments = abi.encode(
jojoToken,
usdc
);

string[] memory inputs = new string[](8);
inputs[0] = "forge";
inputs[1] = "verify-contract";
inputs[2] = Utils.addressToString(address(v));
inputs[3] = "src/token/veJOJO.sol:veJOJO";
inputs[4] = "--chain-id";
inputs[5] = chainId;
inputs[6] = "--constructor-args";
inputs[7] = Utils.bytesToStringWithout0x(arguments);
Utils.logInputs(inputs);
}
}

contract DeployVeJOJOTest is Script {
// add this to be excluded from coverage report
function test() public {}
Expand Down
61 changes: 61 additions & 0 deletions script/deploywstETHOracle.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: GPL-2.0-or-later

pragma solidity ^0.8.19;

import "forge-std/Script.sol";
import "../src/oracle/OracleAdaptorWstETH.sol";
import "./utils.s.sol";

contract OracleAdaptorScript is Script {
// add this to be excluded from coverage report
function test() public { }

function run() external {
uint256 deployerPrivateKey = vm.envUint("JOJO_DEPLOYER_PK");
vm.startBroadcast(deployerPrivateKey);

// wstETH/ETH price source
address wstETHSource = 0x43a5C292A453A3bF3606fa856197f09D7B74251a;
// wstETH/ETH heartbeat interval
uint256 heartbeatInterval = 86_400;
// USDC price source
address usdcSource = 0x7e860098F58bBFC8648a4311b374B1D669a2bc6B;
// USDC heartbeat
uint256 usdcHeartbeat = 86_400;
// ETH price source
address ethSource = 0x71041dddad3595F9CEd3DcCFBe3D1F4b0a16Bb70;
// ETH heartbeat
uint256 ethHeartbeat = 86_400;

JOJOOracleAdaptorWstETH oracle = new JOJOOracleAdaptorWstETH(
wstETHSource,
heartbeatInterval,
usdcSource,
usdcHeartbeat,
ethSource,
ethHeartbeat
);
vm.stopBroadcast();

string memory chainId = vm.envString("CHAIN_ID");
bytes memory arguments = abi.encode(
wstETHSource,
heartbeatInterval,
usdcSource,
usdcHeartbeat,
ethSource,
ethHeartbeat
);

string[] memory inputs = new string[](8);
inputs[0] = "forge";
inputs[1] = "verify-contract";
inputs[2] = Utils.addressToString(address(oracle));
inputs[3] = "src/oracle/OracleAdaptorWstETH.sol:JOJOOracleAdaptorWstETH";
inputs[4] = "--chain-id";
inputs[5] = chainId;
inputs[6] = "--constructor-args";
inputs[7] = Utils.bytesToStringWithout0x(arguments);
Utils.logInputs(inputs);
}
}
31 changes: 13 additions & 18 deletions src/oracle/OracleAdaptorWstETH.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,42 +11,37 @@ import "../interfaces/internal/IChainlink.sol";
import "../libraries/Types.sol";

contract JOJOOracleAdaptorWstETH is Ownable {
uint256 public immutable decimalsCorrection;
uint256 public immutable heartbeatInterval;
uint256 public immutable usdcHeartbeat;
uint256 public immutable ETHHeartbeat;
address public immutable chainlink;
address public immutable usdcSource;
address public immutable ETHSource;
address public immutable wstetheth;
address public immutable usdcusdSource;
address public immutable ETHusdSource;

constructor(
address _source,
uint256 _decimalCorrection,
address _wstetheth,
uint256 _heartbeatInterval,
address _usdcSource,
address _usdcusdSource,
uint256 _usdcHeartbeat,
address _ETHSource,
address _ETHusdSource,
uint256 _ETHHeartbeat
) {
chainlink = _source;
decimalsCorrection = 10 ** _decimalCorrection;
wstetheth = _wstetheth;
heartbeatInterval = _heartbeatInterval;
usdcHeartbeat = _usdcHeartbeat;
usdcSource = _usdcSource;
ETHSource = _ETHSource;
usdcusdSource = _usdcusdSource;
ETHusdSource = _ETHusdSource;
ETHHeartbeat = _ETHHeartbeat;
}

function getAssetPrice() external view returns (uint256) {
(, int256 price,, uint256 updatedAt,) = IChainlink(chainlink).latestRoundData();
(, int256 usdcPrice,, uint256 usdcUpdatedAt,) = IChainlink(usdcSource).latestRoundData();
(, int256 ETHPrice,, uint256 ETHUpdatedAt,) = IChainlink(ETHSource).latestRoundData();
(, int256 wstethethprice,, uint256 updatedAt,) = IChainlink(wstetheth).latestRoundData(); // 18 decimals
(, int256 usdcusdPrice,, uint256 usdcUpdatedAt,) = IChainlink(usdcusdSource).latestRoundData(); // 8 decimals
(, int256 ETHusdPrice,, uint256 ETHUpdatedAt,) = IChainlink(ETHusdSource).latestRoundData(); // 8 decimals

require(block.timestamp - updatedAt <= heartbeatInterval, "ORACLE_HEARTBEAT_FAILED");
require(block.timestamp - usdcUpdatedAt <= usdcHeartbeat, "USDC_ORACLE_HEARTBEAT_FAILED");
require(block.timestamp - ETHUpdatedAt <= ETHHeartbeat, "ETH_ORACLE_HEARTBEAT_FAILED");
uint256 tokenPrice = (((SafeCast.toUint256(price) * SafeCast.toUint256(ETHPrice)) / Types.ONE) * 1e8)
/ SafeCast.toUint256(usdcPrice);
return (tokenPrice * Types.ONE) / decimalsCorrection;
return SafeCast.toUint256(wstethethprice * ETHusdPrice / usdcusdPrice)/1e12; // wstETH-USDC should be 6 decimals
}
}
2 changes: 1 addition & 1 deletion src/support/FlattenHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,5 @@ import "./help/ITokenMessenger.sol";

// DO NOT REMOVE
abstract contract ContractForCodeGeneration {
function order() external view returns (Types.Order memory order) {}
function getorder() external view returns (Types.Order memory order) {}
}
41 changes: 40 additions & 1 deletion src/token/veJOJO.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ contract veJOJO is ReentrancyGuard, Ownable {
uint256 end;
uint256 veJOJOAmount;
uint256 rewardDebt;
address delegate;
}

mapping(address => mapping(uint256 => LockInfo)) public userLocks;
Expand All @@ -28,10 +29,14 @@ contract veJOJO is ReentrancyGuard, Ownable {

uint256 private constant MAX_LOCK_TIME = 4 * 365 days;

mapping(address => uint256) public delegatedVotes;

event Deposit(address indexed user, uint256 lockId, uint256 amount, uint256 lockTime, uint256 veJOJOAmount);
event Withdraw(address indexed user, uint256 lockId, uint256 amount);
event RewardClaimed(address indexed user, uint256 amount);
event RewardAdded(uint256 amount);
event DelegateChanged(address indexed delegator, uint256 indexed lockId, address indexed fromDelegate, address toDelegate);
event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance);

constructor(address _JOJO, address _USDC) Ownable() {
JOJO = IERC20(_JOJO);
Expand All @@ -51,11 +56,14 @@ contract veJOJO is ReentrancyGuard, Ownable {
amount: _amount,
end: block.timestamp + _lockTime,
veJOJOAmount: veJOJOAmount,
rewardDebt: (veJOJOAmount * accRewardPerShare) / 1e18
rewardDebt: (veJOJOAmount * accRewardPerShare) / 1e18,
delegate: msg.sender
});
userLockCount[msg.sender]++;

totalSupply += veJOJOAmount;
delegatedVotes[msg.sender] += veJOJOAmount;
emit DelegateVotesChanged(msg.sender, delegatedVotes[msg.sender] - veJOJOAmount, delegatedVotes[msg.sender]);

emit Deposit(msg.sender, lockId, _amount, _lockTime, veJOJOAmount);
}
Expand All @@ -68,9 +76,15 @@ contract veJOJO is ReentrancyGuard, Ownable {

uint256 amount = userLock.amount;
uint256 veJOJOAmount = userLock.veJOJOAmount;

address currentDelegate = userLock.delegate;
delegatedVotes[currentDelegate] -= veJOJOAmount;
emit DelegateVotesChanged(currentDelegate, delegatedVotes[currentDelegate] + veJOJOAmount, delegatedVotes[currentDelegate]);

userLock.amount = 0;
userLock.end = 0;
userLock.veJOJOAmount = 0;
userLock.delegate = address(0);

JOJO.safeTransfer(msg.sender, amount);

Expand Down Expand Up @@ -138,4 +152,29 @@ contract veJOJO is ReentrancyGuard, Ownable {
function calculateVeJOJO(uint256 _amount, uint256 _lockTime) public pure returns (uint256) {
return (_amount * _lockTime) / MAX_LOCK_TIME;
}

function delegate(uint256 _lockId, address _delegatee) external {
require(_lockId < userLockCount[msg.sender], "Invalid lock ID");
require(_delegatee != address(0), "Cannot delegate to zero address");

LockInfo storage userLock = userLocks[msg.sender][_lockId];
require(userLock.amount > 0, "No locked JOJO");
require(block.timestamp < userLock.end, "Lock expired");

address oldDelegate = userLock.delegate;
require(oldDelegate != _delegatee, "Already delegated to this address");

delegatedVotes[oldDelegate] -= userLock.veJOJOAmount;
emit DelegateVotesChanged(oldDelegate, delegatedVotes[oldDelegate] + userLock.veJOJOAmount, delegatedVotes[oldDelegate]);

userLock.delegate = _delegatee;
delegatedVotes[_delegatee] += userLock.veJOJOAmount;

emit DelegateChanged(msg.sender, _lockId, oldDelegate, _delegatee);
emit DelegateVotesChanged(_delegatee, delegatedVotes[_delegatee] - userLock.veJOJOAmount, delegatedVotes[_delegatee]);
}

function getVotes(address _account) public view returns (uint256) {
return delegatedVotes[_account];
}
}
8 changes: 4 additions & 4 deletions test/impl/OracleTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,17 @@ contract OperationTest is Test {
OracleAdaptor oracleAdaptor3 =
new OracleAdaptor(address(mockToken1ChainLink), 20, 86_400, 0, address(usdcPrice), 5e16);
JOJOOracleAdaptorWstETH jojoOracleAdaptorWstETH = new JOJOOracleAdaptorWstETH(
address(mockToken1ChainLink), 20, 86_400, address(usdcPrice), 86_400, address(mockToken1ChainLink), 86_400
address(mockToken1ChainLink), 86_400, address(usdcPrice), 86_400, address(mockToken1ChainLink), 86_400
);

JOJOOracleAdaptorWstETH jojoOracleAdaptorWstETH2 = new JOJOOracleAdaptorWstETH(
address(mockToken1ChainLink), 20, 0, address(usdcPrice), 0, address(mockToken1ChainLink), 0
address(mockToken1ChainLink), 0, address(usdcPrice), 0, address(mockToken1ChainLink), 0
);
JOJOOracleAdaptorWstETH jojoOracleAdaptorWstETH3 = new JOJOOracleAdaptorWstETH(
address(mockToken1ChainLink), 20, 86_400, address(usdcPrice), 0, address(mockToken1ChainLink), 0
address(mockToken1ChainLink), 86_400, address(usdcPrice), 0, address(mockToken1ChainLink), 0
);
JOJOOracleAdaptorWstETH jojoOracleAdaptorWstETH4 = new JOJOOracleAdaptorWstETH(
address(mockToken1ChainLink), 20, 86_400, address(usdcPrice), 86_400, address(mockToken1ChainLink), 0
address(mockToken1ChainLink), 86_400, address(usdcPrice), 86_400, address(mockToken1ChainLink), 0
);

function testConstOracle() public {
Expand Down
Loading

0 comments on commit b3dafb5

Please sign in to comment.