@gala-chain/launchpad 1.0.15 → 1.0.17
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.
- package/lib/package.json +1 -1
- package/lib/src/api/types/LaunchpadDtos.d.ts +2 -7
- package/lib/src/api/types/LaunchpadDtos.d.ts.map +1 -1
- package/lib/src/api/types/LaunchpadDtos.js +11 -28
- package/lib/src/api/types/LaunchpadDtos.js.map +1 -1
- package/lib/src/api/types/LaunchpadSale.d.ts +6 -1
- package/lib/src/api/types/LaunchpadSale.d.ts.map +1 -1
- package/lib/src/api/types/LaunchpadSale.js +21 -6
- package/lib/src/api/types/LaunchpadSale.js.map +1 -1
- package/lib/src/chaincode/launchpad/buyWithNative.d.ts.map +1 -1
- package/lib/src/chaincode/launchpad/buyWithNative.js +4 -1
- package/lib/src/chaincode/launchpad/buyWithNative.js.map +1 -1
- package/lib/src/chaincode/launchpad/callMemeTokenIn.d.ts.map +1 -1
- package/lib/src/chaincode/launchpad/callMemeTokenIn.js +6 -4
- package/lib/src/chaincode/launchpad/callMemeTokenIn.js.map +1 -1
- package/lib/src/chaincode/launchpad/callMemeTokenOut.d.ts.map +1 -1
- package/lib/src/chaincode/launchpad/callMemeTokenOut.js +19 -8
- package/lib/src/chaincode/launchpad/callMemeTokenOut.js.map +1 -1
- package/lib/src/chaincode/launchpad/callNativeTokenIn.d.ts.map +1 -1
- package/lib/src/chaincode/launchpad/callNativeTokenIn.js +13 -6
- package/lib/src/chaincode/launchpad/callNativeTokenIn.js.map +1 -1
- package/lib/src/chaincode/launchpad/callNativeTokenOut.d.ts.map +1 -1
- package/lib/src/chaincode/launchpad/callNativeTokenOut.js +6 -4
- package/lib/src/chaincode/launchpad/callNativeTokenOut.js.map +1 -1
- package/lib/src/chaincode/launchpad/createSale.d.ts.map +1 -1
- package/lib/src/chaincode/launchpad/createSale.js +8 -13
- package/lib/src/chaincode/launchpad/createSale.js.map +1 -1
- package/lib/src/chaincode/launchpad/finaliseSale.js +2 -2
- package/lib/src/chaincode/launchpad/finaliseSale.js.map +1 -1
- package/lib/src/chaincode/utils/launchpadSaleUtils.d.ts +1 -1
- package/lib/src/chaincode/utils/launchpadSaleUtils.d.ts.map +1 -1
- package/lib/src/chaincode/utils/launchpadSaleUtils.js +5 -2
- package/lib/src/chaincode/utils/launchpadSaleUtils.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/api/types/LaunchpadDtos.ts +12 -22
- package/src/api/types/LaunchpadSale.ts +34 -6
- package/src/chaincode/launchpad/buyExactToken.spec.ts +233 -0
- package/src/chaincode/launchpad/buyWithNative.spec.ts +61 -1
- package/src/chaincode/launchpad/buyWithNative.ts +5 -1
- package/src/chaincode/launchpad/callMemeTokenIn.ts +11 -4
- package/src/chaincode/launchpad/callMemeTokenOut.ts +24 -8
- package/src/chaincode/launchpad/callNativeTokenIn.ts +18 -7
- package/src/chaincode/launchpad/callNativeTokenOut.ts +10 -4
- package/src/chaincode/launchpad/createSale.spec.ts +1 -87
- package/src/chaincode/launchpad/createSale.ts +11 -14
- package/src/chaincode/launchpad/finaliseSale.ts +2 -2
- package/src/chaincode/launchpad/sellExactToken.spec.ts +139 -2
- package/src/chaincode/launchpad/sellWithNative.spec.ts +157 -1
- package/src/chaincode/utils/launchpadSaleUtils.ts +6 -2
|
@@ -34,7 +34,6 @@ describe("createSale", () => {
|
|
|
34
34
|
"TestCollection",
|
|
35
35
|
"TestCategory"
|
|
36
36
|
);
|
|
37
|
-
createSaleDto.websiteUrl = "https://example.com";
|
|
38
37
|
createSaleDto.uniqueKey = randomUniqueKey();
|
|
39
38
|
|
|
40
39
|
const signedDto = createSaleDto.signed(users.testUser1.privateKey);
|
|
@@ -54,60 +53,6 @@ describe("createSale", () => {
|
|
|
54
53
|
expect(response.Data?.isFinalized).toBe(false);
|
|
55
54
|
});
|
|
56
55
|
|
|
57
|
-
it("should create token sale with telegram URL", async () => {
|
|
58
|
-
// Given
|
|
59
|
-
const { ctx, contract } = fixture(LaunchpadContract).registeredUsers(users.testUser1);
|
|
60
|
-
|
|
61
|
-
const createSaleDto = new CreateTokenSaleDTO(
|
|
62
|
-
"Telegram Token",
|
|
63
|
-
"TG",
|
|
64
|
-
"A token with telegram link",
|
|
65
|
-
"https://example.com/tg.png",
|
|
66
|
-
new BigNumber(0),
|
|
67
|
-
"TelegramCollection",
|
|
68
|
-
"SocialCategory"
|
|
69
|
-
);
|
|
70
|
-
createSaleDto.telegramUrl = "https://t.me/testtoken";
|
|
71
|
-
createSaleDto.uniqueKey = randomUniqueKey();
|
|
72
|
-
|
|
73
|
-
const signedDto = createSaleDto.signed(users.testUser1.privateKey);
|
|
74
|
-
|
|
75
|
-
// When
|
|
76
|
-
const response = await contract.CreateSale(ctx, signedDto);
|
|
77
|
-
|
|
78
|
-
// Then
|
|
79
|
-
expect(response.Status).toBe(1);
|
|
80
|
-
expect(response.Data?.telegramUrl).toBe("https://t.me/testtoken");
|
|
81
|
-
expect(response.Data?.websiteUrl).toBe("");
|
|
82
|
-
expect(response.Data?.twitterUrl).toBe("");
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it("should create token sale with twitter URL", async () => {
|
|
86
|
-
// Given
|
|
87
|
-
const { ctx, contract } = fixture(LaunchpadContract).registeredUsers(users.testUser1);
|
|
88
|
-
|
|
89
|
-
const createSaleDto = new CreateTokenSaleDTO(
|
|
90
|
-
"Twitter Token",
|
|
91
|
-
"TWT",
|
|
92
|
-
"A token with twitter link",
|
|
93
|
-
"https://example.com/twt.png",
|
|
94
|
-
new BigNumber(0),
|
|
95
|
-
"TwitterCollection",
|
|
96
|
-
"SocialCategory"
|
|
97
|
-
);
|
|
98
|
-
createSaleDto.twitterUrl = "https://twitter.com/testtoken";
|
|
99
|
-
createSaleDto.uniqueKey = randomUniqueKey();
|
|
100
|
-
|
|
101
|
-
const signedDto = createSaleDto.signed(users.testUser1.privateKey);
|
|
102
|
-
|
|
103
|
-
// When
|
|
104
|
-
const response = await contract.CreateSale(ctx, signedDto);
|
|
105
|
-
|
|
106
|
-
// Then
|
|
107
|
-
expect(response.Status).toBe(1);
|
|
108
|
-
expect(response.Data?.twitterUrl).toBe("https://twitter.com/testtoken");
|
|
109
|
-
});
|
|
110
|
-
|
|
111
56
|
it("should create token sale with pre-buy amount", async () => {
|
|
112
57
|
// Given - Setup GALA token for pre-buy (matching LaunchpadSale nativeToken)
|
|
113
58
|
const galaClass = plainToInstance(TokenClass, {
|
|
@@ -162,7 +107,6 @@ describe("createSale", () => {
|
|
|
162
107
|
"PreBuyCollection",
|
|
163
108
|
"PreBuyCategory"
|
|
164
109
|
);
|
|
165
|
-
createSaleDto.websiteUrl = "https://example.com";
|
|
166
110
|
createSaleDto.uniqueKey = randomUniqueKey();
|
|
167
111
|
|
|
168
112
|
const signedDto = createSaleDto.signed(users.testUser1.privateKey);
|
|
@@ -188,7 +132,6 @@ describe("createSale", () => {
|
|
|
188
132
|
"LowerCollection",
|
|
189
133
|
"LowerCategory"
|
|
190
134
|
);
|
|
191
|
-
createSaleDto.websiteUrl = "https://example.com";
|
|
192
135
|
createSaleDto.uniqueKey = randomUniqueKey();
|
|
193
136
|
|
|
194
137
|
const signedDto = createSaleDto.signed(users.testUser1.privateKey);
|
|
@@ -201,35 +144,7 @@ describe("createSale", () => {
|
|
|
201
144
|
expect(response.Data?.symbol).toBe("LOWER"); // Should be converted to uppercase
|
|
202
145
|
});
|
|
203
146
|
|
|
204
|
-
|
|
205
|
-
// Given
|
|
206
|
-
const { ctx, contract } = fixture(LaunchpadContract).registeredUsers(users.testUser1);
|
|
207
|
-
|
|
208
|
-
const createSaleDto = new CreateTokenSaleDTO(
|
|
209
|
-
"Social Token",
|
|
210
|
-
"SOC",
|
|
211
|
-
"A fully connected social token",
|
|
212
|
-
"https://example.com/soc.png",
|
|
213
|
-
new BigNumber(0),
|
|
214
|
-
"SocialCollection",
|
|
215
|
-
"FullSocialCategory"
|
|
216
|
-
);
|
|
217
|
-
createSaleDto.websiteUrl = "https://socialtoken.com";
|
|
218
|
-
createSaleDto.telegramUrl = "https://t.me/socialtoken";
|
|
219
|
-
createSaleDto.twitterUrl = "https://twitter.com/socialtoken";
|
|
220
|
-
createSaleDto.uniqueKey = randomUniqueKey();
|
|
221
|
-
|
|
222
|
-
const signedDto = createSaleDto.signed(users.testUser1.privateKey);
|
|
223
|
-
|
|
224
|
-
// When
|
|
225
|
-
const response = await contract.CreateSale(ctx, signedDto);
|
|
226
|
-
|
|
227
|
-
// Then
|
|
228
|
-
expect(response.Status).toBe(1);
|
|
229
|
-
expect(response.Data?.websiteUrl).toBe("https://socialtoken.com");
|
|
230
|
-
expect(response.Data?.telegramUrl).toBe("https://t.me/socialtoken");
|
|
231
|
-
expect(response.Data?.twitterUrl).toBe("https://twitter.com/socialtoken");
|
|
232
|
-
});
|
|
147
|
+
// Social links are now handled off-chain (DB source of truth); chain no longer returns them.
|
|
233
148
|
|
|
234
149
|
it("should create sale with custom token image", async () => {
|
|
235
150
|
// Given
|
|
@@ -244,7 +159,6 @@ describe("createSale", () => {
|
|
|
244
159
|
"ImageCollection",
|
|
245
160
|
"ImageCategory"
|
|
246
161
|
);
|
|
247
|
-
createSaleDto.websiteUrl = "https://example.com";
|
|
248
162
|
createSaleDto.uniqueKey = randomUniqueKey();
|
|
249
163
|
|
|
250
164
|
const signedDto = createSaleDto.signed(users.testUser1.privateKey);
|
|
@@ -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 { ConflictError,
|
|
15
|
+
import { ConflictError, TokenInstanceKey, asValidUserAlias } from "@gala-chain/api";
|
|
16
16
|
import {
|
|
17
17
|
GalaChainContext,
|
|
18
18
|
createTokenClass,
|
|
@@ -30,7 +30,6 @@ import {
|
|
|
30
30
|
NativeTokenQuantityDto,
|
|
31
31
|
SaleStatus
|
|
32
32
|
} from "../../api/types";
|
|
33
|
-
import { PreConditionFailedError } from "../../api/utils/error";
|
|
34
33
|
import { buyWithNative } from "./buyWithNative";
|
|
35
34
|
|
|
36
35
|
/**
|
|
@@ -62,10 +61,6 @@ export async function createSale(
|
|
|
62
61
|
let isSaleFinalized = false;
|
|
63
62
|
// Validate input parameters
|
|
64
63
|
|
|
65
|
-
if (!launchpadDetails.websiteUrl && !launchpadDetails.telegramUrl && !launchpadDetails.twitterUrl) {
|
|
66
|
-
throw new PreConditionFailedError("Token sale creation requires atleast one social link.");
|
|
67
|
-
}
|
|
68
|
-
|
|
69
64
|
launchpadDetails.tokenSymbol = launchpadDetails.tokenSymbol.toUpperCase();
|
|
70
65
|
|
|
71
66
|
// Define the token class key
|
|
@@ -86,6 +81,8 @@ export async function createSale(
|
|
|
86
81
|
throw new ConflictError("This token and a sale associated with it already exists");
|
|
87
82
|
}
|
|
88
83
|
|
|
84
|
+
const supplyCapMultiplier = launchpadDetails.adjustableSupplyMultiplier ?? 1;
|
|
85
|
+
|
|
89
86
|
// Call createTokenClass
|
|
90
87
|
await createTokenClass(ctx, {
|
|
91
88
|
network: "GC",
|
|
@@ -96,8 +93,8 @@ export async function createSale(
|
|
|
96
93
|
symbol: launchpadDetails.tokenSymbol,
|
|
97
94
|
description: launchpadDetails.tokenDescription,
|
|
98
95
|
image: launchpadDetails.tokenImage,
|
|
99
|
-
maxSupply: new BigNumber("2e+7"),
|
|
100
|
-
maxCapacity: new BigNumber("2e+7"),
|
|
96
|
+
maxSupply: new BigNumber("2e+7").times(supplyCapMultiplier),
|
|
97
|
+
maxCapacity: new BigNumber("2e+7").times(supplyCapMultiplier),
|
|
101
98
|
totalMintAllowance: new BigNumber(0),
|
|
102
99
|
totalSupply: new BigNumber(0),
|
|
103
100
|
totalBurned: new BigNumber(0),
|
|
@@ -109,12 +106,13 @@ export async function createSale(
|
|
|
109
106
|
tokenClassKey: tokenInstanceKey.getTokenClassKey(),
|
|
110
107
|
tokenInstance: new BigNumber(0),
|
|
111
108
|
owner: vaultAddress,
|
|
112
|
-
quantity: new BigNumber("2e+7")
|
|
109
|
+
quantity: new BigNumber("2e+7").times(supplyCapMultiplier)
|
|
113
110
|
});
|
|
114
111
|
|
|
115
|
-
//Update token class to remove the calling user as an authority in the token class
|
|
112
|
+
// Update token class to remove the calling user as an authority in the token class
|
|
116
113
|
await updateTokenClass(ctx, {
|
|
117
114
|
tokenClass: tokenInstanceKey.getTokenClassKey(),
|
|
115
|
+
overwriteAuthorities: true,
|
|
118
116
|
authorities: [vaultAddress]
|
|
119
117
|
});
|
|
120
118
|
|
|
@@ -123,7 +121,9 @@ export async function createSale(
|
|
|
123
121
|
vaultAddress,
|
|
124
122
|
tokenInstanceKey,
|
|
125
123
|
launchpadDetails.reverseBondingCurveConfiguration?.toChainObject(),
|
|
126
|
-
ctx.callingUser
|
|
124
|
+
ctx.callingUser,
|
|
125
|
+
undefined,
|
|
126
|
+
launchpadDetails.adjustableSupplyMultiplier
|
|
127
127
|
);
|
|
128
128
|
|
|
129
129
|
await putChainObject(ctx, launchpad);
|
|
@@ -160,9 +160,6 @@ export async function createSale(
|
|
|
160
160
|
tokenName: launchpadDetails.tokenName,
|
|
161
161
|
symbol: launchpadDetails.tokenSymbol,
|
|
162
162
|
description: launchpadDetails.tokenDescription,
|
|
163
|
-
websiteUrl: launchpadDetails.websiteUrl ? launchpadDetails.websiteUrl : "",
|
|
164
|
-
telegramUrl: launchpadDetails.telegramUrl ? launchpadDetails.telegramUrl : "",
|
|
165
|
-
twitterUrl: launchpadDetails.twitterUrl ? launchpadDetails.twitterUrl : "",
|
|
166
163
|
initialBuyQuantity: launchpadDetails.preBuyQuantity.toFixed(),
|
|
167
164
|
vaultAddress: vaultAddress,
|
|
168
165
|
creatorAddress: ctx.callingUser,
|
|
@@ -203,8 +203,8 @@ function calculateFinalLaunchpadPrice(
|
|
|
203
203
|
areTokensSorted: boolean
|
|
204
204
|
): { sqrtPrice: BigNumber; finalPrice: BigNumber } {
|
|
205
205
|
const totalTokensSold = new Decimal(sale.fetchTokensSold());
|
|
206
|
-
const basePrice = new Decimal(
|
|
207
|
-
const { exponentFactor, euler, decimals } = getBondingConstants();
|
|
206
|
+
const basePrice = new Decimal(sale.basePrice.toString());
|
|
207
|
+
const { exponentFactor, euler, decimals } = getBondingConstants(sale.adjustableSupplyMultiplier);
|
|
208
208
|
|
|
209
209
|
const exponent = exponentFactor.mul(totalTokensSold).div(decimals);
|
|
210
210
|
const multiplicand = euler.pow(exponent);
|
|
@@ -21,11 +21,11 @@ import {
|
|
|
21
21
|
asValidUserAlias,
|
|
22
22
|
randomUniqueKey
|
|
23
23
|
} from "@gala-chain/api";
|
|
24
|
-
import { currency, fixture, users } from "@gala-chain/test";
|
|
24
|
+
import { currency, fixture, transactionSuccess, users } from "@gala-chain/test";
|
|
25
25
|
import BigNumber from "bignumber.js";
|
|
26
26
|
import { plainToInstance } from "class-transformer";
|
|
27
27
|
|
|
28
|
-
import { ExactTokenQuantityDto, LaunchpadSale } from "../../api/types";
|
|
28
|
+
import { ExactTokenQuantityDto, LaunchpadSale, TradeResDto } from "../../api/types";
|
|
29
29
|
import { LaunchpadContract } from "../LaunchpadContract";
|
|
30
30
|
import launchpadgala from "../test/launchpadgala";
|
|
31
31
|
|
|
@@ -204,4 +204,141 @@ describe("sellExactToken", () => {
|
|
|
204
204
|
expect(response.Data?.inputQuantity).toBe("500");
|
|
205
205
|
expect(new BigNumber(response.Data?.outputQuantity || "0").isPositive()).toBe(true);
|
|
206
206
|
});
|
|
207
|
+
|
|
208
|
+
let galaPurchaseQtyDefaultSupply: BigNumber;
|
|
209
|
+
|
|
210
|
+
test("Adjustable supply: Single transaction yields expected value for default 10 Million supply", async () => {
|
|
211
|
+
// Given
|
|
212
|
+
const multiplier = undefined;
|
|
213
|
+
|
|
214
|
+
sale = new LaunchpadSale(
|
|
215
|
+
vaultAddress,
|
|
216
|
+
currencyInstance.instanceKeyObj(),
|
|
217
|
+
undefined,
|
|
218
|
+
users.testUser1.identityKey,
|
|
219
|
+
undefined,
|
|
220
|
+
multiplier
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
const { ctx, contract } = fixture(LaunchpadContract)
|
|
224
|
+
.registeredUsers(users.testUser1)
|
|
225
|
+
.savedState(
|
|
226
|
+
currencyClass,
|
|
227
|
+
currencyInstance,
|
|
228
|
+
launchpadGalaClass,
|
|
229
|
+
launchpadGalaInstance,
|
|
230
|
+
sale,
|
|
231
|
+
salelaunchpadGalaBalance,
|
|
232
|
+
saleCurrencyBalance,
|
|
233
|
+
userlaunchpadGalaBalance,
|
|
234
|
+
userCurrencyBalance
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
const buyDto = new ExactTokenQuantityDto(vaultAddress, new BigNumber("500"));
|
|
238
|
+
|
|
239
|
+
buyDto.uniqueKey = randomUniqueKey();
|
|
240
|
+
buyDto.sign(users.testUser1.privateKey);
|
|
241
|
+
|
|
242
|
+
const expectedBuyResponse = plainToInstance(TradeResDto, {
|
|
243
|
+
inputQuantity: "0.00825575",
|
|
244
|
+
totalFees: "0",
|
|
245
|
+
totalTokenSold: new BigNumber("500").toString(),
|
|
246
|
+
outputQuantity: new BigNumber("500").toString(),
|
|
247
|
+
tokenName: "AUTOMATEDTESTCOIN",
|
|
248
|
+
tradeType: "Buy",
|
|
249
|
+
uniqueKey: buyDto.uniqueKey,
|
|
250
|
+
vaultAddress: "service|GALA$Unit$none$none$launchpad",
|
|
251
|
+
userAddress: "client|testUser1",
|
|
252
|
+
isFinalized: false,
|
|
253
|
+
functionName: "BuyExactToken"
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
const sellDto = new ExactTokenQuantityDto(vaultAddress, new BigNumber("50"));
|
|
257
|
+
sellDto.uniqueKey = randomUniqueKey();
|
|
258
|
+
const signedDto = sellDto.signed(users.testUser1.privateKey);
|
|
259
|
+
|
|
260
|
+
// When
|
|
261
|
+
const buyRes = await contract.BuyExactToken(ctx, buyDto);
|
|
262
|
+
|
|
263
|
+
const sellRes = await contract.SellExactToken(ctx, signedDto);
|
|
264
|
+
|
|
265
|
+
// Then
|
|
266
|
+
expect(buyRes).toEqual(transactionSuccess(expectedBuyResponse));
|
|
267
|
+
|
|
268
|
+
expect(sellRes.Status).toBe(1);
|
|
269
|
+
expect(sellRes.Data?.inputQuantity).toBe("50");
|
|
270
|
+
expect(sellRes.Data?.outputQuantity).toBe("0.00082579");
|
|
271
|
+
|
|
272
|
+
galaPurchaseQtyDefaultSupply = new BigNumber(sellRes.Data?.outputQuantity ?? 0);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
test("Adjustable supply: Single transaction yields expected quantity for 100x scaled 1 Billion supply", async () => {
|
|
276
|
+
// Given
|
|
277
|
+
const multiplier = 100;
|
|
278
|
+
const inputQty = new BigNumber("50").times(multiplier);
|
|
279
|
+
|
|
280
|
+
sale = new LaunchpadSale(
|
|
281
|
+
vaultAddress,
|
|
282
|
+
currencyInstance.instanceKeyObj(),
|
|
283
|
+
undefined,
|
|
284
|
+
users.testUser1.identityKey,
|
|
285
|
+
undefined,
|
|
286
|
+
multiplier
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
const { ctx, contract } = fixture(LaunchpadContract)
|
|
290
|
+
.registeredUsers(users.testUser1)
|
|
291
|
+
.savedState(
|
|
292
|
+
currencyClass,
|
|
293
|
+
currencyInstance,
|
|
294
|
+
launchpadGalaClass,
|
|
295
|
+
launchpadGalaInstance,
|
|
296
|
+
sale,
|
|
297
|
+
salelaunchpadGalaBalance,
|
|
298
|
+
saleCurrencyBalance,
|
|
299
|
+
userlaunchpadGalaBalance,
|
|
300
|
+
userCurrencyBalance
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
const buyDto = new ExactTokenQuantityDto(vaultAddress, new BigNumber("500").times(multiplier));
|
|
304
|
+
|
|
305
|
+
buyDto.uniqueKey = randomUniqueKey();
|
|
306
|
+
buyDto.sign(users.testUser1.privateKey);
|
|
307
|
+
|
|
308
|
+
const expectedBuyResponse = plainToInstance(TradeResDto, {
|
|
309
|
+
inputQuantity: "0.00825575",
|
|
310
|
+
totalFees: "0",
|
|
311
|
+
totalTokenSold: new BigNumber("500").times(multiplier).toString(),
|
|
312
|
+
outputQuantity: new BigNumber("500").times(multiplier).toString(),
|
|
313
|
+
tokenName: "AUTOMATEDTESTCOIN",
|
|
314
|
+
tradeType: "Buy",
|
|
315
|
+
uniqueKey: buyDto.uniqueKey,
|
|
316
|
+
vaultAddress: "service|GALA$Unit$none$none$launchpad",
|
|
317
|
+
userAddress: "client|testUser1",
|
|
318
|
+
isFinalized: false,
|
|
319
|
+
functionName: "BuyExactToken"
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
const sellDto = new ExactTokenQuantityDto(vaultAddress, inputQty);
|
|
323
|
+
sellDto.uniqueKey = randomUniqueKey();
|
|
324
|
+
const signedDto = sellDto.signed(users.testUser1.privateKey);
|
|
325
|
+
|
|
326
|
+
// When
|
|
327
|
+
const buyRes = await contract.BuyExactToken(ctx, buyDto);
|
|
328
|
+
|
|
329
|
+
const sellRes = await contract.SellExactToken(ctx, signedDto);
|
|
330
|
+
|
|
331
|
+
// Then
|
|
332
|
+
expect(buyRes).toEqual(transactionSuccess(expectedBuyResponse));
|
|
333
|
+
|
|
334
|
+
expect(sellRes.Status).toBe(1);
|
|
335
|
+
expect(sellRes.Data?.inputQuantity).toBe(inputQty.toString());
|
|
336
|
+
expect(sellRes.Data?.outputQuantity).toBe("0.00082579");
|
|
337
|
+
|
|
338
|
+
const galaPurchaseQty100xSupply = new BigNumber(sellRes.Data?.outputQuantity ?? -1);
|
|
339
|
+
|
|
340
|
+
// Compared to the previous test where the Launchpad has the default 10 Million supply,
|
|
341
|
+
// We expect the Meme token Qty to scale 100x and the Gala Qty to remain the same
|
|
342
|
+
expect(galaPurchaseQtyDefaultSupply).toEqual(galaPurchaseQty100xSupply);
|
|
343
|
+
});
|
|
207
344
|
});
|
|
@@ -26,7 +26,7 @@ import { currency, fixture, transactionError, transactionSuccess, users } from "
|
|
|
26
26
|
import BigNumber from "bignumber.js";
|
|
27
27
|
import { plainToInstance } from "class-transformer";
|
|
28
28
|
|
|
29
|
-
import { LaunchpadSale, NativeTokenQuantityDto, TradeResDto } from "../../api/types";
|
|
29
|
+
import { ExactTokenQuantityDto, LaunchpadSale, NativeTokenQuantityDto, TradeResDto } from "../../api/types";
|
|
30
30
|
import { LaunchpadContract } from "../LaunchpadContract";
|
|
31
31
|
import launchpadgala from "../test/launchpadgala";
|
|
32
32
|
|
|
@@ -286,4 +286,160 @@ describe("sellWithNative", () => {
|
|
|
286
286
|
|
|
287
287
|
// todo: check writes map, verify vault balance
|
|
288
288
|
});
|
|
289
|
+
|
|
290
|
+
let galaPurchaseQtyDefaultSupply: BigNumber;
|
|
291
|
+
let memeSaleQtyDefaultSupply: BigNumber;
|
|
292
|
+
|
|
293
|
+
test("Adjustable supply: Single transaction yields expected value for default 10 Million supply", async () => {
|
|
294
|
+
// Given
|
|
295
|
+
const multiplier = undefined;
|
|
296
|
+
galaPurchaseQtyDefaultSupply = new BigNumber("0.00082579");
|
|
297
|
+
memeSaleQtyDefaultSupply = new BigNumber("49.999949130655");
|
|
298
|
+
|
|
299
|
+
sale = new LaunchpadSale(
|
|
300
|
+
vaultAddress,
|
|
301
|
+
currencyInstance.instanceKeyObj(),
|
|
302
|
+
undefined,
|
|
303
|
+
users.testUser1.identityKey,
|
|
304
|
+
undefined,
|
|
305
|
+
multiplier
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
const { ctx, contract } = fixture(LaunchpadContract)
|
|
309
|
+
.registeredUsers(users.testUser1)
|
|
310
|
+
.savedState(
|
|
311
|
+
currencyClass,
|
|
312
|
+
currencyInstance,
|
|
313
|
+
launchpadGalaClass,
|
|
314
|
+
launchpadGalaInstance,
|
|
315
|
+
sale,
|
|
316
|
+
salelaunchpadGalaBalance,
|
|
317
|
+
saleCurrencyBalance,
|
|
318
|
+
userlaunchpadGalaBalance,
|
|
319
|
+
userCurrencyBalance
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
const buyDto = new ExactTokenQuantityDto(vaultAddress, new BigNumber("500"));
|
|
323
|
+
|
|
324
|
+
buyDto.uniqueKey = randomUniqueKey();
|
|
325
|
+
buyDto.sign(users.testUser1.privateKey);
|
|
326
|
+
|
|
327
|
+
const expectedBuyResponse = plainToInstance(TradeResDto, {
|
|
328
|
+
inputQuantity: "0.00825575",
|
|
329
|
+
totalFees: "0",
|
|
330
|
+
totalTokenSold: new BigNumber("500").toString(),
|
|
331
|
+
outputQuantity: new BigNumber("500").toString(),
|
|
332
|
+
tokenName: "AUTOMATEDTESTCOIN",
|
|
333
|
+
tradeType: "Buy",
|
|
334
|
+
uniqueKey: buyDto.uniqueKey,
|
|
335
|
+
vaultAddress: "service|GALA$Unit$none$none$launchpad",
|
|
336
|
+
userAddress: "client|testUser1",
|
|
337
|
+
isFinalized: false,
|
|
338
|
+
functionName: "BuyExactToken"
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
const sellDto = new NativeTokenQuantityDto(vaultAddress, galaPurchaseQtyDefaultSupply);
|
|
342
|
+
sellDto.uniqueKey = randomUniqueKey();
|
|
343
|
+
const signedDto = sellDto.signed(users.testUser1.privateKey);
|
|
344
|
+
|
|
345
|
+
// When
|
|
346
|
+
const buyRes = await contract.BuyExactToken(ctx, buyDto);
|
|
347
|
+
|
|
348
|
+
const sellRes = await contract.SellWithNative(ctx, signedDto);
|
|
349
|
+
|
|
350
|
+
// Then
|
|
351
|
+
expect(buyRes).toEqual(transactionSuccess(expectedBuyResponse));
|
|
352
|
+
|
|
353
|
+
expect(sellRes).toEqual(
|
|
354
|
+
transactionSuccess(
|
|
355
|
+
expect.objectContaining({
|
|
356
|
+
outputQuantity: galaPurchaseQtyDefaultSupply.toString(),
|
|
357
|
+
// extra precision in set constant above accounts for loss of precision
|
|
358
|
+
// when increased by the multiplier below.
|
|
359
|
+
// here, we round to the token decimal places to match the internal logic
|
|
360
|
+
inputQuantity: memeSaleQtyDefaultSupply.decimalPlaces(10).toString()
|
|
361
|
+
})
|
|
362
|
+
)
|
|
363
|
+
);
|
|
364
|
+
galaPurchaseQtyDefaultSupply = new BigNumber(sellRes.Data?.outputQuantity ?? 0);
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
test("Adjustable supply: Single transaction yields expected quantity for 100x scaled 1 Billion supply", async () => {
|
|
368
|
+
// Given
|
|
369
|
+
const multiplier = 100;
|
|
370
|
+
|
|
371
|
+
// Same Gala purchase amount should buy 100x meme token output
|
|
372
|
+
const inputQty = new BigNumber(galaPurchaseQtyDefaultSupply);
|
|
373
|
+
|
|
374
|
+
sale = new LaunchpadSale(
|
|
375
|
+
vaultAddress,
|
|
376
|
+
currencyInstance.instanceKeyObj(),
|
|
377
|
+
undefined,
|
|
378
|
+
users.testUser1.identityKey,
|
|
379
|
+
undefined,
|
|
380
|
+
multiplier
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
const { ctx, contract } = fixture(LaunchpadContract)
|
|
384
|
+
.registeredUsers(users.testUser1)
|
|
385
|
+
.savedState(
|
|
386
|
+
currencyClass,
|
|
387
|
+
currencyInstance,
|
|
388
|
+
launchpadGalaClass,
|
|
389
|
+
launchpadGalaInstance,
|
|
390
|
+
sale,
|
|
391
|
+
salelaunchpadGalaBalance,
|
|
392
|
+
saleCurrencyBalance,
|
|
393
|
+
userlaunchpadGalaBalance,
|
|
394
|
+
userCurrencyBalance
|
|
395
|
+
);
|
|
396
|
+
|
|
397
|
+
const buyDto = new ExactTokenQuantityDto(vaultAddress, new BigNumber("500").times(multiplier));
|
|
398
|
+
|
|
399
|
+
buyDto.uniqueKey = randomUniqueKey();
|
|
400
|
+
buyDto.sign(users.testUser1.privateKey);
|
|
401
|
+
|
|
402
|
+
const expectedBuyResponse = plainToInstance(TradeResDto, {
|
|
403
|
+
inputQuantity: "0.00825575",
|
|
404
|
+
totalFees: "0",
|
|
405
|
+
totalTokenSold: new BigNumber("500").times(multiplier).toString(),
|
|
406
|
+
outputQuantity: new BigNumber("500").times(multiplier).toString(),
|
|
407
|
+
tokenName: "AUTOMATEDTESTCOIN",
|
|
408
|
+
tradeType: "Buy",
|
|
409
|
+
uniqueKey: buyDto.uniqueKey,
|
|
410
|
+
vaultAddress: "service|GALA$Unit$none$none$launchpad",
|
|
411
|
+
userAddress: "client|testUser1",
|
|
412
|
+
isFinalized: false,
|
|
413
|
+
functionName: "BuyExactToken"
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
const sellDto = new NativeTokenQuantityDto(vaultAddress, inputQty);
|
|
417
|
+
sellDto.uniqueKey = randomUniqueKey();
|
|
418
|
+
const signedDto = sellDto.signed(users.testUser1.privateKey);
|
|
419
|
+
|
|
420
|
+
// When
|
|
421
|
+
const buyRes = await contract.BuyExactToken(ctx, buyDto);
|
|
422
|
+
|
|
423
|
+
const sellRes = await contract.SellWithNative(ctx, signedDto);
|
|
424
|
+
|
|
425
|
+
// Then
|
|
426
|
+
expect(buyRes).toEqual(transactionSuccess(expectedBuyResponse));
|
|
427
|
+
|
|
428
|
+
expect(sellRes).toEqual(
|
|
429
|
+
transactionSuccess(
|
|
430
|
+
expect.objectContaining({
|
|
431
|
+
inputQuantity: memeSaleQtyDefaultSupply.times(multiplier).toString(),
|
|
432
|
+
outputQuantity: galaPurchaseQtyDefaultSupply.toString()
|
|
433
|
+
})
|
|
434
|
+
)
|
|
435
|
+
);
|
|
436
|
+
|
|
437
|
+
const galaPurchaseQty100xSupply = new BigNumber(sellRes.Data?.outputQuantity ?? -1);
|
|
438
|
+
const memeSaleQty100xSupply = new BigNumber(sellRes.Data?.inputQuantity ?? -1);
|
|
439
|
+
|
|
440
|
+
// Compared to the previous test where the Launchpad has the default 10 Million supply,
|
|
441
|
+
// We expect the Meme token Qty to scale 100x and the Gala Qty to remain the same
|
|
442
|
+
expect(galaPurchaseQtyDefaultSupply).toEqual(galaPurchaseQty100xSupply);
|
|
443
|
+
expect(memeSaleQtyDefaultSupply).toEqual(memeSaleQty100xSupply.dividedBy(multiplier));
|
|
444
|
+
});
|
|
289
445
|
});
|
|
@@ -54,9 +54,13 @@ export async function fetchAndValidateSale(
|
|
|
54
54
|
return sale;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
export function getBondingConstants() {
|
|
57
|
+
export function getBondingConstants(multiplier?: number) {
|
|
58
|
+
const exponentFactor =
|
|
59
|
+
multiplier && multiplier > 0
|
|
60
|
+
? new Decimal("1166069000000").times(1 / multiplier)
|
|
61
|
+
: new Decimal("1166069000000");
|
|
58
62
|
return {
|
|
59
|
-
exponentFactor:
|
|
63
|
+
exponentFactor: exponentFactor, // exponent factor / b
|
|
60
64
|
euler: new Decimal("2.7182818284590452353602874713527"), // e
|
|
61
65
|
decimals: new Decimal("1e+18") // scaling factor for decimals
|
|
62
66
|
};
|