@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
@@ -0,0 +1,276 @@
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 { currency, fixture, users } from "@gala-chain/test";
25
+ import BigNumber from "bignumber.js";
26
+ import { plainToInstance } from "class-transformer";
27
+
28
+ import { ExactTokenQuantityDto, LaunchpadSale } from "../../api/types";
29
+ import { LaunchpadContract } from "../LaunchpadContract";
30
+ import launchpadgala from "../test/launchpadgala";
31
+
32
+ describe("callNativeTokenOut", () => {
33
+ let currencyClass: TokenClass;
34
+ let currencyInstance: TokenInstance;
35
+ let launchpadGalaClass: TokenClass;
36
+ let launchpadGalaInstance: TokenInstance;
37
+ let launchpadGalaClassKey: TokenClassKey;
38
+ let vaultAddress: UserAlias;
39
+ let sale: LaunchpadSale;
40
+ let salelaunchpadGalaBalance: TokenBalance;
41
+ let saleCurrencyBalance: TokenBalance;
42
+ let userlaunchpadGalaBalance: TokenBalance;
43
+ let userCurrencyBalance: TokenBalance;
44
+
45
+ beforeEach(() => {
46
+ currencyClass = currency.tokenClass();
47
+ currencyInstance = currency.tokenInstance();
48
+ launchpadGalaClass = launchpadgala.tokenClass();
49
+ launchpadGalaInstance = launchpadgala.tokenInstance();
50
+ launchpadGalaClassKey = launchpadgala.tokenClassKey();
51
+
52
+ vaultAddress = asValidUserAlias(`service|${launchpadGalaClassKey.toStringKey()}$launchpad`);
53
+
54
+ // Initialize sale with manual values
55
+ sale = new LaunchpadSale(
56
+ vaultAddress,
57
+ currencyInstance.instanceKeyObj(),
58
+ undefined,
59
+ users.testUser1.identityKey
60
+ );
61
+
62
+ // Create sale balances - sale needs tokens to pay out
63
+ salelaunchpadGalaBalance = plainToInstance(TokenBalance, {
64
+ ...launchpadgala.tokenBalance(),
65
+ owner: vaultAddress,
66
+ quantity: new BigNumber("97.238975330345368866")
67
+ });
68
+ saleCurrencyBalance = plainToInstance(TokenBalance, {
69
+ ...currency.tokenBalance(),
70
+ owner: vaultAddress,
71
+ quantity: new BigNumber("188809.790718")
72
+ });
73
+
74
+ // Create user balances - user needs tokens to swap
75
+ userlaunchpadGalaBalance = plainToInstance(TokenBalance, {
76
+ ...launchpadgala.tokenBalance(),
77
+ owner: users.testUser1.identityKey,
78
+ quantity: new BigNumber("10000") // User has 10k launchpadGala tokens
79
+ });
80
+ userCurrencyBalance = plainToInstance(TokenBalance, {
81
+ ...currency.tokenBalance(),
82
+ owner: users.testUser1.identityKey,
83
+ quantity: new BigNumber("10000") // User has 10k CURRENCY tokens
84
+ });
85
+ });
86
+
87
+ it("should calculate native tokens received for token sale", async () => {
88
+ // Given
89
+ sale.buyToken(new BigNumber("1000"), new BigNumber(0.01)); // Pre-sell some tokens
90
+ const { ctx, contract } = fixture(LaunchpadContract)
91
+ .registeredUsers(users.testUser1)
92
+ .savedState(
93
+ currencyClass,
94
+ currencyInstance,
95
+ launchpadGalaClass,
96
+ launchpadGalaInstance,
97
+ sale,
98
+ salelaunchpadGalaBalance,
99
+ saleCurrencyBalance,
100
+ userlaunchpadGalaBalance,
101
+ userCurrencyBalance
102
+ );
103
+
104
+ const callNativeTokenOutDto = new ExactTokenQuantityDto(vaultAddress, new BigNumber(100));
105
+ callNativeTokenOutDto.uniqueKey = randomUniqueKey();
106
+
107
+ const signedDto = callNativeTokenOutDto.signed(users.testUser1.privateKey);
108
+
109
+ // When
110
+ const response = await contract.CallNativeTokenOut(ctx, signedDto);
111
+
112
+ // Then
113
+ expect(response.Status).toBe(1);
114
+ expect(response.Data).toHaveProperty("calculatedQuantity");
115
+ expect(response.Data).toHaveProperty("extraFees");
116
+ expect(response.Data?.extraFees).toHaveProperty("reverseBondingCurve");
117
+ expect(response.Data?.extraFees).toHaveProperty("transactionFees");
118
+ });
119
+
120
+ it("should calculate correct native tokens for small token sale", async () => {
121
+ // Given
122
+ sale.buyToken(new BigNumber("500"), new BigNumber(0.01)); // Pre-sell some tokens
123
+ const { ctx, contract } = fixture(LaunchpadContract)
124
+ .registeredUsers(users.testUser1)
125
+ .savedState(
126
+ currencyClass,
127
+ currencyInstance,
128
+ launchpadGalaClass,
129
+ launchpadGalaInstance,
130
+ sale,
131
+ salelaunchpadGalaBalance,
132
+ saleCurrencyBalance,
133
+ userlaunchpadGalaBalance,
134
+ userCurrencyBalance
135
+ );
136
+
137
+ const callNativeTokenOutDto = new ExactTokenQuantityDto(vaultAddress, new BigNumber(1));
138
+ callNativeTokenOutDto.uniqueKey = randomUniqueKey();
139
+
140
+ const signedDto = callNativeTokenOutDto.signed(users.testUser1.privateKey);
141
+
142
+ // When
143
+ const response = await contract.CallNativeTokenOut(ctx, signedDto);
144
+
145
+ // Then
146
+ expect(response.Status).toBe(1);
147
+ expect(response.Data?.calculatedQuantity).toBeDefined();
148
+ expect(new BigNumber(response.Data?.calculatedQuantity || "0").isFinite()).toBe(true);
149
+ });
150
+
151
+ it("should handle selling all available tokens", async () => {
152
+ // Given
153
+ sale.buyToken(new BigNumber("300"), new BigNumber(0.01)); // Pre-sell some tokens
154
+ const { ctx, contract } = fixture(LaunchpadContract)
155
+ .registeredUsers(users.testUser1)
156
+ .savedState(
157
+ currencyClass,
158
+ currencyInstance,
159
+ launchpadGalaClass,
160
+ launchpadGalaInstance,
161
+ sale,
162
+ salelaunchpadGalaBalance,
163
+ saleCurrencyBalance,
164
+ userlaunchpadGalaBalance,
165
+ userCurrencyBalance
166
+ );
167
+
168
+ // Try to sell more tokens than were bought
169
+ const callNativeTokenOutDto = new ExactTokenQuantityDto(vaultAddress, new BigNumber(500));
170
+ callNativeTokenOutDto.uniqueKey = randomUniqueKey();
171
+
172
+ const signedDto = callNativeTokenOutDto.signed(users.testUser1.privateKey);
173
+
174
+ // When
175
+ const response = await contract.CallNativeTokenOut(ctx, signedDto);
176
+
177
+ // Then
178
+ expect(response.Status).toBe(1);
179
+ expect(response.Data?.calculatedQuantity).toBeDefined();
180
+ });
181
+
182
+ it("should handle minimal token sale", async () => {
183
+ // Given
184
+ sale.buyToken(new BigNumber("100"), new BigNumber(0.01)); // Pre-sell some tokens
185
+ const { ctx, contract } = fixture(LaunchpadContract)
186
+ .registeredUsers(users.testUser1)
187
+ .savedState(
188
+ currencyClass,
189
+ currencyInstance,
190
+ launchpadGalaClass,
191
+ launchpadGalaInstance,
192
+ sale,
193
+ salelaunchpadGalaBalance,
194
+ saleCurrencyBalance,
195
+ userlaunchpadGalaBalance,
196
+ userCurrencyBalance
197
+ );
198
+
199
+ const callNativeTokenOutDto = new ExactTokenQuantityDto(vaultAddress, new BigNumber("0.001"));
200
+ callNativeTokenOutDto.uniqueKey = randomUniqueKey();
201
+
202
+ const signedDto = callNativeTokenOutDto.signed(users.testUser1.privateKey);
203
+
204
+ // When
205
+ const response = await contract.CallNativeTokenOut(ctx, signedDto);
206
+
207
+ // Then
208
+ expect(response.Status).toBe(1);
209
+ expect(response.Data?.calculatedQuantity).toBeDefined();
210
+ expect(new BigNumber(response.Data?.calculatedQuantity || "0").isFinite()).toBe(true);
211
+ });
212
+
213
+ it("should calculate correct fees for token sale", async () => {
214
+ // Given
215
+ sale.buyToken(new BigNumber("800"), new BigNumber(0.01)); // Pre-sell some tokens
216
+ const { ctx, contract } = fixture(LaunchpadContract)
217
+ .registeredUsers(users.testUser1)
218
+ .savedState(
219
+ currencyClass,
220
+ currencyInstance,
221
+ launchpadGalaClass,
222
+ launchpadGalaInstance,
223
+ sale,
224
+ salelaunchpadGalaBalance,
225
+ saleCurrencyBalance,
226
+ userlaunchpadGalaBalance,
227
+ userCurrencyBalance
228
+ );
229
+
230
+ const callNativeTokenOutDto = new ExactTokenQuantityDto(vaultAddress, new BigNumber(50));
231
+ callNativeTokenOutDto.uniqueKey = randomUniqueKey();
232
+
233
+ const signedDto = callNativeTokenOutDto.signed(users.testUser1.privateKey);
234
+
235
+ // When
236
+ const response = await contract.CallNativeTokenOut(ctx, signedDto);
237
+
238
+ // Then
239
+ expect(response.Status).toBe(1);
240
+ expect(response.Data?.extraFees.reverseBondingCurve).toBeDefined();
241
+ expect(response.Data?.extraFees.transactionFees).toBeDefined();
242
+ expect(new BigNumber(response.Data?.extraFees.reverseBondingCurve || "0").isFinite()).toBe(true);
243
+ expect(new BigNumber(response.Data?.extraFees.transactionFees || "0").isFinite()).toBe(true);
244
+ });
245
+
246
+ it("should handle edge case with large token sale relative to tokens sold", async () => {
247
+ // Given
248
+ sale.buyToken(new BigNumber("200"), new BigNumber(0.01)); // Pre-sell smaller amount
249
+ const { ctx, contract } = fixture(LaunchpadContract)
250
+ .registeredUsers(users.testUser1)
251
+ .savedState(
252
+ currencyClass,
253
+ currencyInstance,
254
+ launchpadGalaClass,
255
+ launchpadGalaInstance,
256
+ sale,
257
+ salelaunchpadGalaBalance,
258
+ saleCurrencyBalance,
259
+ userlaunchpadGalaBalance,
260
+ userCurrencyBalance
261
+ );
262
+
263
+ const callNativeTokenOutDto = new ExactTokenQuantityDto(vaultAddress, new BigNumber(150));
264
+ callNativeTokenOutDto.uniqueKey = randomUniqueKey();
265
+
266
+ const signedDto = callNativeTokenOutDto.signed(users.testUser1.privateKey);
267
+
268
+ // When
269
+ const response = await contract.CallNativeTokenOut(ctx, signedDto);
270
+
271
+ // Then
272
+ expect(response.Status).toBe(1);
273
+ expect(response.Data?.calculatedQuantity).toBeDefined();
274
+ expect(new BigNumber(response.Data?.calculatedQuantity || "0").isFinite()).toBe(true);
275
+ });
276
+ });
@@ -0,0 +1,202 @@
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 { ConfigureLaunchpadFeeAddressDto } from "../../api/types";
19
+ import { LaunchpadContract } from "../LaunchpadContract";
20
+
21
+ describe("configureLaunchpadFeeAddress", () => {
22
+ it("should create initial fee configuration with valid inputs", async () => {
23
+ // Given
24
+ const { ctx, contract } = fixture(LaunchpadContract)
25
+ .caClientIdentity("test-admin", "CuratorOrg")
26
+ .registeredUsers(users.admin);
27
+
28
+ const configDto = new ConfigureLaunchpadFeeAddressDto();
29
+ configDto.newPlatformFeeAddress = asValidUserAlias("client|feeAddress");
30
+ configDto.newFeeAmount = 0.01;
31
+ configDto.newAuthorities = [users.admin.identityKey];
32
+ configDto.uniqueKey = randomUniqueKey();
33
+ const signedDto = configDto.signed(users.admin.privateKey);
34
+
35
+ // When
36
+ const response = await contract.ConfigureLaunchpadFeeAddress(ctx, signedDto);
37
+
38
+ // Then
39
+ expect(response.Status).toBe(1);
40
+ expect(response.Data?.feeAddress).toBe("client|feeAddress");
41
+ expect(response.Data?.feeAmount).toBe(0.01);
42
+ expect(response.Data?.authorities).toContain(users.admin.identityKey);
43
+ });
44
+
45
+ it("should update existing fee configuration", async () => {
46
+ // Given
47
+ const existingConfig = new ConfigureLaunchpadFeeAddressDto();
48
+ existingConfig.newPlatformFeeAddress = asValidUserAlias("client|oldFeeAddress");
49
+ existingConfig.newFeeAmount = 0.01;
50
+ existingConfig.newAuthorities = [users.admin.identityKey];
51
+ existingConfig.uniqueKey = randomUniqueKey();
52
+ const signedExistingDto = existingConfig.signed(users.admin.privateKey);
53
+
54
+ const { ctx, contract } = fixture(LaunchpadContract)
55
+ .caClientIdentity("test-admin", "CuratorOrg")
56
+ .registeredUsers(users.admin);
57
+
58
+ // Create initial config
59
+ await contract.ConfigureLaunchpadFeeAddress(ctx, signedExistingDto);
60
+
61
+ // Update with new values
62
+ const updateDto = new ConfigureLaunchpadFeeAddressDto();
63
+ updateDto.newPlatformFeeAddress = asValidUserAlias("client|newFeeAddress");
64
+ updateDto.newFeeAmount = 0.02;
65
+ updateDto.newAuthorities = [users.admin.identityKey, users.testUser2.identityKey];
66
+ updateDto.uniqueKey = randomUniqueKey();
67
+ const signedUpdateDto = updateDto.signed(users.admin.privateKey);
68
+
69
+ // When
70
+ const response = await contract.ConfigureLaunchpadFeeAddress(ctx, signedUpdateDto);
71
+
72
+ // Then
73
+ expect(response.Status).toBe(1);
74
+ expect(response.Data?.feeAddress).toBe("client|newFeeAddress");
75
+ expect(response.Data?.feeAmount).toBe(0.02);
76
+ expect(response.Data?.authorities).toContain(users.testUser2.identityKey);
77
+ });
78
+
79
+ it("should update only fee address when other fields are not provided", async () => {
80
+ // Given
81
+ const existingConfig = new ConfigureLaunchpadFeeAddressDto();
82
+ existingConfig.newPlatformFeeAddress = asValidUserAlias("client|oldFeeAddress");
83
+ existingConfig.newFeeAmount = 0.01;
84
+ existingConfig.newAuthorities = [users.admin.identityKey];
85
+ existingConfig.uniqueKey = randomUniqueKey();
86
+ const signedExistingDto = existingConfig.signed(users.admin.privateKey);
87
+
88
+ const { ctx, contract } = fixture(LaunchpadContract)
89
+ .caClientIdentity("test-admin", "CuratorOrg")
90
+ .registeredUsers(users.admin);
91
+
92
+ // Create initial config
93
+ await contract.ConfigureLaunchpadFeeAddress(ctx, signedExistingDto);
94
+
95
+ // Update only fee address
96
+ const updateDto = new ConfigureLaunchpadFeeAddressDto();
97
+ updateDto.newPlatformFeeAddress = asValidUserAlias("client|newOnlyAddress");
98
+ updateDto.uniqueKey = randomUniqueKey();
99
+ const signedUpdateDto = updateDto.signed(users.admin.privateKey);
100
+
101
+ // When
102
+ const response = await contract.ConfigureLaunchpadFeeAddress(ctx, signedUpdateDto);
103
+
104
+ // Then
105
+ expect(response.Status).toBe(1);
106
+ expect(response.Data?.feeAddress).toBe("client|newOnlyAddress");
107
+ expect(response.Data?.feeAmount).toBe(0.01); // Should remain the same
108
+ });
109
+
110
+ it("should update only fee amount when other fields are not provided", async () => {
111
+ // Given
112
+ const existingConfig = new ConfigureLaunchpadFeeAddressDto();
113
+ existingConfig.newPlatformFeeAddress = asValidUserAlias("client|feeAddress");
114
+ existingConfig.newFeeAmount = 0.01;
115
+ existingConfig.newAuthorities = [users.admin.identityKey];
116
+ existingConfig.uniqueKey = randomUniqueKey();
117
+ const signedExistingDto = existingConfig.signed(users.admin.privateKey);
118
+
119
+ const { ctx, contract } = fixture(LaunchpadContract)
120
+ .caClientIdentity("test-admin", "CuratorOrg")
121
+ .registeredUsers(users.admin);
122
+
123
+ // Create initial config
124
+ await contract.ConfigureLaunchpadFeeAddress(ctx, signedExistingDto);
125
+
126
+ // Update only fee amount
127
+ const updateDto = new ConfigureLaunchpadFeeAddressDto();
128
+ updateDto.newFeeAmount = 0.05;
129
+ updateDto.uniqueKey = randomUniqueKey();
130
+ const signedUpdateDto = updateDto.signed(users.admin.privateKey);
131
+
132
+ // When
133
+ const response = await contract.ConfigureLaunchpadFeeAddress(ctx, signedUpdateDto);
134
+
135
+ // Then
136
+ expect(response.Status).toBe(1);
137
+ expect(response.Data?.feeAddress).toBe("client|feeAddress"); // Should remain the same
138
+ expect(response.Data?.feeAmount).toBe(0.05);
139
+ });
140
+
141
+ it("should handle zero fee amount configuration after initial setup", async () => {
142
+ // Given - Create initial config with non-zero fee first
143
+ const { ctx, contract } = fixture(LaunchpadContract)
144
+ .caClientIdentity("test-admin", "CuratorOrg")
145
+ .registeredUsers(users.admin);
146
+
147
+ const initialConfigDto = new ConfigureLaunchpadFeeAddressDto();
148
+ initialConfigDto.newPlatformFeeAddress = asValidUserAlias("client|feeAddress");
149
+ initialConfigDto.newFeeAmount = 0.01;
150
+ initialConfigDto.newAuthorities = [users.admin.identityKey];
151
+ initialConfigDto.uniqueKey = randomUniqueKey();
152
+ const signedInitialDto = initialConfigDto.signed(users.admin.privateKey);
153
+
154
+ await contract.ConfigureLaunchpadFeeAddress(ctx, signedInitialDto);
155
+
156
+ // Update with zero fee amount
157
+ const configDto = new ConfigureLaunchpadFeeAddressDto();
158
+ configDto.newPlatformFeeAddress = asValidUserAlias("client|feeAddress"); // Keep same address
159
+ configDto.newFeeAmount = 0;
160
+ configDto.uniqueKey = randomUniqueKey();
161
+ const signedDto = configDto.signed(users.admin.privateKey);
162
+
163
+ // When
164
+ const response = await contract.ConfigureLaunchpadFeeAddress(ctx, signedDto);
165
+
166
+ // Then
167
+ expect(response.Status).toBe(1);
168
+ expect(response.Data?.feeAmount).toBe(0);
169
+ });
170
+
171
+ it("should handle multiple authorities in configuration", async () => {
172
+ // Given
173
+ const { ctx, contract } = fixture(LaunchpadContract)
174
+ .caClientIdentity("test-admin", "CuratorOrg")
175
+ .registeredUsers(users.admin, users.testUser1, users.testUser2, users.testUser3);
176
+
177
+ const authorities = [
178
+ users.admin.identityKey,
179
+ users.testUser1.identityKey,
180
+ users.testUser2.identityKey,
181
+ users.testUser3.identityKey
182
+ ];
183
+
184
+ const configDto = new ConfigureLaunchpadFeeAddressDto();
185
+ configDto.newPlatformFeeAddress = asValidUserAlias("client|feeAddress");
186
+ configDto.newFeeAmount = 0.01;
187
+ configDto.newAuthorities = authorities;
188
+ configDto.uniqueKey = randomUniqueKey();
189
+ const signedDto = configDto.signed(users.admin.privateKey);
190
+
191
+ // When
192
+ const response = await contract.ConfigureLaunchpadFeeAddress(ctx, signedDto);
193
+
194
+ // Then
195
+ expect(response.Status).toBe(1);
196
+ expect(response.Data?.authorities).toHaveLength(4);
197
+ expect(response.Data?.authorities).toContain(users.admin.identityKey);
198
+ expect(response.Data?.authorities).toContain(users.testUser1.identityKey);
199
+ expect(response.Data?.authorities).toContain(users.testUser2.identityKey);
200
+ expect(response.Data?.authorities).toContain(users.testUser3.identityKey);
201
+ });
202
+ });