@gala-chain/launchpad 1.0.5 → 1.0.7

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 (68) hide show
  1. package/lib/package.json +1 -1
  2. package/lib/src/api/types/LaunchpadDtos.d.ts +5 -4
  3. package/lib/src/api/types/LaunchpadDtos.d.ts.map +1 -1
  4. package/lib/src/api/types/LaunchpadDtos.js +17 -15
  5. package/lib/src/api/types/LaunchpadDtos.js.map +1 -1
  6. package/lib/src/chaincode/LaunchpadContract.d.ts +4 -4
  7. package/lib/src/chaincode/LaunchpadContract.d.ts.map +1 -1
  8. package/lib/src/chaincode/LaunchpadContract.js +22 -16
  9. package/lib/src/chaincode/LaunchpadContract.js.map +1 -1
  10. package/lib/src/chaincode/launchpad/buyExactToken.d.ts.map +1 -1
  11. package/lib/src/chaincode/launchpad/buyExactToken.js +8 -1
  12. package/lib/src/chaincode/launchpad/buyExactToken.js.map +1 -1
  13. package/lib/src/chaincode/launchpad/buyWithNative.d.ts.map +1 -1
  14. package/lib/src/chaincode/launchpad/buyWithNative.js +9 -2
  15. package/lib/src/chaincode/launchpad/buyWithNative.js.map +1 -1
  16. package/lib/src/chaincode/launchpad/callMemeTokenOut.d.ts.map +1 -1
  17. package/lib/src/chaincode/launchpad/callMemeTokenOut.js +20 -7
  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.map +1 -1
  21. package/lib/src/chaincode/launchpad/callNativeTokenOut.d.ts.map +1 -1
  22. package/lib/src/chaincode/launchpad/callNativeTokenOut.js.map +1 -1
  23. package/lib/src/chaincode/launchpad/fetchLaunchpadAdressConfig.d.ts +2 -1
  24. package/lib/src/chaincode/launchpad/fetchLaunchpadAdressConfig.d.ts.map +1 -1
  25. package/lib/src/chaincode/launchpad/fetchLaunchpadAdressConfig.js +1 -1
  26. package/lib/src/chaincode/launchpad/fetchLaunchpadAdressConfig.js.map +1 -1
  27. package/lib/src/chaincode/launchpad/fetchLaunchpadFeeAmount.d.ts +12 -0
  28. package/lib/src/chaincode/launchpad/fetchLaunchpadFeeAmount.d.ts.map +1 -0
  29. package/lib/src/chaincode/launchpad/fetchLaunchpadFeeAmount.js +37 -0
  30. package/lib/src/chaincode/launchpad/fetchLaunchpadFeeAmount.js.map +1 -0
  31. package/lib/src/chaincode/launchpad/index.d.ts +0 -1
  32. package/lib/src/chaincode/launchpad/index.d.ts.map +1 -1
  33. package/lib/src/chaincode/launchpad/index.js +0 -1
  34. package/lib/src/chaincode/launchpad/index.js.map +1 -1
  35. package/lib/src/chaincode/launchpad/sellExactToken.d.ts.map +1 -1
  36. package/lib/src/chaincode/launchpad/sellExactToken.js +2 -1
  37. package/lib/src/chaincode/launchpad/sellExactToken.js.map +1 -1
  38. package/lib/src/chaincode/launchpad/sellWithNative.d.ts.map +1 -1
  39. package/lib/src/chaincode/launchpad/sellWithNative.js +2 -1
  40. package/lib/src/chaincode/launchpad/sellWithNative.js.map +1 -1
  41. package/lib/src/chaincode/test/launchpadgala.d.ts +154 -0
  42. package/lib/src/chaincode/test/launchpadgala.d.ts.map +1 -0
  43. package/lib/src/chaincode/test/launchpadgala.js +152 -0
  44. package/lib/src/chaincode/test/launchpadgala.js.map +1 -0
  45. package/lib/tsconfig.tsbuildinfo +1 -1
  46. package/package.json +1 -1
  47. package/src/api/types/LaunchpadDtos.ts +12 -14
  48. package/src/chaincode/LaunchpadContract.ts +25 -16
  49. package/src/chaincode/launchpad/buyExactToken.spec.ts +222 -0
  50. package/src/chaincode/launchpad/buyExactToken.ts +18 -2
  51. package/src/chaincode/launchpad/buyWithNative.spec.ts +221 -0
  52. package/src/chaincode/launchpad/buyWithNative.ts +20 -3
  53. package/src/chaincode/launchpad/callMemeTokenOut.spec.ts +150 -0
  54. package/src/chaincode/launchpad/callMemeTokenOut.ts +26 -15
  55. package/src/chaincode/launchpad/callNativeTokenIn.ts +1 -0
  56. package/src/chaincode/launchpad/callNativeTokenOut.ts +1 -0
  57. package/src/chaincode/launchpad/fetchLaunchpadAdressConfig.ts +5 -2
  58. package/src/chaincode/launchpad/fetchLaunchpadFeeAmount.spec.ts +67 -0
  59. package/src/chaincode/launchpad/fetchLaunchpadFeeAmount.ts +40 -0
  60. package/src/chaincode/launchpad/index.ts +0 -1
  61. package/src/chaincode/launchpad/sellExactToken.ts +2 -1
  62. package/src/chaincode/launchpad/sellWithNative.ts +2 -1
  63. package/src/chaincode/test/launchpadgala.ts +174 -0
  64. package/lib/src/chaincode/launchpad/preMintCalculation.d.ts +0 -17
  65. package/lib/src/chaincode/launchpad/preMintCalculation.d.ts.map +0 -1
  66. package/lib/src/chaincode/launchpad/preMintCalculation.js +0 -52
  67. package/lib/src/chaincode/launchpad/preMintCalculation.js.map +0 -1
  68. package/src/chaincode/launchpad/preMintCalculation.ts +0 -56
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gala-chain/launchpad",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "GalaChain Launchpad Chaincode",
5
5
  "main": "lib/src/index.js",
