Skip to content

Commit

Permalink
feat: SEI adapter
Browse files Browse the repository at this point in the history
  • Loading branch information
kyriediculous committed Apr 2, 2024
1 parent f29eecb commit fb40eab
Show file tree
Hide file tree
Showing 7 changed files with 389 additions and 7 deletions.
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

0 comments on commit fb40eab

Please sign in to comment.