Skip to content

Commit 567dc19

Browse files
authored
Rewards: TransactionSender, RewardsManagerClient, and more (#59)
- 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.
1 parent ff9bc90 commit 567dc19

14 files changed

Lines changed: 905 additions & 327 deletions

api/server.go

Lines changed: 46 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,15 @@ import (
99
"time"
1010

1111
"bridgerton.audius.co/api/dbv1"
12+
"bridgerton.audius.co/api/spl"
13+
"bridgerton.audius.co/api/spl/programs/reward_manager"
1214
"bridgerton.audius.co/config"
1315
"bridgerton.audius.co/trashid"
1416
"github.com/AudiusProject/audiusd/pkg/rewards"
1517
adapter "github.com/axiomhq/axiom-go/adapters/zap"
1618
"github.com/axiomhq/axiom-go/axiom"
1719
"github.com/ethereum/go-ethereum/crypto"
20+
"github.com/gagliardetto/solana-go/rpc"
1821
"github.com/gofiber/contrib/fiberzap/v2"
1922
"github.com/gofiber/fiber/v2"
2023
"github.com/gofiber/fiber/v2/middleware/cors"
@@ -117,23 +120,42 @@ func NewApiServer(config config.Config) *ApiServer {
117120
panic(err)
118121
}
119122

123+
solanaRpc := rpc.New(config.SolanaConfig.RpcProviders[0])
124+
rewardAttester := rewards.NewRewardAttester(privateKey, []rewards.Reward{})
125+
transactionSender := spl.NewTransactionSender(
126+
config.SolanaConfig.FeePayers,
127+
config.SolanaConfig.RpcProviders,
128+
)
129+
rewardManagerClient, err := reward_manager.NewRewardManagerClient(
130+
solanaRpc,
131+
config.SolanaConfig.RewardManagerProgramID,
132+
config.SolanaConfig.RewardManagerState,
133+
config.SolanaConfig.RewardManagerLookupTable,
134+
logger,
135+
)
136+
if err != nil {
137+
panic(err)
138+
}
139+
120140
app := &ApiServer{
121141
App: fiber.New(fiber.Config{
122142
JSONEncoder: json.Marshal,
123143
JSONDecoder: json.Unmarshal,
124144
ErrorHandler: errorHandler(logger),
125145
}),
126-
pool: pool,
127-
queries: dbv1.New(pool),
128-
logger: logger,
129-
started: time.Now(),
130-
resolveHandleCache: resolveHandleCache,
131-
resolveWalletCache: resolveWalletCache,
132-
resolveGrantCache: resolveGrantCache,
133-
rewardAttester: *rewards.NewRewardAttester(privateKey, []rewards.Reward{}),
134-
solanaConfig: config.SolanaConfig,
135-
antiAbuseOracles: config.AntiAbuseOracles,
136-
validators: config.Nodes,
146+
pool: pool,
147+
queries: dbv1.New(pool),
148+
logger: logger,
149+
started: time.Now(),
150+
resolveHandleCache: resolveHandleCache,
151+
resolveWalletCache: resolveWalletCache,
152+
resolveGrantCache: resolveGrantCache,
153+
rewardAttester: *rewardAttester,
154+
transactionSender: *transactionSender,
155+
rewardManagerClient: *rewardManagerClient,
156+
solanaConfig: config.SolanaConfig,
157+
antiAbuseOracles: config.AntiAbuseOracles,
158+
validators: config.Nodes,
137159
}
138160

139161
app.Use(recover.New(recover.Config{
@@ -277,17 +299,19 @@ func NewApiServer(config config.Config) *ApiServer {
277299

278300
type ApiServer struct {
279301
*fiber.App
280-
pool *pgxpool.Pool
281-
queries *dbv1.Queries
282-
logger *zap.Logger
283-
started time.Time
284-
resolveHandleCache otter.Cache[string, int32]
285-
resolveWalletCache otter.Cache[string, int32]
286-
resolveGrantCache otter.Cache[string, bool]
287-
rewardAttester rewards.RewardAttester
288-
solanaConfig config.SolanaConfig
289-
antiAbuseOracles []string
290-
validators []config.Node
302+
pool *pgxpool.Pool
303+
queries *dbv1.Queries
304+
logger *zap.Logger
305+
started time.Time
306+
resolveHandleCache otter.Cache[string, int32]
307+
resolveWalletCache otter.Cache[string, int32]
308+
resolveGrantCache otter.Cache[string, bool]
309+
rewardManagerClient reward_manager.RewardManagerClient
310+
rewardAttester rewards.RewardAttester
311+
transactionSender spl.TransactionSender
312+
solanaConfig config.SolanaConfig
313+
antiAbuseOracles []string
314+
validators []config.Node
291315
}
292316

293317
func (app *ApiServer) home(c *fiber.Ctx) error {

api/server_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ func TestMain(m *testing.M) {
4747
Env: "test",
4848
DbUrl: "postgres://postgres:example@localhost:21300/test",
4949
DelegatePrivateKey: "0633fddb74e32b3cbc64382e405146319c11a1a52dc96598e557c5dbe2f31468",
50+
SolanaConfig: config.SolanaConfig{RpcProviders: []string{""}},
5051
})
5152

5253
// seed db

api/spl/programs/reward_manager/EvaluateAttestations.go

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
package reward_manager
22

33
import (
4-
"encoding/hex"
5-
"strings"
6-
4+
"github.com/ethereum/go-ethereum/common"
75
bin "github.com/gagliardetto/binary"
86
"github.com/gagliardetto/solana-go"
97
)
@@ -12,14 +10,14 @@ type EvaluateAttestation struct {
1210
// Instruction Data
1311
Amount uint64
1412
DisbursementID string
15-
ReceipientEthAddress string
13+
ReceipientEthAddress common.Address
1614

1715
// Used for derivations
1816
RewardManagerState solana.PublicKey `bin:"-" borsh_skip:"true"`
1917
Payer solana.PublicKey `bin:"-" borsh_skip:"true"`
2018
DestinationUserBank solana.PublicKey `bin:"-" borsh_skip:"true"`
2119
TokenSource solana.PublicKey `bin:"-" borsh_skip:"true"`
22-
AntiAbuseOracleEthAddress string `bin:"-" borsh_skip:"true"`
20+
AntiAbuseOracleEthAddress common.Address `bin:"-" borsh_skip:"true"`
2321

2422
// Accounts
2523
solana.AccountMetaSlice `bin:"-" borsh_skip:"true"`
@@ -35,7 +33,7 @@ func (inst *EvaluateAttestation) SetDisbursementID(challengedId string, specifie
3533
return inst
3634
}
3735

38-
func (inst *EvaluateAttestation) SetRecipientEthAddress(recipientEthAddress string) *EvaluateAttestation {
36+
func (inst *EvaluateAttestation) SetRecipientEthAddress(recipientEthAddress common.Address) *EvaluateAttestation {
3937
inst.ReceipientEthAddress = recipientEthAddress
4038
return inst
4139
}
@@ -45,7 +43,7 @@ func (inst *EvaluateAttestation) SetAmount(amount uint64) *EvaluateAttestation {
4543
return inst
4644
}
4745

48-
func (inst *EvaluateAttestation) SetAntiAbuseOracleEthAddress(antiAbuseOracleAddress string) *EvaluateAttestation {
46+
func (inst *EvaluateAttestation) SetAntiAbuseOracleEthAddress(antiAbuseOracleAddress common.Address) *EvaluateAttestation {
4947
inst.AntiAbuseOracleEthAddress = antiAbuseOracleAddress
5048
return inst
5149
}
@@ -71,8 +69,8 @@ func (inst *EvaluateAttestation) SetPayer(payer solana.PublicKey) *EvaluateAttes
7169
}
7270

7371
func (inst EvaluateAttestation) Build() *Instruction {
74-
authority, _, _ := DeriveAuthorityAccount(ProgramID, inst.RewardManagerState)
75-
attestations, _, _ := DeriveAttestationsAccount(ProgramID, authority, inst.DisbursementID)
72+
authority, _, _ := deriveAuthorityAccount(ProgramID, inst.RewardManagerState)
73+
attestations, _, _ := deriveAttestationsAccount(ProgramID, authority, inst.DisbursementID)
7674
disbursement, _, _ := deriveDisbursement(ProgramID, authority, inst.DisbursementID)
7775
antiAbuseOracle, _, _ := deriveSender(ProgramID, authority, inst.AntiAbuseOracleEthAddress)
7876

@@ -151,11 +149,7 @@ func (inst EvaluateAttestation) MarshalWithEncoder(encoder *bin.Encoder) error {
151149
return err
152150
}
153151

154-
address, err := hex.DecodeString(strings.TrimPrefix(inst.ReceipientEthAddress, "0x"))
155-
if err != nil {
156-
return err
157-
}
158-
return encoder.WriteBytes(address, false)
152+
return encoder.WriteBytes(inst.ReceipientEthAddress.Bytes(), false)
159153
}
160154

161155
func (inst *EvaluateAttestation) UnmarshalWithDecoder(decoder *bin.Decoder) error {
@@ -165,9 +159,9 @@ func (inst *EvaluateAttestation) UnmarshalWithDecoder(decoder *bin.Decoder) erro
165159
func NewEvaluateAttestationInstruction(
166160
challengeId string,
167161
specifier string,
168-
recipientEthAddress string,
162+
recipientEthAddress common.Address,
169163
amount uint64,
170-
antiAbuseOracleAddress string,
164+
antiAbuseOracleAddress common.Address,
171165
rewardManagerState solana.PublicKey,
172166
tokenSource solana.PublicKey,
173167
destinationUserBank solana.PublicKey,

api/spl/programs/reward_manager/EvaluateAttestations_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"testing"
66

77
"bridgerton.audius.co/api/spl/programs/reward_manager"
8+
"github.com/ethereum/go-ethereum/common"
89
"github.com/gagliardetto/solana-go"
910
"github.com/stretchr/testify/require"
1011
)
@@ -13,9 +14,9 @@ func TestEvaluateAttestationsInstruction(t *testing.T) {
1314
// Test data
1415
challengeId := "ft"
1516
specifier := "37364e80"
16-
recipientEthAddress := "0x3f6d9fcf0d4466dd5886e3b1def017adfb7916b4"
17+
recipientEthAddress := common.HexToAddress("0x3f6d9fcf0d4466dd5886e3b1def017adfb7916b4")
1718
amount := uint64(200000000)
18-
antiAbuseOracleEthAddress := "0x00b6462e955dA5841b6D9e1E2529B830F00f31Bf"
19+
antiAbuseOracleEthAddress := common.HexToAddress("0x00b6462e955dA5841b6D9e1E2529B830F00f31Bf")
1920

2021
// Expected Accounts
2122
// From successful stage transaction (signature 26gT9HVMhzBDzsKcsiKREYmGcXuZhjAJpCVUu9WFNhVMyKje8SdApYc4ev3HrumZB4LEXLUaPnKyriBPLmtzwrWp)

api/spl/programs/reward_manager/SubmitAttestation.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package reward_manager
22

33
import (
4+
"github.com/ethereum/go-ethereum/common"
45
bin "github.com/gagliardetto/binary"
56
"github.com/gagliardetto/solana-go"
67
)
@@ -10,7 +11,7 @@ type SubmitAttestation struct {
1011
DisbursementID string
1112

1213
// Used for derivations
13-
SenderEthAddress string `bin:"-" borsh_skip:"true"`
14+
SenderEthAddress common.Address `bin:"-" borsh_skip:"true"`
1415
RewardManagerState solana.PublicKey `bin:"-" borsh_skip:"true"`
1516
Payer solana.PublicKey `bin:"-" borsh_skip:"true"`
1617

@@ -28,7 +29,7 @@ func (inst *SubmitAttestation) SetDisbursementID(challengeId string, specifier s
2829
return inst
2930
}
3031

31-
func (inst *SubmitAttestation) SetSenderEthAddress(senderEthAddress string) *SubmitAttestation {
32+
func (inst *SubmitAttestation) SetSenderEthAddress(senderEthAddress common.Address) *SubmitAttestation {
3233
inst.SenderEthAddress = senderEthAddress
3334
return inst
3435
}
@@ -44,9 +45,9 @@ func (inst *SubmitAttestation) SetPayer(payer solana.PublicKey) *SubmitAttestati
4445
}
4546

4647
func (inst SubmitAttestation) Build() *Instruction {
47-
authority, _, _ := DeriveAuthorityAccount(ProgramID, inst.RewardManagerState)
48+
authority, _, _ := deriveAuthorityAccount(ProgramID, inst.RewardManagerState)
4849
sender, _, _ := deriveSender(ProgramID, authority, inst.SenderEthAddress)
49-
attestations, _, _ := DeriveAttestationsAccount(ProgramID, authority, inst.DisbursementID)
50+
attestations, _, _ := deriveAttestationsAccount(ProgramID, authority, inst.DisbursementID)
5051

5152
inst.AccountMetaSlice = []*solana.AccountMeta{
5253
{
@@ -108,7 +109,7 @@ func (inst *SubmitAttestation) UnmarshalWithDecoder(decoder *bin.Decoder) error
108109
func NewSubmitAttestationInstruction(
109110
challengeId string,
110111
specifier string,
111-
senderEthAddress string,
112+
senderEthAddress common.Address,
112113
rewardManagerState solana.PublicKey,
113114
payer solana.PublicKey,
114115

api/spl/programs/reward_manager/accounts.go

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"strings"
88

99
"github.com/AudiusProject/audiusd/pkg/rewards"
10+
"github.com/ethereum/go-ethereum/common"
1011
bin "github.com/gagliardetto/binary"
1112
"github.com/gagliardetto/solana-go"
1213
)
@@ -125,27 +126,23 @@ func (data *AttestationsAccountData) UnmarshalWithDecoder(decoder *bin.Decoder)
125126
return nil
126127
}
127128

128-
func DeriveAuthorityAccount(programId solana.PublicKey, state solana.PublicKey) (solana.PublicKey, uint8, error) {
129+
func deriveAuthorityAccount(programId solana.PublicKey, state solana.PublicKey) (solana.PublicKey, uint8, error) {
129130
seeds := make([][]byte, 1)
130131
seeds[0] = state.Bytes()[0:32]
131132
return solana.FindProgramAddress(seeds, programId)
132133
}
133134

134-
func deriveSender(programId solana.PublicKey, authority solana.PublicKey, ethAddress string) (solana.PublicKey, uint8, error) {
135+
func deriveSender(programId solana.PublicKey, authority solana.PublicKey, ethAddress common.Address) (solana.PublicKey, uint8, error) {
135136
senderSeedPrefix := []byte(SenderSeedPrefix)
136-
// Remove 0x and decode hex
137-
decodedEthAddress, err := hex.DecodeString(strings.TrimPrefix(ethAddress, "0x"))
138-
if err != nil {
139-
return solana.PublicKey{}, 0, err
140-
}
137+
decodedEthAddress := ethAddress.Bytes()
141138
// Pad the eth address if necessary w/ leading 0
142139
senderSeed := make([]byte, len(senderSeedPrefix)+EthAddressByteLength)
143140
copy(senderSeed, senderSeedPrefix)
144141
copy(senderSeed[len(senderSeed)-len(decodedEthAddress):], decodedEthAddress)
145142
return solana.FindProgramAddress([][]byte{authority.Bytes()[0:32], senderSeed}, programId)
146143
}
147144

148-
func DeriveAttestationsAccount(programId solana.PublicKey, authority solana.PublicKey, disbursementId string) (solana.PublicKey, uint8, error) {
145+
func deriveAttestationsAccount(programId solana.PublicKey, authority solana.PublicKey, disbursementId string) (solana.PublicKey, uint8, error) {
149146
attestationsSeed := make([]byte, len(AttestationsSeedPrefix)+len(disbursementId))
150147
copy(attestationsSeed, []byte(AttestationsSeedPrefix))
151148
copy(attestationsSeed[len([]byte(AttestationsSeedPrefix)):], disbursementId)

0 commit comments

Comments
 (0)