@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.
- package/CLAUDE.md +279 -0
- package/lib/package.json +2 -2
- package/lib/src/api/utils/error.js +1 -1
- package/lib/src/api/utils/error.js.map +1 -1
- package/lib/src/chaincode/launchpad/buyExactToken.js +1 -1
- package/lib/src/chaincode/launchpad/buyExactToken.js.map +1 -1
- package/lib/src/chaincode/launchpad/buyWithNative.d.ts.map +1 -1
- package/lib/src/chaincode/launchpad/buyWithNative.js +12 -5
- package/lib/src/chaincode/launchpad/buyWithNative.js.map +1 -1
- package/lib/src/chaincode/launchpad/sellExactToken.js +1 -1
- package/lib/src/chaincode/launchpad/sellExactToken.js.map +1 -1
- package/lib/src/chaincode/launchpad/sellWithNative.d.ts.map +1 -1
- package/lib/src/chaincode/launchpad/sellWithNative.js +10 -2
- package/lib/src/chaincode/launchpad/sellWithNative.js.map +1 -1
- package/lib/src/chaincode/test/launchpadgala.js +1 -1
- package/lib/src/chaincode/test/launchpadgala.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/api/utils/error.ts +1 -1
- package/src/chaincode/launchpad/buyExactToken.spec.ts +90 -20
- package/src/chaincode/launchpad/buyExactToken.ts +1 -1
- package/src/chaincode/launchpad/buyWithNative.spec.ts +99 -26
- package/src/chaincode/launchpad/buyWithNative.ts +21 -6
- package/src/chaincode/launchpad/callMemeTokenIn.spec.ts +244 -0
- package/src/chaincode/launchpad/callMemeTokenOut.spec.ts +1 -1
- package/src/chaincode/launchpad/callNativeTokenIn.spec.ts +269 -0
- package/src/chaincode/launchpad/callNativeTokenOut.spec.ts +276 -0
- package/src/chaincode/launchpad/configureLaunchpadFeeConfig.spec.ts +202 -0
- package/src/chaincode/launchpad/createSale.spec.ts +259 -0
- package/src/chaincode/launchpad/fetchSaleDetails.spec.ts +141 -0
- package/src/chaincode/launchpad/finalizeTokenAllocation.spec.ts +126 -0
- package/src/chaincode/launchpad/sellExactToken.spec.ts +284 -0
- package/src/chaincode/launchpad/sellExactToken.ts +1 -1
- package/src/chaincode/launchpad/sellWithNative.spec.ts +329 -0
- package/src/chaincode/launchpad/sellWithNative.ts +23 -6
- 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.
|
|
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.
|
|
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",
|
package/src/api/utils/error.ts
CHANGED
|
@@ -23,7 +23,8 @@ import {
|
|
|
23
23
|
asValidUserAlias,
|
|
24
24
|
randomUniqueKey
|
|
25
25
|
} from "@gala-chain/api";
|
|
26
|
-
import {
|
|
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:
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
137
|
-
|
|
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
|
-
|
|
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
|
-
|
|
182
|
-
|
|
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
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
134
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
-
|
|
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
|
|
72
|
-
const callNativeTokenInResult = await callNativeTokenIn(ctx,
|
|
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
|
-
|
|
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
|
|