Skip to content

Commit

Permalink
feat: distribute ZRC20 rewards function
Browse files Browse the repository at this point in the history
  • Loading branch information
fbac committed Oct 17, 2024
1 parent 72b517d commit b4c51d7
Show file tree
Hide file tree
Showing 21 changed files with 2,125 additions and 1,868 deletions.
34 changes: 0 additions & 34 deletions precompiles/bank/bank.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,37 +180,3 @@ func (c *Contract) Run(evm *vm.EVM, contract *vm.Contract, readOnly bool) ([]byt
}
}
}

// getEVMCallerAddress returns the caller address.
// Usually the caller is the contract.CallerAddress, which is the address of the contract that called the precompiled contract.
// If contract.CallerAddress != evm.Origin is true, it means the call was made through a contract,
// on which case there is a need to set the caller to the evm.Origin.
func getEVMCallerAddress(evm *vm.EVM, contract *vm.Contract) (common.Address, error) {
caller := contract.CallerAddress
if contract.CallerAddress != evm.Origin {
caller = evm.Origin
}

return caller, nil
}

// getCosmosAddress returns the counterpart cosmos address of the given ethereum address.
// It checks if the address is empty or blocked by the bank keeper.
func getCosmosAddress(bankKeeper bank.Keeper, addr common.Address) (sdk.AccAddress, error) {
toAddr := sdk.AccAddress(addr.Bytes())
if toAddr.Empty() {
return nil, &ptypes.ErrInvalidAddr{
Got: toAddr.String(),
Reason: "empty address",
}
}

if bankKeeper.BlockedAddr(toAddr) {
return nil, &ptypes.ErrInvalidAddr{
Got: toAddr.String(),
Reason: "destination address blocked by bank keeper",
}
}

return toAddr, nil
}
30 changes: 0 additions & 30 deletions precompiles/bank/bank_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,12 @@ package bank

import (
"encoding/json"
"math/big"
"testing"

storetypes "github.com/cosmos/cosmos-sdk/store/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/stretchr/testify/require"
ethermint "github.com/zeta-chain/ethermint/types"
"github.com/zeta-chain/node/testutil/keeper"
"github.com/zeta-chain/node/testutil/sample"
)