6
6
  "types": "lib/src/index.d.ts",
@@ -36,7 +36,6 @@ import {
36
36
  ValidateNested
37
37
  } from "class-validator";
38
38
 
39
- // TODO: Move to @gala-chain/api
40
39
  import { BigNumberIsNotNegative, BigNumberLessThanOrEqualOther, BigNumberMax } from "../validators";
41
40
  import { IsNonZeroBigNumber } from "../validators";
42
41
 
@@ -254,6 +253,10 @@ export class NativeTokenQuantityDto extends SubmitCallDTO {
254
253
  @IsOptional()
255
254
  public extraFees?: TokenExtraFeesDto;
256
255
 
256
+ @IsOptional()
257
+ @IsBoolean()
258
+ public IsPreMint?: boolean;
259
+
257
260
  constructor(vaultAddress: UserAlias, nativeTokenQuantity: BigNumber = new BigNumber(0)) {
258
261
  super();
259
262
  this.vaultAddress = vaultAddress;
@@ -303,10 +306,12 @@ export class TradeResDto {
303
306
 
304
307
  @IsNotEmpty()
305
308
  public functionName: string;
309
+
310
+ @IsString()
311
+ public uniqueKey: string;
306
312
  }
307
313
 
308
314
  export class FetchSaleDto extends ChainCallDTO {
309
- // TODO: UserAlias?
310
315
  @IsUserAlias()
311
316
  @IsNotEmpty()
312
317
  public vaultAddress: UserAlias;
@@ -316,18 +321,6 @@ export class FetchSaleDto extends ChainCallDTO {
316
321
  }
317
322
  }
318
323
 
319
- export class PreMintCalculationDto extends ChainCallDTO {
320
- @BigNumberProperty()
321
- @IsNonZeroBigNumber()
322
- @IsNotEmpty()
323
- public nativeTokenQuantity: BigNumber;
324
-
325
- constructor(nativeTokenQuantity: BigNumber = new BigNumber(0)) {
326
- super();
327
- this.nativeTokenQuantity = nativeTokenQuantity;
328
- }
329
- }
330
-
331
324
  export class ConfigureLaunchpadFeeAddressDto extends SubmitCallDTO {
332
325
  @IsOptional()
333
326
  @IsUserAlias()
@@ -404,3 +397,8 @@ export class BatchSubmitAuthoritiesResDto extends ChainCallDTO {
404
397
  @IsString({ each: true })
405
398
  authorities: string[];
406
399
  }
400
+
401
+ export class TransactionFeeResDto {
402
+ @IsNumber()
403
+ feeAmount;
404
+ }
@@ -12,7 +12,7 @@
12
12
  * See the License for the specific language governing permissions and
13
13
  * limitations under the License.
14
14
  */
15
- import { BatchDto, GalaChainResponse, UnauthorizedError } from "@gala-chain/api";
15
+ import { BatchDto, ChainCallDTO, GalaChainResponse, UnauthorizedError } from "@gala-chain/api";
16
16
  import {
17
17
  BatchWriteLimitExceededError,
18
18
  EVALUATE,
@@ -41,9 +41,9 @@ import {
41
41
  LaunchpadFinalizeFeeAllocation,
42
42
  LaunchpadSale,
43
43
  NativeTokenQuantityDto,
44
- PreMintCalculationDto,
45
44
  TradeCalculationResDto,
46
- TradeResDto
45
+ TradeResDto,
46
+ TransactionFeeResDto
47
47
  } from "../api/types";
48
48
  import {
49
49
  buyExactTokenFeeGate,
@@ -56,7 +56,6 @@ import {
56
56
  authorizeLaunchpadBatchSubmitter,
57
57
  buyExactToken,
58
58
  buyWithNative,
59
- calculatePreMintTokens,
60
59
  callMemeTokenIn,
61
60
  callMemeTokenOut,
62
61
  callNativeTokenIn,
@@ -72,10 +71,13 @@ import {
72
71
  sellExactToken,
73
72
  sellWithNative
74
73
  } from "./launchpad";
74
+ import { fetchLaunchpadFeeAmount } from "./launchpad/fetchLaunchpadFeeAmount";
75
75
 
76
76
  export class LaunchpadContract extends GalaContract {
77
77
  constructor() {
78
- super("Launchpad", version);
78
+ super("Launchpad", version, {
79
+ allowNonRegisteredUsers: true
80
+ });
79
81
  }
80
82
 
81
83
  @Submit({
@@ -204,21 +206,28 @@ export class LaunchpadContract extends GalaContract {
204
206
  return callMemeTokenIn(ctx, dto);
205
207
  }
206
208
 
207
- @GalaTransaction({
208
- type: EVALUATE,
209
- in: PreMintCalculationDto,
210
- out: String
211
- })
212
- public async CalculatePreMintTokens(dto: PreMintCalculationDto): Promise<string> {
213
- return calculatePreMintTokens(dto);
214
- }
215
-
216
209
  @Evaluate({
210
+ in: ChainCallDTO,
217
211
  out: LaunchpadFeeConfig,
218
212
  allowedOrgs: ["CuratorOrg"]
219
213
  })
220
- public async FetchLaunchpadFeeConfig(ctx: GalaChainContext): Promise<LaunchpadFeeConfig> {
221
- return fetchLaunchpadFeeConfig(ctx);
214
+ public async FetchLaunchpadFeeConfig(
215
+ ctx: GalaChainContext,
216
+ dto: ChainCallDTO
217
+ ): Promise<LaunchpadFeeConfig> {
218
+ return fetchLaunchpadFeeConfig(ctx, dto);
219
+ }
220
+
221
+ @GalaTransaction({
222
+ type: EVALUATE,
223
+ in: ChainCallDTO,
224
+ out: TransactionFeeResDto
225
+ })
226
+ public async FetchLaunchpadFeeAmount(
227
+ ctx: GalaChainContext,
228
+ dto: ChainCallDTO
229
+ ): Promise<TransactionFeeResDto> {
230
+ return fetchLaunchpadFeeAmount(ctx, dto);
222
231
  }
223
232
 
224
233
  @GalaTransaction({
@@ -0,0 +1,222 @@
1
+ /*
2
+ * Copyright (c) Gala Games Inc. All rights reserved.
3
+ * Licensed under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License.
5
+ * You may obtain a copy of the License at
6
+ *
7
+ * http://www.apache.org/licenses/LICENSE-2.0
8
+ *
9
+ * Unless required by applicable law or agreed to in writing, software
10
+ * distributed under the License is distributed on an "AS IS" BASIS,
11
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ * See the License for the specific language governing permissions and
13
+ * limitations under the License.
14
+ */
15
+ import {
16
+ GalaChainResponse,
17
+ TokenBalance,
18
+ TokenClass,
19
+ TokenClassKey,
20
+ TokenInstance,
21
+ UserAlias,
22
+ ValidationFailedError,
23
+ asValidUserAlias,
24
+ randomUniqueKey
25
+ } from "@gala-chain/api";
26
+ import { currency, fixture, users } from "@gala-chain/test";
27
+ import BigNumber from "bignumber.js";
28
+ import { plainToInstance } from "class-transformer";
29
+
30
+ import {
31
+ ExactTokenQuantityDto,
32
+ LaunchpadFeeConfig,
33
+ LaunchpadSale,
34
+ NativeTokenQuantityDto
35
+ } from "../../api/types";
36
+ import { LaunchpadContract } from "../LaunchpadContract";
37
+ import launchpadgala from "../test/launchpadgala";
38
+
39
+ describe("buyWithNative", () => {
40
+ let currencyClass: TokenClass;
41
+ let currencyInstance: TokenInstance;
42
+ let launchpadGalaClass: TokenClass;
43
+ let launchpadGalaInstance: TokenInstance;
44
+ let launchpadGalaClassKey: TokenClassKey;
45
+ let vaultAddress: UserAlias;
46
+ let sale: LaunchpadSale;
47
+ let salelaunchpadGalaBalance: TokenBalance;
48
+ let saleCurrencyBalance: TokenBalance;
49
+ let userlaunchpadGalaBalance: TokenBalance;
50
+ let userCurrencyBalance: TokenBalance;
51
+
52
+ beforeEach(() => {
53
+ //Given
54
+ currencyClass = currency.tokenClass();
55
+ currencyInstance = currency.tokenInstance();
56
+ launchpadGalaClass = launchpadgala.tokenClass();
57
+ launchpadGalaInstance = launchpadgala.tokenInstance();
58
+ launchpadGalaClassKey = launchpadgala.tokenClassKey();
59
+
60
+ launchpadGalaClass = plainToInstance(TokenClass, {
61
+ ...launchpadgala.tokenClassPlain(),
62
+ decimals: 18
63
+ });
64
+
65
+ vaultAddress = asValidUserAlias(`service|${launchpadGalaClassKey.toStringKey()}$launchpad`);
66
+
67
+ // Initialize sale with manual values
68
+ sale = new LaunchpadSale(
69
+ vaultAddress,
70
+ launchpadGalaInstance.instanceKeyObj(),
71
+ undefined,
72
+ users.testUser1.identityKey
73
+ );
74
+
75
+ // Create sale balances - sale needs tokens to pay out
76
+ salelaunchpadGalaBalance = plainToInstance(TokenBalance, {
77
+ ...launchpadgala.tokenBalance(),
78
+ owner: vaultAddress,
79
+ quantity: new BigNumber("1e+7")
80
+ });
81
+
82
+ saleCurrencyBalance = plainToInstance(TokenBalance, {
83
+ ...currency.tokenBalance(),
84
+ owner: vaultAddress,
85
+ quantity: new BigNumber("1e+7")
86
+ });
87
+
88
+ // Create user balances - user needs tokens to swap
89
+ userlaunchpadGalaBalance = plainToInstance(TokenBalance, {
90
+ ...launchpadgala.tokenBalance(),
91
+ owner: users.testUser1.identityKey,
92
+ quantity: new BigNumber("1000000") // User has 10k launchpadGala tokens
93
+ });
94
+ userCurrencyBalance = plainToInstance(TokenBalance, {
95
+ ...currency.tokenBalance(),
96
+ owner: users.testUser1.identityKey
97
+ });
98
+ });
99
+
100
+ test("User should be able to buy exact tokens, without fee configured", async () => {
101
+ //Given
102
+ const { ctx, contract } = fixture(LaunchpadContract)
103
+ .registeredUsers(users.testUser1)
104
+ .savedState(
105
+ currencyClass,
106
+ currencyInstance,
107
+ launchpadGalaClass,
108
+ launchpadGalaInstance,
109
+ sale,
110
+ salelaunchpadGalaBalance,
111
+ saleCurrencyBalance,
112
+ userlaunchpadGalaBalance,
113
+ userCurrencyBalance
114
+ );
115
+
116
+ const dto = new ExactTokenQuantityDto(vaultAddress, new BigNumber("500"));
117
+
118
+ dto.uniqueKey = randomUniqueKey();
119
+ dto.sign(users.testUser1.privateKey);
120
+
121
+ //When
122
+ const buyTokenRes = await contract.BuyExactToken(ctx, dto);
123
+
124
+ //Then
125
+ expect(buyTokenRes.Data).toMatchObject({
126
+ inputQuantity: "0.00825575",
127
+ totalFees: "0.00000000",
128
+ outputQuantity: "500",
129
+ tokenName: "AUTOMATEDTESTCOIN",
130
+ tradeType: "Buy",
131
+ vaultAddress: "service|GALA$Unit$none$none$launchpad",
132
+ userAddress: "client|testUser1",
133
+ isFinalized: false,
134
+ functionName: "BuyExactToken"
135
+ });
136
+ expect(buyTokenRes.Data?.inputQuantity).toEqual("0.00825575");
137
+ expect(buyTokenRes.Data?.outputQuantity).toEqual("500");
138
+ });
139
+
140
+ test("User should be able to buy tokens , fee configured check", async () => {
141
+ //Given
142
+ const launchpadConfig = new LaunchpadFeeConfig(users.testUser2.identityKey, Number("0.32"), [
143
+ users.testUser2.identityKey
144
+ ]);
145
+
146
+ const { ctx, contract } = fixture(LaunchpadContract)
147
+ .registeredUsers(users.testUser1)
148
+ .savedState(
149
+ currencyClass,
150
+ currencyInstance,
151
+ launchpadGalaClass,
152
+ launchpadGalaInstance,
153
+ sale,
154
+ salelaunchpadGalaBalance,
155
+ saleCurrencyBalance,
156
+ userlaunchpadGalaBalance,
157
+ userCurrencyBalance,
158
+ launchpadConfig
159
+ );
160
+
161
+ const dto = new ExactTokenQuantityDto(vaultAddress, new BigNumber("5430"));
162
+
163
+ dto.uniqueKey = randomUniqueKey();
164
+ dto.sign(users.testUser1.privateKey);
165
+
166
+ //When
167
+ const buyTokenRes = await contract.BuyExactToken(ctx, dto);
168
+
169
+ //Then
170
+ expect(buyTokenRes.Data).toMatchObject({
171
+ inputQuantity: "0.08991559",
172
+ totalFees: "0.02877299",
173
+ outputQuantity: "5430",
174
+ tokenName: "AUTOMATEDTESTCOIN",
175
+ tradeType: "Buy",
176
+ vaultAddress: "service|GALA$Unit$none$none$launchpad",
177
+ userAddress: "client|testUser1",
178
+ isFinalized: false,
179
+ functionName: "BuyExactToken"
180
+ });
181
+ expect(buyTokenRes.Data?.inputQuantity).toEqual("0.08991559");
182
+ expect(buyTokenRes.Data?.outputQuantity).toEqual("5430");
183
+ });
184
+
185
+ it("should throw error if user has insufficient funds incuding the transaction fees", async () => {
186
+ //Given
187
+ const launchpadConfig = new LaunchpadFeeConfig(users.testUser2.identityKey, Number("0.32"), [
188
+ users.testUser2.identityKey
189
+ ]);
190
+ const { ctx, contract } = fixture(LaunchpadContract)
191
+ .registeredUsers(users.testUser1)
192
+ .savedState(
193
+ currencyClass,
194
+ currencyInstance,
195
+ launchpadGalaClass,
196
+ launchpadGalaInstance,
197
+ sale,
198
+ salelaunchpadGalaBalance,
199
+ saleCurrencyBalance,
200
+ userlaunchpadGalaBalance,
201
+ userCurrencyBalance,
202
+ launchpadConfig
203
+ );
204
+
205
+ const dto = new NativeTokenQuantityDto(vaultAddress, new BigNumber("10000000"));
206
+
207
+ dto.uniqueKey = randomUniqueKey();
208
+ dto.sign(users.testUser1.privateKey);
209
+
210
+ //When
211
+ const buyTokenRes = await contract.BuyWithNative(ctx, dto);
212
+
213
+ //Then
214
+ expect(buyTokenRes).toEqual(
215
+ GalaChainResponse.Error(
216
+ new ValidationFailedError(
217
+ "Insufficient balance: Total amount required including fee is 2166101.31430784"
218
+ )
219
+ )
220
+ );
221
+ });
222
+ });
@@ -12,7 +12,14 @@
12
12
  * See the License for the specific language governing permissions and
13
13
  * limitations under the License.
14
14
  */
15
- import { GalaChainContext, fetchTokenClass, putChainObject, transferToken } from "@gala-chain/chaincode";
15
+ import { ValidationFailedError } from "@gala-chain/api";
16
+ import {
17
+ GalaChainContext,
18
+ fetchOrCreateBalance,
19
+ fetchTokenClass,
20
+ putChainObject,
21
+ transferToken
22
+ } from "@gala-chain/chaincode";
16
23
  import BigNumber from "bignumber.js";
17
24
 
18
25
  import { ExactTokenQuantityDto, LaunchpadSale, TradeResDto } from "../../api/types";
@@ -82,6 +89,14 @@ export async function buyExactToken(
82
89
  // Transfer transaction fees
83
90
  const launchpadFeeAddressConfiguration = await fetchLaunchpadFeeAddress(ctx);
84
91
  if (launchpadFeeAddressConfiguration && transactionFees) {
92
+ const totalRequired = new BigNumber(buyTokenDTO.tokenQuantity).plus(transactionFees);
93
+
94
+ const buyerBalance = await fetchOrCreateBalance(ctx, ctx.callingUser, sale.nativeToken);
95
+ if (buyerBalance.getQuantityTotal().lt(totalRequired)) {
96
+ throw new ValidationFailedError(
97
+ `Insufficient balance: Total amount required including fee is ${totalRequired}`
98
+ );
99
+ }
85
100
  await transferToken(ctx, {
86
101
  from: ctx.callingUser,
87
102
  to: launchpadFeeAddressConfiguration.feeAddress,
@@ -135,6 +150,7 @@ export async function buyExactToken(
135
150
  vaultAddress: buyTokenDTO.vaultAddress,
136
151
  userAddress: ctx.callingUser,
137
152
  isFinalized: isSaleFinalized,
138
- functionName: "BuyExactToken"
153
+ functionName: "BuyExactToken",
154
+ uniqueKey: buyTokenDTO.uniqueKey
139
155
  };
140
156
  }
@@ -0,0 +1,221 @@
1
+ /*
2
+ * Copyright (c) Gala Games Inc. All rights reserved.
3
+ * Licensed under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License.
5
+ * You may obtain a copy of the License at
6
+ *
7
+ * http://www.apache.org/licenses/LICENSE-2.0
8
+ *
9
+ * Unless required by applicable law or agreed to in writing, software
10
+ * distributed under the License is distributed on an "AS IS" BASIS,
11
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ * See the License for the specific language governing permissions and
13
+ * limitations under the License.
14
+ */
15
+ import {
16
+ GalaChainResponse,
17
+ TokenBalance,
18
+ TokenClass,
19
+ TokenClassKey,
20
+ TokenInstance,
21
+ UserAlias,
22
+ ValidationFailedError,
23
+ asValidUserAlias,
24
+ randomUniqueKey
25
+ } from "@gala-chain/api";
26
+ import { currency, fixture, users } from "@gala-chain/test";
27
+ import BigNumber from "bignumber.js";
28
+ import { plainToInstance } from "class-transformer";
29
+
30
+ import { LaunchpadFeeConfig, LaunchpadSale, NativeTokenQuantityDto } from "../../api/types";
31
+ import { LaunchpadContract } from "../LaunchpadContract";
32
+ import launchpadgala from "../test/launchpadgala";
33
+
34
+ describe("buyWithNative", () => {
35
+ let currencyClass: TokenClass;
36
+ let currencyInstance: TokenInstance;
37
+ let launchpadGalaClass: TokenClass;
38
+ let launchpadGalaInstance: TokenInstance;
39
+ let launchpadGalaClassKey: TokenClassKey;
40
+ let vaultAddress: UserAlias;
41
+ let sale: LaunchpadSale;
42
+ let salelaunchpadGalaBalance: TokenBalance;
43
+ let saleCurrencyBalance: TokenBalance;
44
+ let userlaunchpadGalaBalance: TokenBalance;
45
+ let userCurrencyBalance: TokenBalance;
46
+
47
+ beforeEach(() => {
48
+ //Given
49
+ currencyClass = currency.tokenClass();
50
+ currencyInstance = currency.tokenInstance();
51
+ launchpadGalaClass = launchpadgala.tokenClass();
52
+
53
+ launchpadGalaClass = plainToInstance(TokenClass, {
54
+ ...launchpadgala.tokenClassPlain(),
55
+ decimals: 18
56
+ });
57
+
58
+ launchpadGalaInstance = launchpadgala.tokenInstance();
59
+ launchpadGalaClassKey = launchpadgala.tokenClassKey();
60
+
61
+ vaultAddress = asValidUserAlias(`service|${launchpadGalaClassKey.toStringKey()}$launchpad`);
62
+
63
+ // Initialize sale with manual values
64
+ sale = new LaunchpadSale(
65
+ vaultAddress,
66
+ launchpadGalaInstance.instanceKeyObj(),
67
+ undefined,
68
+ users.testUser1.identityKey
69
+ );
70
+
71
+ // Create sale balances - sale needs tokens to pay out
72
+ salelaunchpadGalaBalance = plainToInstance(TokenBalance, {
73
+ ...launchpadgala.tokenBalance(),
74
+ owner: vaultAddress,
75
+ quantity: new BigNumber("1e+7")
76
+ });
77
+
78
+ saleCurrencyBalance = plainToInstance(TokenBalance, {
79
+ ...currency.tokenBalance(),
80
+ owner: vaultAddress,
81
+ quantity: new BigNumber("1e+7")
82
+ });
83
+
84
+ // Create user balances - user needs tokens to swap
85
+ userlaunchpadGalaBalance = plainToInstance(TokenBalance, {
86
+ ...launchpadgala.tokenBalance(),
87
+ owner: users.testUser1.identityKey,
88
+ quantity: new BigNumber("1000000") // User has 10k launchpadGala tokens
89
+ });
90
+ userCurrencyBalance = plainToInstance(TokenBalance, {
91
+ ...currency.tokenBalance(),
92
+ owner: users.testUser1.identityKey
93
+ });
94
+ });
95
+
96
+ test("User should be able to buy tokens with providing native gala , without fee configured", async () => {
97
+ //Given
98
+ const { ctx, contract } = fixture(LaunchpadContract)
99
+ .registeredUsers(users.testUser1)
100
+ .savedState(
101
+ currencyClass,
102
+ currencyInstance,
103
+ launchpadGalaClass,
104
+ launchpadGalaInstance,
105
+ sale,
106
+ salelaunchpadGalaBalance,
107
+ saleCurrencyBalance,
108
+ userlaunchpadGalaBalance,
109
+ userCurrencyBalance
110
+ );
111
+
112
+ const dto = new NativeTokenQuantityDto(vaultAddress, new BigNumber("150"));
113
+
114
+ dto.uniqueKey = randomUniqueKey();
115
+ dto.sign(users.testUser1.privateKey);
116
+
117
+ //When
118
+ const buyTokenRes = await contract.BuyWithNative(ctx, dto);
119
+
120
+ //Then
121
+ expect(buyTokenRes.Data).toMatchObject({
122
+ inputQuantity: "150",
123
+ totalFees: "0.00000000",
124
+ outputQuantity: "2101667.8890651635002",
125
+ tokenName: "AUTOMATEDTESTCOIN",
126
+ tradeType: "Buy",
127
+ vaultAddress: "service|GALA$Unit$none$none$launchpad",
128
+ userAddress: "client|testUser1",
129
+ isFinalized: false,
130
+ functionName: "BuyWithNative"
131
+ });
132
+
133
+ expect(buyTokenRes.Data?.inputQuantity).toEqual("150");
134
+ expect(buyTokenRes.Data?.outputQuantity).toEqual("2101667.8890651635002");
135
+ });
136
+
137
+ test("User should be able to buy tokens , fee configured check", async () => {
138
+ //Given
139
+ const launchpadConfig = new LaunchpadFeeConfig(users.testUser2.identityKey, Number("0.32"), [
140
+ users.testUser2.identityKey
141
+ ]);
142
+
143
+ const { ctx, contract } = fixture(LaunchpadContract)
144
+ .registeredUsers(users.testUser1)
145
+ .savedState(
146
+ currencyClass,
147
+ currencyInstance,
148
+ launchpadGalaClass,
149
+ launchpadGalaInstance,
150
+ sale,
151
+ salelaunchpadGalaBalance,
152
+ saleCurrencyBalance,
153
+ userlaunchpadGalaBalance,
154
+ userCurrencyBalance,
155
+ launchpadConfig
156
+ );
157
+
158
+ const dto = new NativeTokenQuantityDto(vaultAddress, new BigNumber("1000"));
159
+ dto.uniqueKey = randomUniqueKey();
160
+ dto.sign(users.testUser1.privateKey);
161
+
162
+ //When
163
+ const buyTokenRes = await contract.BuyWithNative(ctx, dto);
164
+
165
+ //Then
166
+ expect(buyTokenRes.Data).toMatchObject({
167
+ inputQuantity: "1000",
168
+ totalFees: "320.00000000",
169
+ outputQuantity: "3663321.3628130557168",
170
+ tokenName: "AUTOMATEDTESTCOIN",
171
+ tradeType: "Buy",
172
+ vaultAddress: "service|GALA$Unit$none$none$launchpad",
173
+ userAddress: "client|testUser1",
174
+ isFinalized: false,
175
+ functionName: "BuyWithNative"
176
+ });
177
+
178
+ expect(buyTokenRes.Data?.totalFees).toEqual("320.00000000");
179
+ expect(buyTokenRes.Data?.inputQuantity).toEqual("1000");
180
+ expect(buyTokenRes.Data?.outputQuantity).toEqual("3663321.3628130557168");
181
+ });
182
+
183
+ it("should throw error if user has insufficient funds incuding the transaction fees", async () => {
184
+ //Given
185
+ const launchpadConfig = new LaunchpadFeeConfig(users.testUser2.identityKey, Number("0.32"), [
186
+ users.testUser2.identityKey
187
+ ]);
188
+
189
+ const { ctx, contract } = fixture(LaunchpadContract)
190
+ .registeredUsers(users.testUser1)
191
+ .savedState(
192
+ currencyClass,
193
+ currencyInstance,
194
+ launchpadGalaClass,
195
+ launchpadGalaInstance,
196
+ sale,
197
+ salelaunchpadGalaBalance,
198
+ saleCurrencyBalance,
199
+ userlaunchpadGalaBalance,
200
+ userCurrencyBalance,
201
+ launchpadConfig
202
+ );
203
+
204
+ const dto = new NativeTokenQuantityDto(vaultAddress, new BigNumber("10000000"));
205
+
206
+ dto.uniqueKey = randomUniqueKey();
207
+ dto.sign(users.testUser1.privateKey);
208
+
209
+ //When
210
+ const buyTokenRes = await contract.BuyWithNative(ctx, dto);
211
+
212
+ //Then
213
+ expect(buyTokenRes).toEqual(
214
+ GalaChainResponse.Error(
215
+ new ValidationFailedError(
216
+ "Insufficient balance: Total amount required including fee is 2166101.31430784"
217
+ )
218
+ )
219
+ );
220
+ });
221
+ });
@@ -12,7 +12,14 @@
12
12
  * See the License for the specific language governing permissions and
13
13
  * limitations under the License.
14
14
  */
15
- import { GalaChainContext, fetchTokenClass, putChainObject, transferToken } from "@gala-chain/chaincode";
15
+ import { ValidationFailedError } from "@gala-chain/api";
16
+ import {
17
+ GalaChainContext,
18
+ fetchOrCreateBalance,
19
+ fetchTokenClass,
20
+ putChainObject,
21
+ transferToken
22
+ } from "@gala-chain/chaincode";
16
23
  import BigNumber from "bignumber.js";
17
24
 
18
25
  import { ExactTokenQuantityDto, LaunchpadSale, NativeTokenQuantityDto, TradeResDto } from "../../api/types";
@@ -79,13 +86,22 @@ export async function buyWithNative(
79
86
  // Check for slippage condition
80
87
  if (buyTokenDTO.expectedToken && buyTokenDTO.expectedToken.comparedTo(tokensToBuy) > 0) {
81
88
  throw new SlippageToleranceExceededError(
82
- "Tokens expected from this operation are more than the the actual amount that will be provided."
89
+ "Tokens expected from this operation are more than the actual amount that will be provided."
83
90
  );
84
91
  }
85
92
 
86
93
  // Transfer transaction fees to launchpad fee address
87
94
  const launchpadFeeAddressConfiguration = await fetchLaunchpadFeeAddress(ctx);
88
95
  if (launchpadFeeAddressConfiguration && transactionFees) {
96
+ const totalRequired = new BigNumber(buyTokenDTO.nativeTokenQuantity).plus(transactionFees);
97
+
98
+ const buyerBalance = await fetchOrCreateBalance(ctx, ctx.callingUser, sale.nativeToken);
99
+ if (buyerBalance.getQuantityTotal().lt(totalRequired)) {
100
+ throw new ValidationFailedError(
101
+ `Insufficient balance: Total amount required including fee is ${totalRequired}`
102
+ );
103
+ }
104
+
89
105
  await transferToken(ctx, {
90
106
  from: ctx.callingUser,
91
107
  to: launchpadFeeAddressConfiguration.feeAddress,
@@ -138,6 +154,7 @@ export async function buyWithNative(
138
154
  vaultAddress: buyTokenDTO.vaultAddress,
139
155
  userAddress: ctx.callingUser,
140
156
  isFinalized: isSaleFinalized,
141
- functionName: "BuyWithNative"
157
+ functionName: "BuyWithNative",
158
+ uniqueKey: buyTokenDTO.uniqueKey
142
159
  };
143
160
  }