@gala-chain/launchpad 1.0.13 → 1.0.15
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 +3 -1
- package/lib/src/api/types/LaunchpadDtos.d.ts.map +1 -1
- package/lib/src/api/types/LaunchpadDtos.js +14 -1
- package/lib/src/api/types/LaunchpadDtos.js.map +1 -1
- package/lib/src/api/types/LaunchpadSale.d.ts +5 -2
- package/lib/src/api/types/LaunchpadSale.d.ts.map +1 -1
- package/lib/src/api/types/LaunchpadSale.js +35 -9
- 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.d.ts.map +1 -1
- package/lib/src/chaincode/launchpad/createSale.js +15 -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 +19 -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 +15 -1
- package/src/api/types/LaunchpadSale.ts +32 -10
- package/src/chaincode/LaunchpadContract.ts +3 -3
- package/src/chaincode/launchpad/buyExactToken.spec.ts +3 -38
- package/src/chaincode/launchpad/buyExactToken.ts +22 -56
- package/src/chaincode/launchpad/buyWithNative.spec.ts +105 -45
- package/src/chaincode/launchpad/buyWithNative.ts +15 -65
- package/src/chaincode/launchpad/callMemeTokenIn.ts +36 -12
- package/src/chaincode/launchpad/callMemeTokenOut.spec.ts +2 -2
- 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 +26 -3
- 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 +25 -1
- package/src/cli.ts +3 -1
|
@@ -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, TokenInstanceKey, asValidUserAlias } from "@gala-chain/api";
|
|
15
|
+
import { ConflictError, DefaultError, TokenInstanceKey, asValidUserAlias } from "@gala-chain/api";
|
|
16
16
|
import {
|
|
17
17
|
GalaChainContext,
|
|
18
18
|
createTokenClass,
|
|
@@ -23,7 +23,13 @@ import {
|
|
|
23
23
|
} from "@gala-chain/chaincode";
|
|
24
24
|
import BigNumber from "bignumber.js";
|
|
25
25
|
|
|
26
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
CreateSaleResDto,
|
|
28
|
+
CreateTokenSaleDTO,
|
|
29
|
+
LaunchpadSale,
|
|
30
|
+
NativeTokenQuantityDto,
|
|
31
|
+
SaleStatus
|
|
32
|
+
} from "../../api/types";
|
|
27
33
|
import { PreConditionFailedError } from "../../api/utils/error";
|
|
28
34
|
import { buyWithNative } from "./buyWithNative";
|
|
29
35
|
|
|
@@ -85,7 +91,7 @@ export async function createSale(
|
|
|
85
91
|
network: "GC",
|
|
86
92
|
tokenClass: tokenInstanceKey.getTokenClassKey(),
|
|
87
93
|
isNonFungible: false,
|
|
88
|
-
decimals:
|
|
94
|
+
decimals: LaunchpadSale.SELLING_TOKEN_DECIMALS,
|
|
89
95
|
name: launchpadDetails.tokenName,
|
|
90
96
|
symbol: launchpadDetails.tokenSymbol,
|
|
91
97
|
description: launchpadDetails.tokenDescription,
|
|
@@ -131,6 +137,23 @@ export async function createSale(
|
|
|
131
137
|
isSaleFinalized = tradeStatus.isFinalized;
|
|
132
138
|
}
|
|
133
139
|
|
|
140
|
+
// handling the optional saleStartTime after the preBuyQuantity allows
|
|
141
|
+
// creators to still optionally specify a pre-buy even when a
|
|
142
|
+
// sale is marked Upcoming / Coming Soon.
|
|
143
|
+
// Otherwise `buyWithNative` would throw an error when validating the sale is active.
|
|
144
|
+
if (launchpadDetails.saleStartTime !== undefined) {
|
|
145
|
+
launchpad.saleStartTime = launchpadDetails.saleStartTime;
|
|
146
|
+
|
|
147
|
+
// handle edge case
|
|
148
|
+
// if a sale was immediately sold out with a pre-buy on creation,
|
|
149
|
+
// do not set the ended sale back to UPCOMING
|
|
150
|
+
if (launchpad.saleStatus !== SaleStatus.END) {
|
|
151
|
+
launchpad.saleStatus = SaleStatus.UPCOMING;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
await putChainObject(ctx, launchpad);
|
|
155
|
+
}
|
|
156
|
+
|
|
134
157
|
// Return the response object
|
|
135
158
|
return {
|
|
136
159
|
image: launchpadDetails.tokenImage,
|
|
@@ -12,9 +12,10 @@
|
|
|
12
12
|
* See the License for the specific language governing permissions and
|
|
13
13
|
* limitations under the License.
|
|
14
14
|
*/
|
|
15
|
-
import { FeeReceiptStatus } from "@gala-chain/api";
|
|
15
|
+
import { FeeReceiptStatus, ValidationFailedError } from "@gala-chain/api";
|
|
16
16
|
import {
|
|
17
17
|
GalaChainContext,
|
|
18
|
+
fetchOrCreateBalance,
|
|
18
19
|
transferToken,
|
|
19
20
|
txUnixTimeToDateIndexKeys,
|
|
20
21
|
writeChannelPaymentReceipt,
|
|
@@ -27,7 +28,7 @@ import { SlippageToleranceExceededError } from "../../api/utils/error";
|
|
|
27
28
|
import { fetchLaunchpadFeeAddress } from "../utils";
|
|
28
29
|
|
|
29
30
|
const REVERSE_BONDING_CURVE_FEE_CODE = "LaunchpadReverseBondingCurveFee";
|
|
30
|
-
const NATIVE_TOKEN_DECIMALS =
|
|
31
|
+
const NATIVE_TOKEN_DECIMALS = LaunchpadSale.NATIVE_TOKEN_DECIMALS;
|
|
31
32
|
|
|
32
33
|
export function calculateReverseBondingCurveFee(sale: LaunchpadSale, nativeTokensToReceive: BigNumber) {
|
|
33
34
|
if (
|
|
@@ -104,5 +105,56 @@ export async function payReverseBondingCurveFee(
|
|
|
104
105
|
}
|
|
105
106
|
|
|
106
107
|
export function calculateTransactionFee(tokensBeingTraded: BigNumber, feeAmount?: number) {
|
|
107
|
-
return tokensBeingTraded.multipliedBy(feeAmount ?? 0).toFixed(
|
|
108
|
+
return tokensBeingTraded.multipliedBy(feeAmount ?? 0).toFixed(NATIVE_TOKEN_DECIMALS, BigNumber.ROUND_UP);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Transfers transaction fees to the launchpad fee address if applicable.
|
|
113
|
+
* Optionally validates that the user has sufficient balance when nativeTokensRequired is provided.
|
|
114
|
+
*
|
|
115
|
+
* @param ctx - The context object providing access to the GalaChain environment.
|
|
116
|
+
* @param sale - The launchpad sale object.
|
|
117
|
+
* @param transactionFees - The transaction fees amount (as BigNumber or string).
|
|
118
|
+
* @param nativeToken - The native token instance key (returned from sale.fetchNativeTokenInstanceKey()).
|
|
119
|
+
* @param nativeTokensRequired - Optional. If provided, validates user has sufficient balance for fees + required tokens.
|
|
120
|
+
*/
|
|
121
|
+
export async function transferTransactionFees(
|
|
122
|
+
ctx: GalaChainContext,
|
|
123
|
+
sale: LaunchpadSale,
|
|
124
|
+
transactionFees: BigNumber | string,
|
|
125
|
+
nativeToken: ReturnType<LaunchpadSale["fetchNativeTokenInstanceKey"]>,
|
|
126
|
+
nativeTokensRequired?: BigNumber
|
|
127
|
+
): Promise<void> {
|
|
128
|
+
const launchpadFeeAddressConfiguration = await fetchLaunchpadFeeAddress(ctx);
|
|
129
|
+
const transactionFeesBn =
|
|
130
|
+
typeof transactionFees === "string" ? new BigNumber(transactionFees) : transactionFees;
|
|
131
|
+
|
|
132
|
+
// Check if transaction fees is greater than 0 and if the launchpad fee address configuration where
|
|
133
|
+
// the fees are sent to is defined
|
|
134
|
+
if (!launchpadFeeAddressConfiguration || !transactionFeesBn.isGreaterThan(0)) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// If nativeTokensRequired is provided, validate user has sufficient balance
|
|
139
|
+
if (nativeTokensRequired) {
|
|
140
|
+
const totalRequired = nativeTokensRequired.plus(transactionFeesBn);
|
|
141
|
+
const buyerBalance = await fetchOrCreateBalance(ctx, ctx.callingUser, sale.nativeToken);
|
|
142
|
+
|
|
143
|
+
// Check if the buyer has sufficient balance to pay the transaction fees
|
|
144
|
+
if (buyerBalance.getQuantityTotal().isLessThan(totalRequired)) {
|
|
145
|
+
throw new ValidationFailedError(
|
|
146
|
+
`Insufficient balance: Total amount required including fee is ${totalRequired}`
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Transfer transaction fees to the launchpad fee address
|
|
152
|
+
await transferToken(ctx, {
|
|
153
|
+
from: ctx.callingUser,
|
|
154
|
+
to: launchpadFeeAddressConfiguration.feeAddress,
|
|
155
|
+
tokenInstanceKey: nativeToken,
|
|
156
|
+
quantity: transactionFeesBn,
|
|
157
|
+
allowancesToUse: [],
|
|
158
|
+
authorizedOnBehalf: undefined
|
|
159
|
+
});
|
|
108
160
|
}
|
|
@@ -65,7 +65,7 @@ export async function finalizeSale(ctx: GalaChainContext, sale: LaunchpadSale):
|
|
|
65
65
|
tokenInstanceKey: nativeToken,
|
|
66
66
|
quantity: new BigNumber(sale.nativeTokenQuantity)
|
|
67
67
|
.times(ownerAllocationPercentage)
|
|
68
|
-
.decimalPlaces(
|
|
68
|
+
.decimalPlaces(LaunchpadSale.NATIVE_TOKEN_DECIMALS, BigNumber.ROUND_DOWN),
|
|
69
69
|
allowancesToUse: [],
|
|
70
70
|
authorizedOnBehalf: {
|
|
71
71
|
callingOnBehalf: vaultAddressAlias,
|
|
@@ -79,7 +79,7 @@ export async function finalizeSale(ctx: GalaChainContext, sale: LaunchpadSale):
|
|
|
79
79
|
tokenInstanceKey: nativeToken,
|
|
80
80
|
quantity: new BigNumber(sale.nativeTokenQuantity)
|
|
81
81
|
.times(platformFeePercentage)
|
|
82
|
-
.decimalPlaces(
|
|
82
|
+
.decimalPlaces(LaunchpadSale.NATIVE_TOKEN_DECIMALS, BigNumber.ROUND_DOWN),
|
|
83
83
|
allowancesToUse: [],
|
|
84
84
|
authorizedOnBehalf: {
|
|
85
85
|
callingOnBehalf: vaultAddressAlias,
|
|
@@ -110,7 +110,10 @@ export async function finalizeSale(ctx: GalaChainContext, sale: LaunchpadSale):
|
|
|
110
110
|
const poolInfo = await getSlot0(ctx, poolDTO);
|
|
111
111
|
|
|
112
112
|
// Proceed normally if price in the pool is within an acceptable range
|
|
113
|
-
const priceCloseEnough = sqrtPrice
|
|
113
|
+
const priceCloseEnough = sqrtPrice
|
|
114
|
+
.minus(poolInfo.sqrtPrice)
|
|
115
|
+
.abs()
|
|
116
|
+
.isLessThanOrEqualTo(sqrtPrice.multipliedBy(0.05));
|
|
114
117
|
const expectedNativeTokenRequired = new BigNumber(sale.nativeTokenQuantity).times(
|
|
115
118
|
liquidityAllocationPercentage
|
|
116
119
|
);
|
|
@@ -200,7 +203,7 @@ function calculateFinalLaunchpadPrice(
|
|
|
200
203
|
areTokensSorted: boolean
|
|
201
204
|
): { sqrtPrice: BigNumber; finalPrice: BigNumber } {
|
|
202
205
|
const totalTokensSold = new Decimal(sale.fetchTokensSold());
|
|
203
|
-
const basePrice = new Decimal(
|
|
206
|
+
const basePrice = new Decimal(LaunchpadSale.BASE_PRICE);
|
|
204
207
|
const { exponentFactor, euler, decimals } = getBondingConstants();
|
|
205
208
|
|
|
206
209
|
const exponent = exponentFactor.mul(totalTokensSold).div(decimals);
|
|
@@ -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(
|