@gala-chain/launchpad 1.0.15 → 1.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/lib/package.json +1 -1
  2. package/lib/src/api/types/LaunchpadDtos.d.ts +2 -1
  3. package/lib/src/api/types/LaunchpadDtos.d.ts.map +1 -1
  4. package/lib/src/api/types/LaunchpadDtos.js +11 -1
  5. package/lib/src/api/types/LaunchpadDtos.js.map +1 -1
  6. package/lib/src/api/types/LaunchpadSale.d.ts +6 -1
  7. package/lib/src/api/types/LaunchpadSale.d.ts.map +1 -1
  8. package/lib/src/api/types/LaunchpadSale.js +21 -6
  9. package/lib/src/api/types/LaunchpadSale.js.map +1 -1
  10. package/lib/src/chaincode/launchpad/buyWithNative.d.ts.map +1 -1
  11. package/lib/src/chaincode/launchpad/buyWithNative.js +4 -1
  12. package/lib/src/chaincode/launchpad/buyWithNative.js.map +1 -1
  13. package/lib/src/chaincode/launchpad/callMemeTokenIn.d.ts.map +1 -1
  14. package/lib/src/chaincode/launchpad/callMemeTokenIn.js +6 -4
  15. package/lib/src/chaincode/launchpad/callMemeTokenIn.js.map +1 -1
  16. package/lib/src/chaincode/launchpad/callMemeTokenOut.d.ts.map +1 -1
  17. package/lib/src/chaincode/launchpad/callMemeTokenOut.js +19 -8
  18. package/lib/src/chaincode/launchpad/callMemeTokenOut.js.map +1 -1
  19. package/lib/src/chaincode/launchpad/callNativeTokenIn.d.ts.map +1 -1
  20. package/lib/src/chaincode/launchpad/callNativeTokenIn.js +13 -6
  21. package/lib/src/chaincode/launchpad/callNativeTokenIn.js.map +1 -1
  22. package/lib/src/chaincode/launchpad/callNativeTokenOut.d.ts.map +1 -1
  23. package/lib/src/chaincode/launchpad/callNativeTokenOut.js +6 -4
  24. package/lib/src/chaincode/launchpad/callNativeTokenOut.js.map +1 -1
  25. package/lib/src/chaincode/launchpad/createSale.d.ts.map +1 -1
  26. package/lib/src/chaincode/launchpad/createSale.js +8 -6
  27. package/lib/src/chaincode/launchpad/createSale.js.map +1 -1
  28. package/lib/src/chaincode/launchpad/finaliseSale.js +2 -2
  29. package/lib/src/chaincode/launchpad/finaliseSale.js.map +1 -1
  30. package/lib/src/chaincode/utils/launchpadSaleUtils.d.ts +1 -1
  31. package/lib/src/chaincode/utils/launchpadSaleUtils.d.ts.map +1 -1
  32. package/lib/src/chaincode/utils/launchpadSaleUtils.js +5 -2
  33. package/lib/src/chaincode/utils/launchpadSaleUtils.js.map +1 -1
  34. package/lib/tsconfig.tsbuildinfo +1 -1
  35. package/package.json +1 -1
  36. package/src/api/types/LaunchpadDtos.ts +12 -1
  37. package/src/api/types/LaunchpadSale.ts +34 -6
  38. package/src/chaincode/launchpad/buyExactToken.spec.ts +233 -0
  39. package/src/chaincode/launchpad/buyWithNative.spec.ts +61 -1
  40. package/src/chaincode/launchpad/buyWithNative.ts +5 -1
  41. package/src/chaincode/launchpad/callMemeTokenIn.ts +11 -4
  42. package/src/chaincode/launchpad/callMemeTokenOut.ts +24 -8
  43. package/src/chaincode/launchpad/callNativeTokenIn.ts +18 -7
  44. package/src/chaincode/launchpad/callNativeTokenOut.ts +10 -4
  45. package/src/chaincode/launchpad/createSale.ts +10 -5
  46. package/src/chaincode/launchpad/finaliseSale.ts +2 -2
  47. package/src/chaincode/launchpad/sellExactToken.spec.ts +139 -2
  48. package/src/chaincode/launchpad/sellWithNative.spec.ts +157 -1
  49. package/src/chaincode/utils/launchpadSaleUtils.ts +6 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gala-chain/launchpad",
3
- "version": "1.0.15",
3
+ "version": "1.0.16",
4
4
  "description": "GalaChain Launchpad Chaincode",
5
5
  "main": "lib/src/index.js",
6
6
  "types": "lib/src/index.d.ts",
