Skip to content

Commit 297b1af

Browse files
authored
ELO-335 -- make tokenAddresses optional for reward claiming; only query tokens which have pending rewards. (#223)
1 parent 08c43fc commit 297b1af

File tree

3 files changed

+162
-8
lines changed

3 files changed

+162
-8
lines changed

pkg/rewards/claim.go

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
contractrewardscoordinator "github.com/Layr-Labs/eigenlayer-contracts/pkg/bindings/IRewardsCoordinator"
2020

2121
"github.com/Layr-Labs/eigenlayer-rewards-proofs/pkg/claimgen"
22+
"github.com/Layr-Labs/eigenlayer-rewards-proofs/pkg/distribution"
2223
"github.com/Layr-Labs/eigenlayer-rewards-proofs/pkg/proofDataFetcher/httpProofDataFetcher"
2324

2425
"github.com/Layr-Labs/eigensdk-go/chainio/clients/elcontracts"
@@ -31,6 +32,7 @@ import (
3132
"github.com/ethereum/go-ethereum/ethclient"
3233

3334
"github.com/urfave/cli/v2"
35+
"github.com/wk8/go-ordered-map/v2"
3436
)
3537

3638
type elChainReader interface {
@@ -121,10 +123,15 @@ func Claim(cCtx *cli.Context, p utils.Prompter) error {
121123
return eigenSdkUtils.WrapError("failed to fetch claim amounts for date", err)
122124
}
123125

126+
claimableTokens, present := proofData.Distribution.GetTokensForEarner(config.EarnerAddress)
127+
if !present {
128+
return errors.New("no tokens claimable by earner")
129+
}
130+
124131
cg := claimgen.NewClaimgen(proofData.Distribution)
125132
accounts, claim, err := cg.GenerateClaimProofForEarner(
126133
config.EarnerAddress,
127-
config.TokenAddresses,
134+
getTokensToClaim(claimableTokens, config.TokenAddresses),
128135
rootIndex,
129136
)
130137
if err != nil {
@@ -296,6 +303,44 @@ func getClaimDistributionRoot(
296303
return "", 0, errors.New("invalid claim timestamp")
297304
}
298305

306+
func getTokensToClaim(
307+
claimableTokens *orderedmap.OrderedMap[gethcommon.Address, *distribution.BigInt],
308+
tokenAddresses []gethcommon.Address,
309+
) []gethcommon.Address {
310+
if len(tokenAddresses) == 0 {
311+
tokenAddresses = getAllClaimableTokenAddresses(claimableTokens)
312+
} else {
313+
tokenAddresses = filterClaimableTokenAddresses(claimableTokens, tokenAddresses)
314+
}
315+
316+
return tokenAddresses
317+
}
318+
319+
func getAllClaimableTokenAddresses(
320+
addressesMap *orderedmap.OrderedMap[gethcommon.Address, *distribution.BigInt],
321+
) []gethcommon.Address {
322+
var addresses []gethcommon.Address
323+
for pair := addressesMap.Oldest(); pair != nil; pair = pair.Next() {
324+
addresses = append(addresses, pair.Key)
325+
}
326+
327+
return addresses
328+
}
329+
330+
func filterClaimableTokenAddresses(
331+
addressesMap *orderedmap.OrderedMap[gethcommon.Address, *distribution.BigInt],
332+
providedAddresses []gethcommon.Address,
333+
) []gethcommon.Address {
334+
var addresses []gethcommon.Address
335+
for _, address := range providedAddresses {
336+
if _, ok := addressesMap.Get(address); ok {
337+
addresses = append(addresses, address)
338+
}
339+
}
340+
341+
return addresses
342+
}
343+
299344
func convertClaimTokenLeaves(
300345
claimTokenLeaves []contractrewardscoordinator.IRewardsCoordinatorTokenTreeMerkleLeaf,
301346
) []rewardscoordinator.IRewardsCoordinatorTokenTreeMerkleLeaf {
@@ -307,7 +352,6 @@ func convertClaimTokenLeaves(
307352
})
308353
}
309354
return tokenLeaves
310-
311355
}
312356

313357
func readAndValidateClaimConfig(cCtx *cli.Context, logger logging.Logger) (*ClaimConfig, error) {
@@ -319,7 +363,8 @@ func readAndValidateClaimConfig(cCtx *cli.Context, logger logging.Logger) (*Clai
319363
outputType := cCtx.String(flags.OutputTypeFlag.Name)
320364
broadcast := cCtx.Bool(flags.BroadcastFlag.Name)
321365
tokenAddresses := cCtx.String(TokenAddressesFlag.Name)
322-
tokenAddressArray := stringToAddressArray(strings.Split(tokenAddresses, ","))
366+
splitTokenAddresses := strings.Split(tokenAddresses, ",")
367+
validTokenAddresses := getValidHexAddresses(splitTokenAddresses)
323368
rewardsCoordinatorAddress := cCtx.String(RewardsCoordinatorAddressFlag.Name)
324369

325370
var err error
@@ -395,7 +440,7 @@ func readAndValidateClaimConfig(cCtx *cli.Context, logger logging.Logger) (*Clai
395440
Output: output,
396441
OutputType: outputType,
397442
Broadcast: broadcast,
398-
TokenAddresses: tokenAddressArray,
443+
TokenAddresses: validTokenAddresses,
399444
RewardsCoordinatorAddress: gethcommon.HexToAddress(rewardsCoordinatorAddress),
400445
ChainID: chainID,
401446
ProofStoreBaseURL: proofStoreBaseURL,
@@ -428,10 +473,12 @@ func getEnvFromNetwork(network string) string {
428473
}
429474
}
430475

431-
func stringToAddressArray(addresses []string) []gethcommon.Address {
476+
func getValidHexAddresses(addresses []string) []gethcommon.Address {
432477
var addressArray []gethcommon.Address
433478
for _, address := range addresses {
434-
addressArray = append(addressArray, gethcommon.HexToAddress(address))
479+
if gethcommon.IsHexAddress(address) && address != utils.ZeroAddress.String() {
480+
addressArray = append(addressArray, gethcommon.HexToAddress(address))
481+
}
435482
}
436483
return addressArray
437484
}

pkg/rewards/claim_test.go

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ import (
1212

1313
"github.com/Layr-Labs/eigenlayer-cli/pkg/internal/common/flags"
1414
"github.com/Layr-Labs/eigenlayer-cli/pkg/internal/testutils"
15+
"github.com/Layr-Labs/eigenlayer-cli/pkg/utils"
16+
17+
"github.com/Layr-Labs/eigenlayer-rewards-proofs/pkg/distribution"
1518

1619
rewardscoordinator "github.com/Layr-Labs/eigensdk-go/contracts/bindings/IRewardsCoordinator"
1720
"github.com/Layr-Labs/eigensdk-go/logging"
@@ -21,6 +24,7 @@ import (
2124

2225
"github.com/stretchr/testify/assert"
2326
"github.com/urfave/cli/v2"
27+
"github.com/wk8/go-ordered-map/v2"
2428
)
2529

2630
type fakeELReader struct {
@@ -114,6 +118,48 @@ func TestReadAndValidateConfig_NoRecipientProvided(t *testing.T) {
114118
assert.Equal(t, common.HexToAddress(earnerAddress), config.RecipientAddress)
115119
}
116120

121+
func TestReadAndValidateConfig_NoTokenAddressesProvided(t *testing.T) {
122+
earnerAddress := testutils.GenerateRandomEthereumAddressString()
123+
recipientAddress := testutils.GenerateRandomEthereumAddressString()
124+
fs := flag.NewFlagSet("test", flag.ContinueOnError)
125+
fs.String(flags.ETHRpcUrlFlag.Name, "rpc", "")
126+
fs.String(EarnerAddressFlag.Name, earnerAddress, "")
127+
fs.String(RecipientAddressFlag.Name, recipientAddress, "")
128+
fs.String(RewardsCoordinatorAddressFlag.Name, "0x1234", "")
129+
fs.String(TokenAddressesFlag.Name, "", "")
130+
fs.String(ClaimTimestampFlag.Name, "latest", "")
131+
fs.String(ProofStoreBaseURLFlag.Name, "dummy-url", "")
132+
cliCtx := cli.NewContext(nil, fs, nil)
133+
134+
logger := logging.NewJsonSLogger(os.Stdout, &logging.SLoggerOptions{})
135+
136+
config, err := readAndValidateClaimConfig(cliCtx, logger)
137+
138+
assert.NoError(t, err)
139+
assert.ElementsMatch(t, config.TokenAddresses, []common.Address{})
140+
}
141+
142+
func TestReadAndValidateConfig_ZeroTokenAddressesProvided(t *testing.T) {
143+
earnerAddress := testutils.GenerateRandomEthereumAddressString()
144+
recipientAddress := testutils.GenerateRandomEthereumAddressString()
145+
fs := flag.NewFlagSet("test", flag.ContinueOnError)
146+
fs.String(flags.ETHRpcUrlFlag.Name, "rpc", "")
147+
fs.String(EarnerAddressFlag.Name, earnerAddress, "")
148+
fs.String(RecipientAddressFlag.Name, recipientAddress, "")
149+
fs.String(RewardsCoordinatorAddressFlag.Name, "0x1234", "")
150+
fs.String(TokenAddressesFlag.Name, utils.ZeroAddress.String(), "")
151+
fs.String(ClaimTimestampFlag.Name, "latest", "")
152+
fs.String(ProofStoreBaseURLFlag.Name, "dummy-url", "")
153+
cliCtx := cli.NewContext(nil, fs, nil)
154+
155+
logger := logging.NewJsonSLogger(os.Stdout, &logging.SLoggerOptions{})
156+
157+
config, err := readAndValidateClaimConfig(cliCtx, logger)
158+
159+
assert.NoError(t, err)
160+
assert.ElementsMatch(t, config.TokenAddresses, []common.Address{})
161+
}
162+
117163
func TestReadAndValidateConfig_RecipientProvided(t *testing.T) {
118164
earnerAddress := testutils.GenerateRandomEthereumAddressString()
119165
recipientAddress := testutils.GenerateRandomEthereumAddressString()
@@ -222,3 +268,64 @@ func TestGetClaimDistributionRoot(t *testing.T) {
222268
})
223269
}
224270
}
271+
272+
func TestGetTokensToClaim(t *testing.T) {
273+
// Set up a mock claimableTokens map
274+
claimableTokens := orderedmap.New[common.Address, *distribution.BigInt]()
275+
addr1 := common.HexToAddress(testutils.GenerateRandomEthereumAddressString())
276+
addr2 := common.HexToAddress(testutils.GenerateRandomEthereumAddressString())
277+
addr3 := common.HexToAddress(testutils.GenerateRandomEthereumAddressString())
278+
279+
claimableTokens.Set(addr1, newBigInt(100))
280+
claimableTokens.Set(addr2, newBigInt(200))
281+
282+
// Case 1: No token addresses provided, should return all addresses in claimableTokens
283+
result := getTokensToClaim(claimableTokens, []common.Address{})
284+
expected := []common.Address{addr1, addr2}
285+
assert.ElementsMatch(t, result, expected)
286+
287+
// Case 2: Provided token addresses, should return only those present in claimableTokens
288+
result = getTokensToClaim(claimableTokens, []common.Address{addr2, addr3})
289+
expected = []common.Address{addr2}
290+
assert.ElementsMatch(t, result, expected)
291+
}
292+
293+
func TestGetTokenAddresses(t *testing.T) {
294+
// Set up a mock addresses map
295+
addressesMap := orderedmap.New[common.Address, *distribution.BigInt]()
296+
addr1 := common.HexToAddress(testutils.GenerateRandomEthereumAddressString())
297+
addr2 := common.HexToAddress(testutils.GenerateRandomEthereumAddressString())
298+
299+
addressesMap.Set(addr1, newBigInt(100))
300+
addressesMap.Set(addr2, newBigInt(200))
301+
302+
// Test that the function returns all addresses in the map
303+
result := getAllClaimableTokenAddresses(addressesMap)
304+
expected := []common.Address{addr1, addr2}
305+
assert.ElementsMatch(t, result, expected)
306+
}
307+
308+
func TestFilterClaimableTokenAddresses(t *testing.T) {
309+
// Set up a mock addresses map
310+
addressesMap := orderedmap.New[common.Address, *distribution.BigInt]()
311+
addr1 := common.HexToAddress(testutils.GenerateRandomEthereumAddressString())
312+
addr2 := common.HexToAddress(testutils.GenerateRandomEthereumAddressString())
313+
314+
addressesMap.Set(addr1, newBigInt(100))
315+
addressesMap.Set(addr2, newBigInt(200))
316+
317+
// Test filtering with provided addresses
318+
newMissingAddress := common.HexToAddress(testutils.GenerateRandomEthereumAddressString())
319+
providedAddresses := []common.Address{
320+
addr1,
321+
newMissingAddress,
322+
}
323+
324+
result := filterClaimableTokenAddresses(addressesMap, providedAddresses)
325+
expected := []common.Address{addr1}
326+
assert.ElementsMatch(t, result, expected)
327+
}
328+
329+
func newBigInt(value int64) *distribution.BigInt {
330+
return &distribution.BigInt{Int: big.NewInt(value)}
331+
}

pkg/rewards/flags.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ var (
66
TokenAddressesFlag = cli.StringFlag{
77
Name: "token-addresses",
88
Aliases: []string{"t"},
9-
Usage: "Specify the addresses of the tokens to claim. Comma separated list of addresses",
9+
Usage: "Specify the addresses of the tokens to claim. Comma separated list of addresses. Omit to claim all rewards.",
1010
EnvVars: []string{"TOKEN_ADDRESSES"},
11-
Required: true,
11+
Required: false,
1212
}
1313

1414
RewardsCoordinatorAddressFlag = cli.StringFlag{

0 commit comments

Comments
 (0)