func Test_IBankContract(t *testing.T) {
Expand Down Expand Up @@ -128,29 +124,3 @@ func Test_InvalidABI(t *testing.T) {

initABI()
}

func Test_getEVMCallerAddress(t *testing.T) {
mockEVM := vm.EVM{
TxContext: vm.TxContext{
Origin: common.Address{},
},
}

mockVMContract := vm.NewContract(
contractRef{address: common.Address{}},
contractRef{address: ContractAddress},
big.NewInt(0),
0,
)

// When contract.CallerAddress == evm.Origin, caller is set to contract.CallerAddress.
caller, err := getEVMCallerAddress(&mockEVM, mockVMContract)
require.NoError(t, err)
require.Equal(t, common.Address{}, caller, "address shouldn be the same")

// When contract.CallerAddress != evm.Origin, caller should be set to evm.Origin.
mockEVM.Origin = sample.EthAddress()
caller, err = getEVMCallerAddress(&mockEVM, mockVMContract)
require.NoError(t, err)
require.Equal(t, mockEVM.Origin, caller, "address should be evm.Origin")
}
4 changes: 2 additions & 2 deletions precompiles/bank/method_balance_of.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func (c *Contract) balanceOf(
}

// Get the counterpart cosmos address.
toAddr, err := getCosmosAddress(c.bankKeeper, addr)
toAddr, err := ptypes.GetCosmosAddress(c.bankKeeper, addr)
if err != nil {
return nil, err
}
Expand All @@ -49,7 +49,7 @@ func (c *Contract) balanceOf(

// Bank Keeper GetBalance returns the specified Cosmos coin balance for a given address.
// Check explicitly the balance is a non-negative non-nil value.
coin := c.bankKeeper.GetBalance(ctx, toAddr, ZRC20ToCosmosDenom(zrc20Addr))
coin := c.bankKeeper.GetBalance(ctx, toAddr, ptypes.ZRC20ToCosmosDenom(zrc20Addr))
if !coin.IsValid() {
return nil, &ptypes.ErrInvalidCoin{
Got: coin.GetDenom(),
Expand Down
6 changes: 3 additions & 3 deletions precompiles/bank/method_deposit.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,13 @@ func (c *Contract) deposit(
}

// Get the correct caller address.
caller, err := getEVMCallerAddress(evm, contract)
caller, err := ptypes.GetEVMCallerAddress(evm, contract)
if err != nil {
return nil, err
}

// Get the cosmos address of the caller.
toAddr, err := getCosmosAddress(c.bankKeeper, caller)
toAddr, err := ptypes.GetCosmosAddress(c.bankKeeper, caller)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -94,7 +94,7 @@ func (c *Contract) deposit(
// this way we map ZRC20 addresses to cosmos denoms "zevm/0x12345".
// - Mint coins to the fungible module.
// - Send coins from fungible to the caller.
coinSet, err := createCoinSet(ZRC20ToCosmosDenom(zrc20Addr), amount)
coinSet, err := ptypes.CreateCoinSet(ptypes.ZRC20ToCosmosDenom(zrc20Addr), amount)
if err != nil {
return nil, err
}
Expand Down
8 changes: 4 additions & 4 deletions precompiles/bank/method_withdraw.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,14 @@ func (c *Contract) withdraw(
}

// Get the correct caller address.
caller, err := getEVMCallerAddress(evm, contract)
caller, err := ptypes.GetEVMCallerAddress(evm, contract)
if err != nil {
return nil, err
}

// Get the cosmos address of the caller.
// This address should have enough cosmos coin balance as the requested amount.
fromAddr, err := getCosmosAddress(c.bankKeeper, caller)
fromAddr, err := ptypes.GetCosmosAddress(c.bankKeeper, caller)
if err != nil {
return nil, err
}
Expand All @@ -64,7 +64,7 @@ func (c *Contract) withdraw(
}

// Caller has to have enough cosmos coin balance to withdraw the requested amount.
coin := c.bankKeeper.GetBalance(ctx, fromAddr, ZRC20ToCosmosDenom(zrc20Addr))
coin := c.bankKeeper.GetBalance(ctx, fromAddr, ptypes.ZRC20ToCosmosDenom(zrc20Addr))
if !coin.IsValid() {
return nil, &ptypes.ErrInsufficientBalance{
Requested: amount.String(),
Expand All @@ -79,7 +79,7 @@ func (c *Contract) withdraw(
}
}

coinSet, err := createCoinSet(ZRC20ToCosmosDenom(zrc20Addr), amount)
coinSet, err := ptypes.CreateCoinSet(ptypes.ZRC20ToCosmosDenom(zrc20Addr), amount)
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion precompiles/precompiles.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func StatefulContracts(
// Define the staking contract function.
if EnabledStatefulContracts[staking.ContractAddress] {
stakingContract := func(_ sdktypes.Context, _ ethparams.Rules) vm.PrecompiledContract {
return staking.NewIStakingContract(stakingKeeper, cdc, gasConfig)
return staking.NewIStakingContract(stakingKeeper, *fungibleKeeper, bankKeeper, cdc, gasConfig)
}

// Append the staking contract to the precompiledContracts slice.
Expand Down
33 changes: 28 additions & 5 deletions precompiles/staking/IStaking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ pragma solidity ^0.8.26;
address constant ISTAKING_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000066; // 102

/// @dev The IStaking contract's instance.
IStaking constant ISTAKING_CONTRACT = IStaking(
ISTAKING_PRECOMPILE_ADDRESS
);
IStaking constant ISTAKING_CONTRACT = IStaking(ISTAKING_PRECOMPILE_ADDRESS);

/// @notice Bond status for validator
enum BondStatus {
Expand Down Expand Up @@ -58,6 +56,16 @@ interface IStaking {
uint256 amount
);

/// @notice Distributed event is emitted when distribute function is called successfully.
/// @param zrc20_distributor Distributor address.
/// @param zrc20_token ZRC20 token address.
/// @param amount Distributed amount.
event Distributed(
address indexed zrc20_distributor,
address indexed zrc20_token,
uint256 amount
);

/// @notice Stake coins to validator
/// @param staker Staker address
/// @param validator Validator address
Expand Down Expand Up @@ -95,9 +103,24 @@ interface IStaking {

/// @notice Get all validators
/// @return validators All validators
function getAllValidators() external view returns (Validator[] calldata validators);
function getAllValidators()
external
view
returns (Validator[] calldata validators);

/// @notice Get shares for staker in validator
/// @return shares Staker shares in validator
function getShares(address staker, string memory validator) external view returns (uint256 shares);
function getShares(
address staker,
string memory validator
) external view returns (uint256 shares);

/// @notice Distribute a ZRC20 token as staking rewards.
/// @param zrc20 The ZRC20 token address to be distributed.
/// @param amount The amount of ZRC20 tokens to distribute.
/// @return success Boolean indicating whether the distribution was successful.
function distribute(
address zrc20,
uint256 amount
) external returns (bool success);
}
18 changes: 18 additions & 0 deletions precompiles/staking/const.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package staking

const (
DistributeMethodName = "distribute"
DistributeMethodGas = 10000

GetAllValidatorsMethodName = "getAllValidators"
GetSharesMethodName = "getShares"

MoveStakeMethodName = "moveStake"
MoveStakeMethodGas = 10000

StakeMethodName = "stake"
StakeMethodGas = 10000

UnstakeMethodName = "unstake"
UnstakeMethodGas = 1000
)
36 changes: 33 additions & 3 deletions precompiles/staking/logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const (
MoveStakeEventName = "MoveStake"
)

func (c *Contract) AddStakeLog(
func (c *Contract) addStakeLog(
ctx sdk.Context,
stateDB vm.StateDB,
staker common.Address,
Expand Down Expand Up @@ -49,7 +49,7 @@ func (c *Contract) AddStakeLog(
return nil
}

func (c *Contract) AddUnstakeLog(
func (c *Contract) addUnstakeLog(
ctx sdk.Context,
stateDB vm.StateDB,
staker common.Address,
Expand Down Expand Up @@ -81,7 +81,7 @@ func (c *Contract) AddUnstakeLog(
return nil
}

func (c *Contract) AddMoveStakeLog(
func (c *Contract) addMoveStakeLog(
ctx sdk.Context,
stateDB vm.StateDB,
staker common.Address,
Expand Down Expand Up @@ -123,3 +123,33 @@ func (c *Contract) AddMoveStakeLog(

return nil
}

func (c *Contract) addDistributeLog(
ctx sdk.Context,
stateDB vm.StateDB,
distributor common.Address,
zrc20Token common.Address,
amount *big.Int,
) error {
event := c.Abi().Events[MoveStakeEventName]

topics, err := logs.MakeTopics(
event,
[]interface{}{distributor},
[]interface{}{zrc20Token},
)
if err != nil {
return err
}

Check warning on line 143 in precompiles/staking/logs.go

View check run for this annotation

Codecov / codecov/patch

precompiles/staking/logs.go#L133-L143

Added lines #L133 - L143 were not covered by tests

data, err := logs.PackArguments([]logs.Argument{
{Type: "uint256", Value: amount},
})
if err != nil {
return err
}

Check warning on line 150 in precompiles/staking/logs.go

View check run for this annotation

Codecov / codecov/patch

precompiles/staking/logs.go#L145-L150

Added lines #L145 - L150 were not covered by tests

logs.AddLog(ctx, c.Address(), stateDB, topics, data)

return nil

Check warning on line 154 in precompiles/staking/logs.go

View check run for this annotation

Codecov / codecov/patch

precompiles/staking/logs.go#L152-L154

Added lines #L152 - L154 were not covered by tests
}
Loading

0 comments on commit b4c51d7

Please sign in to comment.