@@ -125,6 +125,12 @@ export class CreateTokenSaleDTO extends SubmitCallDTO {
125
125
  @Type(() => ReverseBondingCurveConfigurationDto)
126
126
  public reverseBondingCurveConfiguration?: ReverseBondingCurveConfigurationDto;
127
127
 
128
+ @IsOptional()
129
+ @IsNumber()
130
+ @Min(100)
131
+ @Max(100)
132
+ public adjustableSupplyMultiplier?: number;
133
+
128
134
  constructor(
129
135
  tokenName: string,
130
136
  tokenSymbol: string,
@@ -134,7 +140,8 @@ export class CreateTokenSaleDTO extends SubmitCallDTO {
134
140
  tokenCollection: string,
135
141
  tokenCategory: string,
136
142
  reverseBondingCurveConfiguration?: ReverseBondingCurveConfigurationDto,
137
- saleStartTime?: number
143
+ saleStartTime?: number,
144
+ adjustableSupplyMultiplier?: number
138
145
  ) {
139
146
  super();
140
147
  this.tokenName = tokenName;
@@ -149,6 +156,10 @@ export class CreateTokenSaleDTO extends SubmitCallDTO {
149
156
  if (saleStartTime !== undefined) {
150
157
  this.saleStartTime = saleStartTime;
151
158
  }
159
+
160
+ if (adjustableSupplyMultiplier !== undefined) {
161
+ this.adjustableSupplyMultiplier = adjustableSupplyMultiplier;
162
+ }
152
163
  }
153
164
  }
154
165
 
@@ -23,7 +23,16 @@ import {
23
23
  } from "@gala-chain/api";
24
24
  import BigNumber from "bignumber.js";
25
25
  import { Exclude, Type } from "class-transformer";
26
- import { IsInt, IsNotEmpty, IsOptional, IsPositive, IsString, ValidateNested } from "class-validator";
26
+ import {
27
+ IsInt,
28
+ IsNotEmpty,
29
+ IsNumber,
30
+ IsOptional,
31
+ IsPositive,
32
+ IsString,
33
+ Min,
34
+ ValidateNested
35
+ } from "class-validator";
27
36
  import { JSONSchema } from "class-validator-jsonschema";
28
37
 
29
38
  import { ReverseBondingCurveConfigurationChainObject } from "./LaunchpadDtos";
@@ -93,6 +102,15 @@ export class LaunchpadSale extends ChainObject {
93
102
  @Type(() => ReverseBondingCurveConfigurationChainObject)
94
103
  public reverseBondingCurveConfiguration?: ReverseBondingCurveConfigurationChainObject;
95
104
 
105
+ /**
106
+ * Optional multiplier used to scale token output (both in bonding curve calculations and initial
107
+ * minting) while preserving the same economics and curve shape.
108
+ */
109
+ @IsOptional()
110
+ @IsNumber()
111
+ @Min(0)
112
+ public adjustableSupplyMultiplier?: number;
113
+
96
114
  @JSONSchema({
97
115
  description:
98
116
  "The market cap has been calculated using the bonding curve equations to approximate a specific final price."
@@ -121,7 +139,8 @@ export class LaunchpadSale extends ChainObject {
121
139
  sellingToken: TokenInstanceKey,
122
140
  reverseBondingCurveConfiguration: ReverseBondingCurveConfigurationChainObject | undefined,
123
141
  saleOwner: UserAlias,
124
- saleStartTime?: number | undefined
142
+ saleStartTime?: number | undefined,
143
+ adjustableSupplyMultiplier?: number
125
144
  ) {
126
145
  super();
127
146
 
@@ -129,7 +148,6 @@ export class LaunchpadSale extends ChainObject {
129
148
  this.saleOwner = saleOwner;
130
149
 
131
150
  this.sellingToken = sellingToken;
132
- this.sellingTokenQuantity = "1e+7";
133
151
 
134
152
  if (saleStartTime) {
135
153
  this.saleStartTime = saleStartTime;
@@ -141,9 +159,19 @@ export class LaunchpadSale extends ChainObject {
141
159
  this.saleStatus = SaleStatus.ONGOING;
142
160
  }
143
161
 
144
- this.basePrice = new BigNumber(LaunchpadSale.BASE_PRICE);
145
- this.exponentFactor = new BigNumber("1166069000000");
146
- this.maxSupply = new BigNumber("1e+7");
162
+ if (adjustableSupplyMultiplier !== undefined) {
163
+ this.adjustableSupplyMultiplier = adjustableSupplyMultiplier;
164
+ this.basePrice = new BigNumber(LaunchpadSale.BASE_PRICE).dividedBy(adjustableSupplyMultiplier);
165
+ this.exponentFactor = new BigNumber("1166069000000");
166
+ this.maxSupply = new BigNumber("1e+7").times(adjustableSupplyMultiplier);
167
+ this.sellingTokenQuantity = new BigNumber("1e+7").times(adjustableSupplyMultiplier).toString();
168
+ } else {
169
+ this.basePrice = new BigNumber(LaunchpadSale.BASE_PRICE);
170
+ this.exponentFactor = new BigNumber("1166069000000");
171
+ this.maxSupply = new BigNumber("1e+7");
172
+ this.sellingTokenQuantity = "1e+7";
173
+ }
174
+
147
175
  this.euler = new BigNumber("2.7182818284590452353602874713527");
148
176
 
149
177
  const nativeTokenInstance = new TokenInstanceKey();
@@ -32,6 +32,7 @@ import {
32
32
  LaunchpadFeeConfig,
33
33
  LaunchpadSale,
34
34
  NativeTokenQuantityDto,
35
+ SaleStatus,
35
36
  TradeResDto
36
37
  } from "../../api/types";
37
38
  import { LaunchpadContract } from "../LaunchpadContract";
@@ -304,4 +305,236 @@ describe("buyWithNative", () => {
304
305
  )
305
306
  );
306
307
  });
308
+
309
+ test("Full sale purchase yields expected totals for 10 Million token sale", async () => {
310
+ // Given
311
+ salelaunchpadGalaBalance = plainToInstance(TokenBalance, {
312
+ ...launchpadgala.tokenBalance(),
313
+ owner: vaultAddress,
314
+ quantity: new BigNumber("0")
315
+ });
316
+
317
+ saleCurrencyBalance = plainToInstance(TokenBalance, {
318
+ ...currency.tokenBalance(),
319
+ owner: vaultAddress,
320
+ quantity: new BigNumber("2e+7")
321
+ });
322
+
323
+ userlaunchpadGalaBalance = plainToInstance(TokenBalance, {
324
+ ...launchpadgala.tokenBalance(),
325
+ owner: users.testUser1.identityKey,
326
+ quantity: new BigNumber("100000000")
327
+ });
328
+
329
+ userCurrencyBalance = plainToInstance(TokenBalance, {
330
+ ...currency.tokenBalance(),
331
+ owner: users.testUser1.identityKey,
332
+ quantity: new BigNumber("0")
333
+ });
334
+
335
+ const launchpadConfig = new LaunchpadFeeConfig(users.testUser2.identityKey, Number("0.001"), [
336
+ users.testUser2.identityKey
337
+ ]);
338
+
339
+ const userStartingGalaQuantity = userlaunchpadGalaBalance.getQuantityTotal();
340
+
341
+ const { ctx, contract, getWrites } = fixture(LaunchpadContract)
342
+ .registeredUsers(users.testUser1)
343
+ .savedState(
344
+ currencyClass,
345
+ currencyInstance,
346
+ launchpadConfig,
347
+ launchpadGalaClass,
348
+ launchpadGalaInstance,
349
+ sale,
350
+ salelaunchpadGalaBalance,
351
+ saleCurrencyBalance,
352
+ userlaunchpadGalaBalance,
353
+ userCurrencyBalance
354
+ );
355
+
356
+ // When
357
+ for (let i = 0; i < 50; i++) {
358
+ const dto = new ExactTokenQuantityDto(vaultAddress, new BigNumber("200000"));
359
+
360
+ dto.uniqueKey = randomUniqueKey();
361
+ dto.sign(users.testUser1.privateKey);
362
+
363
+ const buyTokenRes = await contract.BuyExactToken(ctx, dto);
364
+
365
+ expect(buyTokenRes).toEqual(transactionSuccess());
366
+ }
367
+
368
+ // Then
369
+ const saleKey = sale.getCompositeKey();
370
+ const userGalaBalanceKey = userlaunchpadGalaBalance.getCompositeKey();
371
+ const userMemeBalanceKey = userCurrencyBalance.getCompositeKey();
372
+
373
+ const writes = getWrites();
374
+
375
+ const finalSaleData = JSON.parse(writes[saleKey]);
376
+ const finalUserGalaBalanceData = JSON.parse(writes[userGalaBalanceKey]);
377
+ const finalUserMemeBalanceData = JSON.parse(writes[userMemeBalanceKey]);
378
+
379
+ expect(finalSaleData).toEqual(
380
+ expect.objectContaining({
381
+ saleStatus: SaleStatus.END
382
+ })
383
+ );
384
+
385
+ const finalUserGalaQuantity = new BigNumber(finalUserGalaBalanceData.quantity);
386
+ const finalUserMemeQuantity = new BigNumber(finalUserMemeBalanceData.quantity);
387
+
388
+ // user bought full sale quantity of ten million
389
+ expect(finalUserMemeQuantity).toEqual(new BigNumber("10000000"));
390
+ expect(userStartingGalaQuantity.minus(finalUserGalaQuantity)).toEqual(new BigNumber("1560577.53780865"));
391
+ });
392
+
393
+ test("Adjustable supply: single transaction", async () => {
394
+ // Given
395
+ const multiplier = 100;
396
+
397
+ sale = new LaunchpadSale(
398
+ vaultAddress,
399
+ currencyInstance.instanceKeyObj(),
400
+ undefined,
401
+ users.testUser1.identityKey,
402
+ undefined,
403
+ multiplier
404
+ );
405
+
406
+ const { ctx, contract } = fixture(LaunchpadContract)
407
+ .registeredUsers(users.testUser1)
408
+ .savedState(
409
+ currencyClass,
410
+ currencyInstance,
411
+ launchpadGalaClass,
412
+ launchpadGalaInstance,
413
+ sale,
414
+ salelaunchpadGalaBalance,
415
+ saleCurrencyBalance,
416
+ userlaunchpadGalaBalance,
417
+ userCurrencyBalance
418
+ );
419
+
420
+ const dto = new ExactTokenQuantityDto(vaultAddress, new BigNumber("500").times(multiplier));
421
+
422
+ dto.uniqueKey = randomUniqueKey();
423
+ dto.sign(users.testUser1.privateKey);
424
+
425
+ const expectedResponse = plainToInstance(TradeResDto, {
426
+ inputQuantity: "0.00825575",
427
+ totalFees: "0",
428
+ totalTokenSold: new BigNumber("500").times(multiplier).toString(),
429
+ outputQuantity: new BigNumber("500").times(multiplier).toString(),
430
+ tokenName: "AUTOMATEDTESTCOIN",
431
+ tradeType: "Buy",
432
+ uniqueKey: dto.uniqueKey,
433
+ vaultAddress: "service|GALA$Unit$none$none$launchpad",
434
+ userAddress: "client|testUser1",
435
+ isFinalized: false,
436
+ functionName: "BuyExactToken"
437
+ });
438
+
439
+ // When
440
+ const buyTokenRes = await contract.BuyExactToken(ctx, dto);
441
+
442
+ // Then
443
+ expect(buyTokenRes).toEqual(transactionSuccess(expectedResponse));
444
+ });
445
+
446
+ test("Adjustable supply: Full sale purchase yields expected totals for 1 billion token sale", async () => {
447
+ // Given
448
+ const multiplier = 100;
449
+
450
+ sale = new LaunchpadSale(
451
+ vaultAddress,
452
+ currencyInstance.instanceKeyObj(),
453
+ undefined,
454
+ users.testUser1.identityKey,
455
+ undefined,
456
+ multiplier
457
+ );
458
+
459
+ salelaunchpadGalaBalance = plainToInstance(TokenBalance, {
460
+ ...launchpadgala.tokenBalance(),
461
+ owner: vaultAddress,
462
+ quantity: new BigNumber("0")
463
+ });
464
+
465
+ saleCurrencyBalance = plainToInstance(TokenBalance, {
466
+ ...currency.tokenBalance(),
467
+ owner: vaultAddress,
468
+ quantity: new BigNumber("2e+7").times(multiplier)
469
+ });
470
+
471
+ userlaunchpadGalaBalance = plainToInstance(TokenBalance, {
472
+ ...launchpadgala.tokenBalance(),
473
+ owner: users.testUser1.identityKey,
474
+ quantity: new BigNumber("100000000")
475
+ });
476
+
477
+ userCurrencyBalance = plainToInstance(TokenBalance, {
478
+ ...currency.tokenBalance(),
479
+ owner: users.testUser1.identityKey,
480
+ quantity: new BigNumber("0")
481
+ });
482
+
483
+ const launchpadConfig = new LaunchpadFeeConfig(users.testUser2.identityKey, Number("0.001"), [
484
+ users.testUser2.identityKey
485
+ ]);
486
+
487
+ const userStartingGalaQuantity = userlaunchpadGalaBalance.getQuantityTotal();
488
+
489
+ const { ctx, contract, getWrites } = fixture(LaunchpadContract)
490
+ .registeredUsers(users.testUser1)
491
+ .savedState(
492
+ currencyClass,
493
+ currencyInstance,
494
+ launchpadConfig,
495
+ launchpadGalaClass,
496
+ launchpadGalaInstance,
497
+ sale,
498
+ salelaunchpadGalaBalance,
499
+ saleCurrencyBalance,
500
+ userlaunchpadGalaBalance,
501
+ userCurrencyBalance
502
+ );
503
+
504
+ // When
505
+ for (let i = 0; i < 50; i++) {
506
+ const dto = new ExactTokenQuantityDto(vaultAddress, new BigNumber("200000").times(multiplier));
507
+
508
+ dto.uniqueKey = randomUniqueKey();
509
+ dto.sign(users.testUser1.privateKey);
510
+
511
+ const buyTokenRes = await contract.BuyExactToken(ctx, dto);
512
+
513
+ expect(buyTokenRes).toEqual(transactionSuccess());
514
+ }
515
+
516
+ // Then
517
+ const saleKey = sale.getCompositeKey();
518
+ const userGalaBalanceKey = userlaunchpadGalaBalance.getCompositeKey();
519
+ const userMemeBalanceKey = userCurrencyBalance.getCompositeKey();
520
+
521
+ const writes = getWrites();
522
+
523
+ const finalSaleData = JSON.parse(writes[saleKey]);
524
+ const finalUserGalaBalanceData = JSON.parse(writes[userGalaBalanceKey]);
525
+ const finalUserMemeBalanceData = JSON.parse(writes[userMemeBalanceKey]);
526
+
527
+ expect(finalSaleData).toEqual(
528
+ expect.objectContaining({
529
+ saleStatus: SaleStatus.END
530
+ })
531
+ );
532
+
533
+ const finalUserGalaQuantity = new BigNumber(finalUserGalaBalanceData.quantity);
534
+ const finalUserMemeQuantity = new BigNumber(finalUserMemeBalanceData.quantity);
535
+
536
+ // user bought full sale quantity of ten million
537
+ expect(finalUserMemeQuantity).toEqual(new BigNumber("10000000").times(multiplier));
538
+ expect(userStartingGalaQuantity.minus(finalUserGalaQuantity)).toEqual(new BigNumber("1560577.53780865"));
539
+ });
307
540
  });
@@ -347,8 +347,68 @@ describe("buyWithNative", () => {
347
347
  expect(difference.isLessThanOrEqualTo(tolerance)).toBe(true);
348
348
  }
349
349
  });
350
+
351
+ test("Adjustable supply: Single transaction", async () => {
352
+ //Given
353
+ const multiplier = 100;
354
+
355
+ sale = new LaunchpadSale(
356
+ vaultAddress,
357
+ currencyInstance.instanceKeyObj(),
358
+ undefined,
359
+ users.testUser1.identityKey,
360
+ undefined,
361
+ multiplier
362
+ );
363
+
364
+ saleCurrencyBalance = plainToInstance(TokenBalance, {
365
+ ...currency.tokenBalance(),
366
+ owner: vaultAddress,
367
+ quantity: new BigNumber("2e+7").times(multiplier)
368
+ });
369
+
370
+ const { ctx, contract } = fixture(LaunchpadContract)
371
+ .registeredUsers(users.testUser1)
372
+ .savedState(
373
+ currencyClass,
374
+ currencyInstance,
375
+ launchpadGalaClass,
376
+ launchpadGalaInstance,
377
+ sale,
378
+ salelaunchpadGalaBalance,
379
+ saleCurrencyBalance,
380
+ userlaunchpadGalaBalance,
381
+ userCurrencyBalance
382
+ );
383
+
384
+ const dto = new NativeTokenQuantityDto(vaultAddress, new BigNumber("150"));
385
+
386
+ dto.uniqueKey = randomUniqueKey();
387
+ dto.sign(users.testUser1.privateKey);
388
+
389
+ const expectedOutput = new BigNumber("2101667.8890651635").times(multiplier).toString();
390
+ const expectedResponse = plainToInstance(TradeResDto, {
391
+ inputQuantity: "150",
392
+ totalFees: "0",
393
+ totalTokenSold: expectedOutput,
394
+ outputQuantity: expectedOutput,
395
+ tokenName: "AUTOMATEDTESTCOIN",
396
+ tradeType: "Buy",
397
+ vaultAddress: "service|GALA$Unit$none$none$launchpad",
398
+ userAddress: "client|testUser1",
399
+ isFinalized: false,
400
+ functionName: "BuyWithNative",
401
+ uniqueKey: dto.uniqueKey
402
+ });
403
+
404
+ //When
405
+ const buyTokenRes = await contract.BuyWithNative(ctx, dto);
406
+
407
+ //Then
408
+ expect(buyTokenRes).toEqual(transactionSuccess(expectedResponse));
409
+ });
350
410
  });
351
- function roundToDecimal(value, decimals) {
411
+ function roundToDecimal(value: number, decimals: number) {
352
412
  const factor = Math.pow(10, decimals);
353
413
  return Math.round(value * factor) / factor;
354
414
  }
@@ -59,10 +59,14 @@ export async function buyWithNative(
59
59
  const memeToken = sale.fetchSellingTokenInstanceKey();
60
60
 
61
61
  // If native tokens required exceeds the market cap, the sale can be finalized
62
+ const supplyCap =
63
+ sale.adjustableSupplyMultiplier !== undefined
64
+ ? new BigNumber(LaunchpadSale.MARKET_CAP).times(sale.adjustableSupplyMultiplier).toString()
65
+ : LaunchpadSale.MARKET_CAP;
62
66
  if (
63
67
  buyTokenDTO.nativeTokenQuantity
64
68
  .plus(new BigNumber(sale.nativeTokenQuantity))
65
- .isGreaterThanOrEqualTo(new BigNumber(LaunchpadSale.MARKET_CAP))
69
+ .isGreaterThanOrEqualTo(new BigNumber(supplyCap))
66
70
  ) {
67
71
  isSaleFinalized = true;
68
72
  }
@@ -30,15 +30,21 @@ function calculateMemeTokensRequired(
30
30
  sale: LaunchpadSale,
31
31
  requestedNativeTokenQuantity: BigNumber,
32
32
  nativeTokenDecimals: number,
33
- sellingTokenDecimals: number
33
+ sellingTokenDecimals: number,
34
+ adjustableSupplyMultiplier?: number
34
35
  ): [string, string] {
35
36
  const totalTokensSold = new Decimal(sale.fetchTokensSold()); // current tokens sold / x
36
37
  let nativeTokens = new Decimal(requestedNativeTokenQuantity.toString()).toDecimalPlaces(
37
38
  nativeTokenDecimals,
38
39
  Decimal.ROUND_DOWN
39
40
  );
40
- const basePrice = new Decimal(LaunchpadSale.BASE_PRICE); // base price / a
41
- const { exponentFactor, euler, decimals } = getBondingConstants();
41
+
42
+ const basePrice =
43
+ adjustableSupplyMultiplier && adjustableSupplyMultiplier > 0
44
+ ? new Decimal(LaunchpadSale.BASE_PRICE).dividedBy(adjustableSupplyMultiplier)
45
+ : new Decimal(LaunchpadSale.BASE_PRICE);
46
+
47
+ const { exponentFactor, euler, decimals } = getBondingConstants(adjustableSupplyMultiplier);
42
48
 
43
49
  const nativeTokenInVault = new Decimal(sale.nativeTokenQuantity);
44
50
  if (nativeTokens.greaterThan(nativeTokenInVault)) {
@@ -90,7 +96,8 @@ export async function callMemeTokenIn(
90
96
  sale,
91
97
  sellTokenDTO.nativeTokenQuantity,
92
98
  nativeTokenDecimals,
93
- sellingTokenDecimals
99
+ sellingTokenDecimals,
100
+ sale.adjustableSupplyMultiplier
94
101
  );
95
102
 
96
103
  return {
@@ -29,15 +29,26 @@ function calculateTokensPurchasable(
29
29
  nativeTokens: Decimal,
30
30
  totalTokensSold: Decimal,
31
31
  nativeTokenDecimals: number,
32
- sellingTokenDecimals: number
32
+ sellingTokenDecimals: number,
33
+ adjustableSupplyMultiplier?: number
33
34
  ): [string, string] {
34
- const basePrice = new Decimal(LaunchpadSale.BASE_PRICE);
35
- const { exponentFactor, euler, decimals } = getBondingConstants();
35
+ const basePrice =
36
+ adjustableSupplyMultiplier && adjustableSupplyMultiplier > 0
37
+ ? new Decimal(LaunchpadSale.BASE_PRICE).dividedBy(adjustableSupplyMultiplier)
38
+ : new Decimal(LaunchpadSale.BASE_PRICE);
39
+
40
+ const { exponentFactor, euler, decimals } = getBondingConstants(adjustableSupplyMultiplier);
36
41
 
37
42
  // Round native tokens, then calculate tokens based on that rounded amount
38
43
  const roundedNativeTokens = nativeTokens.toDecimalPlaces(nativeTokenDecimals, Decimal.ROUND_UP);
39
44
 
40
- // Calculate tokens purchasable: newTokens = (decimals / exponentFactor) * ln((nativeTokens * exponentFactor / basePrice) + e^(exponentFactor * totalTokensSold / decimals)) - totalTokensSold
45
+ // Calculate tokens purchasable:
46
+ // newTokens = (decimals / exponentFactor) *
47
+ // ln(
48
+ // (nativeTokens * exponentFactor / basePrice) +
49
+ // e^(exponentFactor * totalTokensSold / decimals)
50
+ // ) -
51
+ // totalTokensSold
41
52
  // Where:
42
53
  // constant = nativeTokens * exponentFactor / basePrice
43
54
  // exponent1 = exponentFactor * totalTokensSold / decimals
@@ -55,9 +66,13 @@ function calculateTokensPurchasable(
55
66
  const result = lnEthScaledBase.minus(totalTokensSold);
56
67
  let roundedResult = result.toDecimalPlaces(sellingTokenDecimals, Decimal.ROUND_DOWN);
57
68
 
58
- // Cap total supply to 10 million
59
- if (roundedResult.add(totalTokensSold).greaterThan(new Decimal("1e+7"))) {
60
- roundedResult = new Decimal("1e+7").minus(new Decimal(totalTokensSold));
69
+ // Cap total supply
70
+ const supplyCap = adjustableSupplyMultiplier
71
+ ? new BigNumber(1e7).times(adjustableSupplyMultiplier).toString()
72
+ : "1e+7";
73
+
74
+ if (roundedResult.add(totalTokensSold).greaterThan(new Decimal(supplyCap))) {
75
+ roundedResult = new Decimal(supplyCap).minus(new Decimal(totalTokensSold));
61
76
  }
62
77
 
63
78
  return [roundedNativeTokens.toFixed(), roundedResult.toFixed()];
@@ -119,7 +134,8 @@ export async function callMemeTokenOut(
119
134
  nativeTokens,
120
135
  totalTokensSold,
121
136
  nativeTokenDecimals,
122
- sellingTokenDecimals
137
+ sellingTokenDecimals,
138
+ sale?.adjustableSupplyMultiplier
123
139
  );
124
140
 
125
141
  // Fetch fee configuration and return result
@@ -29,15 +29,25 @@ function calculateNativeTokensRequired(
29
29
  tokensToBuy: Decimal,
30
30
  totalTokensSold: Decimal,
31
31
  sellingTokenDecimals: number,
32
- nativeTokenDecimals: number
32
+ nativeTokenDecimals: number,
33
+ adjustableSupplyMultiplier?: number
33
34
  ): [string, string] {
34
- const basePrice = new Decimal(LaunchpadSale.BASE_PRICE);
35
- const { exponentFactor, euler, decimals } = getBondingConstants();
35
+ const basePrice =
36
+ adjustableSupplyMultiplier && adjustableSupplyMultiplier > 0
37
+ ? new Decimal(LaunchpadSale.BASE_PRICE).dividedBy(adjustableSupplyMultiplier)
38
+ : new Decimal(LaunchpadSale.BASE_PRICE);
39
+
40
+ const { exponentFactor, euler, decimals } = getBondingConstants(adjustableSupplyMultiplier);
36
41
 
37
42
  // Round tokens first, then calculate native tokens based on that rounded amount
38
43
  const roundedTokensToBuy = tokensToBuy.toDecimalPlaces(sellingTokenDecimals, Decimal.ROUND_DOWN);
39
44
 
40
- // Calculate native tokens required: price = (basePrice / exponentFactor) * (e^(exponentFactor * (totalTokensSold + tokensToBuy) / decimals) - e^(exponentFactor * totalTokensSold / decimals))
45
+ // Calculate native tokens required:
46
+ // price = (basePrice / exponentFactor) *
47
+ // (
48
+ // e^(exponentFactor * (totalTokensSold + tokensToBuy) / decimals) -
49
+ // e^(exponentFactor * totalTokensSold / decimals)
50
+ // )
41
51
  // Where:
42
52
  // exponent1 = exponentFactor * (totalTokensSold + tokensToBuy) / decimals
43
53
  // exponent2 = exponentFactor * totalTokensSold / decimals
@@ -81,13 +91,13 @@ export async function callNativeTokenIn(
81
91
  ctx: GalaChainContext,
82
92
  buyTokenDTO: ExactTokenQuantityDto
83
93
  ): Promise<TradeCalculationResDto> {
84
- const sale = await fetchAndValidateSale(ctx, buyTokenDTO.vaultAddress);
94
+ const sale: LaunchpadSale = await fetchAndValidateSale(ctx, buyTokenDTO.vaultAddress);
85
95
  const totalTokensSold = new Decimal(sale.fetchTokensSold());
86
96
 
87
97
  let tokensToBuy = new Decimal(buyTokenDTO.tokenQuantity.toString());
88
98
 
89
99
  // Adjust tokensToBuy if user is trying to buy more tokens than the total supply
90
- if (tokensToBuy.add(totalTokensSold).greaterThan(new Decimal("1e+7"))) {
100
+ if (tokensToBuy.add(totalTokensSold).greaterThan(new Decimal(sale.maxSupply.toString()))) {
91
101
  tokensToBuy = new Decimal(sale.sellingTokenQuantity);
92
102
  }
93
103
 
@@ -99,7 +109,8 @@ export async function callNativeTokenIn(
99
109
  tokensToBuy,
100
110
  totalTokensSold,
101
111
  sellingTokenDecimals,
102
- nativeTokenDecimals
112
+ nativeTokenDecimals,
113
+ sale.adjustableSupplyMultiplier
103
114
  );
104
115
 
105
116
  const launchpadFeeAddressConfiguration = await fetchLaunchpadFeeAddress(ctx);
@@ -29,13 +29,18 @@ function calculateNativeTokensReceived(
29
29
  sale: LaunchpadSale,
30
30
  tokensToSellBn: BigNumber,
31
31
  sellingTokenDecimals: number,
32
- nativeTokenDecimals: number
32
+ nativeTokenDecimals: number,
33
+ adjustableSupplyMultiplier?: number
33
34
  ): [string, string] {
34
35
  const totalTokensSold = new Decimal(sale.fetchTokensSold());
35
36
 
36
37
  let tokensToSell = new Decimal(tokensToSellBn.toString());
37
- const basePrice = new Decimal(LaunchpadSale.BASE_PRICE);
38
- const { exponentFactor, euler, decimals } = getBondingConstants();
38
+ const basePrice =
39
+ adjustableSupplyMultiplier && adjustableSupplyMultiplier > 0
40
+ ? new Decimal(LaunchpadSale.BASE_PRICE).dividedBy(adjustableSupplyMultiplier)
41
+ : new Decimal(LaunchpadSale.BASE_PRICE);
42
+
43
+ const { exponentFactor, euler, decimals } = getBondingConstants(adjustableSupplyMultiplier);
39
44
 
40
45
  let newTotalTokensSold = totalTokensSold.minus(tokensToSell);
41
46
 
@@ -88,7 +93,8 @@ export async function callNativeTokenOut(
88
93
  sale,
89
94
  sellTokenDTO.tokenQuantity,
90
95
  sellingTokenDecimals,
91
- nativeTokenDecimals
96
+ nativeTokenDecimals,
97
+ sale.adjustableSupplyMultiplier
92
98
  );
93
99
  const launchpadFeeAddressConfiguration = await fetchLaunchpadFeeAddress(ctx);
94
100
 
@@ -86,6 +86,8 @@ export async function createSale(
86
86
  throw new ConflictError("This token and a sale associated with it already exists");
87
87
  }
88
88
 
89
+ const supplyCapMultiplier = launchpadDetails.adjustableSupplyMultiplier ?? 1;
90
+
89
91
  // Call createTokenClass
90
92
  await createTokenClass(ctx, {
91
93
  network: "GC",
@@ -96,8 +98,8 @@ export async function createSale(
96
98
  symbol: launchpadDetails.tokenSymbol,
97
99
  description: launchpadDetails.tokenDescription,
98
100
  image: launchpadDetails.tokenImage,
99
- maxSupply: new BigNumber("2e+7"),
100
- maxCapacity: new BigNumber("2e+7"),
101
+ maxSupply: new BigNumber("2e+7").times(supplyCapMultiplier),
102
+ maxCapacity: new BigNumber("2e+7").times(supplyCapMultiplier),
101
103
  totalMintAllowance: new BigNumber(0),
102
104
  totalSupply: new BigNumber(0),
103
105
  totalBurned: new BigNumber(0),
@@ -109,12 +111,13 @@ export async function createSale(
109
111
  tokenClassKey: tokenInstanceKey.getTokenClassKey(),
110
112
  tokenInstance: new BigNumber(0),
111
113
  owner: vaultAddress,
112
- quantity: new BigNumber("2e+7")
114
+ quantity: new BigNumber("2e+7").times(supplyCapMultiplier)
113
115
  });
114
116
 
115
- //Update token class to remove the calling user as an authority in the token class
117
+ // Update token class to remove the calling user as an authority in the token class
116
118
  await updateTokenClass(ctx, {
117
119
  tokenClass: tokenInstanceKey.getTokenClassKey(),
120
+ overwriteAuthorities: true,
118
121
  authorities: [vaultAddress]
119
122
  });
120
123
 
@@ -123,7 +126,9 @@ export async function createSale(
123
126
  vaultAddress,
124
127
  tokenInstanceKey,
125
128
  launchpadDetails.reverseBondingCurveConfiguration?.toChainObject(),
126
- ctx.callingUser
129
+ ctx.callingUser,
130
+ undefined,
131
+ launchpadDetails.adjustableSupplyMultiplier
127
132
  );
128
133
 
129
134
  await putChainObject(ctx, launchpad);