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

feat: staking precompile delegation and stakingpool queries #1356

Draft
wants to merge 4 commits into
base: evm
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: 1 addition & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,6 @@ require (
github.com/fzipp/gocyclo v0.5.1 // indirect
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect
github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-critic/go-critic v0.6.3 // indirect
github.com/go-kit/kit v0.12.0 // indirect
github.com/go-kit/log v0.2.1 // indirect
Expand Down Expand Up @@ -173,7 +172,6 @@ require (
github.com/gostaticanalysis/nilerr v0.1.1 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 // indirect
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-bexpr v0.1.10 // indirect
Expand Down Expand Up @@ -348,7 +346,7 @@ require (
replace (
github.com/CosmWasm/wasmd => github.com/sei-protocol/sei-wasmd v0.0.7
github.com/confio/ics23/go => github.com/cosmos/cosmos-sdk/ics23/go v0.8.0
github.com/cosmos/cosmos-sdk => github.com/sei-protocol/sei-cosmos v0.2.73-evm-rebase-8
github.com/cosmos/cosmos-sdk => github.com/kyriediculous/sei-cosmos v0.0.0-20240322222612-9e2a3f019263
github.com/cosmos/iavl => github.com/sei-protocol/sei-iavl v0.1.9
github.com/cosmos/ibc-go/v3 => github.com/sei-protocol/sei-ibc-go/v3 v3.3.0
github.com/ethereum/go-ethereum => github.com/sei-protocol/go-ethereum v1.13.5-sei-8
Expand Down
7 changes: 2 additions & 5 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,6 @@ github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2Gihuqh
github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc=
github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9/go.mod h1:106OIgooyS7OzLDOpUGgm9fA3bQENb/cFSyyBmMoJDs=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
Expand Down Expand Up @@ -765,8 +764,6 @@ github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t
github.com/grpc-ecosystem/grpc-gateway v1.12.1/go.mod h1:8XEsbTttt/W+VvjtQhLACqCisSPWTxCZ7sBRjU6iH9c=
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 h1:/c3QmbOGMGTOumP2iT/rCwB7b0QDGLKzqOmktBjT+Is=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM=
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU=
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0=
github.com/guptarohit/asciigraph v0.5.5/go.mod h1:dYl5wwK4gNsnFf9Zp+l06rFiDZ5YtXM6x7SRWZ3KGag=
Expand Down Expand Up @@ -956,6 +953,8 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/kyoh86/exportloopref v0.1.8 h1:5Ry/at+eFdkX9Vsdw3qU4YkvGtzuVfzT4X7S77LoN/M=
github.com/kyoh86/exportloopref v0.1.8/go.mod h1:1tUcJeiioIs7VWe5gcOObrux3lb66+sBqGZrRkMwPgg=
github.com/kyriediculous/sei-cosmos v0.0.0-20240322222612-9e2a3f019263 h1:iTSOaJ2/s+6JDpEl+3QhFWzgFlvF0IoT2ZiNHtuo2fA=
github.com/kyriediculous/sei-cosmos v0.0.0-20240322222612-9e2a3f019263/go.mod h1:9lxLcKzSw+dWguFY8JkE04O1OlEjKxHsuP5zK/pS8q0=
github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g=
github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg=
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
Expand Down Expand Up @@ -1349,8 +1348,6 @@ github.com/sei-protocol/go-ethereum v1.13.5-sei-8 h1:z6rUvSgLXtXQBdF1dOe8A1iDFOR
github.com/sei-protocol/go-ethereum v1.13.5-sei-8/go.mod h1:kcRZmuzRn1lVejiFNTz4l4W7imnpq1bDAnuKS/RyhbQ=
github.com/sei-protocol/goutils v0.0.2 h1:Bfa7Sv+4CVLNM20QcpvGb81B8C5HkQC/kW1CQpIbXDA=
github.com/sei-protocol/goutils v0.0.2/go.mod h1:iYE2DuJfEnM+APPehr2gOUXfuLuPsVxorcDO+Tzq9q8=
github.com/sei-protocol/sei-cosmos v0.2.73-evm-rebase-8 h1:ueyLkyzlvuInVIPKvd4FLqe/N8+wZSRtfh5C0RMi7rQ=
github.com/sei-protocol/sei-cosmos v0.2.73-evm-rebase-8/go.mod h1:xhbsd0d50m0pKrYkWluhCIkLOaqE8AlEROiaPr4VkXQ=
github.com/sei-protocol/sei-db v0.0.30 h1:dlAOTE+7nByzzAD9cb1/DxaHWbxXOHFh58a2ImvcoYs=
github.com/sei-protocol/sei-db v0.0.30/go.mod h1:F/ZKZA8HJPcUzSZPA8yt6pfwlGriJ4RDR4eHKSGLStI=
github.com/sei-protocol/sei-iavl v0.1.9 h1:y4mVYftxLNRs6533zl7N0/Ch+CzRQc04JDfHolIxgBE=
Expand Down
4 changes: 4 additions & 0 deletions precompiles/common/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ type StakingKeeper interface {
Delegate(goCtx context.Context, msg *stakingtypes.MsgDelegate) (*stakingtypes.MsgDelegateResponse, error)
BeginRedelegate(goCtx context.Context, msg *stakingtypes.MsgBeginRedelegate) (*stakingtypes.MsgBeginRedelegateResponse, error)
Undelegate(goCtx context.Context, msg *stakingtypes.MsgUndelegate) (*stakingtypes.MsgUndelegateResponse, error)
Delegation(goCtx context.Context, delegatorAddr, validatorAddr string) (stakingtypes.QueryDelegationResponse, error)
Validator(goCtx context.Context, validatorAddr string) (stakingtypes.QueryValidatorResponse, error)
UnbondingDelegation(goCtx context.Context, delegatorAddr, validatorAddr string) (stakingtypes.QueryUnbondingDelegationResponse, error)
UnbondingDelegationByUnbondingId(goCtx context.Context, delegatorAddr, validatorAddr string, unbondingId uint64) (stakingtypes.UnbondingDelegationEntry, error)
}

type GovKeeper interface {
Expand Down
63 changes: 43 additions & 20 deletions precompiles/staking/Staking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,48 @@ pragma solidity ^0.8.0;

address constant STAKING_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000001005;

IStaking constant STAKING_CONTRACT = IStaking(
STAKING_PRECOMPILE_ADDRESS
);
IStaking constant STAKING_CONTRACT = IStaking(STAKING_PRECOMPILE_ADDRESS);

enum BondStatus {
Unbonded,
Unbonding,
Bonded
}

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

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

interface IStaking {
// Transactions
function delegate(
string memory valAddress,
uint256 amount
) external returns (bool success);

function redelegate(
string memory srcAddress,
string memory dstAddress,
uint256 amount
) external returns (bool success);

function undelegate(
string memory valAddress,
uint256 amount
) external returns (bool success);
}
// Messages
function delegate(address validator, uint256 amount) external returns (uint256 shares);

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

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

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

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

function getUnbondingDelegationByUnbondingID(address delegator, address validator)
external
view
returns (UnbondingDelegationEntry[] memory);

function getUnbondingDelegationByUnbondingID(address delegator, address validator, uint256 unbondingID)
external
view
returns (UnbondingDelegationEntry memory);
}
116 changes: 101 additions & 15 deletions precompiles/staking/staking.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@ import (
)

const (
DelegateMethod = "delegate"
RedelegateMethod = "redelegate"
UndelegateMethod = "undelegate"
DelegateMethod = "delegate"
RedelegateMethod = "redelegate"
UndelegateMethod = "undelegate"
DelegationQuery = "getDelegation"
StakingPoolQuery = "getStakingPool"
UnbondingDelegationQuery = "getUnbondingDelegation"
UnbondingDelegationByUnbondingIDQuery = "getUnbondingDelegationByUnbondingId"
)

const (
Expand Down Expand Up @@ -49,9 +53,13 @@ type Precompile struct {
evmKeeper pcommon.EVMKeeper
address common.Address

DelegateID []byte
RedelegateID []byte
UndelegateID []byte
DelegateID []byte
RedelegateID []byte
UndelegateID []byte
DelegationID []byte
StakingPoolID []byte
UnbondingDelegationID []byte
UnbondingDelegationByUnbondingID []byte
}

func NewPrecompile(stakingKeeper pcommon.StakingKeeper, evmKeeper pcommon.EVMKeeper) (*Precompile, error) {
Expand All @@ -72,7 +80,16 @@ func NewPrecompile(stakingKeeper pcommon.StakingKeeper, evmKeeper pcommon.EVMKee
p.RedelegateID = m.ID
case UndelegateMethod:
p.UndelegateID = m.ID
case DelegationQuery:
p.DelegationID = m.ID
case StakingPoolQuery:
p.StakingPoolID = m.ID
case UnbondingDelegationQuery:
p.UnbondingDelegationID = m.ID
case UnbondingDelegationByUnbondingIDQuery:
p.UnbondingDelegationByUnbondingID = m.ID
}

}

return p, nil
Expand All @@ -88,6 +105,12 @@ func (p Precompile) RequiredGas(input []byte) uint64 {
return 70000
} else if bytes.Equal(methodID, p.UndelegateID) {
return 50000
} else if bytes.Equal(methodID, p.DelegationID) {
return 5000
} else if bytes.Equal(methodID, p.StakingPoolID) {
return 5000
} else if bytes.Equal(methodID, p.UnbondingDelegationID) {
return 10000
}
panic("unknown method")
}
Expand All @@ -109,31 +132,40 @@ func (p Precompile) Run(evm *vm.EVM, caller common.Address, input []byte) (bz []
return p.redelegate(ctx, method, caller, args)
case UndelegateMethod:
return p.undelegate(ctx, method, caller, args)
case DelegationQuery:
return p.getDelegation(ctx, method, caller, args)
case StakingPoolQuery:
return p.getStakingPool(ctx, method, args)
case UnbondingDelegationQuery:
return p.getUnbondingDelegation(ctx, method, args)
case UnbondingDelegationByUnbondingIDQuery:
return p.getUnbondingDelegationByUnbondingId(ctx, method, args)
default:
return
}
return
}

func (p Precompile) delegate(ctx sdk.Context, method *abi.Method, caller common.Address, args []interface{}) ([]byte, error) {
pcommon.AssertArgsLength(args, 2)
delegator := p.evmKeeper.GetSeiAddressOrDefault(ctx, caller)
validatorBech32 := args[0].(string)
validatorBech32 := p.evmKeeper.GetSeiAddressOrDefault(ctx, args[0].(common.Address)).String()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

validator address is different from normal account address and doesn't have associated EVM address, so we can't change this line

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the length of these ?

I changed these do address mainly because they were previously string which in solidity is really a dynamic bytes array.

So if the validator addresses are 32 Bytes or shorter we can use a normal value type like bytes32.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kind bump @codchen

amount := args[1].(*big.Int)
_, err := p.stakingKeeper.Delegate(sdk.WrapSDKContext(ctx), &stakingtypes.MsgDelegate{
res, err := p.stakingKeeper.Delegate(sdk.WrapSDKContext(ctx), &stakingtypes.MsgDelegate{
DelegatorAddress: delegator.String(),
ValidatorAddress: validatorBech32,
Amount: sdk.NewCoin(p.evmKeeper.GetBaseDenom(ctx), sdk.NewIntFromBigInt(amount)),
})
if err != nil {
return nil, err
}
return method.Outputs.Pack(true)
return method.Outputs.Pack(res.Shares)
}

func (p Precompile) redelegate(ctx sdk.Context, method *abi.Method, caller common.Address, args []interface{}) ([]byte, error) {
pcommon.AssertArgsLength(args, 3)
delegator := p.evmKeeper.GetSeiAddressOrDefault(ctx, caller)
srcValidatorBech32 := args[0].(string)
dstValidatorBech32 := args[1].(string)
srcValidatorBech32 := p.evmKeeper.GetSeiAddressOrDefault(ctx, args[0].(common.Address)).String()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

dstValidatorBech32 := p.evmKeeper.GetSeiAddressOrDefault(ctx, args[1].(common.Address)).String()
amount := args[2].(*big.Int)
_, err := p.stakingKeeper.BeginRedelegate(sdk.WrapSDKContext(ctx), &stakingtypes.MsgBeginRedelegate{
DelegatorAddress: delegator.String(),
Expand All @@ -150,15 +182,69 @@ func (p Precompile) redelegate(ctx sdk.Context, method *abi.Method, caller commo
func (p Precompile) undelegate(ctx sdk.Context, method *abi.Method, caller common.Address, args []interface{}) ([]byte, error) {
pcommon.AssertArgsLength(args, 2)
delegator := p.evmKeeper.GetSeiAddressOrDefault(ctx, caller)
validatorBech32 := args[0].(string)
validatorBech32 := p.evmKeeper.GetSeiAddressOrDefault(ctx, args[0].(common.Address)).String()
amount := args[1].(*big.Int)
_, err := p.stakingKeeper.Undelegate(sdk.WrapSDKContext(ctx), &stakingtypes.MsgUndelegate{
res, err := p.stakingKeeper.Undelegate(sdk.WrapSDKContext(ctx), &stakingtypes.MsgUndelegate{
DelegatorAddress: delegator.String(),
ValidatorAddress: validatorBech32,
Amount: sdk.NewCoin(p.evmKeeper.GetBaseDenom(ctx), sdk.NewIntFromBigInt(amount)),
})
if err != nil {
return nil, err
}
return method.Outputs.Pack(true)
return method.Outputs.Pack(res.UnbondingId)
}

func (p Precompile) getDelegation(ctx sdk.Context, method *abi.Method, caller common.Address, args []interface{}) ([]byte, error) {
pcommon.AssertArgsLength(args, 2)
delegator := p.evmKeeper.GetSeiAddressOrDefault(ctx, caller)
validatorBech32 := p.evmKeeper.GetSeiAddressOrDefault(ctx, args[0].(common.Address)).String()
delegation, err := p.stakingKeeper.Delegation(sdk.WrapSDKContext(ctx), delegator.String(), validatorBech32)
if err != nil {
return nil, err
}
return method.Outputs.Pack(delegation.DelegationResponse.Balance)
}

func (p Precompile) getStakingPool(ctx sdk.Context, method *abi.Method, args []interface{}) ([]byte, error) {
pcommon.AssertArgsLength(args, 1)
validatorBech32 := p.evmKeeper.GetSeiAddressOrDefault(ctx, args[0].(common.Address)).String()
validator, err := p.stakingKeeper.Validator(sdk.WrapSDKContext(ctx), validatorBech32)
if err != nil {
return nil, err
}
return method.Outputs.Pack(struct {
TotalShares *big.Int
TotalTokens *big.Int
Status stakingtypes.BondStatus
Jailed bool
}{
validator.Validator.DelegatorShares.BigInt(),
validator.Validator.Tokens.BigInt(),
validator.Validator.Status,
validator.Validator.Jailed,
})
}

func (p Precompile) getUnbondingDelegation(ctx sdk.Context, method *abi.Method, args []interface{}) ([]byte, error) {
pcommon.AssertArgsLength(args, 2)
delegatorBech32 := p.evmKeeper.GetSeiAddressOrDefault(ctx, args[0].(common.Address)).String()
validatorBech32 := p.evmKeeper.GetSeiAddressOrDefault(ctx, args[1].(common.Address)).String()
unbonding, err := p.stakingKeeper.UnbondingDelegation(sdk.WrapSDKContext(ctx), delegatorBech32, validatorBech32)
if err != nil {
return nil, err
}
return method.Outputs.Pack(unbonding)
}

func (p Precompile) getUnbondingDelegationByUnbondingId(ctx sdk.Context, method *abi.Method, args []interface{}) ([]byte, error) {
pcommon.AssertArgsLength(args, 1)
delegatorBech32 := p.evmKeeper.GetSeiAddressOrDefault(ctx, args[0].(common.Address)).String()
validatorBech32 := p.evmKeeper.GetSeiAddressOrDefault(ctx, args[1].(common.Address)).String()
unbondingId := args[2].(uint64)
unbondingEntry, err := p.stakingKeeper.UnbondingDelegationByUnbondingId(sdk.WrapSDKContext(ctx), delegatorBech32, validatorBech32, unbondingId)
if err != nil {
return nil, err
}
return method.Outputs.Pack(unbondingEntry)
}