Skip to content

Commit 83a31a1

Browse files
authored
Merge pull request #33 from algorandfoundation/feat/incentives-elegibility
feat: incentive eligibility check for automated fees
2 parents ee1c17f + 68f7902 commit 83a31a1

File tree

11 files changed

+197
-122
lines changed

11 files changed

+197
-122
lines changed

codecov.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
coverage:
2+
status:
3+
project:
4+
default:
5+
target: 60%
6+
threshold: 10%
7+
patch:
8+
default:
9+
target: 60%

internal/accounts.go

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import (
1212
// Account represents a user's account, including address, status, balance, and number of keys.
1313
type Account struct {
1414
Participation *api.AccountParticipation
15+
// IncentiveEligible determines the minimum fee
16+
IncentiveEligible bool
1517
// Account Address is the algorand encoded address
1618
Address string
1719
// Status is the Online/Offline/"NotParticipating" status of the account
@@ -69,9 +71,10 @@ func AccountsFromState(state *StateModel, t Time, client api.ClientWithResponses
6971
val, ok := values[key.Address]
7072
if !ok {
7173
var account = api.Account{
72-
Address: key.Address,
73-
Status: "Unknown",
74-
Amount: 0,
74+
Address: key.Address,
75+
Status: "Unknown",
76+
IncentiveEligible: nil,
77+
Amount: 0,
7578
}
7679
if state.Status.State != SyncingState {
7780
var err error
@@ -83,13 +86,22 @@ func AccountsFromState(state *StateModel, t Time, client api.ClientWithResponses
8386
}
8487
}
8588

89+
// Check for eligibility
90+
var incentiveEligible = false
91+
if account.IncentiveEligible == nil {
92+
incentiveEligible = false
93+
} else {
94+
incentiveEligible = *account.IncentiveEligible
95+
}
96+
8697
values[key.Address] = Account{
87-
Participation: account.Participation,
88-
Address: key.Address,
89-
Status: account.Status,
90-
Balance: account.Amount / 1000000,
91-
Expires: GetExpiresTime(t, key, state),
92-
Keys: 1,
98+
Participation: account.Participation,
99+
Address: key.Address,
100+
Status: account.Status,
101+
Balance: account.Amount / 1000000,
102+
Expires: GetExpiresTime(t, key, state),
103+
IncentiveEligible: incentiveEligible,
104+
Keys: 1,
93105
}
94106
} else {
95107
val.Keys++

internal/participation.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,7 @@ func FindParticipationIdForVoteKey(slice *[]api.ParticipationKey, votekey []byte
108108
return nil
109109
}
110110

111-
func ToLoraDeepLink(network string, offline bool, part api.ParticipationKey) (string, error) {
112-
fee := 2000000
111+
func ToLoraDeepLink(network string, offline bool, incentiveEligible bool, part api.ParticipationKey) (string, error) {
113112
var loraNetwork = strings.Replace(strings.Replace(network, "-v1.0", "", 1), "-v1", "", 1)
114113
if loraNetwork == "dockernet" || loraNetwork == "tuinet" {
115114
loraNetwork = "localnet"
@@ -124,8 +123,7 @@ func ToLoraDeepLink(network string, offline bool, part api.ParticipationKey) (st
124123
)
125124
} else {
126125
query = fmt.Sprintf(
127-
"type[0]=keyreg&fee[0]=%d&sender[0]=%s&selkey[0]=%s&sprfkey[0]=%s&votekey[0]=%s&votefst[0]=%d&votelst[0]=%d&votekd[0]=%d",
128-
fee,
126+
"type[0]=keyreg&sender[0]=%s&selkey[0]=%s&sprfkey[0]=%s&votekey[0]=%s&votefst[0]=%d&votelst[0]=%d&votekd[0]=%d",
129127
part.Address,
130128
base64.RawURLEncoding.EncodeToString(part.Key.SelectionParticipationKey),
131129
base64.RawURLEncoding.EncodeToString(*part.Key.StateProofKey),
@@ -134,6 +132,9 @@ func ToLoraDeepLink(network string, offline bool, part api.ParticipationKey) (st
134132
part.Key.VoteLastValid,
135133
part.Key.VoteKeyDilution,
136134
)
135+
if incentiveEligible {
136+
query += fmt.Sprintf("&fee[0]=%d", 2000000)
137+
}
137138
}
138139
return fmt.Sprintf("https://lora.algokit.io/%s/transaction-wizard?%s", loraNetwork, strings.Replace(query, "[0]", encodedIndex, -1)), nil
139140
}

internal/participation_test.go

Lines changed: 51 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,47 @@ import (
44
"context"
55
"fmt"
66
"github.com/algorandfoundation/hack-tui/api"
7-
"github.com/oapi-codegen/oapi-codegen/v2/pkg/securityprovider"
7+
"github.com/algorandfoundation/hack-tui/internal/test"
8+
"github.com/algorandfoundation/hack-tui/internal/test/mock"
89
"testing"
910
)
1011

12+
func Test_ToLoraDeeplink(t *testing.T) {
13+
link, err := ToLoraDeepLink("tuinet-v1", true, true, api.ParticipationKey{
14+
Address: "ABC",
15+
EffectiveFirstValid: nil,
16+
EffectiveLastValid: nil,
17+
Id: "",
18+
Key: api.AccountParticipation{},
19+
LastBlockProposal: nil,
20+
LastStateProof: nil,
21+
LastVote: nil,
22+
})
23+
if err != nil {
24+
t.Error(err)
25+
}
26+
if link != "https://lora.algokit.io/localnet/transaction-wizard?type%5B0%5D=keyreg&sender%5B0%5D=ABC" {
27+
t.Error("Link should be a known deeplink")
28+
}
29+
30+
link, err = ToLoraDeepLink("tuinet-v1", false, true, mock.Keys[0])
31+
if err != nil {
32+
t.Error(err)
33+
}
34+
if link != "https://lora.algokit.io/localnet/transaction-wizard?type%5B0%5D=keyreg&sender%5B0%5D=ABC&selkey%5B0%5D=VEVTVEtFWQ&sprfkey%5B0%5D=VEVTVEtFWQ&votekey%5B0%5D=VEVTVEtFWQ&votefst%5B0%5D=0&votelst%5B0%5D=30000&votekd%5B0%5D=100&fee%5B0%5D=2000000" {
35+
t.Error("Link should be a known deeplink fee")
36+
}
37+
38+
link, err = ToLoraDeepLink("tuinet-v1", false, false, mock.Keys[0])
39+
if err != nil {
40+
t.Error(err)
41+
}
42+
if link != "https://lora.algokit.io/localnet/transaction-wizard?type%5B0%5D=keyreg&sender%5B0%5D=ABC&selkey%5B0%5D=VEVTVEtFWQ&sprfkey%5B0%5D=VEVTVEtFWQ&votekey%5B0%5D=VEVTVEtFWQ&votefst%5B0%5D=0&votelst%5B0%5D=30000&votekd%5B0%5D=100" {
43+
t.Error("Link should be a known deeplink fee")
44+
}
45+
46+
}
47+
1148
func Test_ListParticipationKeys(t *testing.T) {
1249
ctx := context.Background()
1350
client, err := api.NewClientWithResponses("https://mainnet-api.4160.nodely.dev:443")
@@ -23,16 +60,9 @@ func Test_ListParticipationKeys(t *testing.T) {
2360
}
2461

2562
// Setup elevated client
26-
apiToken, err := securityprovider.NewSecurityProviderApiKey("header", "X-Algo-API-Token", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
27-
if err != nil {
28-
t.Fatal(err)
29-
}
30-
client, err = api.NewClientWithResponses("http://localhost:8080", api.WithRequestEditorFn(apiToken.Intercept))
31-
if err != nil {
32-
t.Fatal(err)
33-
}
63+
tClient := test.GetClient(false)
3464

35-
keys, err := GetPartKeys(ctx, client)
65+
keys, err := GetPartKeys(ctx, tClient)
3666
if err != nil {
3767
t.Fatal(err)
3868
}
@@ -54,25 +84,17 @@ func Test_ReadParticipationKey(t *testing.T) {
5484
t.Fatal(err)
5585
}
5686

57-
// Setup elevated client
58-
apiToken, err := securityprovider.NewSecurityProviderApiKey("header", "X-Algo-API-Token", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
59-
if err != nil {
60-
t.Fatal(err)
61-
}
62-
client, err = api.NewClientWithResponses("http://localhost:8080", api.WithRequestEditorFn(apiToken.Intercept))
63-
if err != nil {
64-
t.Fatal(err)
65-
}
87+
tClient := test.GetClient(false)
6688

67-
keys, err := GetPartKeys(ctx, client)
89+
keys, err := GetPartKeys(ctx, tClient)
6890
if err != nil {
6991
t.Fatal(err)
7092
}
7193
if keys == nil {
7294
t.Fatal(err)
7395
}
7496

75-
_, err = ReadPartKey(ctx, client, (*keys)[0].Id)
97+
_, err = ReadPartKey(ctx, tClient, (*keys)[0].Id)
7698

7799
if err != nil {
78100
t.Fatal(err)
@@ -88,31 +110,23 @@ func Test_GenerateParticipationKey(t *testing.T) {
88110
if err != nil {
89111
t.Fatal(err)
90112
}
91-
92113
// Generate error
93114
_, err = GenerateKeyPair(ctx, client, "", nil)
94115
if err == nil {
95116
t.Fatal(err)
96117
}
97118

98-
// Setup elevated client
99-
apiToken, err := securityprovider.NewSecurityProviderApiKey("header", "X-Algo-API-Token", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
100-
if err != nil {
101-
t.Fatal(err)
102-
}
103-
client, err = api.NewClientWithResponses("http://localhost:8080", api.WithRequestEditorFn(apiToken.Intercept))
104-
if err != nil {
105-
t.Fatal(err)
106-
}
119+
// Setup test client
120+
tClient := test.GetClient(false)
107121

108122
params := api.GenerateParticipationKeysParams{
109123
Dilution: nil,
110124
First: 0,
111-
Last: 10000,
125+
Last: 30,
112126
}
113127

114128
// This returns nothing and sucks
115-
key, err := GenerateKeyPair(ctx, client, "QNZ7GONNHTNXFW56Y24CNJQEMYKZKKI566ASNSWPD24VSGKJWHGO6QOP7U", &params)
129+
key, err := GenerateKeyPair(ctx, tClient, "ABC", &params)
116130
if err != nil {
117131
t.Fatal(err)
118132
}
@@ -121,21 +135,14 @@ func Test_GenerateParticipationKey(t *testing.T) {
121135

122136
func Test_DeleteParticipationKey(t *testing.T) {
123137
ctx := context.Background()
124-
// Setup elevated client
125-
apiToken, err := securityprovider.NewSecurityProviderApiKey("header", "X-Algo-API-Token", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
126-
if err != nil {
127-
t.Fatal(err)
128-
}
129-
client, err := api.NewClientWithResponses("http://localhost:8080", api.WithRequestEditorFn(apiToken.Intercept))
130-
if err != nil {
131-
t.Fatal(err)
132-
}
138+
139+
client := test.GetClient(false)
133140
params := api.GenerateParticipationKeysParams{
134141
Dilution: nil,
135142
First: 0,
136-
Last: 10000,
143+
Last: 30000,
137144
}
138-
key, err := GenerateKeyPair(ctx, client, "QNZ7GONNHTNXFW56Y24CNJQEMYKZKKI566ASNSWPD24VSGKJWHGO6QOP7U", &params)
145+
key, err := GenerateKeyPair(ctx, client, "ABC", &params)
139146
if err != nil {
140147
t.Fatal(err)
141148
}

internal/test/client.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,36 @@ algod_crypto_vrf_hash_total 0`
6767
}
6868
return &res, nil
6969
}
70+
func (c *Client) GetParticipationKeyByIDWithResponse(ctx context.Context, participationId string, reqEditors ...api.RequestEditorFn) (*api.GetParticipationKeyByIDResponse, error) {
71+
var res api.GetParticipationKeyByIDResponse
72+
if !c.Invalid {
73+
httpResponse := http.Response{StatusCode: 200}
74+
res = api.GetParticipationKeyByIDResponse{
75+
Body: nil,
76+
HTTPResponse: &httpResponse,
77+
JSON200: &mock.Keys[0],
78+
JSON400: nil,
79+
JSON401: nil,
80+
JSON404: nil,
81+
JSON500: nil,
82+
}
83+
} else {
84+
httpResponse := http.Response{StatusCode: 404}
85+
res = api.GetParticipationKeyByIDResponse{
86+
Body: nil,
87+
HTTPResponse: &httpResponse,
88+
JSON200: nil,
89+
JSON400: nil,
90+
JSON401: nil,
91+
JSON404: nil,
92+
JSON500: nil,
93+
}
94+
}
95+
if c.Errors {
96+
return nil, errors.New("test error")
97+
}
98+
return &res, nil
99+
}
70100
func (c *Client) GetParticipationKeysWithResponse(ctx context.Context, reqEditors ...api.RequestEditorFn) (*api.GetParticipationKeysResponse, error) {
71101
var res api.GetParticipationKeysResponse
72102
clone := mock.Keys

ui/internal/test/state.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,12 @@ func GetState(client api.ClientWithResponsesInterface) *internal.StateModel {
4242
val, ok := values[key.Address]
4343
if !ok {
4444
values[key.Address] = internal.Account{
45-
Address: key.Address,
46-
Status: "Offline",
47-
Balance: 0,
48-
Expires: internal.GetExpiresTime(clock, key, sm),
49-
Keys: 1,
45+
Address: key.Address,
46+
Status: "Offline",
47+
Balance: 0,
48+
IncentiveEligible: true,
49+
Expires: internal.GetExpiresTime(clock, key, sm),
50+
Keys: 1,
5051
}
5152
} else {
5253
val.Keys++

ui/modal/testdata/Test_Snapshot/TransactionModal.golden

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222

2323

24+
2425

2526

2627
╭──Register Offline─────────────────────────────────────────╮
@@ -29,22 +30,20 @@
2930
│ Scan the QR code with Pera or Defly │
3031
│ (make sure you use the testnet-v1.0 network) │
3132
│ │
32-
│ █████████████████████████████████ │
33-
│ ██ ▄▄▄▄▄ █▀▀ ▄█▀▄██▀█ ▄█ ▄▄▄▄▄ ██ │
34-
│ ██ █ █ ██▄█ █▀█▄██▄▀ █ █ █ ██ │
35-
│ ██ █▄▄▄█ █▄ ▄▀▄▄▀▀ █ ▄█ █▄▄▄█ ██ │
36-
│ ██▄▄▄▄▄▄▄█▄▀▄▀ █▄█ ▀▄▀▄█▄▄▄▄▄▄▄██ │
37-
│ ██ ▀ █ ▀▄██▀█ ▀▀██ ▀██▀██▄█▀ ▀██ │
38-
│ ██▄ ▀▄██▄ ▀▄ ▄▀▀ ▀▀▀█▀█▀ ▄▄█▄ ██ │
39-
│ ██ ▄▀▄ █▄ ██▀▄█ █ ▄▀██ ▄█▄ █ ██ │
40-
│ ██▀▄▀▀█▀▄▀▀▀ ██▄██▄▀ ▀▀█ ▄▀██ ▄██ │
41-
│ ███▀██▀▄▄ ▄▀▀▀▄▀██▀ ▀▄██ ▀█▀█ ▀██ │
42-
│ ██▄▀ ▀▄▄▄ ▄ █▀▀ ▀█▀▄▀▀▄▀▄▀▄▄▄▄██ │
43-
│ ██▄█▄▄██▄█▀█▀███ ▄▀ █▀ ▄▄▄ █▀▀ ██ │
44-
│ ██ ▄▄▄▄▄ ██▄█▄█▄█▀▀▀▀▄ █▄█ ██▄ ██ │
45-
│ ██ █ █ █▄▄▄█▄▀██ ▄ █▄▄▄ ██▀ ██ │
46-
│ ██ █▄▄▄█ █▄ ▄█▀ ██▀▀ ██▀ ▀▄▀▄██ │
47-
│ ██▄▄▄▄▄▄▄█▄█▄███▄▄▄▄█▄▄████▄█▄▄██ │
33+
│ █████████████████████████████ │
34+
│ ██ ▄▄▄▄▄ █▀█▄▀█▀█ ▀█ ▄▄▄▄▄ ██ │
35+
│ ██ █ █ ██▀▄▀ █ █ █ █ █ ██ │
36+
│ ██ █▄▄▄█ █▄▀▀▀█▀ ██ █▄▄▄█ ██ │
37+
│ ██▄▄▄▄▄▄▄█▄█▄█▄▀ ▀ █▄▄▄▄▄▄▄██ │
38+
│ ██▄▀ ▀ █▄▀▀ ▄█▄ █▀▀▀▀▄██ ▀██ │
39+
│ ███▀ ▄▀▄▄▀▀█▄█▄▀▀█▀▄▀▀▀▄█ ▄██ │
40+
│ ███▀█ ▀▄ █ ▄▀▄▄█▄█▀ ██▀ ▀██ │
41+
│ ██▄▀█ ▄▄ █▄ █▀ ██▄▀█▄▄█▄▄██ │
42+
│ ██▄███▄█▄▄▀▄▀█ █▀ ▄▄▄ █▀ ██ │
43+
│ ██ ▄▄▄▄▄ ██▄ ▀▀▀▀█ █▄█ ██ ██ │
44+
│ ██ █ █ █▄ ▀█ ▄▀▀ ▄▄ ▀█████ │
45+
│ ██ █▄▄▄█ █▄▀ █▄▀ ▀▄▀▄ ▄▄▀▄██ │
46+
│ ██▄▄▄▄▄▄▄█▄█▄██▄█▄▄▄▄▄███▄▄██ │
4847
│ │
4948
│ -or- │
5049
│ │
@@ -75,6 +74,7 @@
7574

7675

7776

77+
7878

7979

8080

0 commit comments

Comments
 (0)