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

Add UnlockTime and CurrentTime to Adapter #70

Merged
merged 4 commits into from
Nov 30, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
},
"scripts": {
"clean": "rimraf cache out",
"lint": "yarn lint:sol && yarn prettier:check",
"lint:sol": "forge fmt --check && yarn solhint \"{src,test}/**/*.sol\"",
"lint": "yarn lint:sol && yarn prettier:write",
"lint:sol": "yarn solhint \"{src,test,script}/**/*.sol\"",
"postinstall": "husky install",
"prettier:check": "prettier --check \"**/*.{json,md,yml}\"",
"prettier:write": "prettier --write \"**/*.{json,md,yml}\""
Expand Down
2 changes: 2 additions & 0 deletions script/Adapter_Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
//
// Copyright (c) Tenderize Labs Ltd

// solhint-disable no-console

pragma solidity >=0.8.19;

import { Script } from "forge-std/Script.sol";
Expand All @@ -18,18 +20,18 @@
import { LivepeerAdapter } from "core/adapters/LivepeerAdapter.sol";
import { GraphAdapter } from "core/adapters/GraphAdapter.sol";

contract Adapter_Deploy is Script {

Check warning on line 23 in script/Adapter_Deploy.s.sol

View workflow job for this annotation

GitHub Actions / lint

Contract name must be in CamelCase
// Contracts are deployed deterministically.
// e.g. `foo = new Foo{salt: salt}(constructorArgs)`
// The presence of the salt argument tells forge to use https://github.com/Arachnid/deterministic-deployment-proxy
bytes32 private constant salt = 0x0;

Check warning on line 27 in script/Adapter_Deploy.s.sol

View workflow job for this annotation

GitHub Actions / lint

Constant name must be in capitalized SNAKE_CASE

address private constant LPT = address(0x0);
address private constant GRT = address(0x0);
// address private constant MATIC = 0x0;

function run() public {
string memory json_output;

Check warning on line 34 in script/Adapter_Deploy.s.sol

View workflow job for this annotation

GitHub Actions / lint

Variable "json_output" is unused

// Start broadcasting with private key from `.env` file
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
Expand Down
2 changes: 2 additions & 0 deletions script/Tenderize_Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
//
// Copyright (c) Tenderize Labs Ltd

// solhint-disable no-console

pragma solidity >=0.8.19;

import { Script, console2 } from "forge-std/Script.sol";
Expand All @@ -21,11 +23,11 @@
import { Unlocks } from "core/unlocks/Unlocks.sol";
import { Factory } from "core/factory/Factory.sol";

contract Tenderize_Deploy is Script {

Check warning on line 26 in script/Tenderize_Deploy.s.sol

View workflow job for this annotation

GitHub Actions / lint

Contract name must be in CamelCase
// Contracts are deployed deterministically.
// e.g. `foo = new Foo{salt: salt}(constructorArgs)`
// The presence of the salt argument tells forge to use https://github.com/Arachnid/deterministic-deployment-proxy
bytes32 private constant salt = bytes32(uint256(1));

Check warning on line 30 in script/Tenderize_Deploy.s.sol

View workflow job for this annotation

GitHub Actions / lint

Constant name must be in capitalized SNAKE_CASE

function run() public {
string memory json_output;
Expand Down
2 changes: 2 additions & 0 deletions script/XYZ_Data.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
//
// Copyright (c) Tenderize Labs Ltd

// solhint-disable no-console

pragma solidity >=0.8.19;

import { Script, console2 } from "forge-std/Script.sol";
Expand All @@ -19,12 +21,12 @@
import { Tenderizer } from "core/tenderizer/Tenderizer.sol";
import { Factory } from "core/factory/Factory.sol";

contract XYZ_Data is Script {

Check warning on line 24 in script/XYZ_Data.s.sol

View workflow job for this annotation

GitHub Actions / lint

Contract name must be in CamelCase
bytes32 private constant salt = 0x0;

Check warning on line 25 in script/XYZ_Data.s.sol

View workflow job for this annotation

GitHub Actions / lint

Constant name must be in capitalized SNAKE_CASE

address constant LPT = 0x042Dd916727378Cf913B7719a5071D5789139feb;

Check warning on line 27 in script/XYZ_Data.s.sol

View workflow job for this annotation

GitHub Actions / lint

Explicitly mark visibility of state
address constant GRT = 0xdcfF31D3311BAbf2f14EC947BdAcE2404D3C922f;

Check warning on line 28 in script/XYZ_Data.s.sol

View workflow job for this annotation

GitHub Actions / lint

Explicitly mark visibility of state
address constant POL = 0x3e278E3fEE9A2E82437d9eAa0a7836095CbD8070;

Check warning on line 29 in script/XYZ_Data.s.sol

View workflow job for this annotation

GitHub Actions / lint

Explicitly mark visibility of state

address[] livepeer = [
0x07CA020fDDE5c57C1C3A783befdb08929cf77fec, // coaction
Expand Down
2 changes: 2 additions & 0 deletions script/XYZ_Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
//
// Copyright (c) Tenderize Labs Ltd

// solhint-disable no-console

pragma solidity >=0.8.19;

import { Script, console2 } from "forge-std/Script.sol";
Expand Down
2 changes: 2 additions & 0 deletions script/XYZ_Faucet.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
//
// Copyright (c) Tenderize Labs Ltd

// solhint-disable no-console

pragma solidity >=0.8.19;

import { Script, console2 } from "forge-std/Script.sol";
Expand Down
4 changes: 4 additions & 0 deletions src/adapters/Adapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ interface Adapter is IERC165 {

function unlockMaturity(uint256 unlockID) external view returns (uint256);

function unlockTime() external view returns (uint256);

function currentTime() external view returns (uint256);

function stake(address validator, uint256 amount) external;

function unstake(address validator, uint256 amount) external returns (uint256 unlockID);
Expand Down
8 changes: 8 additions & 0 deletions src/adapters/GraphAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,14 @@ contract GraphAdapter is Adapter {
}
}

function unlockTime() external view override returns (uint256) {
return GRAPH.thawingPeriod();
}

function currentTime() external view override returns (uint256) {
return block.number;
}

function stake(address validator, uint256 amount) external override {
GRT.safeApprove(address(GRAPH), amount);
GRAPH.delegate(validator, amount);
Expand Down
8 changes: 8 additions & 0 deletions src/adapters/LivepeerAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,14 @@ contract LivepeerAdapter is Adapter {
}
}

function unlockTime() external view override returns (uint256) {
return LIVEPEER_ROUNDS.roundLength() * LIVEPEER.unbondingPeriod();
}

function currentTime() external view override returns (uint256) {
return block.number;
}

function stake(address validator, uint256 amount) public {
LPT.approve(address(LIVEPEER), amount);
LIVEPEER.bond(amount, validator);
Expand Down
9 changes: 9 additions & 0 deletions src/adapters/PolygonAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,14 @@ contract PolygonAdapter is Adapter {
return u.withdrawEpoch + WITHDRAW_DELAY;
}

function unlockTime() external pure override returns (uint256) {
return WITHDRAW_DELAY;
}

function currentTime() external view override returns (uint256) {
return MATIC_STAKE_MANAGER.epoch();
}

function stake(address validator, uint256 amount) external override {
// approve tokens
POLY.safeApprove(address(MATIC_STAKE_MANAGER), amount);
Expand Down Expand Up @@ -128,6 +136,7 @@ contract PolygonAdapter is Adapter {

// This call will revert if there are no rewards
// In which case we don't throw, just return the current staked amount.
// solhint-disable-next-line no-empty-blocks
try validatorShares.restake() { }
catch {
return currentStake;
Expand Down
2 changes: 2 additions & 0 deletions src/adapters/interfaces/ILivepeer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ interface ILivepeerBondingManager {
returns (uint256 amount, uint256 withdrawRound);

function isRegisteredTranscoder(address _transcoder) external view returns (bool);

function unbondingPeriod() external view returns (uint256);
}

interface ILivepeerRoundsManager {
Expand Down
3 changes: 3 additions & 0 deletions src/adapters/interfaces/IPolygon.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

// SPDX-License-Identifier: MIT

// solhint-disable func-name-mixedcase

pragma solidity >=0.8.19;

struct DelegatorUnbond {
Expand All @@ -12,6 +14,7 @@ struct DelegatorUnbond {
interface IMaticStakeManager {
function getValidatorId(address user) external view returns (uint256);
function getValidatorContract(uint256 validatorId) external view returns (address);
function epoch() external view returns (uint256);
}

interface IValidatorShares {
Expand Down
19 changes: 8 additions & 11 deletions src/tenderizer/Tenderizer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ contract Tenderizer is TenderizerImmutableArgs, TenderizerEvents, TToken, Multic
uint256 private constant MAX_FEE = 0.005e6; // 0.5%
uint256 private constant FEE_BASE = 1e6;

// solhint-disable-next-line no-empty-blocks
constructor(address _registry, address _unlocks) TenderizerImmutableArgs(_registry, _unlocks) { }

// @inheritdoc TToken
Expand Down Expand Up @@ -174,10 +175,6 @@ contract Tenderizer is TenderizerImmutableArgs, TenderizerEvents, TToken, Multic
return string.concat(ERC20(asset()).symbol(), "-", addressToString(validator()));
}

function _adapter() internal view returns (Adapter) {
return Adapter(_registry().adapter(asset()));
}

function previewDeposit(uint256 assets) external view returns (uint256) {
uint256 out = abi.decode(_staticcall(address(this), abi.encodeCall(this._previewDeposit, (assets))), (uint256));
Storage storage $ = _loadStorage();
Expand All @@ -201,31 +198,31 @@ contract Tenderizer is TenderizerImmutableArgs, TenderizerEvents, TToken, Multic
// using a `staticcall` to `this`.
// This is a hacky workaround while better solidity features are being developed.
function _previewDeposit(uint256 assets) public returns (uint256) {
return abi.decode(_adapter()._delegatecall(abi.encodeCall(_adapter().previewDeposit, (assets))), (uint256));
return abi.decode(adapter()._delegatecall(abi.encodeCall(adapter().previewDeposit, (assets))), (uint256));
}

function _previewWithdraw(uint256 unlockID) public returns (uint256) {
return abi.decode(_adapter()._delegatecall(abi.encodeCall(_adapter().previewWithdraw, (unlockID))), (uint256));
return abi.decode(adapter()._delegatecall(abi.encodeCall(adapter().previewWithdraw, (unlockID))), (uint256));
}

function _unlockMaturity(uint256 unlockID) public returns (uint256) {
return abi.decode(_adapter()._delegatecall(abi.encodeCall(_adapter().unlockMaturity, (unlockID))), (uint256));
return abi.decode(adapter()._delegatecall(abi.encodeCall(adapter().unlockMaturity, (unlockID))), (uint256));
}
// ===============================================================================================================

function _rebase(address validator, uint256 currentStake) internal returns (uint256 newStake) {
newStake = abi.decode(_adapter()._delegatecall(abi.encodeCall(_adapter().rebase, (validator, currentStake))), (uint256));
newStake = abi.decode(adapter()._delegatecall(abi.encodeCall(adapter().rebase, (validator, currentStake))), (uint256));
}

function _stake(address validator, uint256 amount) internal {
_adapter()._delegatecall(abi.encodeCall(_adapter().stake, (validator, amount)));
adapter()._delegatecall(abi.encodeCall(adapter().stake, (validator, amount)));
}

function _unstake(address validator, uint256 amount) internal returns (uint256 unlockID) {
unlockID = abi.decode(_adapter()._delegatecall(abi.encodeCall(_adapter().unstake, (validator, amount))), (uint256));
unlockID = abi.decode(adapter()._delegatecall(abi.encodeCall(adapter().unstake, (validator, amount))), (uint256));
}

function _withdraw(address validator, uint256 unlockID) internal returns (uint256 withdrawAmount) {
withdrawAmount = abi.decode(_adapter()._delegatecall(abi.encodeCall(_adapter().withdraw, (validator, unlockID))), (uint256));
withdrawAmount = abi.decode(adapter()._delegatecall(abi.encodeCall(adapter().withdraw, (validator, unlockID))), (uint256));
}
}
5 changes: 5 additions & 0 deletions src/tenderizer/TenderizerBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pragma solidity >=0.8.19;
import { Clone } from "clones/Clone.sol";
import { Unlocks } from "core/unlocks/Unlocks.sol";
import { Registry } from "core/registry/Registry.sol";
import { Adapter } from "core/adapters/Adapter.sol";

/// @title TenderizerImmutableArgs
/// @notice Immutable arguments for Tenderizer
Expand Down Expand Up @@ -46,6 +47,10 @@ abstract contract TenderizerImmutableArgs is Clone {
return _getArgAddress(20); // start: 20 end: 39
}

function adapter() public view returns (Adapter) {
return Adapter(_registry().adapter(asset()));
}

function _registry() internal view returns (Registry) {
return Registry(registry);
}
Expand Down
9 changes: 8 additions & 1 deletion src/unlocks/Unlocks.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import { ERC721 } from "solmate/tokens/ERC721.sol";
import { ERC20 } from "solmate/tokens/ERC20.sol";

import { Adapter } from "core/adapters/Adapter.sol";
import { Tenderizer } from "core/tenderizer/Tenderizer.sol";
import { Registry } from "core/registry/Registry.sol";
import { Renderer } from "core/unlocks/Renderer.sol";
Expand All @@ -27,6 +28,7 @@ pragma solidity >=0.8.19;
struct Metadata {
uint256 amount;
uint256 maturity;
uint256 progress;
uint256 unlockId;
string symbol;
string name;
Expand Down Expand Up @@ -104,9 +106,14 @@ contract Unlocks is ERC721 {
(address tenderizer, uint256 unlockId) = _decodeTokenId(tokenId);
address asset = Tenderizer(tenderizer).asset();

Adapter adapter = Tenderizer(tenderizer).adapter();
uint256 maturity = Tenderizer(tenderizer).unlockMaturity(unlockId);
uint256 currentTime = adapter.currentTime();

return Metadata({
amount: Tenderizer(tenderizer).previewWithdraw(unlockId),
maturity: Tenderizer(tenderizer).unlockMaturity(unlockId),
maturity: maturity,
progress: maturity > currentTime ? 100 - (maturity - currentTime) * 100 / adapter.unlockTime() : 100,
unlockId: unlockId,
symbol: ERC20(asset).symbol(),
name: ERC20(asset).name(),
Expand Down
8 changes: 4 additions & 4 deletions src/utils/TWAP.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@

pragma solidity >=0.8.19;

import "@uniswap/v3-core/interfaces/IUniswapV3Pool.sol";
import "@uniswap/v3-core/libraries/TickMath.sol";
import "@uniswap/v3-core/libraries/FixedPoint96.sol";
import "@uniswap/v3-core/libraries/FullMath.sol";
import { IUniswapV3Pool } from "@uniswap/v3-core/interfaces/IUniswapV3Pool.sol";
import { TickMath } from "@uniswap/v3-core/libraries/TickMath.sol";
import { FixedPoint96 } from "@uniswap/v3-core/libraries/FixedPoint96.sol";
import { FullMath } from "@uniswap/v3-core/libraries/FullMath.sol";

library TWAP {
function getSqrtTwapX96(address uniswapV3Pool, uint32 twapInterval) internal view returns (uint160 sqrtPriceX96) {
Expand Down
3 changes: 3 additions & 0 deletions test/adapters/PolygonAdapter.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
//
// Copyright (c) Tenderize Labs Ltd

// solhint-disable state-visibility
// solhint-disable func-name-mixedcase

pragma solidity >=0.8.19;

import { Test, stdError } from "forge-std/Test.sol";
Expand Down
4 changes: 2 additions & 2 deletions test/helpers/StakingXYZ.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ import { MockERC20 } from "solmate/test/utils/mocks/MockERC20.sol";
pragma solidity >=0.8.19;

contract StakingXYZ {
address immutable token;
address private immutable token;
uint256 public nextRewardTimeStamp;

uint256 public immutable APR;
uint256 public constant APR_PRECISION = 1e6;
uint256 public constant SECONDS_IN_A_YEAR = 31_536_000;

uint256 immutable unlockTime;
uint256 public immutable unlockTime;

struct Unlock {
uint256 amount;
Expand Down
12 changes: 10 additions & 2 deletions test/helpers/XYZAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import { StakingXYZ } from "./StakingXYZ.sol";
pragma solidity >=0.8.19;

contract XYZAdapter is Adapter {
address immutable STAKINGXYZ;
address immutable XYZ_TOKEN;
address private immutable STAKINGXYZ;
address private immutable XYZ_TOKEN;

constructor(address _stakingXYZ, address _xyz) {
STAKINGXYZ = _stakingXYZ;
Expand All @@ -43,6 +43,14 @@ contract XYZAdapter is Adapter {
(, maturity) = StakingXYZ(STAKINGXYZ).unlocks(address(this), unlockID);
}

function unlockTime() external view returns (uint256) {
return StakingXYZ(STAKINGXYZ).unlockTime();
}

function currentTime() external view returns (uint256) {
return block.timestamp;
}

function stake(address, uint256 amount) external {
ERC20(XYZ_TOKEN).approve(STAKINGXYZ, amount);
StakingXYZ(STAKINGXYZ).stake(amount);
Expand Down
5 changes: 1 addition & 4 deletions test/tenderizer/Tenderizer.harness.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,11 @@ import { Unlocks } from "core/unlocks/Unlocks.sol";
import { Registry } from "core/registry/Registry.sol";

// solhint-disable func-name-mixedcase
// solhint-disable no-empty-blocks

contract TenderizerHarness is Tenderizer {
constructor(address _registry, address _unlocks) Tenderizer(_registry, _unlocks) { }

function exposed_adapter() public view returns (Adapter) {
return _adapter();
}

function exposed_registry() public view returns (Registry) {
return _registry();
}
Expand Down
2 changes: 1 addition & 1 deletion test/tenderizer/Tenderizer.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ contract TenderizerTest is TenderizerSetup, TenderizerEvents {
assertEq(address(tenderizer.validator()), validator, "invalid validator");
assertEq(address(tenderizer.exposed_registry()), registry, "invalid registry");
assertEq(address(tenderizer.exposed_unlocks()), unlocks, "invalid unlocks");
assertEq(address(tenderizer.exposed_adapter()), adapter, "invalid adapter");
assertEq(address(tenderizer.adapter()), adapter, "invalid adapter");
}

function test_PreviewDeposit() public {
Expand Down
2 changes: 1 addition & 1 deletion test/unlocks/Renderer.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ contract RendererTest is Test {
address private validator = vm.addr(4);
uint256 private id = 1;
Metadata private metadata =
Metadata({ amount: 100, maturity: 1000, unlockId: id, symbol: "GRT", name: "Graph", validator: validator });
Metadata({ amount: 100, maturity: 1000, progress: 100, unlockId: id, symbol: "GRT", name: "Graph", validator: validator });
RendererV1 private rendererV1;

bytes32 internal constant IMPL_SLOT = bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1);
Expand Down
Loading