Skip to content

Rewards: TransactionSender, RewardsManagerClient, and more#59

Merged
rickyrombo merged 5 commits intomainfrom
mjp-rewards-7
May 1, 2025
Merged

Rewards: TransactionSender, RewardsManagerClient, and more#59
rickyrombo merged 5 commits intomainfrom
mjp-rewards-7

Conversation

@rickyrombo
Copy link
Copy Markdown
Contributor

  • Creates RewardsManagerClient to encapsulate the given instance of the rewards program, and implement helpers to get marshalled account data.
  • Creates TransactionSender to manage the sending of Solana transactions, as well as handle adding compute budget instructions. (Replaces solana relay for this feature!)
  • Updates solana-go to get websockets support (required using a commit hash, there hasn't been a release yet)
  • Updates spl programs to use common.Address for eth addresses
  • Makes derive methods private again, now encapsulated as part of the RewardsManagerClient
  • Normalizes the AAO and Validator attestations so that they're not treated differently when constructing the transaction
  • Adds some better errors/wrapped errors for easier debugging etc.

Once I create ClaimableTokensClient in the future, can remove SolanaConfig from the api struct and just have it have services, much like SDK.

Comment thread api/server.go
resolveWalletCache: resolveWalletCache,
resolveGrantCache: resolveGrantCache,
rewardAttester: *rewardAttester,
transactionSender: *transactionSender,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

i wonder if this should be solanaTransactionSender/splTransactionSender. could get confusing when we do more relay-to-core stuff

default:
return "UnknownError"
}
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

i think "idiomatic" go would ask you to implement

func (e RewardManagerError) Error() string {
    return e.String()
}

and probably just call this Error instead of RewardManagerError

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

oh interesting, like make this into an actual go error? I didn't think of that. maybe i should rename this to RewardManagerErrorCode instead then actually

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think what you want is a collection of errors

var (
  ErrAlreadySent = errors.New("already sent")
  ...
)

and then you can compose if you want with errors.Join which allows you to do errors.Is(err, ErrAlreadySent) in a switch like this

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

something like this

var (
	ErrIncorrectOwner             = errors.New("reward manager: incorrect owner")
	ErrSignCollision             = errors.New("reward manager: sign collision")
	ErrWrongSigner               = errors.New("reward manager: wrong signer")
	ErrNotEnoughSigners          = errors.New("reward manager: not enough signers")
	ErrSecp256InstructionMissing = errors.New("reward manager: secp256 instruction missing")
	ErrInstructionLoadError      = errors.New("reward manager: instruction load error")
	ErrRepeatedSenders           = errors.New("reward manager: repeated senders")
	ErrSignatureVerificationFailed = errors.New("reward manager: signature verification failed")
	ErrOperatorCollision         = errors.New("reward manager: operator collision")
	ErrAlreadySent               = errors.New("reward manager: already sent")
	ErrIncorrectMessages         = errors.New("reward manager: incorrect messages")
	ErrMessagesOverflow          = errors.New("reward manager: messages overflow")
	ErrMathOverflow              = errors.New("reward manager: math overflow")
	ErrInvalidRecipient          = errors.New("reward manager: invalid recipient")
)

then you could use it like this:

func SendReward() error {
	// something goes wrong
	return fmt.Errorf("failed to send reward: %w", rewardmanager.ErrAlreadySent)
}

if errors.Is(err, rewardmanager.ErrAlreadySent) {
fmt.Println("reward was already sent, skipping")
return
}

Copy link
Copy Markdown
Contributor Author

@rickyrombo rickyrombo May 2, 2025

Choose a reason for hiding this comment

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

No I actually need the numbers to map to the codes so I can decode the json I get back from the RPC. I think this would be nice, but separate from what I'm trying to do

}
defer subscription.Unsubscribe()

subCtx, cancel := context.WithCancel(context.Background())
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

should this just be the passed in ctx?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

idts - i want to be able to cancel the polling etc without canceling the parent context

subCtx, cancel := context.WithCancel(context.Background())
resChan := make(chan *ws.SignatureResult)
errChan := make(chan error, 1)
go func() {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nice

Comment thread api/v1_claim_rewards.go
rewardClaim := rewards.RewardClaim{
RewardID: row.ChallengeID,
Amount: uint64(5), // TODO: Change me!
Amount: uint64(1000), // TODO: Change me!
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

can we import this from the core stuff?

}

if simResult.Value.Err != nil {
str, err := json.Marshal(simResult.Value.Err)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

kinda nasty to marshal and unmarshal this - is that what they recommend?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this seemed sensible to me, so that if it doesn't fit the expected structure it's still neatly stringified for logging. open to suggestions though

Comment thread api/v1_claim_rewards.go
if value, exists := antiAbuseOracleMap[oracleEndpoint]; exists {
return oracleEndpoint, value, nil
return &config.Node{
DelegateOwnerWallet: value,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

i don't love using this interface for AAO in addition to "validator node"

can we make a different struct for it?

perhaps we can rename Node to Validator (endpoint, delegateownerwallet, ownerwallet, isstoragedisabled) and Node (endpoint, address).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think everything can be a node, and what type of node is based on what list it's in. Validators are nodes in the validator list, AAOs are nodes in the AAO list. Maybe validator can have a separate struct though for the sake of IsStorageDisabled but I don't think it matters that much, especially given that's going away eventually

@rickyrombo rickyrombo merged commit 567dc19 into main May 1, 2025
2 checks passed
@rickyrombo rickyrombo deleted the mjp-rewards-7 branch May 1, 2025 23:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants