@gala-chain/launchpad 1.0.9 → 1.0.10

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 (27) hide show
  1. package/CLAUDE.md +279 -0
  2. package/lib/package.json +2 -2
  3. package/lib/src/chaincode/launchpad/buyWithNative.d.ts.map +1 -1
  4. package/lib/src/chaincode/launchpad/buyWithNative.js +11 -4
  5. package/lib/src/chaincode/launchpad/buyWithNative.js.map +1 -1
  6. package/lib/src/chaincode/launchpad/sellWithNative.d.ts.map +1 -1
  7. package/lib/src/chaincode/launchpad/sellWithNative.js +9 -1
  8. package/lib/src/chaincode/launchpad/sellWithNative.js.map +1 -1
  9. package/lib/src/chaincode/test/launchpadgala.js +1 -1
  10. package/lib/src/chaincode/test/launchpadgala.js.map +1 -1
  11. package/lib/tsconfig.tsbuildinfo +1 -1
  12. package/package.json +2 -2
  13. package/src/chaincode/launchpad/buyExactToken.spec.ts +90 -20
  14. package/src/chaincode/launchpad/buyWithNative.spec.ts +99 -25
  15. package/src/chaincode/launchpad/buyWithNative.ts +20 -5
  16. package/src/chaincode/launchpad/callMemeTokenIn.spec.ts +244 -0
  17. package/src/chaincode/launchpad/callMemeTokenOut.spec.ts +1 -1
  18. package/src/chaincode/launchpad/callNativeTokenIn.spec.ts +269 -0
  19. package/src/chaincode/launchpad/callNativeTokenOut.spec.ts +276 -0
  20. package/src/chaincode/launchpad/configureLaunchpadFeeConfig.spec.ts +202 -0
  21. package/src/chaincode/launchpad/createSale.spec.ts +259 -0
  22. package/src/chaincode/launchpad/fetchSaleDetails.spec.ts +141 -0
  23. package/src/chaincode/launchpad/finalizeTokenAllocation.spec.ts +126 -0
  24. package/src/chaincode/launchpad/sellExactToken.spec.ts +284 -0
  25. package/src/chaincode/launchpad/sellWithNative.spec.ts +329 -0
  26. package/src/chaincode/launchpad/sellWithNative.ts +22 -5
  27. package/src/chaincode/test/launchpadgala.ts +1 -1
