Skip to content
This repository has been archived by the owner on Jun 9, 2024. It is now read-only.

chore(distribution): Test calling query view methods in distribution precompile #1341

Merged
merged 2 commits into from
Dec 6, 2023
Merged
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
47 changes: 32 additions & 15 deletions contracts/src/cosmos/precompile/Distribution.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,23 @@ import {Cosmos} from "../CosmosTypes.sol";
* @dev Interface of the distribution module's precompiled contract
*/
interface IDistributionModule {
////////////////////////////////////////// EVENTS /////////////////////////////////////////////
/**
* @dev The caller (msg.sender) can set the address that will receive the deligation rewards.
* @dev Emitted by the distribution module when `amount` is withdrawn from a delegation with
* `validator` as rewards.
* @param validator The validator address to withdraw the rewards from.
* @param amount The amount of rewards withdrawn.
*/
event WithdrawRewards(address indexed validator, Cosmos.Coin[] amount);

/**
* @dev Emitted by the distribution module when `withdrawAddress` is set to receive rewards
* upon withdrawal.
* @param withdrawAddress The address to set as the withdraw address.
*/
function setWithdrawAddress(address withdrawAddress) external returns (bool);
event SetWithdrawAddress(address indexed withdrawAddress);

/////////////////////////////////////// READ METHODS //////////////////////////////////////////

/**
* @dev Returns whether withdrawing delegation rewards is enabled.
Expand All @@ -44,11 +56,14 @@ interface IDistributionModule {
function getWithdrawAddress(address delegator) external view returns (address);

/**
* @dev Withdraw the rewrads accumulated by the caller(msg.sender). Returns the rewards claimed.
* @param delegator The delegator to withdraw the rewards from.
* @param validator The validator (operator address) to withdraw the rewards from.
* @dev Returns the rewards accumulated by the delegator for the validator.
* @param delegator The delegator to retrieve the rewards for.
* @param validator The validator (operator address) to retrieve the rewards for.
*/
function withdrawDelegatorReward(address delegator, address validator) external returns (Cosmos.Coin[] memory);
function getDelegatorValidatorReward(address delegator, address validator)
external
view
returns (Cosmos.Coin[] memory);

/**
* @dev Returns the all rewards accumulated by the delegator.
Expand All @@ -62,20 +77,22 @@ interface IDistributionModule {
*/
function getTotalDelegatorReward(address delegator) external view returns (Cosmos.Coin[] memory);

////////////////////////////////////// WRITE METHODS //////////////////////////////////////////

/**
* @dev Emitted by the distribution module when `amount` is withdrawn from a delegation with
* `validator` as rewards.
* @param validator The validator address to withdraw the rewards from.
* @param amount The amount of rewards withdrawn.
* @dev The caller (msg.sender) can set the address that will receive the deligation rewards.
* @param withdrawAddress The address to set as the withdraw address.
*/
event WithdrawRewards(address indexed validator, Cosmos.Coin[] amount);
function setWithdrawAddress(address withdrawAddress) external returns (bool);

/**
* @dev Emitted by the distribution module when `withdrawAddress` is set to receive rewards
* upon withdrawal.
* @param withdrawAddress The address to set as the withdraw address.
* @dev Withdraw the rewrads accumulated by the caller(msg.sender). Returns the rewards claimed.
* @param delegator The delegator to withdraw the rewards from.
* @param validator The validator (operator address) to withdraw the rewards from.
*/
event SetWithdrawAddress(address indexed withdrawAddress);
function withdrawDelegatorReward(address delegator, address validator) external returns (Cosmos.Coin[] memory);

//////////////////////////////////////////// UTILS ////////////////////////////////////////////

/**
* @dev Represents a delegator's rewards for one particular validator.
Expand Down
52 changes: 52 additions & 0 deletions contracts/src/cosmos/precompile/testing/DistributionQuerier.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2023 Berachain Foundation
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.

pragma solidity ^0.8.17;

import "../Distribution.sol";

contract DistributionQuerier {
IDistributionModule distributionModule = IDistributionModule(0x0000000000000000000000000000000000000069);

function getTotalCall(address delegator) external view returns (Cosmos.Coin[] memory) {
return distributionModule.getTotalDelegatorReward(delegator);
}

function getTotalStaticCall(address delegator) external view returns (Cosmos.Coin[] memory) {
(bool success, bytes memory data) = address(distributionModule).staticcall(
abi.encodeWithSelector(IDistributionModule.getTotalDelegatorReward.selector, delegator)
);
require(success, "call failed");
return abi.decode(data, (Cosmos.Coin[]));
}

function getTotalLowLevelCall(address delegator) external returns (Cosmos.Coin[] memory) {
(bool success, bytes memory data) = address(distributionModule).call(
abi.encodeWithSelector(IDistributionModule.getTotalDelegatorReward.selector, delegator)
);
require(success, "call failed");
return abi.decode(data, (Cosmos.Coin[]));
}
}
56 changes: 48 additions & 8 deletions cosmos/precompile/distribution/distribution.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,47 @@ func (c *Contract) WithdrawDelegatorReward(
return amount, nil
}

// GetDelegatorReward implements `getAllDelegatorRewards(address)`.
// GetDelegatorValidatorReward implements `getDelegatorValidatorReward(address,adresss)`.
func (c *Contract) GetDelegatorValidatorReward(
ctx context.Context,
delegator common.Address,
validator common.Address,
) ([]lib.CosmosCoin, error) {
delAddr, err := cosmlib.StringFromEthAddress(c.addressCodec, delegator)
if err != nil {
return nil, err
}
valAddr, err := cosmlib.StringFromEthAddress(c.vs.ValidatorAddressCodec(), validator)
if err != nil {
return nil, err
}

// NOTE: CacheContext is necessary here because this is a view method, but the Cosmos SDK
// distribution module's querier performs writes to the context kv stores. The cache context is
// never committed and discarded after this function call.
cacheCtx, _ := sdk.UnwrapSDKContext(ctx).CacheContext()
res, err := c.querier.DelegationRewards( // performs writes to the context kv stores
cacheCtx,
&distributiontypes.QueryDelegationRewardsRequest{
DelegatorAddress: delAddr,
ValidatorAddress: valAddr,
},
)
if err != nil {
return nil, err
}

amount := make([]lib.CosmosCoin, 0)
for _, coin := range res.Rewards {
amount = append(amount, lib.CosmosCoin{
Denom: coin.Denom,
Amount: coin.Amount.TruncateInt().BigInt(),
})
}
return amount, nil
}

// GetAllDelegatorRewards implements `getAllDelegatorRewards(address)`.
func (c *Contract) GetAllDelegatorRewards(
ctx context.Context,
delegator common.Address,
Expand All @@ -180,9 +220,9 @@ func (c *Contract) GetAllDelegatorRewards(
return nil, err
}

// NOTE: CacheContext is necessary here because this is a view method (EVM static call), but
// the Cosmos SDK distribution module's querier performs writes to the context kv stores. The
// cache context is never committed and discarded after this function call.
// NOTE: CacheContext is necessary here because this is a view method, but the Cosmos SDK
// distribution module's querier performs writes to the context kv stores. The cache context is
// never committed and discarded after this function call.
cacheCtx, _ := sdk.UnwrapSDKContext(ctx).CacheContext()
res, err := c.querier.DelegationTotalRewards( // performs writes to the context kv stores
cacheCtx,
Expand Down Expand Up @@ -221,7 +261,7 @@ func (c *Contract) GetAllDelegatorRewards(
return rewards, nil
}

// GetDelegatorReward implements `getTotalDelegatorReward(address)`.
// GetTotalDelegatorReward implements `getTotalDelegatorReward(address)`.
func (c *Contract) GetTotalDelegatorReward(
ctx context.Context,
delegator common.Address,
Expand All @@ -231,9 +271,9 @@ func (c *Contract) GetTotalDelegatorReward(
return nil, err
}

// NOTE: CacheContext is necessary here because this is a view method (EVM static call), but
// the Cosmos SDK distribution module's querier performs writes to the context kv stores. The
// cache context is never committed and discarded after this function call.
// NOTE: CacheContext is necessary here because this is a view method, but the Cosmos SDK
// distribution module's querier performs writes to the context kv stores. The cache context is
// never committed and discarded after this function call.
cacheCtx, _ := sdk.UnwrapSDKContext(ctx).CacheContext()
res, err := c.querier.DelegationTotalRewards( // performs writes to the context kv stores
cacheCtx,
Expand Down
14 changes: 11 additions & 3 deletions cosmos/precompile/distribution/distribution_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,19 +248,27 @@ var _ = Describe("Distribution Precompile Test", func() {
Expect(err).ToNot(HaveOccurred())
Expect(res1[0].Denom).To(Equal(sdk.DefaultBondDenom))
rewards, _ := tokens.TruncateDecimal()
Expect(res1[0].Amount.Cmp(rewards[0].Amount.BigInt())).To(Equal(0))
expectedReward := rewards[0].Amount.BigInt()
Expect(res1[0].Amount.Cmp(expectedReward)).To(Equal(0))

res3, err := contract.GetAllDelegatorRewards(pCtx, common.BytesToAddress(addr))
Expect(err).ToNot(HaveOccurred())
Expect(res3[0].Validator).To(Equal(common.BytesToAddress(valAddr)))
Expect(res3[0].Rewards[0].Amount).To(Equal(rewards[0].Amount.BigInt()))
Expect(res3[0].Rewards[0].Amount.Cmp(expectedReward)).To(Equal(0))

res4, err := contract.GetDelegatorValidatorReward(
pCtx, common.BytesToAddress(addr), common.BytesToAddress(valAddr),
)
Expect(err).ToNot(HaveOccurred())
Expect(res4[0].Denom).To(Equal(sdk.DefaultBondDenom))
Expect(res4[0].Amount.Cmp(expectedReward)).To(Equal(0))

res2, err := contract.WithdrawDelegatorReward(
pCtx, common.BytesToAddress(addr), common.BytesToAddress(valAddr),
)
Expect(err).ToNot(HaveOccurred())
Expect(res2[0].Denom).To(Equal(sdk.DefaultBondDenom))
Expect(res2[0].Amount).To(Equal(rewards[0].Amount.BigInt()))
Expect(res2[0].Amount).To(Equal(expectedReward))
})
})

Expand Down
10 changes: 9 additions & 1 deletion e2e/localnet/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ require (
github.com/bits-and-blooms/bitset v1.11.0 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
github.com/cockroachdb/redact v1.1.5 // indirect
github.com/consensys/bavard v0.1.13 // indirect
github.com/consensys/gnark-crypto v0.12.1 // indirect
github.com/containerd/continuity v0.3.0 // indirect
Expand All @@ -28,6 +30,7 @@ require (
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/ethereum/c-kzg-4844 v0.4.0 // indirect
github.com/fjl/memsize v0.0.1 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
Expand All @@ -39,16 +42,20 @@ require (
github.com/google/uuid v1.4.0 // indirect
github.com/gorilla/websocket v1.5.1 // indirect
github.com/gotestyourself/gotestyourself v2.2.0+incompatible // indirect
github.com/hashicorp/go-bexpr v0.1.12 // indirect
github.com/holiman/uint256 v1.2.3 // indirect
github.com/lib/pq v1.10.9 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mmcloughlin/addchain v0.4.0 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0-rc4 // indirect
github.com/opencontainers/runc v1.1.5 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/rs/cors v1.9.0 // indirect
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/supranational/blst v0.3.11 // indirect
Expand All @@ -63,7 +70,8 @@ require (
golang.org/x/sys v0.14.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/tools v0.14.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gotest.tools v2.2.0+incompatible // indirect
rsc.io/tmplfunc v0.0.3 // indirect
Expand Down
Loading