@@ -22,6 +22,7 @@ import (
22
22
"github.com/code-payments/code-server/pkg/code/common"
23
23
"github.com/code-payments/code-server/pkg/code/data/account"
24
24
"github.com/code-payments/code-server/pkg/code/data/action"
25
+ "github.com/code-payments/code-server/pkg/code/data/currency"
25
26
"github.com/code-payments/code-server/pkg/code/data/fulfillment"
26
27
"github.com/code-payments/code-server/pkg/code/data/intent"
27
28
"github.com/code-payments/code-server/pkg/code/data/nonce"
58
59
cachedAirdropStatus = cache .NewCache (10_000 )
59
60
)
60
61
62
+ type AirdropIntegration interface {
63
+ // GetWelcomeBonusAmount returns the amount that should be paid for the
64
+ // welcome bonus. Return 0 amount if the airdrop should not be sent.
65
+ GetWelcomeBonusAmount (ctx context.Context , owner * common.Account ) (float64 , currency_lib.Code , error )
66
+ }
67
+
68
+ type defaultAirdropIntegration struct {}
69
+
70
+ // NewDefaultAirdropIntegration retuns an AirdropIntegration that sends $1 USD
71
+ // to everyone
72
+ func NewDefaultAirdropIntegration () AirdropIntegration {
73
+ return & defaultAirdropIntegration {}
74
+ }
75
+
76
+ func (i * defaultAirdropIntegration ) GetWelcomeBonusAmount (ctx context.Context , owner * common.Account ) (float64 , currency_lib.Code , error ) {
77
+ return 1.0 , currency_lib .USD , nil
78
+ }
79
+
61
80
func (s * transactionServer ) Airdrop (ctx context.Context , req * transactionpb.AirdropRequest ) (* transactionpb.AirdropResponse , error ) {
62
81
log := s .log .WithFields (logrus.Fields {
63
82
"method" : "Airdrop" ,
@@ -159,9 +178,7 @@ func (s *transactionServer) Airdrop(ctx context.Context, req *transactionpb.Aird
159
178
}, nil
160
179
}
161
180
162
- // airdrop gives Kin airdrops denominated in Kin for performing certain
163
- // actions in the Code app. This funciton is idempotent with the given
164
- // intent ID.
181
+ // Note: this function is idempotent with the given intent ID.
165
182
func (s * transactionServer ) airdrop (ctx context.Context , intentId string , owner * common.Account , airdropType AirdropType ) (* intent.Record , error ) {
166
183
log := s .log .WithFields (logrus.Fields {
167
184
"method" : "airdrop" ,
@@ -170,15 +187,6 @@ func (s *transactionServer) airdrop(ctx context.Context, intentId string, owner
170
187
"airdrop_type" : airdropType .String (),
171
188
})
172
189
173
- var quarkAmount uint64
174
- switch airdropType {
175
- case AirdropTypeGetFirstCrypto :
176
- quarkAmount = common .ToCoreMintQuarks (1 ) // todo: configurable
177
- default :
178
- return nil , errors .New ("unhandled airdrop type" )
179
- }
180
- coreMintAmount := float64 (common .FromCoreMintQuarks (quarkAmount )) // todo: doesn't handle fractional amount
181
-
182
190
// Find the destination account, which will be the user's primary account
183
191
primaryAccountInfoRecord , err := s .data .GetLatestAccountInfoByOwnerAddressAndType (ctx , owner .PublicKey ().ToBase58 (), commonpb .AccountType_PRIMARY )
184
192
if err == account .ErrAccountInfoNotFound {
@@ -194,12 +202,52 @@ func (s *transactionServer) airdrop(ctx context.Context, intentId string, owner
194
202
return nil , err
195
203
}
196
204
197
- usdRateRecord , err := s .data .GetExchangeRate (ctx , currency_lib .USD , exchange_rate_util .GetLatestExchangeRateTime ())
205
+ var nativeAmount float64
206
+ var currencyCode currency_lib.Code
207
+ switch airdropType {
208
+ case AirdropTypeGetFirstCrypto :
209
+ nativeAmount , currencyCode , err = s .airdropIntegration .GetWelcomeBonusAmount (ctx , owner )
210
+ if err != nil {
211
+ log .WithError (err ).Warn ("failure getting welcome bonus amount from integration" )
212
+ return nil , err
213
+ }
214
+ if nativeAmount == 0 {
215
+ log .Trace ("integration did not allow welcome bonus" )
216
+ return nil , ErrInvalidAirdropTarget
217
+ }
218
+ default :
219
+ return nil , errors .New ("unhandled airdrop type" )
220
+ }
221
+
222
+ exchangeRateTime := exchange_rate_util .GetLatestExchangeRateTime ()
223
+
224
+ usdRateRecord , err := s .data .GetExchangeRate (ctx , currency_lib .USD , exchangeRateTime )
198
225
if err != nil {
199
226
log .WithError (err ).Warn ("failure getting usd rate" )
200
227
return nil , err
201
228
}
202
229
230
+ var otherRateRecord * currency.ExchangeRateRecord
231
+ switch currencyCode {
232
+ case currency_lib .USD :
233
+ otherRateRecord = usdRateRecord
234
+ case common .CoreMintSymbol :
235
+ otherRateRecord = & currency.ExchangeRateRecord {
236
+ Time : exchangeRateTime ,
237
+ Rate : 1.0 ,
238
+ Symbol : string (common .CoreMintSymbol ),
239
+ }
240
+ default :
241
+ otherRateRecord , err = s .data .GetExchangeRate (ctx , currencyCode , exchangeRateTime )
242
+ if err != nil {
243
+ log .WithError (err ).Warn ("failure getting other rate" )
244
+ return nil , err
245
+ }
246
+ }
247
+
248
+ coreMintAmount := nativeAmount / otherRateRecord .Rate
249
+ quarkAmount := uint64 (coreMintAmount * float64 (common .CoreMintQuarksPerUnit ))
250
+
203
251
var isAirdropperManuallyUnlocked bool
204
252
s .airdropperLock .Lock ()
205
253
defer func () {
@@ -233,7 +281,7 @@ func (s *transactionServer) airdrop(ctx context.Context, intentId string, owner
233
281
234
282
// Make the intent to transfer the funds. We accomplish this by having the
235
283
// sender setup and act like an internal Code account who always publicly
236
- // transfers the airdrop like a Code->Code withdrawal .
284
+ // transfers the airdrop like a Code->Code payment .
237
285
//
238
286
// todo: This is an interesting concept we could consider expanding further.
239
287
// Instead of constructing and validating everything manually, we could
@@ -267,9 +315,9 @@ func (s *transactionServer) airdrop(ctx context.Context, intentId string, owner
267
315
DestinationTokenAccount : destination .PublicKey ().ToBase58 (),
268
316
Quantity : quarkAmount ,
269
317
270
- ExchangeCurrency : common . CoreMintSymbol ,
271
- ExchangeRate : 1.0 ,
272
- NativeAmount : coreMintAmount ,
318
+ ExchangeCurrency : currencyCode ,
319
+ ExchangeRate : otherRateRecord . Rate ,
320
+ NativeAmount : nativeAmount ,
273
321
UsdMarketValue : usdRateRecord .Rate * coreMintAmount ,
274
322
275
323
IsWithdrawal : false ,
0 commit comments