@gala-chain/launchpad 1.0.9 → 1.0.11

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 (36) hide show
  1. package/CLAUDE.md +279 -0
  2. package/lib/package.json +2 -2
  3. package/lib/src/api/utils/error.js +1 -1
  4. package/lib/src/api/utils/error.js.map +1 -1
  5. package/lib/src/chaincode/launchpad/buyExactToken.js +1 -1
  6. package/lib/src/chaincode/launchpad/buyExactToken.js.map +1 -1
  7. package/lib/src/chaincode/launchpad/buyWithNative.d.ts.map +1 -1
  8. package/lib/src/chaincode/launchpad/buyWithNative.js +12 -5
  9. package/lib/src/chaincode/launchpad/buyWithNative.js.map +1 -1
  10. package/lib/src/chaincode/launchpad/sellExactToken.js +1 -1
  11. package/lib/src/chaincode/launchpad/sellExactToken.js.map +1 -1
  12. package/lib/src/chaincode/launchpad/sellWithNative.d.ts.map +1 -1
  13. package/lib/src/chaincode/launchpad/sellWithNative.js +10 -2
  14. package/lib/src/chaincode/launchpad/sellWithNative.js.map +1 -1
  15. package/lib/src/chaincode/test/launchpadgala.js +1 -1
  16. package/lib/src/chaincode/test/launchpadgala.js.map +1 -1
  17. package/lib/tsconfig.tsbuildinfo +1 -1
  18. package/package.json +2 -2
  19. package/src/api/utils/error.ts +1 -1
  20. package/src/chaincode/launchpad/buyExactToken.spec.ts +90 -20
  21. package/src/chaincode/launchpad/buyExactToken.ts +1 -1
  22. package/src/chaincode/launchpad/buyWithNative.spec.ts +99 -26
  23. package/src/chaincode/launchpad/buyWithNative.ts +21 -6
  24. package/src/chaincode/launchpad/callMemeTokenIn.spec.ts +244 -0
  25. package/src/chaincode/launchpad/callMemeTokenOut.spec.ts +1 -1
  26. package/src/chaincode/launchpad/callNativeTokenIn.spec.ts +269 -0
  27. package/src/chaincode/launchpad/callNativeTokenOut.spec.ts +276 -0
  28. package/src/chaincode/launchpad/configureLaunchpadFeeConfig.spec.ts +202 -0
  29. package/src/chaincode/launchpad/createSale.spec.ts +259 -0
  30. package/src/chaincode/launchpad/fetchSaleDetails.spec.ts +141 -0
  31. package/src/chaincode/launchpad/finalizeTokenAllocation.spec.ts +126 -0
  32. package/src/chaincode/launchpad/sellExactToken.spec.ts +284 -0
  33. package/src/chaincode/launchpad/sellExactToken.ts +1 -1
  34. package/src/chaincode/launchpad/sellWithNative.spec.ts +329 -0
  35. package/src/chaincode/launchpad/sellWithNative.ts +23 -6
  36. package/src/chaincode/test/launchpadgala.ts +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gala-chain/launchpad",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
4
4
  "description": "GalaChain Launchpad Chaincode",
5
5
  "main": "lib/src/index.js",
6
6
  "types": "lib/src/index.d.ts",
