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 #2784

Merged
merged 31 commits into from
Sep 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
44dc9ce
add boilerplate for staking precompile with empty delegate method
skosito Aug 28, 2024
766a30a
add simple undelegate and redelegate methods
skosito Aug 28, 2024
9e97065
add new methods to run
skosito Aug 28, 2024
6984671
add simple tests
skosito Aug 28, 2024
6c2128b
add delegator caller checks
skosito Aug 28, 2024
dacf5fb
fix del addr origin check
skosito Aug 28, 2024
f16df7f
add unit tests for delegate method
skosito Aug 28, 2024
b335fc9
changelog
skosito Aug 28, 2024
f5dc0c3
add undelegate tests
skosito Aug 28, 2024
758864d
add redelegate unit tests
skosito Aug 28, 2024
f7aa77e
renamings
skosito Aug 29, 2024
d1df9ec
generate
skosito Aug 29, 2024
0003316
pr comments
skosito Aug 29, 2024
378ea6d
codecov
skosito Aug 29, 2024
781cdd4
renaming
skosito Aug 29, 2024
7f1999d
e2e test and fixes
skosito Aug 29, 2024
9eb8775
generate
skosito Aug 29, 2024
7f8c962
Merge branch 'develop' into stake-precompile
skosito Aug 29, 2024
a063f84
fix tests
skosito Aug 30, 2024
c425696
fix view methods required gas
skosito Aug 30, 2024
43f063e
add queries unit tests
skosito Aug 30, 2024
2e6cb8a
Merge branch 'develop' into stake-precompile
skosito Aug 30, 2024
5af835f
add more unit tests and refactor a bit
skosito Aug 30, 2024
17380ca
fmt
skosito Aug 30, 2024
e4ae503
Merge branch 'develop' into stake-precompile
skosito Aug 30, 2024
ecf6d12
fixes after merge
skosito Aug 30, 2024
0bef0e7
cleanup
skosito Aug 30, 2024
f991479
PR comments
skosito Sep 2, 2024
09f35e8
add todo with issue
skosito Sep 2, 2024
f88bfa1
PR comment and bug fix with validators append
skosito Sep 4, 2024
5e1f021
Merge branch 'develop' into stake-precompile
skosito Sep 5, 2024
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
7 changes: 6 additions & 1 deletion app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,12 @@ func New(
&app.FeeMarketKeeper,
tracer,
evmSs,
precompiles.StatefulContracts(&app.FungibleKeeper, appCodec, storetypes.TransientGasConfig()),
precompiles.StatefulContracts(
&app.FungibleKeeper,
app.StakingKeeper,
appCodec,
storetypes.TransientGasConfig(),
),
app.ConsensusParamsKeeper,
aggregateAllKeys(keys, tKeys, memKeys),
)
Expand Down
3 changes: 2 additions & 1 deletion changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@
* [2681](https://github.com/zeta-chain/node/pull/2681) - implement `MsgUpdateERC20CustodyPauseStatus` to pause or unpause ERC20 Custody contract (to be used for the migration process for smart contract V2)
* [2644](https://github.com/zeta-chain/node/pull/2644) - add created_timestamp to cctx status
* [2673](https://github.com/zeta-chain/node/pull/2673) - add relayer key importer, encryption and decryption
* [2633](https://github.com/zeta-chain/node/pull/2633) - support for stateful precompiled contracts.
* [2633](https://github.com/zeta-chain/node/pull/2633) - support for stateful precompiled contracts
* [2788](https://github.com/zeta-chain/node/pull/2788) - add common importable zetacored rpc package
* [2784](https://github.com/zeta-chain/node/pull/2784) - staking precompiled contract

### Refactor

Expand Down
3 changes: 2 additions & 1 deletion cmd/zetae2e/local/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,8 @@ func localE2ETest(cmd *cobra.Command, _ []string) {

if !skipPrecompiles {
precompiledContractTests = []string{
e2etests.TestZetaPrecompilesPrototypeName,
e2etests.TestPrecompilesPrototypeName,
e2etests.TestPrecompilesStakingName,
lumtis marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down
1 change: 1 addition & 0 deletions codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,4 @@ ignore:
- "precompiles/**/*.json"
- "precompiles/**/*.sol"
- "precompiles/prototype/IPrototype.go"
- "precompiles/staking/IStaking.go"
5 changes: 5 additions & 0 deletions contrib/localnet/orchestrator/start-zetae2e.sh
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,11 @@ fund_eth_from_config '.additional_accounts.user_v2_ether_revert.evm_address' 100
# unlock v2 erc20 revert tests accounts
fund_eth_from_config '.additional_accounts.user_v2_erc20_revert.evm_address' 10000 "V2 ERC20 revert tester"

# unlock precompile tests accounts
address=$(yq -r '.additional_accounts.user_precompile.evm_address' config.yml)
echo "funding precompile tester address ${address} with 10000 Ether"
geth --exec "eth.sendTransaction({from: eth.coinbase, to: '${address}', value: web3.toWei(10000,'ether')})" attach http://eth:8545 > /dev/null
lumtis marked this conversation as resolved.
Show resolved Hide resolved

# unlock local solana relayer accounts
if host solana > /dev/null; then
solana_url=$(config_str '.rpcs.solana')
Expand Down
3 changes: 3 additions & 0 deletions contrib/localnet/scripts/start-zetacored.sh
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,9 @@ then
# migration tester
address=$(yq -r '.additional_accounts.user_migration.bech32_address' /root/config.yml)
zetacored add-genesis-account "$address" 100000000000000000000000000azeta
# precompiles tester
address=$(yq -r '.additional_accounts.user_precompile.bech32_address' /root/config.yml)
zetacored add-genesis-account "$address" 100000000000000000000000000azeta
lumtis marked this conversation as resolved.
Show resolved Hide resolved
# v2 ether tester
address=$(yq -r '.additional_accounts.user_v2_ether.bech32_address' /root/config.yml)
zetacored add-genesis-account "$address" 100000000000000000000000000azeta
Expand Down
13 changes: 10 additions & 3 deletions e2e/e2etests/e2etests.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,8 @@ const (
/*
Stateful precompiled contracts tests
*/
TestZetaPrecompilesPrototypeName = "precompile_contracts_prototype"
TestPrecompilesPrototypeName = "precompile_contracts_prototype"
TestPrecompilesStakingName = "precompile_contracts_staking"
)

// AllE2ETests is an ordered list of all e2e tests
Expand Down Expand Up @@ -814,9 +815,15 @@ var AllE2ETests = []runner.E2ETest{
Stateful precompiled contracts tests
*/
runner.NewE2ETest(
TestZetaPrecompilesPrototypeName,
TestPrecompilesPrototypeName,
"test stateful precompiled contracts prototype",
[]runner.ArgDefinition{},
TestPrecompilesRegular,
TestPrecompilesPrototype,
),
runner.NewE2ETest(
TestPrecompilesStakingName,
"test stateful precompiled contracts staking",
[]runner.ArgDefinition{},
TestPrecompilesStaking,
),
}
2 changes: 1 addition & 1 deletion e2e/e2etests/test_precompiles_prototype.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"github.com/zeta-chain/node/precompiles/prototype"
)

func TestPrecompilesRegular(r *runner.E2ERunner, args []string) {
func TestPrecompilesPrototype(r *runner.E2ERunner, args []string) {
require.Len(r, args, 0, "No arguments expected")

iPrototype, err := prototype.NewIPrototype(prototype.ContractAddress, r.ZEVMClient)
Expand Down
79 changes: 79 additions & 0 deletions e2e/e2etests/test_precompiles_staking.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package e2etests

import (
"math/big"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/stretchr/testify/require"

"github.com/zeta-chain/node/e2e/runner"
"github.com/zeta-chain/node/e2e/utils"
"github.com/zeta-chain/node/precompiles/staking"
)

func TestPrecompilesStaking(r *runner.E2ERunner, args []string) {
lumtis marked this conversation as resolved.
Show resolved Hide resolved
require.Len(r, args, 0, "No arguments expected")

stakingContract, err := staking.NewIStaking(staking.ContractAddress, r.ZEVMClient)
require.NoError(r, err, "Failed to create staking contract caller")

previousGasLimit := r.ZEVMAuth.GasLimit
r.ZEVMAuth.GasLimit = 10000000
skosito marked this conversation as resolved.
Show resolved Hide resolved
defer func() {
r.ZEVMAuth.GasLimit = previousGasLimit
}()

validators, err := stakingContract.GetAllValidators(&bind.CallOpts{})
require.NoError(r, err)
require.GreaterOrEqual(r, len(validators), 2)

// shares are 0 for both validators at the start
sharesBeforeVal1, err := stakingContract.GetShares(&bind.CallOpts{}, r.ZEVMAuth.From, validators[0].OperatorAddress)
require.NoError(r, err)
require.Equal(r, int64(0), sharesBeforeVal1.Int64())

sharesBeforeVal2, err := stakingContract.GetShares(&bind.CallOpts{}, r.ZEVMAuth.From, validators[1].OperatorAddress)
require.NoError(r, err)
require.Equal(r, int64(0), sharesBeforeVal2.Int64())

// stake 3 to validator1
tx, err := stakingContract.Stake(r.ZEVMAuth, r.ZEVMAuth.From, validators[0].OperatorAddress, big.NewInt(3))
require.NoError(r, err)
utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout)

// check shares are set to 3
sharesAfterVal1, err := stakingContract.GetShares(&bind.CallOpts{}, r.ZEVMAuth.From, validators[0].OperatorAddress)
require.NoError(r, err)
require.Equal(r, big.NewInt(3e18).String(), sharesAfterVal1.String())

// unstake 1 from validator1
tx, err = stakingContract.Unstake(r.ZEVMAuth, r.ZEVMAuth.From, validators[0].OperatorAddress, big.NewInt(1))
require.NoError(r, err)
utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout)

// check shares are set to 2
sharesAfterVal1, err = stakingContract.GetShares(&bind.CallOpts{}, r.ZEVMAuth.From, validators[0].OperatorAddress)
require.NoError(r, err)
require.Equal(r, big.NewInt(2e18).String(), sharesAfterVal1.String())

// move 1 stake from validator1 to validator2
tx, err = stakingContract.MoveStake(
r.ZEVMAuth,
r.ZEVMAuth.From,
validators[0].OperatorAddress,
validators[1].OperatorAddress,
big.NewInt(1),
)
require.NoError(r, err)

utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout)

// check shares for both validator1 and validator2 are 1
sharesAfterVal1, err = stakingContract.GetShares(&bind.CallOpts{}, r.ZEVMAuth.From, validators[0].OperatorAddress)
require.NoError(r, err)
require.Equal(r, big.NewInt(1e18).String(), sharesAfterVal1.String())

sharesAfterVal2, err := stakingContract.GetShares(&bind.CallOpts{}, r.ZEVMAuth.From, validators[1].OperatorAddress)
require.NoError(r, err)
require.Equal(r, big.NewInt(1e18).String(), sharesAfterVal2.String())
}
22 changes: 18 additions & 4 deletions precompiles/precompiles.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,54 @@ import (
"github.com/cosmos/cosmos-sdk/codec"
storetypes "github.com/cosmos/cosmos-sdk/store/types"
sdktypes "github.com/cosmos/cosmos-sdk/types"
stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm"
ethparams "github.com/ethereum/go-ethereum/params"
evmkeeper "github.com/zeta-chain/ethermint/x/evm/keeper"

"github.com/zeta-chain/node/precompiles/prototype"
"github.com/zeta-chain/node/x/fungible/keeper"
"github.com/zeta-chain/node/precompiles/staking"
fungiblekeeper "github.com/zeta-chain/node/x/fungible/keeper"
)

// EnabledStatefulContracts contains the list of all enabled stateful precompiles.
// This is useful for listing and reading from other packages, such as BlockedAddrs() function.
// Setting to false a contract here will disable it, not being included in the blockchain.
var EnabledStatefulContracts = map[common.Address]bool{
prototype.ContractAddress: true,
staking.ContractAddress: true,
}

// StatefulContracts returns all the registered precompiled contracts.
func StatefulContracts(
fungibleKeeper *keeper.Keeper,
fungibleKeeper *fungiblekeeper.Keeper,
stakingKeeper *stakingkeeper.Keeper,
cdc codec.Codec,
gasConfig storetypes.GasConfig,
) (precompiledContracts []evmkeeper.CustomContractFn) {
// Initialize at 0 the custom compiled contracts and the addresses.
precompiledContracts = make([]evmkeeper.CustomContractFn, 0)

// Define the regular contract function.
// Define the prototype contract function.
if EnabledStatefulContracts[prototype.ContractAddress] {
prototypeContract := func(_ sdktypes.Context, _ ethparams.Rules) vm.PrecompiledContract {
return prototype.NewIPrototypeContract(fungibleKeeper, cdc, gasConfig)
}

// Append the regular contract to the precompiledContracts slice.
// Append the prototype contract to the precompiledContracts slice.
precompiledContracts = append(precompiledContracts, prototypeContract)
}

// Define the staking contract function.
if EnabledStatefulContracts[staking.ContractAddress] {
stakingContract := func(_ sdktypes.Context, _ ethparams.Rules) vm.PrecompiledContract {
return staking.NewIStakingContract(stakingKeeper, cdc, gasConfig)
}

// Append the staking contract to the precompiledContracts slice.
precompiledContracts = append(precompiledContracts, stakingContract)
}

return precompiledContracts
}
17 changes: 9 additions & 8 deletions precompiles/precompiles_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
)

func Test_StatefulContracts(t *testing.T) {
k, ctx, _, _ := keeper.FungibleKeeper(t)
k, ctx, sdkk, _ := keeper.FungibleKeeper(t)
gasConfig := storetypes.TransientGasConfig()

var encoding ethermint.EncodingConfig
Expand All @@ -25,15 +25,16 @@ func Test_StatefulContracts(t *testing.T) {
}

// StatefulContracts() should return all the enabled contracts.
contracts := StatefulContracts(k, appCodec, gasConfig)
contracts := StatefulContracts(k, &sdkk.StakingKeeper, appCodec, gasConfig)
require.NotNil(t, contracts, "StatefulContracts() should not return a nil slice")
require.Len(t, contracts, expectedContracts, "StatefulContracts() should return all the enabled contracts")

// Extract the contract function from the first contract.
customContractFn := contracts[0]
contract := customContractFn(ctx, ethparams.Rules{})
for _, customContractFn := range contracts {
// Extract the contract function.
contract := customContractFn(ctx, ethparams.Rules{})

// Check the contract function returns a valid address.
contractAddr := contract.Address()
require.NotNil(t, contractAddr, "The called contract should have a valid address")
// Check the contract function returns a valid address.
contractAddr := contract.Address()
require.NotNil(t, contractAddr, "The called contract should have a valid address")
}
skosito marked this conversation as resolved.
Show resolved Hide resolved
}
1 change: 1 addition & 0 deletions precompiles/prototype/prototype.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ func initABI() {
var methodID [4]byte
copy(methodID[:], ABI.Methods[methodName].ID[:4])
switch methodName {
// TODO: https://github.com/zeta-chain/node/issues/2812
case Bech32ToHexAddrMethodName:
GasRequiredByMethod[methodID] = 500
case Bech32ifyMethodName:
Expand Down
1 change: 0 additions & 1 deletion precompiles/prototype/prototype_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,6 @@ func Test_GetGasStabilityPoolBalance(t *testing.T) {
)

t.Run("should fail with invalid arguments", func(t *testing.T) {

t.Run("invalid number of arguments", func(t *testing.T) {
args := []interface{}{int64(1337), "second argument"}
_, err := contract.GetGasStabilityPoolBalance(ctx, &methodID, args)
Expand Down
Loading
Loading