@@ -0,0 +1,126 @@
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 { asValidUserAlias, randomUniqueKey } from "@gala-chain/api";
16
+ import { fixture, users } from "@gala-chain/test";
17
+
18
+ import { FinalizeTokenAllocationDto, LaunchpadFeeConfig } from "../../api/types";
19
+ import { LaunchpadContract } from "../LaunchpadContract";
20
+
21
+ describe("finalizeTokenAllocation", () => {
22
+ let feeConfig: LaunchpadFeeConfig;
23
+
24
+ beforeEach(() => {
25
+ feeConfig = new LaunchpadFeeConfig(asValidUserAlias("client|platformFeeAddress"), 0.01, [
26
+ users.admin.identityKey
27
+ ]);
28
+ });
29
+
30
+ it("should create new token allocation successfully", async () => {
31
+ // Given
32
+ const { ctx, contract } = fixture(LaunchpadContract)
33
+ .caClientIdentity("test-admin", "CuratorOrg")
34
+ .registeredUsers(users.admin)
35
+ .savedState(feeConfig);
36
+
37
+ const finalizeDto = new FinalizeTokenAllocationDto();
38
+ finalizeDto.platformFeePercentage = 0.02;
39
+ finalizeDto.ownerFeePercentage = 0.03;
40
+ finalizeDto.uniqueKey = randomUniqueKey();
41
+ const signedDto = finalizeDto.signed(users.admin.privateKey);
42
+
43
+ // When
44
+ const response = await contract.FinalizeTokenAllocation(ctx, signedDto);
45
+
46
+ // Then
47
+ expect(response.Status).toBe(1);
48
+ expect(response.Data?.platformFeePercentage).toBe(0.02);
49
+ expect(response.Data?.ownerAllocationPercentage).toBe(0.03);
50
+ });
51
+
52
+ it("should update existing token allocation", async () => {
53
+ // Given - Create existing allocation first
54
+ const { ctx, contract } = fixture(LaunchpadContract)
55
+ .caClientIdentity("test-admin", "CuratorOrg")
56
+ .registeredUsers(users.admin)
57
+ .savedState(feeConfig);
58
+
59
+ const initialDto = new FinalizeTokenAllocationDto();
60
+ initialDto.platformFeePercentage = 0.01;
61
+ initialDto.ownerFeePercentage = 0.02;
62
+ initialDto.uniqueKey = randomUniqueKey();
63
+ const signedInitialDto = initialDto.signed(users.admin.privateKey);
64
+
65
+ await contract.FinalizeTokenAllocation(ctx, signedInitialDto);
66
+
67
+ // Update with new values
68
+ const updateDto = new FinalizeTokenAllocationDto();
69
+ updateDto.platformFeePercentage = 0.05;
70
+ updateDto.ownerFeePercentage = 0.06;
71
+ updateDto.uniqueKey = randomUniqueKey();
72
+ const signedUpdateDto = updateDto.signed(users.admin.privateKey);
73
+
74
+ // When
75
+ const response = await contract.FinalizeTokenAllocation(ctx, signedUpdateDto);
76
+
77
+ // Then
78
+ expect(response.Status).toBe(1);
79
+ expect(response.Data?.platformFeePercentage).toBe(0.05);
80
+ expect(response.Data?.ownerAllocationPercentage).toBe(0.06);
81
+ });
82
+
83
+ it("should handle zero percentage allocations", async () => {
84
+ // Given
85
+ const { ctx, contract } = fixture(LaunchpadContract)
86
+ .caClientIdentity("test-admin", "CuratorOrg")
87
+ .registeredUsers(users.admin)
88
+ .savedState(feeConfig);
89
+
90
+ const finalizeDto = new FinalizeTokenAllocationDto();
91
+ finalizeDto.platformFeePercentage = 0;
92
+ finalizeDto.ownerFeePercentage = 0;
93
+ finalizeDto.uniqueKey = randomUniqueKey();
94
+ const signedDto = finalizeDto.signed(users.admin.privateKey);
95
+
96
+ // When
97
+ const response = await contract.FinalizeTokenAllocation(ctx, signedDto);
98
+
99
+ // Then
100
+ expect(response.Status).toBe(1);
101
+ expect(response.Data?.platformFeePercentage).toBe(0);
102
+ expect(response.Data?.ownerAllocationPercentage).toBe(0);
103
+ });
104
+
105
+ it("should handle high percentage allocations", async () => {
106
+ // Given
107
+ const { ctx, contract } = fixture(LaunchpadContract)
108
+ .caClientIdentity("test-admin", "CuratorOrg")
109
+ .registeredUsers(users.admin)
110
+ .savedState(feeConfig);
111
+
112
+ const finalizeDto = new FinalizeTokenAllocationDto();
113
+ finalizeDto.platformFeePercentage = 0.1; // 10%
114
+ finalizeDto.ownerFeePercentage = 0.15; // 15%
115
+ finalizeDto.uniqueKey = randomUniqueKey();
116
+ const signedDto = finalizeDto.signed(users.admin.privateKey);
117
+
118
+ // When
119
+ const response = await contract.FinalizeTokenAllocation(ctx, signedDto);
120
+
121
+ // Then
122
+ expect(response.Status).toBe(1);
123
+ expect(response.Data?.platformFeePercentage).toBe(0.1);
124
+ expect(response.Data?.ownerAllocationPercentage).toBe(0.15);
125
+ });
126
+ });
@@ -0,0 +1,284 @@
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
+ TokenBalance,
17
+ TokenClass,
18
+ TokenClassKey,
19
+ TokenInstance,
20
+ UserAlias,
21
+ asValidUserAlias,
22
+ randomUniqueKey
23
+ } from "@gala-chain/api";
24
+ import { InvalidDecimalError } from "@gala-chain/chaincode";
25
+ import { currency, fixture, transactionError, users } from "@gala-chain/test";
26
+ import BigNumber from "bignumber.js";
27
+ import { plainToInstance } from "class-transformer";
28
+
29
+ import { ExactTokenQuantityDto, LaunchpadSale } from "../../api/types";
30
+ import { LaunchpadContract } from "../LaunchpadContract";
31
+ import launchpadgala from "../test/launchpadgala";
32
+
33
+ describe("sellExactToken", () => {
34
+ let currencyClass: TokenClass;
35
+ let currencyInstance: TokenInstance;
36
+ let launchpadGalaClass: TokenClass;
37
+ let launchpadGalaInstance: TokenInstance;
38
+ let launchpadGalaClassKey: TokenClassKey;
39
+ let vaultAddress: UserAlias;
40
+ let sale: LaunchpadSale;
41
+ let salelaunchpadGalaBalance: TokenBalance;
42
+ let saleCurrencyBalance: TokenBalance;
43
+ let userlaunchpadGalaBalance: TokenBalance;
44
+ let userCurrencyBalance: TokenBalance;
45
+
46
+ beforeEach(() => {
47
+ currencyClass = currency.tokenClass();
48
+ currencyInstance = currency.tokenInstance();
49
+ launchpadGalaClass = launchpadgala.tokenClass();
50
+ launchpadGalaInstance = launchpadgala.tokenInstance();
51
+ launchpadGalaClassKey = launchpadgala.tokenClassKey();
52
+
53
+ vaultAddress = asValidUserAlias(`service|${launchpadGalaClassKey.toStringKey()}$launchpad`);
54
+
55
+ // Initialize sale with manual values
56
+ sale = new LaunchpadSale(
57
+ vaultAddress,
58
+ currencyInstance.instanceKeyObj(),
59
+ undefined,
60
+ users.testUser1.identityKey
61
+ );
62
+
63
+ // Create sale balances - sale needs tokens to pay out
64
+ salelaunchpadGalaBalance = plainToInstance(TokenBalance, {
65
+ ...launchpadgala.tokenBalance(),
66
+ owner: vaultAddress,
67
+ quantity: new BigNumber("1000000") // Large amount in vault
68
+ });
69
+ saleCurrencyBalance = plainToInstance(TokenBalance, {
70
+ ...currency.tokenBalance(),
71
+ owner: vaultAddress,
72
+ quantity: new BigNumber("1000000") // Large amount in vault
73
+ });
74
+
75
+ // Create user balances - user needs tokens to sell
76
+ userlaunchpadGalaBalance = plainToInstance(TokenBalance, {
77
+ ...launchpadgala.tokenBalance(),
78
+ owner: users.testUser1.identityKey,
79
+ quantity: new BigNumber("10000") // User has tokens to sell
80
+ });
81
+ userCurrencyBalance = plainToInstance(TokenBalance, {
82
+ ...currency.tokenBalance(),
83
+ owner: users.testUser1.identityKey,
84
+ quantity: new BigNumber("1000") // User has some CURRENCY tokens
85
+ });
86
+ });
87
+
88
+ it("should sell exact token amount successfully", async () => {
89
+ // Given
90
+ sale.buyToken(new BigNumber("1000"), new BigNumber("50")); // Users bought tokens, sale now has GALA
91
+ const { ctx, contract } = fixture(LaunchpadContract)
92
+ .registeredUsers(users.testUser1)
93
+ .savedState(
94
+ currencyClass,
95
+ currencyInstance,
96
+ launchpadGalaClass,
97
+ launchpadGalaInstance,
98
+ sale,
99
+ salelaunchpadGalaBalance,
100
+ saleCurrencyBalance,
101
+ userlaunchpadGalaBalance,
102
+ userCurrencyBalance
103
+ );
104
+
105
+ const sellDto = new ExactTokenQuantityDto(vaultAddress, new BigNumber("100"));
106
+ sellDto.uniqueKey = randomUniqueKey();
107
+ const signedDto = sellDto.signed(users.testUser1.privateKey);
108
+
109
+ // When
110
+ const response = await contract.SellExactToken(ctx, signedDto);
111
+
112
+ // Then
113
+ expect(response.Status).toBe(1);
114
+ expect(response.Data).toHaveProperty("outputQuantity");
115
+ expect(response.Data).toHaveProperty("inputQuantity", "100");
116
+ expect(response.Data).toHaveProperty("isFinalized");
117
+ });
118
+
119
+ it("should reject sell when native token has 0 decimals and bonding curve produces fractional quantity", async () => {
120
+ // Given
121
+ const zeroDecimalNativeClass = plainToInstance(TokenClass, {
122
+ ...launchpadgala.tokenClassPlain(),
123
+ decimals: 0 // Integer-only native token
124
+ });
125
+
126
+ // Simulate prior buys to establish sale state
127
+ sale.buyToken(new BigNumber("5000"), new BigNumber("100"));
128
+
129
+ const { ctx, contract } = fixture(LaunchpadContract)
130
+ .registeredUsers(users.testUser1)
131
+ .savedState(
132
+ currencyClass,
133
+ currencyInstance,
134
+ zeroDecimalNativeClass,
135
+ launchpadGalaInstance,
136
+ sale,
137
+ salelaunchpadGalaBalance,
138
+ saleCurrencyBalance,
139
+ userlaunchpadGalaBalance,
140
+ userCurrencyBalance
141
+ );
142
+
143
+ // Choose a token quantity that will produce fractional native tokens from bonding curve
144
+ const sellDto = new ExactTokenQuantityDto(vaultAddress, new BigNumber("100"));
145
+ sellDto.uniqueKey = randomUniqueKey();
146
+ const signedDto = sellDto.signed(users.testUser1.privateKey);
147
+
148
+ // When
149
+ const response = await contract.SellExactToken(ctx, signedDto);
150
+
151
+ // Then - Expect error due to decimal precision mismatch
152
+ expect(response).toEqual(
153
+ transactionError(new InvalidDecimalError(new BigNumber("0.00166022"), zeroDecimalNativeClass.decimals))
154
+ );
155
+ });
156
+
157
+ it("should reject sell when meme token has 0 decimals and input dto contains greater fractional precision", async () => {
158
+ // Given
159
+ const zeroDecimalMemeClass = plainToInstance(TokenClass, {
160
+ ...currency.tokenClassPlain(),
161
+ decimals: 0 // Integer-only native token
162
+ });
163
+
164
+ // Simulate prior buys to establish sale state
165
+ sale.buyToken(new BigNumber("5000"), new BigNumber("100"));
166
+
167
+ const { ctx, contract } = fixture(LaunchpadContract)
168
+ .registeredUsers(users.testUser1)
169
+ .savedState(
170
+ zeroDecimalMemeClass,
171
+ currencyInstance,
172
+ launchpadGalaClass,
173
+ launchpadGalaInstance,
174
+ sale,
175
+ salelaunchpadGalaBalance,
176
+ saleCurrencyBalance,
177
+ userlaunchpadGalaBalance,
178
+ userCurrencyBalance
179
+ );
180
+
181
+ // Choose a token quantity that will produce fractional native tokens from bonding curve
182
+ const sellDto = new ExactTokenQuantityDto(vaultAddress, new BigNumber("100.555"));
183
+ sellDto.uniqueKey = randomUniqueKey();
184
+ const signedDto = sellDto.signed(users.testUser1.privateKey);
185
+
186
+ // When
187
+ const response = await contract.SellExactToken(ctx, signedDto);
188
+
189
+ // Then - Expect error due to decimal precision mismatch
190
+ expect(response).toEqual(
191
+ transactionError(new InvalidDecimalError(sellDto.tokenQuantity, zeroDecimalMemeClass.decimals))
192
+ );
193
+ });
194
+
195
+ it("should handle small token sell amount", async () => {
196
+ // Given
197
+ sale.buyToken(new BigNumber("500"), new BigNumber("25")); // Users bought tokens, sale now has GALA
198
+ const { ctx, contract } = fixture(LaunchpadContract)
199
+ .registeredUsers(users.testUser1)
200
+ .savedState(
201
+ currencyClass,
202
+ currencyInstance,
203
+ launchpadGalaClass,
204
+ launchpadGalaInstance,
205
+ sale,
206
+ salelaunchpadGalaBalance,
207
+ saleCurrencyBalance,
208
+ userlaunchpadGalaBalance,
209
+ userCurrencyBalance
210
+ );
211
+
212
+ const sellDto = new ExactTokenQuantityDto(vaultAddress, new BigNumber("1"));
213
+ sellDto.uniqueKey = randomUniqueKey();
214
+ const signedDto = sellDto.signed(users.testUser1.privateKey);
215
+
216
+ // When
217
+ const response = await contract.SellExactToken(ctx, signedDto);
218
+
219
+ // Then
220
+ expect(response.Status).toBe(1);
221
+ expect(response.Data?.inputQuantity).toBe("1");
222
+ expect(new BigNumber(response.Data?.outputQuantity || "0").isPositive()).toBe(true);
223
+ });
224
+
225
+ it("should handle sell with expected native token parameter", async () => {
226
+ // Given
227
+ sale.buyToken(new BigNumber("800"), new BigNumber("40")); // Users bought tokens, sale now has GALA
228
+ const { ctx, contract } = fixture(LaunchpadContract)
229
+ .registeredUsers(users.testUser1)
230
+ .savedState(
231
+ currencyClass,
232
+ currencyInstance,
233
+ launchpadGalaClass,
234
+ launchpadGalaInstance,
235
+ sale,
236
+ salelaunchpadGalaBalance,
237
+ saleCurrencyBalance,
238
+ userlaunchpadGalaBalance,
239
+ userCurrencyBalance
240
+ );
241
+
242
+ const sellDto = new ExactTokenQuantityDto(vaultAddress, new BigNumber("50"));
243
+ sellDto.expectedNativeToken = new BigNumber("0.0001"); // Set realistic expectation for slippage protection
244
+ sellDto.uniqueKey = randomUniqueKey();
245
+ const signedDto = sellDto.signed(users.testUser1.privateKey);
246
+
247
+ // When
248
+ const response = await contract.SellExactToken(ctx, signedDto);
249
+
250
+ // Then
251
+ expect(response.Status).toBe(1);
252
+ expect(response.Data?.inputQuantity).toBe("50");
253
+ });
254
+
255
+ it("should handle large token sell amount", async () => {
256
+ // Given
257
+ sale.buyToken(new BigNumber("2000"), new BigNumber("100")); // Users bought tokens, sale now has GALA
258
+ const { ctx, contract } = fixture(LaunchpadContract)
259
+ .registeredUsers(users.testUser1)
260
+ .savedState(
261
+ currencyClass,
262
+ currencyInstance,
263
+ launchpadGalaClass,
264
+ launchpadGalaInstance,
265
+ sale,
266
+ salelaunchpadGalaBalance,
267
+ saleCurrencyBalance,
268
+ userlaunchpadGalaBalance,
269
+ userCurrencyBalance
270
+ );
271
+
272
+ const sellDto = new ExactTokenQuantityDto(vaultAddress, new BigNumber("500"));
273
+ sellDto.uniqueKey = randomUniqueKey();
274
+ const signedDto = sellDto.signed(users.testUser1.privateKey);
275
+
276
+ // When
277
+ const response = await contract.SellExactToken(ctx, signedDto);
278
+
279
+ // Then
280
+ expect(response.Status).toBe(1);
281
+ expect(response.Data?.inputQuantity).toBe("500");
282
+ expect(new BigNumber(response.Data?.outputQuantity || "0").isPositive()).toBe(true);
283
+ });
284
+ });