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

SEI Integration #76

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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: 3 additions & 1 deletion src/adapters/Adapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import { IERC165 } from "core/interfaces/IERC165.sol";

pragma solidity >=0.8.19;

error WithdrawPending();

interface Adapter is IERC165 {
function previewDeposit(address validator, uint256 assets) external view returns (uint256);

Expand All @@ -24,7 +26,7 @@ interface Adapter is IERC165 {

function currentTime() external view returns (uint256);

function stake(address validator, uint256 amount) external returns (uint256 staked);
function stake(address validator, uint256 amount) external payable returns (uint256 staked);

function unstake(address validator, uint256 amount) external returns (uint256 unlockID);

Expand Down
6 changes: 2 additions & 4 deletions src/adapters/GraphAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ uint256 constant VERSION = 1;

import { ERC20 } from "solmate/tokens/ERC20.sol";
import { SafeTransferLib } from "solmate/utils/SafeTransferLib.sol";
import { Adapter } from "core/adapters/Adapter.sol";
import { Adapter, WithdrawPending } from "core/adapters/Adapter.sol";
import { IGraphStaking, IGraphEpochManager } from "core/adapters/interfaces/IGraph.sol";
import { IERC165 } from "core/interfaces/IERC165.sol";

Expand All @@ -29,8 +29,6 @@ contract GraphAdapter is Adapter {

uint256 private constant STORAGE = uint256(keccak256("xyz.tenderize.graph.adapter.storage.location")) - 1;

error WithdrawPending();

struct Unlock {
uint256 shares;
uint256 epoch;
Expand Down Expand Up @@ -109,7 +107,7 @@ contract GraphAdapter is Adapter {
return GRAPH_STAKING.hasStake(validator);
}

function stake(address validator, uint256 amount) external override returns (uint256) {
function stake(address validator, uint256 amount) external payable override returns (uint256) {
GRT.safeApprove(address(GRAPH_STAKING), amount);
uint256 delShares = GRAPH_STAKING.delegate(validator, amount);
IGraphStaking.DelegationPool memory delPool = GRAPH_STAKING.delegationPools(validator);
Expand Down
2 changes: 1 addition & 1 deletion src/adapters/LivepeerAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ contract LivepeerAdapter is Adapter {
return block.number;
}

function stake(address validator, uint256 amount) public returns (uint256) {
function stake(address validator, uint256 amount) public payable returns (uint256) {
LPT.safeApprove(address(LIVEPEER_BONDING), amount);
LIVEPEER_BONDING.bond(amount, validator);
return amount;
Expand Down
2 changes: 1 addition & 1 deletion src/adapters/PolygonAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ contract PolygonAdapter is Adapter {
return POLYGON_STAKEMANAGER.epoch();
}

function stake(address validator, uint256 amount) external override returns (uint256) {
function stake(address validator, uint256 amount) external payable override returns (uint256) {
// approve tokens
POL.safeApprove(address(POLYGON_STAKEMANAGER), amount);

Expand Down
107 changes: 107 additions & 0 deletions src/adapters/SeiAdapter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// SPDX-License-Identifier: MIT
//
// _____ _ _
// |_ _| | | (_)
// | | ___ _ __ __| | ___ _ __ _ _______
// | |/ _ \ '_ \ / _` |/ _ \ '__| |_ / _ \
// | | __/ | | | (_| | __/ | | |/ / __/
// \_/\___|_| |_|\__,_|\___|_| |_/___\___|
//
// Copyright (c) Tenderize Labs Ltd

pragma solidity >=0.8.19;

uint256 constant VERSION = 1;

import { ERC20 } from "solmate/tokens/ERC20.sol";
import { SafeTransferLib } from "solmate/utils/SafeTransferLib.sol";
import { Adapter, WithdrawPending as WithdrawPendingError } from "core/adapters/Adapter.sol";
import { ISei, StakingPool, UnbondingDelegation, BondStatus, UNSTAKE_TIME } from "core/adapters/interfaces/ISei.sol";
import { IERC165 } from "core/interfaces/IERC165.sol";
import { FixedPointMathLib } from "solmate/utils/FixedPointMathLib.sol";

address constant SEI_STAKING_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000001005;
ISei constant SEI_STAKING_CONTRACT = ISei(SEI_STAKING_PRECOMPILE_ADDRESS);

contract SeiAdapter is Adapter {
using SafeTransferLib for ERC20;
using FixedPointMathLib for uint256;

uint256 private constant STORAGE = uint256(keccak256("xyz.tenderize.sei.adapter.storage.location")) - 1;

struct Storage {
address validator;
}

function _loadStorage() internal pure returns (Storage storage $) {
uint256 slot = STORAGE;

// solhint-disable-next-line no-inline-assembly
assembly {
$.slot := slot
}
}

function supportsInterface(bytes4 interfaceId) external pure override returns (bool) {
return interfaceId == type(Adapter).interfaceId || interfaceId == type(IERC165).interfaceId;
}

function previewDeposit(address validator, uint256 assets) external view override returns (uint256) {
return _previewDeposit(validator, assets);
}

function previewWithdraw(uint256 unlockID) external view override returns (uint256) {
UnbondingDelegation memory unbond = SEI_STAKING_CONTRACT.getUnbondingDelegation(_loadStorage().validator, unlockID);
return unbond.amount;
}

function unlockMaturity(uint256 unlockID) external view override returns (uint256) {
UnbondingDelegation memory unbond = SEI_STAKING_CONTRACT.getUnbondingDelegation(_loadStorage().validator, unlockID);
return unbond.completionTime;
}

function unlockTime() external view override returns (uint256) {
return UNSTAKE_TIME;
}

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

function isValidator(address validator) public view override returns (bool) {
return SEI_STAKING_CONTRACT.getStakingPool(validator).status == BondStatus.Bonded;
}

function stake(address validator, uint256 amount) external payable override returns (uint256 out) {
out = _previewDeposit(validator, amount);
SEI_STAKING_CONTRACT.delegate(validator, amount);
}

function unstake(address validator, uint256 amount) external override returns (uint256 unlockID) {
unlockID = SEI_STAKING_CONTRACT.undelegate(validator, amount);
}

function withdraw(address validator, uint256 unlockID) external override returns (uint256 amount) {
UnbondingDelegation memory unbond = SEI_STAKING_CONTRACT.getUnbondingDelegation(validator, unlockID);
// TODO: check unbonding time denomination
if (unbond.completionTime > block.timestamp) {
revert WithdrawPendingError();
}
amount = unbond.amount;
}

function rebase(address validator, uint256 /*currentStake*/ ) external override returns (uint256 newStake) {
Storage storage $ = _loadStorage();
if ($.validator == address(0)) $.validator = validator;

uint256 shares = SEI_STAKING_CONTRACT.getDelegation(address(this), validator);
StakingPool memory stakingPool = SEI_STAKING_CONTRACT.getStakingPool(validator);
newStake = shares.mulDivDown(stakingPool.totalTokens, stakingPool.totalShares);
}

function _previewDeposit(address validator, uint256 assets) internal view returns (uint256 out) {
StakingPool memory stakingPool = SEI_STAKING_CONTRACT.getStakingPool(validator);
uint256 shares = assets.mulDivDown(stakingPool.totalShares, stakingPool.totalTokens);
return shares.mulDivDown(stakingPool.totalTokens + assets, stakingPool.totalShares + shares);
}
}
49 changes: 49 additions & 0 deletions src/adapters/interfaces/ISei.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// SPDX-License-Identifier: MIT
//
// _____ _ _
// |_ _| | | (_)
// | | ___ _ __ __| | ___ _ __ _ _______
// | |/ _ \ '_ \ / _` |/ _ \ '__| |_ / _ \
// | | __/ | | | (_| | __/ | | |/ / __/
// \_/\___|_| |_|\__,_|\___|_| |_/___\___|
//
// Copyright (c) Tenderize Labs Ltd

pragma solidity >=0.8.19;

uint256 constant UNSTAKE_TIME = 21 days;

enum BondStatus {
Unbonded,
Unbonding,
Bonded
}

struct UnbondingDelegation {
uint256 initialAmount;
uint256 amount;
uint256 creationHeight;
uint256 completionTime;
}

struct StakingPool {
uint256 totalShares;
uint256 totalTokens;
BondStatus status;
bool jailed;
}

interface ISei {
// Transactions
function delegate(address validator, uint256 amount) external returns (bool success);

function redelegate(address src, address dst, uint256 amount) external returns (bool success);

function undelegate(address validator, uint256 amount) external returns (uint256 unbondingID);

function getDelegation(address delegator, address validator) external view returns (uint256 shares);

function getStakingPool(address validator) external view returns (StakingPool memory);

function getUnbondingDelegation(address validator, uint256 unbondingID) external view returns (UnbondingDelegation memory);
}
Loading
Loading