@gala-chain/launchpad 1.0.13 → 1.0.14
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 +1 -0
- package/lib/src/api/types/LaunchpadDtos.d.ts.map +1 -1
- package/lib/src/api/types/LaunchpadDtos.js +5 -0
- package/lib/src/api/types/LaunchpadDtos.js.map +1 -1
- package/lib/src/api/types/LaunchpadSale.d.ts +2 -1
- package/lib/src/api/types/LaunchpadSale.d.ts.map +1 -1
- package/lib/src/api/types/LaunchpadSale.js +18 -6
- package/lib/src/api/types/LaunchpadSale.js.map +1 -1
- package/lib/src/chaincode/LaunchpadContract.js +6 -6
- package/lib/src/chaincode/LaunchpadContract.js.map +1 -1
- package/lib/src/chaincode/launchpad/buyExactToken.d.ts.map +1 -1
- package/lib/src/chaincode/launchpad/buyExactToken.js +18 -42
- 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 -43
- package/lib/src/chaincode/launchpad/buyWithNative.js.map +1 -1
- package/lib/src/chaincode/launchpad/callMemeTokenIn.d.ts +4 -10
- package/lib/src/chaincode/launchpad/callMemeTokenIn.d.ts.map +1 -1
- package/lib/src/chaincode/launchpad/callMemeTokenIn.js +13 -9
- package/lib/src/chaincode/launchpad/callMemeTokenIn.js.map +1 -1
- package/lib/src/chaincode/launchpad/callMemeTokenOut.d.ts +4 -10
- package/lib/src/chaincode/launchpad/callMemeTokenOut.d.ts.map +1 -1
- package/lib/src/chaincode/launchpad/callMemeTokenOut.js +46 -22
- package/lib/src/chaincode/launchpad/callMemeTokenOut.js.map +1 -1
- package/lib/src/chaincode/launchpad/callNativeTokenIn.d.ts +4 -10
- package/lib/src/chaincode/launchpad/callNativeTokenIn.d.ts.map +1 -1
- package/lib/src/chaincode/launchpad/callNativeTokenIn.js +35 -14
- package/lib/src/chaincode/launchpad/callNativeTokenIn.js.map +1 -1
- package/lib/src/chaincode/launchpad/callNativeTokenOut.d.ts +4 -10
- package/lib/src/chaincode/launchpad/callNativeTokenOut.d.ts.map +1 -1
- package/lib/src/chaincode/launchpad/callNativeTokenOut.js +17 -12
- package/lib/src/chaincode/launchpad/callNativeTokenOut.js.map +1 -1
- package/lib/src/chaincode/launchpad/configureLaunchpadFeeConfig.js +1 -1
- package/lib/src/chaincode/launchpad/configureLaunchpadFeeConfig.js.map +1 -1
- package/lib/src/chaincode/launchpad/createSale.js +1 -1
- package/lib/src/chaincode/launchpad/createSale.js.map +1 -1
- package/lib/src/chaincode/launchpad/fees.d.ts +11 -0
- package/lib/src/chaincode/launchpad/fees.d.ts.map +1 -1
- package/lib/src/chaincode/launchpad/fees.js +42 -3
- package/lib/src/chaincode/launchpad/fees.js.map +1 -1
- package/lib/src/chaincode/launchpad/finaliseSale.d.ts.map +1 -1
- package/lib/src/chaincode/launchpad/finaliseSale.js +7 -4
- package/lib/src/chaincode/launchpad/finaliseSale.js.map +1 -1
- package/lib/src/chaincode/launchpad/sellExactToken.d.ts.map +1 -1
- package/lib/src/chaincode/launchpad/sellExactToken.js +14 -23
- 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 -19
- package/lib/src/chaincode/launchpad/sellWithNative.js.map +1 -1
- package/lib/src/chaincode/test/launchpadgala.d.ts.map +1 -1
- package/lib/src/chaincode/test/launchpadgala.js +2 -1
- package/lib/src/chaincode/test/launchpadgala.js.map +1 -1
- package/lib/src/chaincode/utils/launchpadSaleUtils.d.ts +4 -0
- package/lib/src/chaincode/utils/launchpadSaleUtils.d.ts.map +1 -1
- package/lib/src/chaincode/utils/launchpadSaleUtils.js +10 -1
- package/lib/src/chaincode/utils/launchpadSaleUtils.js.map +1 -1
- package/lib/src/cli.js +3 -1
- package/lib/src/cli.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/api/types/LaunchpadDtos.ts +4 -0
- package/src/api/types/LaunchpadSale.ts +14 -7
- package/src/chaincode/LaunchpadContract.ts +3 -3
- package/src/chaincode/launchpad/buyExactToken.spec.ts +2 -37
- package/src/chaincode/launchpad/buyExactToken.ts +22 -56
- package/src/chaincode/launchpad/buyWithNative.spec.ts +2 -37
- package/src/chaincode/launchpad/buyWithNative.ts +15 -65
- package/src/chaincode/launchpad/callMemeTokenIn.ts +36 -12
- package/src/chaincode/launchpad/callMemeTokenOut.spec.ts +1 -1
- package/src/chaincode/launchpad/callMemeTokenOut.ts +72 -26
- package/src/chaincode/launchpad/callNativeTokenIn.ts +62 -19
- package/src/chaincode/launchpad/callNativeTokenOut.ts +38 -15
- package/src/chaincode/launchpad/configureLaunchpadFeeConfig.spec.ts +0 -1
- package/src/chaincode/launchpad/configureLaunchpadFeeConfig.ts +1 -1
- package/src/chaincode/launchpad/createSale.ts +1 -1
- package/src/chaincode/launchpad/fees.ts +55 -3
- package/src/chaincode/launchpad/finaliseSale.ts +7 -4
- package/src/chaincode/launchpad/launchpadBatchSubmitAuthorizations.spec.ts +150 -68
- package/src/chaincode/launchpad/sellExactToken.spec.ts +4 -81
- package/src/chaincode/launchpad/sellExactToken.ts +16 -25
- package/src/chaincode/launchpad/sellWithNative.spec.ts +2 -42
- package/src/chaincode/launchpad/sellWithNative.ts +14 -23
- package/src/chaincode/test/launchpadgala.ts +3 -1
- package/src/chaincode/utils/launchpadSaleUtils.ts +13 -1
- package/src/cli.ts +3 -1
|
@@ -12,8 +12,9 @@
|
|
|
12
12
|
* See the License for the specific language governing permissions and
|
|
13
13
|
* limitations under the License.
|
|
14
14
|
*/
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
15
|
+
import { randomUniqueKey } from "@gala-chain/api";
|
|
16
|
+
import { fixture, transactionSuccess, users } from "@gala-chain/test";
|
|
17
|
+
import { plainToInstance } from "class-transformer";
|
|
17
18
|
|
|
18
19
|
import {
|
|
19
20
|
AuthorizeBatchSubmitterDto,
|
|
@@ -22,6 +23,7 @@ import {
|
|
|
22
23
|
FetchBatchSubmitAuthoritiesDto,
|
|
23
24
|
LaunchpadBatchSubmitAuthorities
|
|
24
25
|
} from "../../api/types";
|
|
26
|
+
import { LaunchpadContract } from "../LaunchpadContract";
|
|
25
27
|
import {
|
|
26
28
|
authorizeLaunchpadBatchSubmitter,
|
|
27
29
|
deauthorizeLaunchpadBatchSubmitter,
|
|
@@ -29,37 +31,8 @@ import {
|
|
|
29
31
|
getLaunchpadBatchSubmitAuthorities
|
|
30
32
|
} from "./launchpadBatchSubmitAuthorizations";
|
|
31
33
|
|
|
32
|
-
jest.mock("@gala-chain/chaincode", () => ({
|
|
33
|
-
...jest.requireActual("@gala-chain/chaincode"),
|
|
34
|
-
getObjectByKey: jest.fn(),
|
|
35
|
-
putChainObject: jest.fn()
|
|
36
|
-
}));
|
|
37
|
-
|
|
38
|
-
// Mock context for testing
|
|
39
|
-
const createMockContext = (callingUser: string): GalaChainContext => {
|
|
40
|
-
const mockStub = {
|
|
41
|
-
createCompositeKey: (indexKey: string, attributes: string[]) => {
|
|
42
|
-
return `${indexKey}${attributes.join("|")}`;
|
|
43
|
-
},
|
|
44
|
-
getState: jest.fn(),
|
|
45
|
-
putState: jest.fn()
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
return {
|
|
49
|
-
callingUser,
|
|
50
|
-
stub: mockStub as any,
|
|
51
|
-
clientIdentity: {
|
|
52
|
-
getMSPID: () => "CuratorOrg"
|
|
53
|
-
} as any
|
|
54
|
-
} as GalaChainContext;
|
|
55
|
-
};
|
|
56
|
-
|
|
57
34
|
describe("BatchSubmitAuthorizations", () => {
|
|
58
|
-
|
|
59
|
-
jest.clearAllMocks();
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
describe("BatchSubmitAuthorizations chain object", () => {
|
|
35
|
+
describe("BatchSubmitAuthorities chain object", () => {
|
|
63
36
|
it("should create with initial authorities", () => {
|
|
64
37
|
const auth = new LaunchpadBatchSubmitAuthorities(["user1", "user2"]);
|
|
65
38
|
expect(auth.authorities).toEqual(["user1", "user2"]);
|
|
@@ -99,87 +72,196 @@ describe("BatchSubmitAuthorizations", () => {
|
|
|
99
72
|
});
|
|
100
73
|
});
|
|
101
74
|
|
|
102
|
-
describe("
|
|
75
|
+
describe("fetchLaunchpadBatchSubmitAuthorities", () => {
|
|
103
76
|
it("should return existing authorizations", async () => {
|
|
104
|
-
|
|
105
|
-
const existingAuth = new LaunchpadBatchSubmitAuthorities([
|
|
77
|
+
// Given
|
|
78
|
+
const existingAuth = new LaunchpadBatchSubmitAuthorities([
|
|
79
|
+
users.testUser1.identityKey,
|
|
80
|
+
users.testUser2.identityKey
|
|
81
|
+
]);
|
|
106
82
|
|
|
107
|
-
|
|
108
|
-
(putChainObject as jest.Mock).mockResolvedValue(undefined);
|
|
83
|
+
const { ctx } = fixture(LaunchpadContract).registeredUsers(users.testUser1).savedState(existingAuth);
|
|
109
84
|
|
|
85
|
+
// When
|
|
110
86
|
const result = await fetchLaunchpadBatchSubmitAuthorities(ctx);
|
|
111
87
|
|
|
112
|
-
|
|
88
|
+
// Then
|
|
89
|
+
expect(result).toBeInstanceOf(LaunchpadBatchSubmitAuthorities);
|
|
90
|
+
expect(result.authorities).toContain(users.testUser1.identityKey);
|
|
91
|
+
expect(result.authorities).toContain(users.testUser2.identityKey);
|
|
113
92
|
});
|
|
114
93
|
});
|
|
115
94
|
|
|
116
|
-
describe("
|
|
95
|
+
describe("authorizeLaunchpadBatchSubmitter", () => {
|
|
117
96
|
it("should authorize new users when caller is authorized", async () => {
|
|
118
|
-
|
|
119
|
-
const existingAuth = new LaunchpadBatchSubmitAuthorities([
|
|
120
|
-
|
|
121
|
-
|
|
97
|
+
// Given
|
|
98
|
+
const existingAuth = new LaunchpadBatchSubmitAuthorities([users.testUser1.identityKey]);
|
|
99
|
+
|
|
100
|
+
const { ctx } = fixture(LaunchpadContract).registeredUsers(users.testUser1).savedState(existingAuth);
|
|
122
101
|
|
|
123
102
|
const dto = new AuthorizeBatchSubmitterDto();
|
|
124
|
-
dto.authorities = [
|
|
103
|
+
dto.authorities = [users.testUser2.identityKey];
|
|
104
|
+
dto.uniqueKey = randomUniqueKey();
|
|
105
|
+
dto.sign(users.testUser1.privateKey);
|
|
125
106
|
|
|
107
|
+
// When
|
|
126
108
|
const result = await authorizeLaunchpadBatchSubmitter(ctx, dto);
|
|
127
109
|
|
|
110
|
+
// Then
|
|
128
111
|
expect(result).toBeInstanceOf(BatchSubmitAuthoritiesResDto);
|
|
129
|
-
expect(result.authorities).toContain(
|
|
130
|
-
expect(result.authorities).toContain(
|
|
131
|
-
expect(result.authorities).toContain("user3");
|
|
112
|
+
expect(result.authorities).toContain(users.testUser1.identityKey);
|
|
113
|
+
expect(result.authorities).toContain(users.testUser2.identityKey);
|
|
132
114
|
});
|
|
133
115
|
|
|
134
116
|
it("should create new authorities object when none exists", async () => {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
(getObjectByKey as jest.Mock).mockRejectedValue(new NotFoundError("Not found"));
|
|
138
|
-
(putChainObject as jest.Mock).mockResolvedValue(undefined);
|
|
117
|
+
// Given
|
|
118
|
+
const { ctx } = fixture(LaunchpadContract).registeredUsers(users.testUser1);
|
|
139
119
|
|
|
140
120
|
const dto = new AuthorizeBatchSubmitterDto();
|
|
141
|
-
dto.authorities = [
|
|
121
|
+
dto.authorities = [users.testUser1.identityKey, users.testUser2.identityKey];
|
|
122
|
+
dto.uniqueKey = randomUniqueKey();
|
|
123
|
+
dto.sign(users.testUser1.privateKey);
|
|
142
124
|
|
|
125
|
+
// When
|
|
143
126
|
const result = await authorizeLaunchpadBatchSubmitter(ctx, dto);
|
|
144
127
|
|
|
128
|
+
// Then
|
|
145
129
|
expect(result).toBeInstanceOf(BatchSubmitAuthoritiesResDto);
|
|
146
|
-
expect(result.authorities).toContain(
|
|
147
|
-
expect(result.authorities).toContain(
|
|
148
|
-
expect(putChainObject).toHaveBeenCalled();
|
|
130
|
+
expect(result.authorities).toContain(users.testUser1.identityKey);
|
|
131
|
+
expect(result.authorities).toContain(users.testUser2.identityKey);
|
|
149
132
|
});
|
|
150
133
|
});
|
|
151
134
|
|
|
152
|
-
describe("
|
|
135
|
+
describe("deauthorizeLaunchpadBatchSubmitter", () => {
|
|
153
136
|
it("should deauthorize user when caller is authorized", async () => {
|
|
154
|
-
|
|
155
|
-
const existingAuth = new LaunchpadBatchSubmitAuthorities([
|
|
137
|
+
// Given
|
|
138
|
+
const existingAuth = new LaunchpadBatchSubmitAuthorities([
|
|
139
|
+
users.testUser1.identityKey,
|
|
140
|
+
users.testUser2.identityKey
|
|
141
|
+
]);
|
|
156
142
|
|
|
157
|
-
|
|
158
|
-
(putChainObject as jest.Mock).mockResolvedValue(undefined);
|
|
143
|
+
const { ctx } = fixture(LaunchpadContract).registeredUsers(users.testUser1).savedState(existingAuth);
|
|
159
144
|
|
|
160
145
|
const dto = new DeauthorizeBatchSubmitterDto();
|
|
161
|
-
dto.authority =
|
|
146
|
+
dto.authority = users.testUser2.identityKey;
|
|
147
|
+
dto.uniqueKey = randomUniqueKey();
|
|
148
|
+
dto.sign(users.testUser1.privateKey);
|
|
162
149
|
|
|
150
|
+
// When
|
|
163
151
|
const result = await deauthorizeLaunchpadBatchSubmitter(ctx, dto);
|
|
164
152
|
|
|
153
|
+
// Then
|
|
165
154
|
expect(result).toBeInstanceOf(BatchSubmitAuthoritiesResDto);
|
|
166
|
-
expect(result.authorities).toContain(
|
|
167
|
-
expect(result.authorities).not.toContain(
|
|
155
|
+
expect(result.authorities).toContain(users.testUser1.identityKey);
|
|
156
|
+
expect(result.authorities).not.toContain(users.testUser2.identityKey);
|
|
168
157
|
});
|
|
169
158
|
});
|
|
170
159
|
|
|
171
|
-
describe("
|
|
160
|
+
describe("getLaunchpadBatchSubmitAuthorities", () => {
|
|
172
161
|
it("should return current authorizations", async () => {
|
|
173
|
-
|
|
174
|
-
const existingAuth = new LaunchpadBatchSubmitAuthorities([
|
|
162
|
+
// Given
|
|
163
|
+
const existingAuth = new LaunchpadBatchSubmitAuthorities([
|
|
164
|
+
users.testUser1.identityKey,
|
|
165
|
+
users.testUser2.identityKey
|
|
166
|
+
]);
|
|
175
167
|
|
|
176
|
-
|
|
168
|
+
const { ctx } = fixture(LaunchpadContract).registeredUsers(users.testUser1).savedState(existingAuth);
|
|
177
169
|
|
|
178
170
|
const dto = new FetchBatchSubmitAuthoritiesDto();
|
|
171
|
+
dto.uniqueKey = randomUniqueKey();
|
|
172
|
+
dto.sign(users.testUser1.privateKey);
|
|
173
|
+
|
|
174
|
+
// When
|
|
179
175
|
const result = await getLaunchpadBatchSubmitAuthorities(ctx, dto);
|
|
180
176
|
|
|
177
|
+
// Then
|
|
181
178
|
expect(result).toBeInstanceOf(BatchSubmitAuthoritiesResDto);
|
|
182
|
-
expect(result.authorities).toEqual([
|
|
179
|
+
expect(result.authorities).toEqual([users.testUser1.identityKey, users.testUser2.identityKey]);
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
describe("AuthorizeBatchSubmitter contract method", () => {
|
|
184
|
+
it("should authorize new users through contract", async () => {
|
|
185
|
+
// Given
|
|
186
|
+
const existingAuth = new LaunchpadBatchSubmitAuthorities([users.admin.identityKey]);
|
|
187
|
+
|
|
188
|
+
const { ctx, contract } = fixture(LaunchpadContract)
|
|
189
|
+
.caClientIdentity("test-admin", "CuratorOrg")
|
|
190
|
+
.registeredUsers(users.admin)
|
|
191
|
+
.savedState(existingAuth);
|
|
192
|
+
|
|
193
|
+
const dto = new AuthorizeBatchSubmitterDto();
|
|
194
|
+
dto.authorities = [users.testUser2.identityKey];
|
|
195
|
+
dto.uniqueKey = randomUniqueKey();
|
|
196
|
+
dto.sign(users.admin.privateKey);
|
|
197
|
+
|
|
198
|
+
const expectedResponse = plainToInstance(BatchSubmitAuthoritiesResDto, {
|
|
199
|
+
authorities: [users.admin.identityKey, users.testUser2.identityKey]
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// When
|
|
203
|
+
const result = await contract.AuthorizeBatchSubmitter(ctx, dto);
|
|
204
|
+
|
|
205
|
+
// Then
|
|
206
|
+
expect(result).toEqual(transactionSuccess(expectedResponse));
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
describe("DeauthorizeBatchSubmitter contract method", () => {
|
|
211
|
+
it("should deauthorize user through contract", async () => {
|
|
212
|
+
// Given
|
|
213
|
+
const existingAuth = new LaunchpadBatchSubmitAuthorities([
|
|
214
|
+
users.admin.identityKey,
|
|
215
|
+
users.testUser2.identityKey
|
|
216
|
+
]);
|
|
217
|
+
|
|
218
|
+
const { ctx, contract } = fixture(LaunchpadContract)
|
|
219
|
+
.caClientIdentity("test-admin", "CuratorOrg")
|
|
220
|
+
.registeredUsers(users.admin)
|
|
221
|
+
.savedState(existingAuth);
|
|
222
|
+
|
|
223
|
+
const dto = new DeauthorizeBatchSubmitterDto();
|
|
224
|
+
dto.authority = users.testUser2.identityKey;
|
|
225
|
+
dto.uniqueKey = randomUniqueKey();
|
|
226
|
+
dto.sign(users.admin.privateKey);
|
|
227
|
+
|
|
228
|
+
const expectedResponse = plainToInstance(BatchSubmitAuthoritiesResDto, {
|
|
229
|
+
authorities: [users.admin.identityKey]
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
// When
|
|
233
|
+
const result = await contract.DeauthorizeBatchSubmitter(ctx, dto);
|
|
234
|
+
|
|
235
|
+
// Then
|
|
236
|
+
expect(result).toEqual(transactionSuccess(expectedResponse));
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
describe("GetBatchSubmitAuthorities contract method", () => {
|
|
241
|
+
it("should return current authorizations through contract", async () => {
|
|
242
|
+
// Given
|
|
243
|
+
const existingAuth = new LaunchpadBatchSubmitAuthorities([
|
|
244
|
+
users.testUser1.identityKey,
|
|
245
|
+
users.testUser2.identityKey
|
|
246
|
+
]);
|
|
247
|
+
|
|
248
|
+
const { ctx, contract } = fixture(LaunchpadContract)
|
|
249
|
+
.registeredUsers(users.testUser1)
|
|
250
|
+
.savedState(existingAuth);
|
|
251
|
+
|
|
252
|
+
const dto = new FetchBatchSubmitAuthoritiesDto();
|
|
253
|
+
dto.uniqueKey = randomUniqueKey();
|
|
254
|
+
dto.sign(users.testUser1.privateKey);
|
|
255
|
+
|
|
256
|
+
const expectedResponse = plainToInstance(BatchSubmitAuthoritiesResDto, {
|
|
257
|
+
authorities: [users.testUser1.identityKey, users.testUser2.identityKey]
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
// When
|
|
261
|
+
const result = await contract.GetBatchSubmitAuthorities(ctx, dto);
|
|
262
|
+
|
|
263
|
+
// Then
|
|
264
|
+
expect(result).toEqual(transactionSuccess(expectedResponse));
|
|
183
265
|
});
|
|
184
266
|
});
|
|
185
267
|
});
|
|
@@ -21,8 +21,7 @@ import {
|
|
|
21
21
|
asValidUserAlias,
|
|
22
22
|
randomUniqueKey
|
|
23
23
|
} from "@gala-chain/api";
|
|
24
|
-
import {
|
|
25
|
-
import { currency, fixture, transactionError, users } from "@gala-chain/test";
|
|
24
|
+
import { currency, fixture, users } from "@gala-chain/test";
|
|
26
25
|
import BigNumber from "bignumber.js";
|
|
27
26
|
import { plainToInstance } from "class-transformer";
|
|
28
27
|
|
|
@@ -87,7 +86,7 @@ describe("sellExactToken", () => {
|
|
|
87
86
|
|
|
88
87
|
it("should sell exact token amount successfully", async () => {
|
|
89
88
|
// Given
|
|
90
|
-
sale.buyToken(new BigNumber("1000"), new BigNumber("
|
|
89
|
+
sale.buyToken(new BigNumber("1000"), new BigNumber("100")); // Users bought tokens, sale now has GALA
|
|
91
90
|
const { ctx, contract } = fixture(LaunchpadContract)
|
|
92
91
|
.registeredUsers(users.testUser1)
|
|
93
92
|
.savedState(
|
|
@@ -116,82 +115,6 @@ describe("sellExactToken", () => {
|
|
|
116
115
|
expect(response.Data).toHaveProperty("isFinalized");
|
|
117
116
|
});
|
|
118
117
|
|
|
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
118
|
it("should handle small token sell amount", async () => {
|
|
196
119
|
// Given
|
|
197
120
|
sale.buyToken(new BigNumber("500"), new BigNumber("25")); // Users bought tokens, sale now has GALA
|
|
@@ -224,7 +147,7 @@ describe("sellExactToken", () => {
|
|
|
224
147
|
|
|
225
148
|
it("should handle sell with expected native token parameter", async () => {
|
|
226
149
|
// Given
|
|
227
|
-
sale.buyToken(new BigNumber("800"), new BigNumber("
|
|
150
|
+
sale.buyToken(new BigNumber("800"), new BigNumber("50")); // Users bought tokens, sale now has GALA
|
|
228
151
|
const { ctx, contract } = fixture(LaunchpadContract)
|
|
229
152
|
.registeredUsers(users.testUser1)
|
|
230
153
|
.savedState(
|
|
@@ -254,7 +177,7 @@ describe("sellExactToken", () => {
|
|
|
254
177
|
|
|
255
178
|
it("should handle large token sell amount", async () => {
|
|
256
179
|
// Given
|
|
257
|
-
sale.buyToken(new BigNumber("2000"), new BigNumber("
|
|
180
|
+
sale.buyToken(new BigNumber("2000"), new BigNumber("500")); // Users bought tokens, sale now has GALA
|
|
258
181
|
const { ctx, contract } = fixture(LaunchpadContract)
|
|
259
182
|
.registeredUsers(users.testUser1)
|
|
260
183
|
.savedState(
|
|
@@ -18,9 +18,9 @@ import { BigNumber } from "bignumber.js";
|
|
|
18
18
|
|
|
19
19
|
import { ExactTokenQuantityDto, TradeResDto } from "../../api/types";
|
|
20
20
|
import { SlippageToleranceExceededError } from "../../api/utils/error";
|
|
21
|
-
import { fetchAndValidateSale
|
|
21
|
+
import { fetchAndValidateSale } from "../utils";
|
|
22
22
|
import { callNativeTokenOut } from "./callNativeTokenOut";
|
|
23
|
-
import { payReverseBondingCurveFee } from "./fees";
|
|
23
|
+
import { payReverseBondingCurveFee, transferTransactionFees } from "./fees";
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* Executes the sale of an exact amount of tokens for native tokens (e.g., GALA).
|
|
@@ -49,25 +49,26 @@ export async function sellExactToken(
|
|
|
49
49
|
|
|
50
50
|
// Determine how much native token (e.g., GALA) the user will receive for the exact token quantity
|
|
51
51
|
const callNativeTokenOutResult = await callNativeTokenOut(ctx, sellTokenDTO);
|
|
52
|
-
const
|
|
53
|
-
const
|
|
52
|
+
const tokensBeingSold = new BigNumber(callNativeTokenOutResult.originalQuantity); // number of tokens user wants to sell
|
|
53
|
+
const nativeTokensPayout = new BigNumber(callNativeTokenOutResult.calculatedQuantity); // number of native tokens user will receive
|
|
54
|
+
const transactionFees = new BigNumber(callNativeTokenOutResult.extraFees.transactionFees); // transaction fees
|
|
54
55
|
|
|
55
56
|
const nativeTokensLeftInVault = new BigNumber(sale.nativeTokenQuantity);
|
|
56
57
|
const nativeToken = sale.fetchNativeTokenInstanceKey();
|
|
57
58
|
const memeToken = sale.fetchSellingTokenInstanceKey();
|
|
58
59
|
|
|
59
60
|
// Abort if the vault doesn't have enough native tokens to pay the user
|
|
60
|
-
if (
|
|
61
|
+
if (new BigNumber(sellTokenDTO.tokenQuantity).isGreaterThan(nativeTokensLeftInVault)) {
|
|
61
62
|
throw new ValidationFailedError("Not enough GALA in sale contract to carry out this operation.");
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
// Enforce slippage tolerance: expected amount must not be greater than what will actually be received
|
|
65
66
|
if (
|
|
66
67
|
sellTokenDTO.expectedNativeToken &&
|
|
67
|
-
sellTokenDTO.expectedNativeToken.
|
|
68
|
+
sellTokenDTO.expectedNativeToken.isGreaterThan(nativeTokensPayout)
|
|
68
69
|
) {
|
|
69
70
|
throw new SlippageToleranceExceededError(
|
|
70
|
-
`expected ${sellTokenDTO.expectedNativeToken.toString()}, but only ${
|
|
71
|
+
`expected ${sellTokenDTO.expectedNativeToken.toString()}, but only ${nativeTokensPayout.toString()} tokens can be provided. Reduce the expected amount or adjust your slippage tolerance.`
|
|
71
72
|
);
|
|
72
73
|
}
|
|
73
74
|
|
|
@@ -76,29 +77,19 @@ export async function sellExactToken(
|
|
|
76
77
|
await payReverseBondingCurveFee(
|
|
77
78
|
ctx,
|
|
78
79
|
sale,
|
|
79
|
-
|
|
80
|
+
nativeTokensPayout,
|
|
80
81
|
sellTokenDTO.extraFees?.maxAcceptableReverseBondingCurveFee
|
|
81
82
|
);
|
|
82
83
|
|
|
83
84
|
// Transfer launchpad transaction fee if applicable
|
|
84
|
-
|
|
85
|
-
if (launchpadFeeAddressConfiguration && transactionFees) {
|
|
86
|
-
await transferToken(ctx, {
|
|
87
|
-
from: ctx.callingUser,
|
|
88
|
-
to: launchpadFeeAddressConfiguration.feeAddress,
|
|
89
|
-
tokenInstanceKey: nativeToken,
|
|
90
|
-
quantity: new BigNumber(transactionFees),
|
|
91
|
-
allowancesToUse: [],
|
|
92
|
-
authorizedOnBehalf: undefined
|
|
93
|
-
});
|
|
94
|
-
}
|
|
85
|
+
await transferTransactionFees(ctx, sale, transactionFees, nativeToken);
|
|
95
86
|
|
|
96
87
|
// Transfer meme tokens from user to vault
|
|
97
88
|
await transferToken(ctx, {
|
|
98
89
|
from: ctx.callingUser,
|
|
99
90
|
to: sellTokenDTO.vaultAddress,
|
|
100
91
|
tokenInstanceKey: memeToken,
|
|
101
|
-
quantity:
|
|
92
|
+
quantity: tokensBeingSold,
|
|
102
93
|
allowancesToUse: [],
|
|
103
94
|
authorizedOnBehalf: undefined
|
|
104
95
|
});
|
|
@@ -108,7 +99,7 @@ export async function sellExactToken(
|
|
|
108
99
|
from: sellTokenDTO.vaultAddress,
|
|
109
100
|
to: ctx.callingUser,
|
|
110
101
|
tokenInstanceKey: nativeToken,
|
|
111
|
-
quantity:
|
|
102
|
+
quantity: nativeTokensPayout,
|
|
112
103
|
allowancesToUse: [],
|
|
113
104
|
authorizedOnBehalf: {
|
|
114
105
|
callingOnBehalf: sellTokenDTO.vaultAddress,
|
|
@@ -117,16 +108,16 @@ export async function sellExactToken(
|
|
|
117
108
|
});
|
|
118
109
|
|
|
119
110
|
// Update sale state with this transaction
|
|
120
|
-
sale.sellToken(
|
|
111
|
+
sale.sellToken(tokensBeingSold, nativeTokensPayout);
|
|
121
112
|
await putChainObject(ctx, sale);
|
|
122
113
|
|
|
123
114
|
const token = await fetchTokenClass(ctx, sale.sellingToken);
|
|
124
115
|
return {
|
|
125
|
-
inputQuantity:
|
|
126
|
-
totalFees:
|
|
116
|
+
inputQuantity: tokensBeingSold.toFixed(),
|
|
117
|
+
totalFees: transactionFees
|
|
127
118
|
.plus(sellTokenDTO.extraFees?.maxAcceptableReverseBondingCurveFee ?? 0)
|
|
128
119
|
.toFixed(),
|
|
129
|
-
outputQuantity:
|
|
120
|
+
outputQuantity: nativeTokensPayout.toFixed(),
|
|
130
121
|
tokenName: token.name,
|
|
131
122
|
tradeType: "Sell",
|
|
132
123
|
vaultAddress: sellTokenDTO.vaultAddress,
|
|
@@ -125,46 +125,6 @@ describe("sellWithNative", () => {
|
|
|
125
125
|
);
|
|
126
126
|
});
|
|
127
127
|
|
|
128
|
-
it("should reject sell when dto contains fractional precision greater than native TokenClass.decimals", async () => {
|
|
129
|
-
// Given - Setup meme token with 0 decimals to force decimal precision error
|
|
130
|
-
const zeroDecimalLaunchpadGalaClass = plainToInstance(TokenClass, {
|
|
131
|
-
...launchpadgala.tokenClassPlain(),
|
|
132
|
-
decimals: 8 // This codebase currently hard-codes 8 as NATIVE_TOKEN_DECIMALS...
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
// Simulate prior buys to establish sale state with native tokens
|
|
136
|
-
sale.buyToken(new BigNumber("100"), new BigNumber("100"));
|
|
137
|
-
|
|
138
|
-
const { ctx, contract } = fixture(LaunchpadContract)
|
|
139
|
-
.registeredUsers(users.testUser1)
|
|
140
|
-
.savedState(
|
|
141
|
-
currencyClass,
|
|
142
|
-
currencyInstance,
|
|
143
|
-
zeroDecimalLaunchpadGalaClass,
|
|
144
|
-
launchpadGalaInstance,
|
|
145
|
-
sale,
|
|
146
|
-
salelaunchpadGalaBalance,
|
|
147
|
-
saleCurrencyBalance,
|
|
148
|
-
userlaunchpadGalaBalance,
|
|
149
|
-
userCurrencyBalance
|
|
150
|
-
);
|
|
151
|
-
|
|
152
|
-
// Request native tokens that will require fractional meme tokens from bonding curve
|
|
153
|
-
const sellDto = new NativeTokenQuantityDto(vaultAddress, new BigNumber("0.123456789"));
|
|
154
|
-
sellDto.uniqueKey = randomUniqueKey();
|
|
155
|
-
sellDto.sign(users.testUser1.privateKey);
|
|
156
|
-
|
|
157
|
-
// When
|
|
158
|
-
const response = await contract.SellWithNative(ctx, sellDto);
|
|
159
|
-
|
|
160
|
-
// Then - Expect error due to decimal precision mismatch
|
|
161
|
-
expect(response).toEqual(
|
|
162
|
-
transactionError(
|
|
163
|
-
new InvalidDecimalError(sellDto.nativeTokenQuantity, zeroDecimalLaunchpadGalaClass.decimals)
|
|
164
|
-
)
|
|
165
|
-
);
|
|
166
|
-
});
|
|
167
|
-
|
|
168
128
|
it("should sell tokens for native currency successfully", async () => {
|
|
169
129
|
// Given
|
|
170
130
|
sale.buyToken(new BigNumber("10000"), new BigNumber("10")); // Users bought tokens, sale now has GALA
|
|
@@ -188,12 +148,12 @@ describe("sellWithNative", () => {
|
|
|
188
148
|
|
|
189
149
|
const expectedResponse = plainToInstance(TradeResDto, {
|
|
190
150
|
functionName: "SellWithNative",
|
|
191
|
-
inputQuantity: "6008.
|
|
151
|
+
inputQuantity: "6008.9271949683",
|
|
192
152
|
isFinalized: false,
|
|
193
153
|
outputQuantity: "0.1",
|
|
194
154
|
tokenName: "AUTOMATEDTESTCOIN",
|
|
195
155
|
totalFees: "0",
|
|
196
|
-
totalTokenSold: "3991.
|
|
156
|
+
totalTokenSold: "3991.0728050317",
|
|
197
157
|
tradeType: "Sell",
|
|
198
158
|
uniqueKey: sellDto.uniqueKey,
|
|
199
159
|
userAddress: "client|testUser1",
|