@gala-chain/launchpad 1.0.1 → 1.0.2
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/LaunchpadBatchSubmitAuthorities.d.ts +12 -0
- package/lib/src/api/types/LaunchpadBatchSubmitAuthorities.d.ts.map +1 -0
- package/lib/src/api/types/LaunchpadBatchSubmitAuthorities.js +56 -0
- package/lib/src/api/types/LaunchpadBatchSubmitAuthorities.js.map +1 -0
- package/lib/src/api/types/LaunchpadDtos.d.ts +14 -0
- package/lib/src/api/types/LaunchpadDtos.d.ts.map +1 -1
- package/lib/src/api/types/LaunchpadDtos.js +45 -1
- package/lib/src/api/types/LaunchpadDtos.js.map +1 -1
- package/lib/src/api/types/LaunchpadFeeConfig.d.ts +3 -2
- package/lib/src/api/types/LaunchpadFeeConfig.d.ts.map +1 -1
- package/lib/src/api/types/LaunchpadFeeConfig.js +14 -4
- package/lib/src/api/types/LaunchpadFeeConfig.js.map +1 -1
- package/lib/src/api/types/index.d.ts +1 -0
- package/lib/src/api/types/index.d.ts.map +1 -1
- package/lib/src/api/types/index.js +1 -0
- package/lib/src/api/types/index.js.map +1 -1
- package/lib/src/chaincode/LaunchpadContract.d.ts +6 -1
- package/lib/src/chaincode/LaunchpadContract.d.ts.map +1 -1
- package/lib/src/chaincode/LaunchpadContract.js +85 -0
- 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 +15 -0
- 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 +24 -0
- package/lib/src/chaincode/launchpad/buyWithNative.js.map +1 -1
- package/lib/src/chaincode/launchpad/callMemeTokenIn.d.ts +1 -0
- package/lib/src/chaincode/launchpad/callMemeTokenIn.d.ts.map +1 -1
- package/lib/src/chaincode/launchpad/callMemeTokenIn.js +3 -1
- package/lib/src/chaincode/launchpad/callMemeTokenIn.js.map +1 -1
- package/lib/src/chaincode/launchpad/callMemeTokenOut.d.ts +1 -0
- package/lib/src/chaincode/launchpad/callMemeTokenOut.d.ts.map +1 -1
- package/lib/src/chaincode/launchpad/callMemeTokenOut.js +4 -1
- package/lib/src/chaincode/launchpad/callMemeTokenOut.js.map +1 -1
- package/lib/src/chaincode/launchpad/callNativeTokenIn.d.ts +1 -0
- package/lib/src/chaincode/launchpad/callNativeTokenIn.d.ts.map +1 -1
- package/lib/src/chaincode/launchpad/callNativeTokenIn.js +6 -2
- package/lib/src/chaincode/launchpad/callNativeTokenIn.js.map +1 -1
- package/lib/src/chaincode/launchpad/callNativeTokenOut.d.ts +1 -0
- package/lib/src/chaincode/launchpad/callNativeTokenOut.d.ts.map +1 -1
- package/lib/src/chaincode/launchpad/callNativeTokenOut.js +3 -1
- package/lib/src/chaincode/launchpad/callNativeTokenOut.js.map +1 -1
- package/lib/src/chaincode/launchpad/configureLaunchpadFeeConfig.d.ts +12 -0
- package/lib/src/chaincode/launchpad/configureLaunchpadFeeConfig.d.ts.map +1 -1
- package/lib/src/chaincode/launchpad/configureLaunchpadFeeConfig.js +26 -6
- package/lib/src/chaincode/launchpad/configureLaunchpadFeeConfig.js.map +1 -1
- package/lib/src/chaincode/launchpad/fees.d.ts +1 -0
- package/lib/src/chaincode/launchpad/fees.d.ts.map +1 -1
- package/lib/src/chaincode/launchpad/fees.js +6 -2
- package/lib/src/chaincode/launchpad/fees.js.map +1 -1
- package/lib/src/chaincode/launchpad/finaliseSale.js +3 -3
- package/lib/src/chaincode/launchpad/finaliseSale.js.map +1 -1
- package/lib/src/chaincode/launchpad/index.d.ts +1 -0
- package/lib/src/chaincode/launchpad/index.d.ts.map +1 -1
- package/lib/src/chaincode/launchpad/index.js +1 -0
- package/lib/src/chaincode/launchpad/index.js.map +1 -1
- package/lib/src/chaincode/launchpad/launchpadBatchSubmitAuthorizations.d.ts +24 -0
- package/lib/src/chaincode/launchpad/launchpadBatchSubmitAuthorizations.d.ts.map +1 -0
- package/lib/src/chaincode/launchpad/launchpadBatchSubmitAuthorizations.js +84 -0
- package/lib/src/chaincode/launchpad/launchpadBatchSubmitAuthorizations.js.map +1 -0
- package/lib/src/chaincode/launchpad/sellExactToken.d.ts.map +1 -1
- package/lib/src/chaincode/launchpad/sellExactToken.js +27 -8
- 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 +26 -2
- package/lib/src/chaincode/launchpad/sellWithNative.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/api/types/LaunchpadBatchSubmitAuthorities.ts +53 -0
- package/src/api/types/LaunchpadDtos.ts +38 -0
- package/src/api/types/LaunchpadFeeConfig.ts +13 -4
- package/src/api/types/index.ts +1 -0
- package/src/chaincode/LaunchpadContract.ts +96 -1
- package/src/chaincode/launchpad/buyExactToken.ts +17 -1
- package/src/chaincode/launchpad/buyWithNative.ts +28 -1
- package/src/chaincode/launchpad/callMemeTokenIn.ts +8 -3
- package/src/chaincode/launchpad/callMemeTokenOut.ts +8 -2
- package/src/chaincode/launchpad/callNativeTokenIn.ts +10 -3
- package/src/chaincode/launchpad/callNativeTokenOut.ts +8 -3
- package/src/chaincode/launchpad/configureLaunchpadFeeConfig.ts +27 -4
- package/src/chaincode/launchpad/fees.ts +5 -1
- package/src/chaincode/launchpad/finaliseSale.ts +3 -3
- package/src/chaincode/launchpad/index.ts +1 -0
- package/src/chaincode/launchpad/launchpadBatchSubmitAuthorizations.spec.ts +185 -0
- package/src/chaincode/launchpad/launchpadBatchSubmitAuthorizations.ts +105 -0
- package/src/chaincode/launchpad/sellExactToken.ts +30 -11
- package/src/chaincode/launchpad/sellWithNative.ts +30 -2
|
@@ -46,8 +46,8 @@ export async function finalizeSale(ctx: GalaChainContext, sale: LaunchpadSale):
|
|
|
46
46
|
const key = ctx.stub.createCompositeKey(LaunchpadFinalizeFeeAllocation.INDEX_KEY, []);
|
|
47
47
|
const feeAllocation = await getObjectByKey(ctx, LaunchpadFinalizeFeeAllocation, key).catch(() => undefined);
|
|
48
48
|
|
|
49
|
-
const
|
|
50
|
-
if (!
|
|
49
|
+
const launchpadFeeAddressConfiguration = await fetchLaunchpadFeeAddress(ctx);
|
|
50
|
+
if (!launchpadFeeAddressConfiguration) {
|
|
51
51
|
throw new PreConditionFailedError("Platform fee configuration is yet to be defined.");
|
|
52
52
|
}
|
|
53
53
|
|
|
@@ -75,7 +75,7 @@ export async function finalizeSale(ctx: GalaChainContext, sale: LaunchpadSale):
|
|
|
75
75
|
|
|
76
76
|
await transferToken(ctx, {
|
|
77
77
|
from: vaultAddressAlias,
|
|
78
|
-
to:
|
|
78
|
+
to: launchpadFeeAddressConfiguration.feeAddress,
|
|
79
79
|
tokenInstanceKey: nativeToken,
|
|
80
80
|
quantity: new BigNumber(sale.nativeTokenQuantity)
|
|
81
81
|
.times(platformFeePercentage)
|
|
@@ -0,0 +1,185 @@
|
|
|
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 { NotFoundError } from "@gala-chain/api";
|
|
16
|
+
import { GalaChainContext, getObjectByKey, putChainObject } from "@gala-chain/chaincode";
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
AuthorizeBatchSubmitterDto,
|
|
20
|
+
BatchSubmitAuthoritiesResDto,
|
|
21
|
+
DeauthorizeBatchSubmitterDto,
|
|
22
|
+
FetchBatchSubmitAuthoritiesDto,
|
|
23
|
+
LaunchpadBatchSubmitAuthorities
|
|
24
|
+
} from "../../api/types";
|
|
25
|
+
import {
|
|
26
|
+
authorizeLaunchpadBatchSubmitter,
|
|
27
|
+
deauthorizeLaunchpadBatchSubmitter,
|
|
28
|
+
fetchLaunchpadBatchSubmitAuthorities,
|
|
29
|
+
getLaunchpadBatchSubmitAuthorities
|
|
30
|
+
} from "./launchpadBatchSubmitAuthorizations";
|
|
31
|
+
|
|
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
|
+
describe("BatchSubmitAuthorizations", () => {
|
|
58
|
+
beforeEach(() => {
|
|
59
|
+
jest.clearAllMocks();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
describe("BatchSubmitAuthorizations chain object", () => {
|
|
63
|
+
it("should create with initial authorities", () => {
|
|
64
|
+
const auth = new LaunchpadBatchSubmitAuthorities(["user1", "user2"]);
|
|
65
|
+
expect(auth.authorities).toEqual(["user1", "user2"]);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("should add authority", () => {
|
|
69
|
+
const auth = new LaunchpadBatchSubmitAuthorities(["user1"]);
|
|
70
|
+
auth.addAuthority("user2");
|
|
71
|
+
expect(auth.authorities).toEqual(["user1", "user2"]);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("should not add duplicate authority", () => {
|
|
75
|
+
const auth = new LaunchpadBatchSubmitAuthorities(["user1"]);
|
|
76
|
+
auth.addAuthority("user1");
|
|
77
|
+
expect(auth.authorities).toEqual(["user1"]);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("should remove authority", () => {
|
|
81
|
+
const auth = new LaunchpadBatchSubmitAuthorities(["user1", "user2"]);
|
|
82
|
+
auth.removeAuthority("user1");
|
|
83
|
+
expect(auth.authorities).toEqual(["user2"]);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("should check if user is authorized", () => {
|
|
87
|
+
const auth = new LaunchpadBatchSubmitAuthorities(["user1", "user2"]);
|
|
88
|
+
expect(auth.isAuthorized("user1")).toBe(true);
|
|
89
|
+
expect(auth.isAuthorized("user3")).toBe(false);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("should get authorized authorities", () => {
|
|
93
|
+
const auth = new LaunchpadBatchSubmitAuthorities(["user1", "user2"]);
|
|
94
|
+
const authorities = auth.getAuthorities();
|
|
95
|
+
expect(authorities).toEqual(["user1", "user2"]);
|
|
96
|
+
// Should return a copy, not the original array
|
|
97
|
+
authorities.push("user3");
|
|
98
|
+
expect(auth.authorities).toEqual(["user1", "user2"]);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
describe("fetchBatchSubmitAuthorizations", () => {
|
|
103
|
+
it("should return existing authorizations", async () => {
|
|
104
|
+
const ctx = createMockContext("user1");
|
|
105
|
+
const existingAuth = new LaunchpadBatchSubmitAuthorities(["user1", "user2"]);
|
|
106
|
+
|
|
107
|
+
(getObjectByKey as jest.Mock).mockResolvedValue(existingAuth);
|
|
108
|
+
(putChainObject as jest.Mock).mockResolvedValue(undefined);
|
|
109
|
+
|
|
110
|
+
const result = await fetchLaunchpadBatchSubmitAuthorities(ctx);
|
|
111
|
+
|
|
112
|
+
expect(result).toBe(existingAuth);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
describe("authorizeBatchSubmitter", () => {
|
|
117
|
+
it("should authorize new users when caller is authorized", async () => {
|
|
118
|
+
const ctx = createMockContext("user1");
|
|
119
|
+
const existingAuth = new LaunchpadBatchSubmitAuthorities(["user1"]);
|
|
120
|
+
(putChainObject as jest.Mock).mockResolvedValue(existingAuth);
|
|
121
|
+
(putChainObject as jest.Mock).mockResolvedValue(undefined);
|
|
122
|
+
|
|
123
|
+
const dto = new AuthorizeBatchSubmitterDto();
|
|
124
|
+
dto.authorities = ["user2", "user3"];
|
|
125
|
+
|
|
126
|
+
const result = await authorizeLaunchpadBatchSubmitter(ctx, dto);
|
|
127
|
+
|
|
128
|
+
expect(result).toBeInstanceOf(BatchSubmitAuthoritiesResDto);
|
|
129
|
+
expect(result.authorities).toContain("user1");
|
|
130
|
+
expect(result.authorities).toContain("user2");
|
|
131
|
+
expect(result.authorities).toContain("user3");
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it("should create new authorities object when none exists", async () => {
|
|
135
|
+
const ctx = createMockContext("user1");
|
|
136
|
+
|
|
137
|
+
(getObjectByKey as jest.Mock).mockRejectedValue(new NotFoundError("Not found"));
|
|
138
|
+
(putChainObject as jest.Mock).mockResolvedValue(undefined);
|
|
139
|
+
|
|
140
|
+
const dto = new AuthorizeBatchSubmitterDto();
|
|
141
|
+
dto.authorities = ["user1", "user2"];
|
|
142
|
+
|
|
143
|
+
const result = await authorizeLaunchpadBatchSubmitter(ctx, dto);
|
|
144
|
+
|
|
145
|
+
expect(result).toBeInstanceOf(BatchSubmitAuthoritiesResDto);
|
|
146
|
+
expect(result.authorities).toContain("user1");
|
|
147
|
+
expect(result.authorities).toContain("user2");
|
|
148
|
+
expect(putChainObject).toHaveBeenCalled();
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
describe("deauthorizeBatchSubmitter", () => {
|
|
153
|
+
it("should deauthorize user when caller is authorized", async () => {
|
|
154
|
+
const ctx = createMockContext("user1");
|
|
155
|
+
const existingAuth = new LaunchpadBatchSubmitAuthorities(["user1", "user2"]);
|
|
156
|
+
|
|
157
|
+
(getObjectByKey as jest.Mock).mockResolvedValue(existingAuth);
|
|
158
|
+
(putChainObject as jest.Mock).mockResolvedValue(undefined);
|
|
159
|
+
|
|
160
|
+
const dto = new DeauthorizeBatchSubmitterDto();
|
|
161
|
+
dto.authority = "user2";
|
|
162
|
+
|
|
163
|
+
const result = await deauthorizeLaunchpadBatchSubmitter(ctx, dto);
|
|
164
|
+
|
|
165
|
+
expect(result).toBeInstanceOf(BatchSubmitAuthoritiesResDto);
|
|
166
|
+
expect(result.authorities).toContain("user1");
|
|
167
|
+
expect(result.authorities).not.toContain("user2");
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
describe("getBatchSubmitAuthorizations", () => {
|
|
172
|
+
it("should return current authorizations", async () => {
|
|
173
|
+
const ctx = createMockContext("user1");
|
|
174
|
+
const existingAuth = new LaunchpadBatchSubmitAuthorities(["user1", "user2"]);
|
|
175
|
+
|
|
176
|
+
(getObjectByKey as jest.Mock).mockResolvedValue(existingAuth);
|
|
177
|
+
|
|
178
|
+
const dto = new FetchBatchSubmitAuthoritiesDto();
|
|
179
|
+
const result = await getLaunchpadBatchSubmitAuthorities(ctx, dto);
|
|
180
|
+
|
|
181
|
+
expect(result).toBeInstanceOf(BatchSubmitAuthoritiesResDto);
|
|
182
|
+
expect(result.authorities).toEqual(["user1", "user2"]);
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
});
|
|
@@ -0,0 +1,105 @@
|
|
|
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 { ChainError } from "@gala-chain/api";
|
|
16
|
+
import { GalaChainContext, getObjectByKey, putChainObject } from "@gala-chain/chaincode";
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
AuthorizeBatchSubmitterDto,
|
|
20
|
+
BatchSubmitAuthoritiesResDto,
|
|
21
|
+
DeauthorizeBatchSubmitterDto,
|
|
22
|
+
ErrorCode,
|
|
23
|
+
FetchBatchSubmitAuthoritiesDto,
|
|
24
|
+
LaunchpadBatchSubmitAuthorities
|
|
25
|
+
} from "../../api";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Fetches the batch submit authorities from the chain.
|
|
29
|
+
*/
|
|
30
|
+
export async function fetchLaunchpadBatchSubmitAuthorities(
|
|
31
|
+
ctx: GalaChainContext
|
|
32
|
+
): Promise<LaunchpadBatchSubmitAuthorities> {
|
|
33
|
+
const key = ctx.stub.createCompositeKey(LaunchpadBatchSubmitAuthorities.INDEX_KEY, []);
|
|
34
|
+
|
|
35
|
+
return await getObjectByKey(ctx, LaunchpadBatchSubmitAuthorities, key);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Authorizes users to call BatchSubmit operations.
|
|
40
|
+
* Only existing authorized users can add new authorizations.
|
|
41
|
+
*/
|
|
42
|
+
export async function authorizeLaunchpadBatchSubmitter(
|
|
43
|
+
ctx: GalaChainContext,
|
|
44
|
+
dto: AuthorizeBatchSubmitterDto
|
|
45
|
+
): Promise<BatchSubmitAuthoritiesResDto> {
|
|
46
|
+
const key = ctx.stub.createCompositeKey(LaunchpadBatchSubmitAuthorities.INDEX_KEY, []);
|
|
47
|
+
const authorities = await getObjectByKey(ctx, LaunchpadBatchSubmitAuthorities, key).catch((e) => {
|
|
48
|
+
const chainError = ChainError.from(e);
|
|
49
|
+
if (chainError.matches(ErrorCode.NOT_FOUND)) {
|
|
50
|
+
return new LaunchpadBatchSubmitAuthorities([]);
|
|
51
|
+
} else {
|
|
52
|
+
throw chainError;
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Add new authorities
|
|
57
|
+
for (const authority of dto.authorities) {
|
|
58
|
+
authorities.addAuthority(authority);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
await putChainObject(ctx, authorities);
|
|
62
|
+
|
|
63
|
+
const result = new BatchSubmitAuthoritiesResDto();
|
|
64
|
+
result.authorities = authorities.getAuthorities();
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Deauthorizes a user from calling BatchSubmit operations.
|
|
70
|
+
*/
|
|
71
|
+
export async function deauthorizeLaunchpadBatchSubmitter(
|
|
72
|
+
ctx: GalaChainContext,
|
|
73
|
+
dto: DeauthorizeBatchSubmitterDto
|
|
74
|
+
): Promise<BatchSubmitAuthoritiesResDto> {
|
|
75
|
+
const authorities = await fetchLaunchpadBatchSubmitAuthorities(ctx);
|
|
76
|
+
|
|
77
|
+
authorities.removeAuthority(dto.authority);
|
|
78
|
+
await putChainObject(ctx, authorities);
|
|
79
|
+
|
|
80
|
+
const result = new BatchSubmitAuthoritiesResDto();
|
|
81
|
+
result.authorities = authorities.getAuthorities();
|
|
82
|
+
return result;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Fetches the current batch submit authorizations.
|
|
87
|
+
*/
|
|
88
|
+
export async function getLaunchpadBatchSubmitAuthorities(
|
|
89
|
+
ctx: GalaChainContext,
|
|
90
|
+
dto: FetchBatchSubmitAuthoritiesDto
|
|
91
|
+
): Promise<BatchSubmitAuthoritiesResDto> {
|
|
92
|
+
const authorities = await fetchLaunchpadBatchSubmitAuthorities(ctx);
|
|
93
|
+
const result = new BatchSubmitAuthoritiesResDto();
|
|
94
|
+
|
|
95
|
+
result.authorities = authorities.getAuthorities();
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Checks if the calling user is authorized to perform batch submit operations.
|
|
101
|
+
*/
|
|
102
|
+
export async function isAuthorizedForLaunchpadBatchSubmit(ctx: GalaChainContext): Promise<boolean> {
|
|
103
|
+
const authorities = await fetchLaunchpadBatchSubmitAuthorities(ctx);
|
|
104
|
+
return authorities.isAuthorized(ctx.callingUser);
|
|
105
|
+
}
|
|
@@ -12,13 +12,13 @@
|
|
|
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
16
|
import { GalaChainContext, fetchTokenClass, putChainObject, transferToken } from "@gala-chain/chaincode";
|
|
16
17
|
import { BigNumber } from "bignumber.js";
|
|
17
18
|
|
|
18
|
-
import { ExactTokenQuantityDto,
|
|
19
|
+
import { ExactTokenQuantityDto, TradeResDto } from "../../api/types";
|
|
19
20
|
import { SlippageToleranceExceededError } from "../../api/utils/error";
|
|
20
|
-
import { fetchAndValidateSale } from "../utils";
|
|
21
|
-
import { callMemeTokenIn } from "./callMemeTokenIn";
|
|
21
|
+
import { fetchAndValidateSale, fetchLaunchpadFeeAddress } from "../utils";
|
|
22
22
|
import { callNativeTokenOut } from "./callNativeTokenOut";
|
|
23
23
|
import { payReverseBondingCurveFee } from "./fees";
|
|
24
24
|
|
|
@@ -48,24 +48,24 @@ export async function sellExactToken(
|
|
|
48
48
|
ctx: GalaChainContext,
|
|
49
49
|
sellTokenDTO: ExactTokenQuantityDto
|
|
50
50
|
): Promise<TradeResDto> {
|
|
51
|
+
// Fetch and validate the current sale object
|
|
51
52
|
const sale = await fetchAndValidateSale(ctx, sellTokenDTO.vaultAddress);
|
|
52
53
|
|
|
54
|
+
// Determine how much native token (e.g., GALA) the user will receive for the exact token quantity
|
|
53
55
|
const callNativeTokenOutResult = await callNativeTokenOut(ctx, sellTokenDTO);
|
|
54
|
-
|
|
56
|
+
const nativeTokensToProvide = new BigNumber(callNativeTokenOutResult.calculatedQuantity);
|
|
57
|
+
const transactionFees = callNativeTokenOutResult.extraFees.transactionFees;
|
|
58
|
+
|
|
55
59
|
const nativeTokensLeftInVault = new BigNumber(sale.nativeTokenQuantity);
|
|
56
60
|
const nativeToken = sale.fetchNativeTokenInstanceKey();
|
|
57
61
|
const memeToken = sale.fetchSellingTokenInstanceKey();
|
|
58
62
|
|
|
63
|
+
// Abort if the vault doesn't have enough native tokens to pay the user
|
|
59
64
|
if (nativeTokensLeftInVault.comparedTo(nativeTokensToProvide) < 0) {
|
|
60
|
-
|
|
61
|
-
const nativeTokensBeingSoldDto = new NativeTokenQuantityDto(
|
|
62
|
-
sellTokenDTO.vaultAddress,
|
|
63
|
-
nativeTokensToProvide
|
|
64
|
-
);
|
|
65
|
-
const callMemeTokenInResult = await callMemeTokenIn(ctx, nativeTokensBeingSoldDto);
|
|
66
|
-
sellTokenDTO.tokenQuantity = new BigNumber(callMemeTokenInResult.calculatedQuantity);
|
|
65
|
+
throw new ValidationFailedError("Not enough GALA in sale contract to carry out this operation.");
|
|
67
66
|
}
|
|
68
67
|
|
|
68
|
+
// Enforce slippage tolerance: expected amount must not be greater than what will actually be received
|
|
69
69
|
if (
|
|
70
70
|
sellTokenDTO.expectedNativeToken &&
|
|
71
71
|
sellTokenDTO.expectedNativeToken.comparedTo(nativeTokensToProvide) > 0
|
|
@@ -84,6 +84,20 @@ export async function sellExactToken(
|
|
|
84
84
|
sellTokenDTO.extraFees?.maxAcceptableReverseBondingCurveFee
|
|
85
85
|
);
|
|
86
86
|
|
|
87
|
+
// Transfer launchpad transaction fee if applicable
|
|
88
|
+
const launchpadFeeAddressConfiguration = await fetchLaunchpadFeeAddress(ctx);
|
|
89
|
+
if (launchpadFeeAddressConfiguration && transactionFees) {
|
|
90
|
+
await transferToken(ctx, {
|
|
91
|
+
from: ctx.callingUser,
|
|
92
|
+
to: launchpadFeeAddressConfiguration.feeAddress,
|
|
93
|
+
tokenInstanceKey: nativeToken,
|
|
94
|
+
quantity: new BigNumber(transactionFees),
|
|
95
|
+
allowancesToUse: [],
|
|
96
|
+
authorizedOnBehalf: undefined
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Transfer meme tokens from user to vault
|
|
87
101
|
await transferToken(ctx, {
|
|
88
102
|
from: ctx.callingUser,
|
|
89
103
|
to: sellTokenDTO.vaultAddress,
|
|
@@ -93,6 +107,7 @@ export async function sellExactToken(
|
|
|
93
107
|
authorizedOnBehalf: undefined
|
|
94
108
|
});
|
|
95
109
|
|
|
110
|
+
// Transfer native tokens from vault to user
|
|
96
111
|
await transferToken(ctx, {
|
|
97
112
|
from: sellTokenDTO.vaultAddress,
|
|
98
113
|
to: ctx.callingUser,
|
|
@@ -105,12 +120,16 @@ export async function sellExactToken(
|
|
|
105
120
|
}
|
|
106
121
|
});
|
|
107
122
|
|
|
123
|
+
// Update sale state with this transaction
|
|
108
124
|
sale.sellToken(sellTokenDTO.tokenQuantity, nativeTokensToProvide);
|
|
109
125
|
await putChainObject(ctx, sale);
|
|
110
126
|
|
|
111
127
|
const token = await fetchTokenClass(ctx, sale.sellingToken);
|
|
112
128
|
return {
|
|
113
129
|
inputQuantity: sellTokenDTO.tokenQuantity.toFixed(),
|
|
130
|
+
totalFees: new BigNumber(transactionFees)
|
|
131
|
+
.plus(sellTokenDTO.extraFees?.maxAcceptableReverseBondingCurveFee ?? 0)
|
|
132
|
+
.toFixed(),
|
|
114
133
|
outputQuantity: nativeTokensToProvide.toFixed(),
|
|
115
134
|
tokenName: token.name,
|
|
116
135
|
tradeType: "Sell",
|
|
@@ -12,12 +12,13 @@
|
|
|
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
16
|
import { GalaChainContext, fetchTokenClass, putChainObject, transferToken } from "@gala-chain/chaincode";
|
|
16
17
|
import { BigNumber } from "bignumber.js";
|
|
17
18
|
|
|
18
19
|
import { NativeTokenQuantityDto, TradeResDto } from "../../api/types";
|
|
19
20
|
import { SlippageToleranceExceededError } from "../../api/utils/error";
|
|
20
|
-
import { fetchAndValidateSale } from "../utils";
|
|
21
|
+
import { fetchAndValidateSale, fetchLaunchpadFeeAddress } from "../utils";
|
|
21
22
|
import { callMemeTokenIn } from "./callMemeTokenIn";
|
|
22
23
|
import { payReverseBondingCurveFee } from "./fees";
|
|
23
24
|
|
|
@@ -48,18 +49,25 @@ export async function sellWithNative(
|
|
|
48
49
|
ctx: GalaChainContext,
|
|
49
50
|
sellTokenDTO: NativeTokenQuantityDto
|
|
50
51
|
): Promise<TradeResDto> {
|
|
52
|
+
// Fetch and validate the sale object
|
|
51
53
|
const sale = await fetchAndValidateSale(ctx, sellTokenDTO.vaultAddress);
|
|
52
54
|
|
|
53
55
|
const nativeTokensLeftInVault = new BigNumber(sale.nativeTokenQuantity);
|
|
56
|
+
|
|
57
|
+
// Cap nativeTokenQuantity to the vault balance if the requested amount exceeds it
|
|
54
58
|
if (nativeTokensLeftInVault.comparedTo(sellTokenDTO.nativeTokenQuantity) < 0) {
|
|
55
|
-
|
|
59
|
+
throw new ValidationFailedError("Not enough GALA in sale contract to carry out this operation.");
|
|
56
60
|
}
|
|
57
61
|
|
|
62
|
+
// Calculate how many tokens need to be sold to get the requested native amount
|
|
58
63
|
const callMemeTokenInResult = await callMemeTokenIn(ctx, sellTokenDTO);
|
|
64
|
+
const transactionFees = callMemeTokenInResult.extraFees.transactionFees;
|
|
59
65
|
const tokensToSell = new BigNumber(callMemeTokenInResult.calculatedQuantity);
|
|
66
|
+
|
|
60
67
|
const nativeToken = sale.fetchNativeTokenInstanceKey();
|
|
61
68
|
const memeToken = sale.fetchSellingTokenInstanceKey();
|
|
62
69
|
|
|
70
|
+
// Enforce slippage tolerance
|
|
63
71
|
if (sellTokenDTO.expectedToken && sellTokenDTO.expectedToken.comparedTo(tokensToSell) < 0) {
|
|
64
72
|
throw new SlippageToleranceExceededError(
|
|
65
73
|
"Token amount expected to cost for this operation is less than the the actual amount required."
|
|
@@ -75,6 +83,20 @@ export async function sellWithNative(
|
|
|
75
83
|
sellTokenDTO.extraFees?.maxAcceptableReverseBondingCurveFee
|
|
76
84
|
);
|
|
77
85
|
|
|
86
|
+
// Transfer launchpad transaction fees if applicable
|
|
87
|
+
const launchpadFeeAddressConfiguration = await fetchLaunchpadFeeAddress(ctx);
|
|
88
|
+
if (launchpadFeeAddressConfiguration && transactionFees) {
|
|
89
|
+
await transferToken(ctx, {
|
|
90
|
+
from: ctx.callingUser,
|
|
91
|
+
to: launchpadFeeAddressConfiguration.feeAddress,
|
|
92
|
+
tokenInstanceKey: nativeToken,
|
|
93
|
+
quantity: new BigNumber(transactionFees),
|
|
94
|
+
allowancesToUse: [],
|
|
95
|
+
authorizedOnBehalf: undefined
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Send meme tokens from user to vault
|
|
78
100
|
await transferToken(ctx, {
|
|
79
101
|
from: ctx.callingUser,
|
|
80
102
|
to: sellTokenDTO.vaultAddress,
|
|
@@ -83,6 +105,8 @@ export async function sellWithNative(
|
|
|
83
105
|
allowancesToUse: [],
|
|
84
106
|
authorizedOnBehalf: undefined
|
|
85
107
|
});
|
|
108
|
+
|
|
109
|
+
// Send native tokens from vault to user
|
|
86
110
|
await transferToken(ctx, {
|
|
87
111
|
from: sellTokenDTO.vaultAddress,
|
|
88
112
|
to: ctx.callingUser,
|
|
@@ -95,12 +119,16 @@ export async function sellWithNative(
|
|
|
95
119
|
}
|
|
96
120
|
});
|
|
97
121
|
|
|
122
|
+
// Update internal sale tracking
|
|
98
123
|
sale.sellToken(tokensToSell, sellTokenDTO.nativeTokenQuantity);
|
|
99
124
|
await putChainObject(ctx, sale);
|
|
100
125
|
|
|
101
126
|
const token = await fetchTokenClass(ctx, sale.sellingToken);
|
|
102
127
|
return {
|
|
103
128
|
inputQuantity: tokensToSell.toFixed(),
|
|
129
|
+
totalFees: new BigNumber(transactionFees)
|
|
130
|
+
.plus(sellTokenDTO.extraFees?.maxAcceptableReverseBondingCurveFee ?? 0)
|
|
131
|
+
.toFixed(),
|
|
104
132
|
outputQuantity: sellTokenDTO.nativeTokenQuantity.toFixed(),
|
|
105
133
|
tokenName: token.name,
|
|
106
134
|
tradeType: "Sell",
|