@@ -25,7 +25,7 @@
25
25
  "dependencies": {
26
26
  "@gala-chain/api": "~2.3.4",
27
27
  "@gala-chain/chaincode": "~2.3.4",
28
- "@gala-chain/dex": "1.0.4",
28
+ "@gala-chain/dex": "1.0.20",
29
29
  "@grpc/grpc-js": "1.10.10",
30
30
  "decimal.js": "^10.5.0",
31
31
  "dotenv": "^16.0.1",
@@ -21,7 +21,7 @@ export const ErrorCode = {
21
21
 
22
22
  export class SlippageToleranceExceededError extends ChainError {
23
23
  constructor(message: string) {
24
- super(message, 412);
24
+ super(`Slippage tolerance exceeded: ${message}`, 412);
25
25
  }
26
26
  }
27
27
 
@@ -23,7 +23,8 @@ import {
23
23
  asValidUserAlias,
24
24
  randomUniqueKey
25
25
  } from "@gala-chain/api";
26
- import { currency, fixture, users } from "@gala-chain/test";
26
+ import { InvalidDecimalError } from "@gala-chain/chaincode";
27
+ import { currency, fixture, transactionError, transactionSuccess, users } from "@gala-chain/test";
27
28
  import BigNumber from "bignumber.js";
28
29
  import { plainToInstance } from "class-transformer";
29
30
 
@@ -31,7 +32,8 @@ import {
31
32
  ExactTokenQuantityDto,
32
33
  LaunchpadFeeConfig,
33
34
  LaunchpadSale,
34
- NativeTokenQuantityDto
35
+ NativeTokenQuantityDto,
36
+ TradeResDto
35
37
  } from "../../api/types";
36
38
  import { LaunchpadContract } from "../LaunchpadContract";
37
39
  import launchpadgala from "../test/launchpadgala";
@@ -59,7 +61,7 @@ describe("buyWithNative", () => {
59
61
 
60
62
  launchpadGalaClass = plainToInstance(TokenClass, {
61
63
  ...launchpadgala.tokenClassPlain(),
62
- decimals: 18
64
+ decimals: 8
63
65
  });
64
66
 
65
67
  vaultAddress = asValidUserAlias(`service|${launchpadGalaClassKey.toStringKey()}$launchpad`);
@@ -97,8 +99,8 @@ describe("buyWithNative", () => {
97
99
  });
98
100
  });
99
101
 
100
- test("User should be able to buy exact tokens, without fee configured", async () => {
101
- //Given
102
+ it("should properly round buy qty to native token decimals limit when bonding curve produces greater fractional precision", async () => {
103
+ // Given
102
104
  const { ctx, contract } = fixture(LaunchpadContract)
103
105
  .registeredUsers(users.testUser1)
104
106
  .savedState(
@@ -113,32 +115,97 @@ describe("buyWithNative", () => {
113
115
  userCurrencyBalance
114
116
  );
115
117
 
118
+ // Choose a token quantity that will produce fractional native tokens from bonding curve
119
+ // The bonding curve calculation will produce a value like 0.00825575
116
120
  const dto = new ExactTokenQuantityDto(vaultAddress, new BigNumber("500"));
121
+ dto.uniqueKey = randomUniqueKey();
122
+ dto.sign(users.testUser1.privateKey);
123
+
124
+ // When
125
+ const buyTokenRes = await contract.BuyExactToken(ctx, dto);
126
+
127
+ // Then
128
+ expect(buyTokenRes).toEqual(transactionSuccess());
129
+ });
117
130
 
131
+ it("should reject buy when meme token has 0 decimals and input dto contains fractional quantity", async () => {
132
+ // Given - Setup token with 0 decimals to force decimal precision error
133
+ const zeroDecimalCurrencyClass = plainToInstance(TokenClass, {
134
+ ...currency.tokenClassPlain(),
135
+ decimals: 0 // Integer-only token
136
+ });
137
+
138
+ const { ctx, contract } = fixture(LaunchpadContract)
139
+ .registeredUsers(users.testUser1)
140
+ .savedState(
141
+ zeroDecimalCurrencyClass,
142
+ currencyInstance,
143
+ launchpadGalaClass,
144
+ launchpadGalaInstance,
145
+ sale,
146
+ salelaunchpadGalaBalance,
147
+ saleCurrencyBalance,
148
+ userlaunchpadGalaBalance,
149
+ userCurrencyBalance
150
+ );
151
+
152
+ const dto = new ExactTokenQuantityDto(vaultAddress, new BigNumber("500.555"));
118
153
  dto.uniqueKey = randomUniqueKey();
119
154
  dto.sign(users.testUser1.privateKey);
120
155
 
121
- //When
156
+ // When
122
157
  const buyTokenRes = await contract.BuyExactToken(ctx, dto);
123
158
 
124
- //Then
125
- expect(buyTokenRes.Data).toMatchObject({
159
+ // Then
160
+ expect(buyTokenRes).toEqual(
161
+ transactionError(new InvalidDecimalError(dto.tokenQuantity, zeroDecimalCurrencyClass.decimals))
162
+ );
163
+ });
164
+
165
+ test("User should be able to buy exact tokens, without fee configured", async () => {
166
+ // Given
167
+ const { ctx, contract } = fixture(LaunchpadContract)
168
+ .registeredUsers(users.testUser1)
169
+ .savedState(
170
+ currencyClass,
171
+ currencyInstance,
172
+ launchpadGalaClass,
173
+ launchpadGalaInstance,
174
+ sale,
175
+ salelaunchpadGalaBalance,
176
+ saleCurrencyBalance,
177
+ userlaunchpadGalaBalance,
178
+ userCurrencyBalance
179
+ );
180
+
181
+ const dto = new ExactTokenQuantityDto(vaultAddress, new BigNumber("500"));
182
+
183
+ dto.uniqueKey = randomUniqueKey();
184
+ dto.sign(users.testUser1.privateKey);
185
+
186
+ const expectedResponse = plainToInstance(TradeResDto, {
126
187
  inputQuantity: "0.00825575",
127
188
  totalFees: "0.00000000",
189
+ totalTokenSold: "500",
128
190
  outputQuantity: "500",
129
191
  tokenName: "AUTOMATEDTESTCOIN",
130
192
  tradeType: "Buy",
193
+ uniqueKey: dto.uniqueKey,
131
194
  vaultAddress: "service|GALA$Unit$none$none$launchpad",
132
195
  userAddress: "client|testUser1",
133
196
  isFinalized: false,
134
197
  functionName: "BuyExactToken"
135
198
  });
136
- expect(buyTokenRes.Data?.inputQuantity).toEqual("0.00825575");
137
- expect(buyTokenRes.Data?.outputQuantity).toEqual("500");
199
+
200
+ // When
201
+ const buyTokenRes = await contract.BuyExactToken(ctx, dto);
202
+
203
+ // Then
204
+ expect(buyTokenRes).toEqual(transactionSuccess(expectedResponse));
138
205
  });
139
206
 
140
207
  test("User should be able to buy tokens , fee configured check", async () => {
141
- //Given
208
+ // Given
142
209
  const launchpadConfig = new LaunchpadFeeConfig(users.testUser2.identityKey, Number("0.32"), [
143
210
  users.testUser2.identityKey
144
211
  ]);
@@ -163,26 +230,28 @@ describe("buyWithNative", () => {
163
230
  dto.uniqueKey = randomUniqueKey();
164
231
  dto.sign(users.testUser1.privateKey);
165
232
 
166
- //When
167
- const buyTokenRes = await contract.BuyExactToken(ctx, dto);
168
-
169
- //Then
170
- expect(buyTokenRes.Data).toMatchObject({
233
+ const expectedResponse = plainToInstance(TradeResDto, {
171
234
  inputQuantity: "0.08991559",
172
235
  totalFees: "0.02877299",
236
+ totalTokenSold: "5430",
173
237
  outputQuantity: "5430",
174
238
  tokenName: "AUTOMATEDTESTCOIN",
175
239
  tradeType: "Buy",
176
240
  vaultAddress: "service|GALA$Unit$none$none$launchpad",
177
241
  userAddress: "client|testUser1",
178
242
  isFinalized: false,
179
- functionName: "BuyExactToken"
243
+ functionName: "BuyExactToken",
244
+ uniqueKey: dto.uniqueKey
180
245
  });
181
- expect(buyTokenRes.Data?.inputQuantity).toEqual("0.08991559");
182
- expect(buyTokenRes.Data?.outputQuantity).toEqual("5430");
246
+
247
+ // When
248
+ const buyTokenRes = await contract.BuyExactToken(ctx, dto);
249
+
250
+ // Then
251
+ expect(buyTokenRes).toEqual(transactionSuccess(expectedResponse));
183
252
  });
184
253
 
185
- test("User should be able to finalise sale , if fee is configured", async () => {
254
+ test("User should be able to finalize sale , if fee is configured", async () => {
186
255
  //Given
187
256
  salelaunchpadGalaBalance = plainToInstance(TokenBalance, {
188
257
  ...launchpadgala.tokenBalance(),
@@ -229,6 +298,7 @@ describe("buyWithNative", () => {
229
298
  const buyTokenRes = await contract.BuyExactToken(ctx, dto);
230
299
 
231
300
  //Then
301
+ expect(buyTokenRes).toEqual(transactionSuccess());
232
302
  expect(buyTokenRes.Data?.isFinalized).toBe(true);
233
303
  });
234
304
 
@@ -82,7 +82,7 @@ export async function buyExactToken(
82
82
  // Ensure the expected native token amount is not less than the actual amount required
83
83
  if (buyTokenDTO.expectedNativeToken && buyTokenDTO.expectedNativeToken.comparedTo(nativeTokensToBuy) < 0) {
84
84
  throw new SlippageToleranceExceededError(
85
- "Gala tokens expected to perform this operation are less than the actual amount required."
85
+ `expected ${buyTokenDTO.expectedNativeToken.toString()}, but at least ${nativeTokensToBuy.toString()} are required to complete this operation. Increase the expected amount or adjust your slippage tolerance.`
86
86
  );
87
87
  }
88
88
 
@@ -13,7 +13,6 @@
13
13
  * limitations under the License.
14
14
  */
15
15
  import {
16
- GalaChainResponse,
17
16
  TokenBalance,
18
17
  TokenClass,
19
18
  TokenClassKey,
@@ -23,11 +22,12 @@ import {
23
22
  asValidUserAlias,
24
23
  randomUniqueKey
25
24
  } from "@gala-chain/api";
26
- import { currency, fixture, users } from "@gala-chain/test";
25
+ import { InvalidDecimalError } from "@gala-chain/chaincode";
26
+ import { currency, fixture, transactionError, transactionSuccess, users } from "@gala-chain/test";
27
27
  import BigNumber from "bignumber.js";
28
28
  import { plainToInstance } from "class-transformer";
29
29
 
30
- import { LaunchpadFeeConfig, LaunchpadSale, NativeTokenQuantityDto } from "../../api/types";
30
+ import { LaunchpadFeeConfig, LaunchpadSale, NativeTokenQuantityDto, TradeResDto } from "../../api/types";
31
31
  import { LaunchpadContract } from "../LaunchpadContract";
32
32
  import launchpadgala from "../test/launchpadgala";
33
33
 
@@ -63,7 +63,7 @@ describe("buyWithNative", () => {
63
63
  // Initialize sale with manual values
64
64
  sale = new LaunchpadSale(
65
65
  vaultAddress,
66
- launchpadGalaInstance.instanceKeyObj(),
66
+ currencyInstance.instanceKeyObj(),
67
67
  undefined,
68
68
  users.testUser1.identityKey
69
69
  );
@@ -93,7 +93,79 @@ describe("buyWithNative", () => {
93
93
  });
94
94
  });
95
95
 
96
- test("User should be able to buy tokens with providing native gala , without fee configured", async () => {
96
+ it("should round buy when meme token has 0 decimals and bonding curve produces fractional quantity", async () => {
97
+ // Given
98
+ const zeroDecimalCurrencyClass = plainToInstance(TokenClass, {
99
+ ...currency.tokenClassPlain(),
100
+ decimals: 0 // Integer-only meme token
101
+ });
102
+
103
+ const { ctx, contract } = fixture(LaunchpadContract)
104
+ .registeredUsers(users.testUser1)
105
+ .savedState(
106
+ currencyInstance,
107
+ zeroDecimalCurrencyClass,
108
+ launchpadGalaInstance,
109
+ launchpadGalaClass,
110
+ sale,
111
+ salelaunchpadGalaBalance,
112
+ saleCurrencyBalance,
113
+ userlaunchpadGalaBalance,
114
+ userCurrencyBalance
115
+ );
116
+
117
+ // Use a native token amount that will produce fractional meme tokens from bonding curve
118
+ const dto = new NativeTokenQuantityDto(vaultAddress, new BigNumber("0.01"));
119
+ dto.uniqueKey = randomUniqueKey();
120
+ dto.sign(users.testUser1.privateKey);
121
+
122
+ // When
123
+ const buyTokenRes = await contract.BuyWithNative(ctx, dto);
124
+
125
+ // Then - Expect code to round transferToken qty to decimal limit specified by TokenClass
126
+ expect(buyTokenRes).not.toEqual(
127
+ transactionError(
128
+ new InvalidDecimalError(new BigNumber("605.60177406237267161"), zeroDecimalCurrencyClass.decimals)
129
+ )
130
+ );
131
+ });
132
+
133
+ it("should reject buy when input dto has higher fractional precision than GALA TokenClass", async () => {
134
+ // Given
135
+ const zeroDecimalLaunchpadClass = plainToInstance(TokenClass, {
136
+ ...launchpadgala.tokenClassPlain(),
137
+ decimals: 0 // Integer-only meme token
138
+ });
139
+
140
+ const { ctx, contract } = fixture(LaunchpadContract)
141
+ .registeredUsers(users.testUser1)
142
+ .savedState(
143
+ currencyInstance,
144
+ currencyClass,
145
+ zeroDecimalLaunchpadClass,
146
+ launchpadGalaInstance,
147
+ sale,
148
+ salelaunchpadGalaBalance,
149
+ saleCurrencyBalance,
150
+ userlaunchpadGalaBalance,
151
+ userCurrencyBalance
152
+ );
153
+
154
+ // Use a native token amount that will produce fractional meme tokens from bonding curve
155
+ const dto = new NativeTokenQuantityDto(vaultAddress, new BigNumber("0.01"));
156
+ dto.uniqueKey = randomUniqueKey();
157
+ dto.sign(users.testUser1.privateKey);
158
+
159
+ // When
160
+ const buyTokenRes = await contract.BuyWithNative(ctx, dto);
161
+
162
+ // Then - Expect error due to decimal precision mismatch
163
+ expect(buyTokenRes).toEqual(
164
+ transactionError(new InvalidDecimalError(dto.nativeTokenQuantity, zeroDecimalLaunchpadClass.decimals))
165
+ );
166
+ });
167
+
168
+ test("User buys tokens by providing native gala, without fee needing to be configured", async () => {
97
169
  //Given
98
170
  const { ctx, contract } = fixture(LaunchpadContract)
99
171
  .registeredUsers(users.testUser1)
@@ -114,27 +186,28 @@ describe("buyWithNative", () => {
114
186
  dto.uniqueKey = randomUniqueKey();
115
187
  dto.sign(users.testUser1.privateKey);
116
188
 
117
- //When
118
- const buyTokenRes = await contract.BuyWithNative(ctx, dto);
119
-
120
- //Then
121
- expect(buyTokenRes.Data).toMatchObject({
189
+ const expectedResponse = plainToInstance(TradeResDto, {
122
190
  inputQuantity: "150",
123
191
  totalFees: "0.00000000",
124
- outputQuantity: "2101667.8890651635002",
192
+ totalTokenSold: "2101667.8890651635",
193
+ outputQuantity: "2101667.8890651635",
125
194
  tokenName: "AUTOMATEDTESTCOIN",
126
195
  tradeType: "Buy",
127
196
  vaultAddress: "service|GALA$Unit$none$none$launchpad",
128
197
  userAddress: "client|testUser1",
129
198
  isFinalized: false,
130
- functionName: "BuyWithNative"
199
+ functionName: "BuyWithNative",
200
+ uniqueKey: dto.uniqueKey
131
201
  });
132
202
 
133
- expect(buyTokenRes.Data?.inputQuantity).toEqual("150");
134
- expect(buyTokenRes.Data?.outputQuantity).toEqual("2101667.8890651635002");
203
+ //When
204
+ const buyTokenRes = await contract.BuyWithNative(ctx, dto);
205
+
206
+ //Then
207
+ expect(buyTokenRes).toEqual(transactionSuccess(expectedResponse));
135
208
  });
136
209
 
137
- test("User should be able to buy tokens , fee configured check", async () => {
210
+ test("User buys tokens, configured fee is checked", async () => {
138
211
  //Given
139
212
  const launchpadConfig = new LaunchpadFeeConfig(users.testUser2.identityKey, Number("0.32"), [
140
213
  users.testUser2.identityKey
@@ -159,25 +232,25 @@ describe("buyWithNative", () => {
159
232
  dto.uniqueKey = randomUniqueKey();
160
233
  dto.sign(users.testUser1.privateKey);
161
234
 
162
- //When
163
- const buyTokenRes = await contract.BuyWithNative(ctx, dto);
164
-
165
- //Then
166
- expect(buyTokenRes.Data).toMatchObject({
235
+ const expectedResponse = plainToInstance(TradeResDto, {
167
236
  inputQuantity: "1000",
168
237
  totalFees: "320.00000000",
169
- outputQuantity: "3663321.3628130557168",
238
+ totalTokenSold: "3663321.3628130557",
239
+ outputQuantity: "3663321.3628130557",
170
240
  tokenName: "AUTOMATEDTESTCOIN",
171
241
  tradeType: "Buy",
172
242
  vaultAddress: "service|GALA$Unit$none$none$launchpad",
173
243
  userAddress: "client|testUser1",
174
244
  isFinalized: false,
175
- functionName: "BuyWithNative"
245
+ functionName: "BuyWithNative",
246
+ uniqueKey: dto.uniqueKey
176
247
  });
177
248
 
178
- expect(buyTokenRes.Data?.totalFees).toEqual("320.00000000");
179
- expect(buyTokenRes.Data?.inputQuantity).toEqual("1000");
180
- expect(buyTokenRes.Data?.outputQuantity).toEqual("3663321.3628130557168");
249
+ //When
250
+ const buyTokenRes = await contract.BuyWithNative(ctx, dto);
251
+
252
+ //Then
253
+ expect(buyTokenRes).toEqual(transactionSuccess(expectedResponse));
181
254
  });
182
255
 
183
256
  it("should throw error if user has insufficient funds incuding the transaction fees", async () => {
@@ -211,7 +284,7 @@ describe("buyWithNative", () => {
211
284
 
212
285
  //Then
213
286
  expect(buyTokenRes).toEqual(
214
- GalaChainResponse.Error(
287
+ transactionError(
215
288
  new ValidationFailedError(
216
289
  "Insufficient balance: Total amount required including fee is 2166101.31430784"
217
290
  )
@@ -12,11 +12,12 @@
12
12
  * See the License for the specific language governing permissions and
13
13
  * limitations under the License.
14
14
  */
15
- import { ValidationFailedError } from "@gala-chain/api";
15
+ import { TokenClass, ValidationFailedError } from "@gala-chain/api";
16
16
  import {
17
17
  GalaChainContext,
18
18
  fetchOrCreateBalance,
19
19
  fetchTokenClass,
20
+ getObjectByKey,
20
21
  putChainObject,
21
22
  transferToken
22
23
  } from "@gala-chain/chaincode";
@@ -65,11 +66,24 @@ export async function buyWithNative(
65
66
  const nativeToken = sale.fetchNativeTokenInstanceKey();
66
67
  const memeToken = sale.fetchSellingTokenInstanceKey();
67
68
 
69
+ // Round tokensToBuy based on decimals property of sellToken TokenClass entry,
70
+ // because otherwise `transferToken()` call below will fail with
71
+ // an INVALID_DECIMALS error.
72
+ const { collection, category, type, additionalKey } = sale.sellingToken;
73
+
74
+ const memeTokenClass = await getObjectByKey(
75
+ ctx,
76
+ TokenClass,
77
+ TokenClass.getCompositeKeyFromParts(TokenClass.INDEX_KEY, [collection, category, type, additionalKey])
78
+ );
79
+
80
+ tokensToBuy = tokensToBuy.decimalPlaces(memeTokenClass.decimals);
81
+
68
82
  // If vault has fewer tokens than what user wants to buy, cap the purchase
69
83
  if (tokensLeftInVault.comparedTo(tokensToBuy) <= 0) {
70
- tokensToBuy = tokensLeftInVault;
71
- const nativeTokensrequiredToBuyDto = new ExactTokenQuantityDto(buyTokenDTO.vaultAddress, tokensToBuy);
72
- const callNativeTokenInResult = await callNativeTokenIn(ctx, nativeTokensrequiredToBuyDto);
84
+ tokensToBuy = tokensLeftInVault.decimalPlaces(memeTokenClass.decimals);
85
+ const nativeTokensRequiredToBuyDto = new ExactTokenQuantityDto(buyTokenDTO.vaultAddress, tokensToBuy);
86
+ const callNativeTokenInResult = await callNativeTokenIn(ctx, nativeTokensRequiredToBuyDto);
73
87
  transactionFees = callMemeTokenOutResult.extraFees.transactionFees;
74
88
  buyTokenDTO.nativeTokenQuantity = new BigNumber(callNativeTokenInResult.calculatedQuantity);
75
89
  isSaleFinalized = true;
@@ -80,13 +94,14 @@ export async function buyWithNative(
80
94
  buyTokenDTO.nativeTokenQuantity
81
95
  .plus(new BigNumber(sale.nativeTokenQuantity))
82
96
  .gte(new BigNumber(LaunchpadSale.MARKET_CAP))
83
- )
97
+ ) {
84
98
  isSaleFinalized = true;
99
+ }
85
100
 
86
101
  // Check for slippage condition
87
102
  if (buyTokenDTO.expectedToken && buyTokenDTO.expectedToken.comparedTo(tokensToBuy) > 0) {
88
103
  throw new SlippageToleranceExceededError(
89
- "Tokens expected from this operation are more than the actual amount that will be provided."
104
+ `expected ${buyTokenDTO.expectedToken.toString()}, but only ${tokensToBuy.toString()} tokens can be provided. Reduce the expected amount or adjust your slippage tolerance.`
90
105
  );
91
106
  }
92
107