Skip to content

Commit 4fd38ec

Browse files
committed
Add integration point to Airdrop to enable customizable welcome bonus amount
1 parent 7007cae commit 4fd38ec

File tree

2 files changed

+70
-17
lines changed

2 files changed

+70
-17
lines changed

pkg/code/server/transaction/airdrop.go

Lines changed: 65 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"github.com/code-payments/code-server/pkg/code/common"
2323
"github.com/code-payments/code-server/pkg/code/data/account"
2424
"github.com/code-payments/code-server/pkg/code/data/action"
25+
"github.com/code-payments/code-server/pkg/code/data/currency"
2526
"github.com/code-payments/code-server/pkg/code/data/fulfillment"
2627
"github.com/code-payments/code-server/pkg/code/data/intent"
2728
"github.com/code-payments/code-server/pkg/code/data/nonce"
@@ -58,6 +59,24 @@ var (
5859
cachedAirdropStatus = cache.NewCache(10_000)
5960
)
6061

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+
6180
func (s *transactionServer) Airdrop(ctx context.Context, req *transactionpb.AirdropRequest) (*transactionpb.AirdropResponse, error) {
6281
log := s.log.WithFields(logrus.Fields{
6382
"method": "Airdrop",
@@ -159,9 +178,7 @@ func (s *transactionServer) Airdrop(ctx context.Context, req *transactionpb.Aird
159178
}, nil
160179
}
161180

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.
165182
func (s *transactionServer) airdrop(ctx context.Context, intentId string, owner *common.Account, airdropType AirdropType) (*intent.Record, error) {
166183
log := s.log.WithFields(logrus.Fields{
167184
"method": "airdrop",
@@ -170,15 +187,6 @@ func (s *transactionServer) airdrop(ctx context.Context, intentId string, owner
170187
"airdrop_type": airdropType.String(),
171188
})
172189

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-
182190
// Find the destination account, which will be the user's primary account
183191
primaryAccountInfoRecord, err := s.data.GetLatestAccountInfoByOwnerAddressAndType(ctx, owner.PublicKey().ToBase58(), commonpb.AccountType_PRIMARY)
184192
if err == account.ErrAccountInfoNotFound {
@@ -194,12 +202,52 @@ func (s *transactionServer) airdrop(ctx context.Context, intentId string, owner
194202
return nil, err
195203
}
196204

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)
198225
if err != nil {
199226
log.WithError(err).Warn("failure getting usd rate")
200227
return nil, err
201228
}
202229

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+
203251
var isAirdropperManuallyUnlocked bool
204252
s.airdropperLock.Lock()
205253
defer func() {
@@ -233,7 +281,7 @@ func (s *transactionServer) airdrop(ctx context.Context, intentId string, owner
233281

234282
// Make the intent to transfer the funds. We accomplish this by having the
235283
// 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.
237285
//
238286
// todo: This is an interesting concept we could consider expanding further.
239287
// Instead of constructing and validating everything manually, we could
@@ -267,9 +315,9 @@ func (s *transactionServer) airdrop(ctx context.Context, intentId string, owner
267315
DestinationTokenAccount: destination.PublicKey().ToBase58(),
268316
Quantity: quarkAmount,
269317

270-
ExchangeCurrency: common.CoreMintSymbol,
271-
ExchangeRate: 1.0,
272-
NativeAmount: coreMintAmount,
318+
ExchangeCurrency: currencyCode,
319+
ExchangeRate: otherRateRecord.Rate,
320+
NativeAmount: nativeAmount,
273321
UsdMarketValue: usdRateRecord.Rate * coreMintAmount,
274322

275323
IsWithdrawal: false,

pkg/code/server/transaction/server.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ type transactionServer struct {
2626

2727
auth *auth_util.RPCSignatureVerifier
2828

29+
airdropIntegration AirdropIntegration
30+
2931
jupiterClient *jupiter.Client
3032

3133
messagingClient messaging.InternalMessageClient
@@ -50,6 +52,7 @@ type transactionServer struct {
5052

5153
func NewTransactionServer(
5254
data code_data.Provider,
55+
airdropIntegration AirdropIntegration,
5356
jupiterClient *jupiter.Client,
5457
messagingClient messaging.InternalMessageClient,
5558
antispamGuard *antispam.Guard,
@@ -69,6 +72,8 @@ func NewTransactionServer(
6972

7073
auth: auth_util.NewRPCSignatureVerifier(data),
7174

75+
airdropIntegration: airdropIntegration,
76+
7277
jupiterClient: jupiterClient,
7378

7479
messagingClient: messagingClient,

0 commit comments

Comments
 (0)