code.vegaprotocol.io/vega@v0.79.0/core/integration/steps/volume_rebate_program.go (about) 1 // Copyright (C) 2023 Gobalsky Labs Limited 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 package steps 17 18 import ( 19 "fmt" 20 "time" 21 22 "code.vegaprotocol.io/vega/core/integration/stubs" 23 "code.vegaprotocol.io/vega/core/types" 24 "code.vegaprotocol.io/vega/core/volumerebate" 25 "code.vegaprotocol.io/vega/libs/num" 26 27 "github.com/cucumber/godog" 28 ) 29 30 func VolumeRebateProgramTiers( 31 tiers map[string][]*types.VolumeRebateBenefitTier, 32 volumeRebateTierName string, 33 table *godog.Table, 34 ) error { 35 rows := parseVolumeRebateTiersTable(table) 36 vbts := make([]*types.VolumeRebateBenefitTier, 0, len(rows)) 37 for _, r := range rows { 38 row := volumeRebateTiersRow{row: r} 39 p := &types.VolumeRebateBenefitTier{ 40 MinimumPartyMakerVolumeFraction: row.fraction(), 41 AdditionalMakerRebate: row.rebate(), 42 } 43 44 vbts = append(vbts, p) 45 } 46 tiers[volumeRebateTierName] = vbts 47 return nil 48 } 49 50 func parseVolumeRebateTiersTable(table *godog.Table) []RowWrapper { 51 return StrictParseTable(table, []string{ 52 "fraction", 53 "rebate", 54 }, []string{}) 55 } 56 57 type volumeRebateTiersRow struct { 58 row RowWrapper 59 } 60 61 func (r volumeRebateTiersRow) fraction() num.Decimal { 62 return r.row.MustDecimal("fraction") 63 } 64 65 func (r volumeRebateTiersRow) rebate() num.Decimal { 66 return r.row.MustDecimal("rebate") 67 } 68 69 func VolumeRebateProgram( 70 vde *volumerebate.Engine, 71 tiers map[string][]*types.VolumeRebateBenefitTier, 72 ts *stubs.TimeStub, 73 table *godog.Table, 74 ) error { 75 rows := parseVolumeRebateTable(table) 76 vdp := types.VolumeRebateProgram{} 77 78 for _, r := range rows { 79 row := volumeRebateRow{row: r} 80 vdp.ID = row.id() 81 vdp.WindowLength = row.windowLength() 82 vdp.EndOfProgramTimestamp = row.closingTS(ts.GetTimeNow()) 83 tierName := row.tiers() 84 if tier := tiers[tierName]; tier != nil { 85 vdp.VolumeRebateBenefitTiers = tier 86 } 87 vde.UpdateProgram(&vdp) 88 } 89 return nil 90 } 91 92 func parseVolumeRebateTable(table *godog.Table) []RowWrapper { 93 return StrictParseTable(table, []string{ 94 "id", 95 "tiers", 96 "closing timestamp", 97 "window length", 98 }, []string{ 99 "closing delta", 100 }) 101 } 102 103 type volumeRebateRow struct { 104 row RowWrapper 105 } 106 107 func (r volumeRebateRow) id() string { 108 return r.row.MustStr("id") 109 } 110 111 func (r volumeRebateRow) tiers() string { 112 return r.row.MustStr("tiers") 113 } 114 115 func (r volumeRebateRow) closingTimestamp() int64 { 116 return r.row.MustI64("closing timestamp") 117 } 118 119 func (r volumeRebateRow) closingTS(now time.Time) time.Time { 120 if delta, ok := r.closingDelta(); ok { 121 return now.Add(delta) 122 } 123 ts := r.closingTimestamp() 124 if ts == 0 { 125 return time.Time{} 126 } 127 return time.Unix(ts, 0) 128 } 129 130 func (r volumeRebateRow) closingDelta() (time.Duration, bool) { 131 if r.row.HasColumn("closing delta") { 132 return r.row.MustDurationStr("closing delta"), true 133 } 134 return 0, false 135 } 136 137 func (r volumeRebateRow) windowLength() uint64 { 138 return r.row.MustU64("window length") 139 } 140 141 func PartyHasTheFollowingRebate(party, RebateFactor string, vde *volumerebate.Engine) error { 142 df := vde.VolumeRebateFactorForParty(types.PartyID(party)) 143 df2, _ := num.DecimalFromString(RebateFactor) 144 if !df.Equal(df2) { 145 return fmt.Errorf("%s has the Rebate of %s when we expected %s", party, df, df2) 146 } 147 return nil 148 } 149 150 func PartyHasTheFollowingMakerVolumeFraction(party, fraction string, vde *volumerebate.Engine) error { 151 tn := vde.MakerVolumeFractionForParty(types.PartyID(party)) 152 tn2, _ := num.DecimalFromString(fraction) 153 if !tn.Equal(tn2) { 154 return fmt.Errorf("%s has the maker volume fraction of %s when we expected %s", party, tn, tn2) 155 } 156 return nil 157 } 158 159 func AMMHasTheFollowingMakerVolumeFraction(exec Execution, vde *volumerebate.Engine, alias, value string) error { 160 id, ok := exec.GetAMMSubAccountID(alias) 161 if !ok { 162 return fmt.Errorf("unknown vAMM alias %s", alias) 163 } 164 // from this point, it's the same as for a normal party 165 return PartyHasTheFollowingMakerVolumeFraction(id, value, vde) 166 } 167 168 func AMMHasTheFollowingRebate(exec Execution, vde *volumerebate.Engine, alias, factor string) error { 169 id, ok := exec.GetAMMSubAccountID(alias) 170 if !ok { 171 return fmt.Errorf("unknown vAMM alias %s", alias) 172 } 173 return PartyHasTheFollowingRebate(id, factor, vde) 174 }