@gala-chain/launchpad 1.0.0

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.
Files changed (200) hide show
  1. package/.dev-env +7 -0
  2. package/.dockerignore +9 -0
  3. package/.eslintignore +4 -0
  4. package/.eslintrc.json +37 -0
  5. package/.github/workflows/publish-on-tag.yml +72 -0
  6. package/.github/workflows/test-on-push.yml +32 -0
  7. package/.nvmrc +1 -0
  8. package/.prettierignore +4 -0
  9. package/.prettierrc +12 -0
  10. package/Dockerfile +31 -0
  11. package/LICENSE +201 -0
  12. package/README.md +2 -0
  13. package/api-config.json +16 -0
  14. package/e2e/__snapshots__/api.spec.ts.snap +2482 -0
  15. package/e2e/api-config.json +17 -0
  16. package/e2e/api.spec.ts +48 -0
  17. package/e2e/jest.config.js +42 -0
  18. package/e2e/tsconfig.json +10 -0
  19. package/jest.config.ts +27 -0
  20. package/lib/jest.config.d.ts +13 -0
  21. package/lib/jest.config.d.ts.map +1 -0
  22. package/lib/jest.config.js +28 -0
  23. package/lib/jest.config.js.map +1 -0
  24. package/lib/package.json +54 -0
  25. package/lib/src/api/index.d.ts +4 -0
  26. package/lib/src/api/index.d.ts.map +1 -0
  27. package/lib/src/api/index.js +21 -0
  28. package/lib/src/api/index.js.map +1 -0
  29. package/lib/src/api/types/LaunchpadDtos.d.ts +102 -0
  30. package/lib/src/api/types/LaunchpadDtos.d.ts.map +1 -0
  31. package/lib/src/api/types/LaunchpadDtos.js +410 -0
  32. package/lib/src/api/types/LaunchpadDtos.js.map +1 -0
  33. package/lib/src/api/types/LaunchpadFeeConfig.d.ts +9 -0
  34. package/lib/src/api/types/LaunchpadFeeConfig.d.ts.map +1 -0
  35. package/lib/src/api/types/LaunchpadFeeConfig.js +56 -0
  36. package/lib/src/api/types/LaunchpadFeeConfig.js.map +1 -0
  37. package/lib/src/api/types/LaunchpadFinalizeAllocation.d.ts +10 -0
  38. package/lib/src/api/types/LaunchpadFinalizeAllocation.d.ts.map +1 -0
  39. package/lib/src/api/types/LaunchpadFinalizeAllocation.js +70 -0
  40. package/lib/src/api/types/LaunchpadFinalizeAllocation.js.map +1 -0
  41. package/lib/src/api/types/LaunchpadSale.d.ts +35 -0
  42. package/lib/src/api/types/LaunchpadSale.d.ts.map +1 -0
  43. package/lib/src/api/types/LaunchpadSale.js +195 -0
  44. package/lib/src/api/types/LaunchpadSale.js.map +1 -0
  45. package/lib/src/api/types/index.d.ts +5 -0
  46. package/lib/src/api/types/index.d.ts.map +1 -0
  47. package/lib/src/api/types/index.js +22 -0
  48. package/lib/src/api/types/index.js.map +1 -0
  49. package/lib/src/api/utils/error.d.ts +20 -0
  50. package/lib/src/api/utils/error.d.ts.map +1 -0
  51. package/lib/src/api/utils/error.js +35 -0
  52. package/lib/src/api/utils/error.js.map +1 -0
  53. package/lib/src/api/utils/index.d.ts +2 -0
  54. package/lib/src/api/utils/index.d.ts.map +1 -0
  55. package/lib/src/api/utils/index.js +19 -0
  56. package/lib/src/api/utils/index.js.map +1 -0
  57. package/lib/src/api/validators/decorators.d.ts +19 -0
  58. package/lib/src/api/validators/decorators.d.ts.map +1 -0
  59. package/lib/src/api/validators/decorators.js +391 -0
  60. package/lib/src/api/validators/decorators.js.map +1 -0
  61. package/lib/src/api/validators/index.d.ts +2 -0
  62. package/lib/src/api/validators/index.d.ts.map +1 -0
  63. package/lib/src/api/validators/index.js +19 -0
  64. package/lib/src/api/validators/index.js.map +1 -0
  65. package/lib/src/chaincode/LaunchpadContract.d.ts +20 -0
  66. package/lib/src/chaincode/LaunchpadContract.d.ts.map +1 -0
  67. package/lib/src/chaincode/LaunchpadContract.js +217 -0
  68. package/lib/src/chaincode/LaunchpadContract.js.map +1 -0
  69. package/lib/src/chaincode/dexLaunchpadFeeGate.d.ts +14 -0
  70. package/lib/src/chaincode/dexLaunchpadFeeGate.d.ts.map +1 -0
  71. package/lib/src/chaincode/dexLaunchpadFeeGate.js +47 -0
  72. package/lib/src/chaincode/dexLaunchpadFeeGate.js.map +1 -0
  73. package/lib/src/chaincode/index.d.ts +2 -0
  74. package/lib/src/chaincode/index.d.ts.map +1 -0
  75. package/lib/src/chaincode/index.js +19 -0
  76. package/lib/src/chaincode/index.js.map +1 -0
  77. package/lib/src/chaincode/launchpad/buyExactToken.d.ts +20 -0
  78. package/lib/src/chaincode/launchpad/buyExactToken.d.ts.map +1 -0
  79. package/lib/src/chaincode/launchpad/buyExactToken.js +113 -0
  80. package/lib/src/chaincode/launchpad/buyExactToken.js.map +1 -0
  81. package/lib/src/chaincode/launchpad/buyWithNative.d.ts +22 -0
  82. package/lib/src/chaincode/launchpad/buyWithNative.d.ts.map +1 -0
  83. package/lib/src/chaincode/launchpad/buyWithNative.js +106 -0
  84. package/lib/src/chaincode/launchpad/buyWithNative.js.map +1 -0
  85. package/lib/src/chaincode/launchpad/callMemeTokenIn.d.ts +27 -0
  86. package/lib/src/chaincode/launchpad/callMemeTokenIn.d.ts.map +1 -0
  87. package/lib/src/chaincode/launchpad/callMemeTokenIn.js +76 -0
  88. package/lib/src/chaincode/launchpad/callMemeTokenIn.js.map +1 -0
  89. package/lib/src/chaincode/launchpad/callMemeTokenOut.d.ts +25 -0
  90. package/lib/src/chaincode/launchpad/callMemeTokenOut.d.ts.map +1 -0
  91. package/lib/src/chaincode/launchpad/callMemeTokenOut.js +56 -0
  92. package/lib/src/chaincode/launchpad/callMemeTokenOut.js.map +1 -0
  93. package/lib/src/chaincode/launchpad/callNativeTokenIn.d.ts +25 -0
  94. package/lib/src/chaincode/launchpad/callNativeTokenIn.d.ts.map +1 -0
  95. package/lib/src/chaincode/launchpad/callNativeTokenIn.js +51 -0
  96. package/lib/src/chaincode/launchpad/callNativeTokenIn.js.map +1 -0
  97. package/lib/src/chaincode/launchpad/callNativeTokenOut.d.ts +26 -0
  98. package/lib/src/chaincode/launchpad/callNativeTokenOut.d.ts.map +1 -0
  99. package/lib/src/chaincode/launchpad/callNativeTokenOut.js +60 -0
  100. package/lib/src/chaincode/launchpad/callNativeTokenOut.js.map +1 -0
  101. package/lib/src/chaincode/launchpad/configureLaunchpadFeeConfig.d.ts +4 -0
  102. package/lib/src/chaincode/launchpad/configureLaunchpadFeeConfig.d.ts.map +1 -0
  103. package/lib/src/chaincode/launchpad/configureLaunchpadFeeConfig.js +58 -0
  104. package/lib/src/chaincode/launchpad/configureLaunchpadFeeConfig.js.map +1 -0
  105. package/lib/src/chaincode/launchpad/createSale.d.ts +26 -0
  106. package/lib/src/chaincode/launchpad/createSale.d.ts.map +1 -0
  107. package/lib/src/chaincode/launchpad/createSale.js +129 -0
  108. package/lib/src/chaincode/launchpad/createSale.js.map +1 -0
  109. package/lib/src/chaincode/launchpad/fees.d.ts +6 -0
  110. package/lib/src/chaincode/launchpad/fees.d.ts.map +1 -0
  111. package/lib/src/chaincode/launchpad/fees.js +85 -0
  112. package/lib/src/chaincode/launchpad/fees.js.map +1 -0
  113. package/lib/src/chaincode/launchpad/fetchLaunchpadAdressConfig.d.ts +4 -0
  114. package/lib/src/chaincode/launchpad/fetchLaunchpadAdressConfig.d.ts.map +1 -0
  115. package/lib/src/chaincode/launchpad/fetchLaunchpadAdressConfig.js +36 -0
  116. package/lib/src/chaincode/launchpad/fetchLaunchpadAdressConfig.js.map +1 -0
  117. package/lib/src/chaincode/launchpad/fetchSaleDetails.d.ts +19 -0
  118. package/lib/src/chaincode/launchpad/fetchSaleDetails.d.ts.map +1 -0
  119. package/lib/src/chaincode/launchpad/fetchSaleDetails.js +49 -0
  120. package/lib/src/chaincode/launchpad/fetchSaleDetails.js.map +1 -0
  121. package/lib/src/chaincode/launchpad/finaliseSale.d.ts +4 -0
  122. package/lib/src/chaincode/launchpad/finaliseSale.d.ts.map +1 -0
  123. package/lib/src/chaincode/launchpad/finaliseSale.js +144 -0
  124. package/lib/src/chaincode/launchpad/finaliseSale.js.map +1 -0
  125. package/lib/src/chaincode/launchpad/finalizeTokenAllocation.d.ts +4 -0
  126. package/lib/src/chaincode/launchpad/finalizeTokenAllocation.d.ts.map +1 -0
  127. package/lib/src/chaincode/launchpad/finalizeTokenAllocation.js +43 -0
  128. package/lib/src/chaincode/launchpad/finalizeTokenAllocation.js.map +1 -0
  129. package/lib/src/chaincode/launchpad/index.d.ts +15 -0
  130. package/lib/src/chaincode/launchpad/index.d.ts.map +1 -0
  131. package/lib/src/chaincode/launchpad/index.js +32 -0
  132. package/lib/src/chaincode/launchpad/index.js.map +1 -0
  133. package/lib/src/chaincode/launchpad/preMintCalculation.d.ts +17 -0
  134. package/lib/src/chaincode/launchpad/preMintCalculation.d.ts.map +1 -0
  135. package/lib/src/chaincode/launchpad/preMintCalculation.js +56 -0
  136. package/lib/src/chaincode/launchpad/preMintCalculation.js.map +1 -0
  137. package/lib/src/chaincode/launchpad/sellExactToken.d.ts +22 -0
  138. package/lib/src/chaincode/launchpad/sellExactToken.d.ts.map +1 -0
  139. package/lib/src/chaincode/launchpad/sellExactToken.js +102 -0
  140. package/lib/src/chaincode/launchpad/sellExactToken.js.map +1 -0
  141. package/lib/src/chaincode/launchpad/sellWithNative.d.ts +23 -0
  142. package/lib/src/chaincode/launchpad/sellWithNative.d.ts.map +1 -0
  143. package/lib/src/chaincode/launchpad/sellWithNative.js +97 -0
  144. package/lib/src/chaincode/launchpad/sellWithNative.js.map +1 -0
  145. package/lib/src/chaincode/utils/index.d.ts +2 -0
  146. package/lib/src/chaincode/utils/index.d.ts.map +1 -0
  147. package/lib/src/chaincode/utils/index.js +19 -0
  148. package/lib/src/chaincode/utils/index.js.map +1 -0
  149. package/lib/src/chaincode/utils/launchpadSaleUtils.d.ts +11 -0
  150. package/lib/src/chaincode/utils/launchpadSaleUtils.d.ts.map +1 -0
  151. package/lib/src/chaincode/utils/launchpadSaleUtils.js +65 -0
  152. package/lib/src/chaincode/utils/launchpadSaleUtils.js.map +1 -0
  153. package/lib/src/cli.d.ts +3 -0
  154. package/lib/src/cli.d.ts.map +1 -0
  155. package/lib/src/cli.js +41 -0
  156. package/lib/src/cli.js.map +1 -0
  157. package/lib/src/index.d.ts +14 -0
  158. package/lib/src/index.d.ts.map +1 -0
  159. package/lib/src/index.js +32 -0
  160. package/lib/src/index.js.map +1 -0
  161. package/lib/tsconfig.tsbuildinfo +1 -0
  162. package/package.json +54 -0
  163. package/src/api/index.ts +18 -0
  164. package/src/api/types/LaunchpadDtos.ts +362 -0
  165. package/src/api/types/LaunchpadFeeConfig.ts +45 -0
  166. package/src/api/types/LaunchpadFinalizeAllocation.ts +65 -0
  167. package/src/api/types/LaunchpadSale.ts +200 -0
  168. package/src/api/types/index.ts +19 -0
  169. package/src/api/utils/error.ts +32 -0
  170. package/src/api/utils/index.ts +16 -0
  171. package/src/api/validators/decorators.spec.ts +277 -0
  172. package/src/api/validators/decorators.ts +401 -0
  173. package/src/api/validators/index.ts +16 -0
  174. package/src/chaincode/LaunchpadContract.ts +211 -0
  175. package/src/chaincode/dexLaunchpadFeeGate.ts +43 -0
  176. package/src/chaincode/index.ts +16 -0
  177. package/src/chaincode/launchpad/buyExactToken.ts +128 -0
  178. package/src/chaincode/launchpad/buyWithNative.ts +120 -0
  179. package/src/chaincode/launchpad/callMemeTokenIn.ts +82 -0
  180. package/src/chaincode/launchpad/callMemeTokenOut.ts +80 -0
  181. package/src/chaincode/launchpad/callNativeTokenIn.ts +71 -0
  182. package/src/chaincode/launchpad/callNativeTokenOut.ts +81 -0
  183. package/src/chaincode/launchpad/configureLaunchpadFeeConfig.ts +69 -0
  184. package/src/chaincode/launchpad/createSale.ts +157 -0
  185. package/src/chaincode/launchpad/fees.ts +105 -0
  186. package/src/chaincode/launchpad/fetchLaunchpadAdressConfig.ts +38 -0
  187. package/src/chaincode/launchpad/fetchSaleDetails.ts +53 -0
  188. package/src/chaincode/launchpad/finaliseSale.ts +217 -0
  189. package/src/chaincode/launchpad/finalizeTokenAllocation.ts +47 -0
  190. package/src/chaincode/launchpad/index.ts +28 -0
  191. package/src/chaincode/launchpad/preMintCalculation.ts +61 -0
  192. package/src/chaincode/launchpad/sellExactToken.ts +122 -0
  193. package/src/chaincode/launchpad/sellWithNative.ts +112 -0
  194. package/src/chaincode/utils/index.ts +15 -0
  195. package/src/chaincode/utils/launchpadSaleUtils.ts +68 -0
  196. package/src/cli.ts +63 -0
  197. package/src/index.ts +32 -0
  198. package/tsconfig.base.json +29 -0
  199. package/tsconfig.json +19 -0
  200. package/tsconfig.spec.json +11 -0
@@ -0,0 +1,157 @@
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 { ConflictError, TokenInstanceKey, asValidUserAlias } from "@gala-chain/api";
16
+ import {
17
+ GalaChainContext,
18
+ createTokenClass,
19
+ getObjectByKey,
20
+ mintTokenWithAllowance,
21
+ putChainObject,
22
+ updateTokenClass
23
+ } from "@gala-chain/chaincode";
24
+ import { BigNumber } from "bignumber.js";
25
+
26
+ import { CreateSaleResDto, CreateTokenSaleDTO, LaunchpadSale, NativeTokenQuantityDto } from "../../api/types";
27
+ import { PreConditionFailedError } from "../../api/utils/error";
28
+ import { buyWithNative } from "./buyWithNative";
29
+
30
+ BigNumber.config({
31
+ ROUNDING_MODE: BigNumber.ROUND_UP
32
+ });
33
+
34
+ /**
35
+ * Creates a new token sale (Launchpad) in the GalaChain environment.
36
+ *
37
+ * This function validates input parameters, creates a token class, mints an initial supply
38
+ * of tokens, and initializes the Launchpad sale object. If a pre-buy amount is specified,
39
+ * it simulates a token purchase using native tokens.
40
+ *
41
+ * @param ctx - The context object providing access to the GalaChain environment.
42
+ * @param launchpadDetails - An object containing details for the token sale, including:
43
+ * - `tokenName`: The name of the token being created.
44
+ * - `tokenSymbol`: The symbol for the token.
45
+ * - `tokenDescription`: A description of the token.
46
+ * - `tokenImage`: An optional image URL for the token.
47
+ * - `preBuyAmount`: The amount of native tokens to use for a pre-buy (optional).
48
+ *
49
+ * @returns A promise that resolves to a `CreateSaleResDto` object containing details about
50
+ * the created sale, including the vault address and token collection name.
51
+ *
52
+ * @throws DefaultError if:
53
+ * - Required fields (`tokenName`, `tokenSymbol`, `tokenDescription`) are missing or empty.
54
+ * - The token class creation fails (no response key returned).
55
+ */
56
+ export async function createSale(
57
+ ctx: GalaChainContext,
58
+ launchpadDetails: CreateTokenSaleDTO
59
+ ): Promise<CreateSaleResDto> {
60
+ let isSaleFinalized = false;
61
+ // Validate input parameters
62
+
63
+ if (!launchpadDetails.websiteUrl && !launchpadDetails.telegramUrl && !launchpadDetails.twitterUrl) {
64
+ throw new PreConditionFailedError("Token sale creation requires atleast one social link.");
65
+ }
66
+
67
+ launchpadDetails.tokenSymbol = launchpadDetails.tokenSymbol.toUpperCase();
68
+
69
+ // Define the token class key
70
+ const tokenInstanceKey = new TokenInstanceKey();
71
+ tokenInstanceKey.collection = `${launchpadDetails.tokenCollection}`;
72
+ tokenInstanceKey.category = `${launchpadDetails.tokenCategory}`;
73
+ tokenInstanceKey.type = `${launchpadDetails.tokenSymbol}`;
74
+ tokenInstanceKey.additionalKey = `${ctx.callingUser.replace(/\|/, ":")}`;
75
+ tokenInstanceKey.instance = new BigNumber(0);
76
+
77
+ // Validate uniqueness of sale and token
78
+ const vaultAddress = asValidUserAlias(
79
+ `service|${tokenInstanceKey.getTokenClassKey().toStringKey()}$launchpad`
80
+ );
81
+ const key = ctx.stub.createCompositeKey(LaunchpadSale.INDEX_KEY, [vaultAddress]);
82
+ const sale = await getObjectByKey(ctx, LaunchpadSale, key).catch(() => undefined);
83
+ if (sale) {
84
+ throw new ConflictError("This token and a sale associated with it already exists");
85
+ }
86
+
87
+ // Call createTokenClass
88
+ await createTokenClass(ctx, {
89
+ network: "GC",
90
+ tokenClass: tokenInstanceKey.getTokenClassKey(),
91
+ isNonFungible: false,
92
+ decimals: 18,
93
+ name: launchpadDetails.tokenName,
94
+ symbol: launchpadDetails.tokenSymbol,
95
+ description: launchpadDetails.tokenDescription,
96
+ image: launchpadDetails.tokenImage,
97
+ maxSupply: new BigNumber("2e+7"),
98
+ maxCapacity: new BigNumber("2e+7"),
99
+ totalMintAllowance: new BigNumber(0),
100
+ totalSupply: new BigNumber(0),
101
+ totalBurned: new BigNumber(0),
102
+ authorities: [vaultAddress, ctx.callingUser]
103
+ });
104
+
105
+ // Mint tokens using the calling user's allowance
106
+ await mintTokenWithAllowance(ctx, {
107
+ tokenClassKey: tokenInstanceKey.getTokenClassKey(),
108
+ tokenInstance: new BigNumber(0),
109
+ owner: vaultAddress,
110
+ quantity: new BigNumber("2e+7")
111
+ });
112
+
113
+ //Update token class to remove the calling user as an authority in the token class
114
+ await updateTokenClass(ctx, {
115
+ tokenClass: tokenInstanceKey.getTokenClassKey(),
116
+ authorities: [vaultAddress]
117
+ });
118
+
119
+ // Create the LaunchpadSale object
120
+ const launchpad = new LaunchpadSale(
121
+ vaultAddress,
122
+ tokenInstanceKey,
123
+ launchpadDetails.reverseBondingCurveConfiguration?.toChainObject(),
124
+ ctx.callingUser
125
+ );
126
+
127
+ await putChainObject(ctx, launchpad);
128
+
129
+ if (launchpadDetails.preBuyQuantity.isGreaterThan(0)) {
130
+ const nativeTokenDto = new NativeTokenQuantityDto(
131
+ asValidUserAlias(launchpad.vaultAddress),
132
+ launchpadDetails.preBuyQuantity
133
+ );
134
+ const tradeStatus = await buyWithNative(ctx, nativeTokenDto);
135
+ isSaleFinalized = tradeStatus.isFinalized;
136
+ }
137
+
138
+ // Return the response object
139
+ return {
140
+ image: launchpadDetails.tokenImage,
141
+ tokenName: launchpadDetails.tokenName,
142
+ symbol: launchpadDetails.tokenSymbol,
143
+ description: launchpadDetails.tokenDescription,
144
+ websiteUrl: launchpadDetails.websiteUrl ? launchpadDetails.websiteUrl : "",
145
+ telegramUrl: launchpadDetails.telegramUrl ? launchpadDetails.telegramUrl : "",
146
+ twitterUrl: launchpadDetails.twitterUrl ? launchpadDetails.twitterUrl : "",
147
+ initialBuyQuantity: launchpadDetails.preBuyQuantity.toFixed(),
148
+ vaultAddress: vaultAddress,
149
+ creatorAddress: ctx.callingUser,
150
+ collection: launchpadDetails.tokenCollection,
151
+ category: launchpadDetails.tokenCategory,
152
+ functionName: "CreateSale",
153
+ isFinalized: isSaleFinalized,
154
+ tokenStringKey: tokenInstanceKey.getTokenClassKey().toStringKey(),
155
+ reverseBondingCurveConfiguration: launchpadDetails.reverseBondingCurveConfiguration
156
+ } satisfies CreateSaleResDto;
157
+ }
@@ -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 { FeeReceiptStatus } from "@gala-chain/api";
16
+ import {
17
+ GalaChainContext,
18
+ getObjectByKey,
19
+ transferToken,
20
+ txUnixTimeToDateIndexKeys,
21
+ writeChannelPaymentReceipt,
22
+ writeUserPaymentReceipt
23
+ } from "@gala-chain/chaincode";
24
+ import BigNumber from "bignumber.js";
25
+
26
+ import { LaunchpadFeeConfig, LaunchpadSale } from "../../api/types";
27
+ import { SlippageToleranceExceededError } from "../../api/utils/error";
28
+
29
+ const REVERSE_BONDING_CURVE_FEE_CODE = "LaunchpadReverseBondingCurveFee";
30
+ const NATIVE_TOKEN_DECIMALS = 8;
31
+
32
+ export function calculateReverseBondingCurveFee(sale: LaunchpadSale, nativeTokensToReceive: BigNumber) {
33
+ if (
34
+ !sale.reverseBondingCurveConfiguration ||
35
+ sale.reverseBondingCurveConfiguration.maxFeePortion.isZero()
36
+ ) {
37
+ return BigNumber(0);
38
+ }
39
+
40
+ const circulatingSupplyProportional = sale.fetchCirculatingSupplyProportional();
41
+ const { minFeePortion, maxFeePortion } = sale.reverseBondingCurveConfiguration;
42
+ const feePortionDiff = maxFeePortion.minus(minFeePortion);
43
+ const portionAboveBaseline = circulatingSupplyProportional.multipliedBy(feePortionDiff);
44
+ const feePortion = minFeePortion.plus(portionAboveBaseline);
45
+
46
+ const feeAmount = nativeTokensToReceive
47
+ .multipliedBy(feePortion)
48
+ .decimalPlaces(NATIVE_TOKEN_DECIMALS, BigNumber.ROUND_UP);
49
+
50
+ return feeAmount;
51
+ }
52
+
53
+ export async function payReverseBondingCurveFee(
54
+ ctx: GalaChainContext,
55
+ sale: LaunchpadSale,
56
+ nativeTokensToReceive: BigNumber,
57
+ maxAcceptableFee?: BigNumber
58
+ ) {
59
+ const feeAmount = await calculateReverseBondingCurveFee(sale, nativeTokensToReceive);
60
+
61
+ if (feeAmount.isZero()) {
62
+ return; // No fee
63
+ }
64
+
65
+ if (maxAcceptableFee && feeAmount.isGreaterThan(maxAcceptableFee)) {
66
+ throw new SlippageToleranceExceededError("Fee exceeds maximum acceptable amount");
67
+ }
68
+
69
+ const launchpadConfigKey = ctx.stub.createCompositeKey(LaunchpadFeeConfig.INDEX_KEY, []);
70
+ const launchpadConfig = await getObjectByKey(ctx, LaunchpadFeeConfig, launchpadConfigKey);
71
+ const nativeToken = sale.fetchNativeTokenInstanceKey();
72
+ const { year, month, day } = txUnixTimeToDateIndexKeys(ctx.txUnixTime);
73
+ const txId = ctx.stub.getTxID();
74
+
75
+ await writeChannelPaymentReceipt(ctx, {
76
+ year,
77
+ month,
78
+ day,
79
+ feeCode: REVERSE_BONDING_CURVE_FEE_CODE,
80
+ paidByUser: ctx.callingUser,
81
+ txId,
82
+ quantity: feeAmount,
83
+ status: FeeReceiptStatus.Settled
84
+ });
85
+
86
+ await writeUserPaymentReceipt(ctx, {
87
+ paidByUser: ctx.callingUser,
88
+ year,
89
+ month,
90
+ day,
91
+ feeCode: REVERSE_BONDING_CURVE_FEE_CODE,
92
+ txId,
93
+ quantity: feeAmount,
94
+ status: FeeReceiptStatus.Settled
95
+ });
96
+
97
+ await transferToken(ctx, {
98
+ to: launchpadConfig.feeAddress,
99
+ from: ctx.callingUser,
100
+ tokenInstanceKey: nativeToken,
101
+ quantity: feeAmount,
102
+ allowancesToUse: [],
103
+ authorizedOnBehalf: undefined
104
+ });
105
+ }
@@ -0,0 +1,38 @@
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, UnauthorizedError } from "@gala-chain/api";
16
+ import { GalaChainContext } from "@gala-chain/chaincode";
17
+
18
+ import { LaunchpadFeeConfig } from "../../api/types";
19
+ import { fetchLaunchpadFeeAddress } from "../utils";
20
+
21
+ export async function fetchLaunchpadFeeConfig(ctx: GalaChainContext): Promise<LaunchpadFeeConfig> {
22
+ const curatorOrgMsp = process.env.CURATOR_ORG_MSP ?? "CuratorOrg";
23
+
24
+ const platformFeeAddress = await fetchLaunchpadFeeAddress(ctx);
25
+
26
+ if (ctx.clientIdentity.getMSPID() !== curatorOrgMsp) {
27
+ throw new UnauthorizedError(`CallingUser ${ctx.callingUser} is not authorized to create or update`);
28
+ }
29
+
30
+ if (!platformFeeAddress) {
31
+ throw new NotFoundError(
32
+ "Platform fee configuration has yet to be defined. Platform fee configuration is not defined."
33
+ );
34
+ } else if (!platformFeeAddress.authorities.includes(ctx.callingUser)) {
35
+ throw new UnauthorizedError(`CallingUser ${ctx.callingUser} is not authorized to create or update`);
36
+ }
37
+ return platformFeeAddress;
38
+ }
@@ -0,0 +1,53 @@
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 } from "@gala-chain/chaincode";
17
+ import { BigNumber } from "bignumber.js";
18
+
19
+ import { FetchSaleDto, LaunchpadSale } from "../../api/types";
20
+
21
+ BigNumber.config({
22
+ ROUNDING_MODE: BigNumber.ROUND_UP
23
+ });
24
+
25
+ /**
26
+ * Fetches the details of a specific token sale (LaunchpadSale) using its sale address.
27
+ *
28
+ * This function retrieves the sale object from the chain using a composite key derived
29
+ * from the sale address. If the sale record is not found, an error is thrown.
30
+ *
31
+ * @param ctx - The context object providing access to the GalaChain environment.
32
+ * @param fetchSaleDTO - An object containing the sale address:
33
+ * - `vaultAddress`: The address of the sale to be fetched.
34
+ *
35
+ * @returns A promise that resolves to a `LaunchpadSale` object containing details about
36
+ * the specified token sale.
37
+ *
38
+ * @throws NotFoundError if no sale record is found for the provided sale address.
39
+ */
40
+ export async function fetchSaleDetails(
41
+ ctx: GalaChainContext,
42
+ fetchSaleDTO: FetchSaleDto
43
+ ): Promise<LaunchpadSale> {
44
+ const key = ctx.stub.createCompositeKey(LaunchpadSale.INDEX_KEY, [fetchSaleDTO.vaultAddress]);
45
+
46
+ const sale = await getObjectByKey(ctx, LaunchpadSale, key);
47
+
48
+ if (sale === undefined) {
49
+ throw new NotFoundError("Sale record not found.");
50
+ }
51
+
52
+ return sale;
53
+ }
@@ -0,0 +1,217 @@
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
+ // TODO: dependencies on DEX
16
+ import { BurnTokenQuantity } from "@gala-chain/api";
17
+ import {
18
+ GalaChainContext,
19
+ burnTokens,
20
+ fetchOrCreateBalance,
21
+ getObjectByKey,
22
+ putChainObject,
23
+ resolveUserAlias,
24
+ transferToken
25
+ } from "@gala-chain/chaincode";
26
+ import {
27
+ AddLiquidityDTO,
28
+ CreatePoolDto,
29
+ GetAddLiquidityEstimationDto,
30
+ addLiquidity,
31
+ createPool,
32
+ generateKeyFromClassKey,
33
+ getAddLiquidityEstimation,
34
+ getPoolData,
35
+ getSlot0,
36
+ sortString
37
+ } from "@gala-chain/dex";
38
+ import BigNumber from "bignumber.js";
39
+ import Decimal from "decimal.js";
40
+
41
+ import { LaunchpadFinalizeFeeAllocation, LaunchpadSale } from "../../api/types";
42
+ import { PreConditionFailedError } from "../../api/utils/error";
43
+ import { fetchLaunchpadFeeAddress, getBondingConstants } from "../utils";
44
+
45
+ export async function finalizeSale(ctx: GalaChainContext, sale: LaunchpadSale): Promise<void> {
46
+ const key = ctx.stub.createCompositeKey(LaunchpadFinalizeFeeAllocation.INDEX_KEY, []);
47
+ const feeAllocation = await getObjectByKey(ctx, LaunchpadFinalizeFeeAllocation, key).catch(() => undefined);
48
+
49
+ const platformFeeAddressConfiguration = await fetchLaunchpadFeeAddress(ctx);
50
+ if (!platformFeeAddressConfiguration) {
51
+ throw new PreConditionFailedError("Platform fee configuration is yet to be defined.");
52
+ }
53
+
54
+ const platformFeePercentage = feeAllocation ? feeAllocation.platformFeePercentage : 0.1;
55
+ const ownerAllocationPercentage = feeAllocation ? feeAllocation.ownerAllocationPercentage : 0.6;
56
+ const liquidityAllocationPercentage = feeAllocation ? feeAllocation.liquidityAllocationPercentage : 0.3;
57
+
58
+ const nativeToken = sale.fetchNativeTokenInstanceKey();
59
+ const memeToken = sale.fetchSellingTokenInstanceKey();
60
+ const vaultAddressAlias = await resolveUserAlias(ctx, sale.vaultAddress);
61
+
62
+ await transferToken(ctx, {
63
+ from: vaultAddressAlias,
64
+ to: sale.saleOwner,
65
+ tokenInstanceKey: nativeToken,
66
+ quantity: new BigNumber(sale.nativeTokenQuantity)
67
+ .times(ownerAllocationPercentage)
68
+ .decimalPlaces(8, BigNumber.ROUND_DOWN),
69
+ allowancesToUse: [],
70
+ authorizedOnBehalf: {
71
+ callingOnBehalf: vaultAddressAlias,
72
+ callingUser: ctx.callingUser
73
+ }
74
+ });
75
+
76
+ await transferToken(ctx, {
77
+ from: vaultAddressAlias,
78
+ to: platformFeeAddressConfiguration.feeAddress,
79
+ tokenInstanceKey: nativeToken,
80
+ quantity: new BigNumber(sale.nativeTokenQuantity)
81
+ .times(platformFeePercentage)
82
+ .decimalPlaces(8, BigNumber.ROUND_DOWN),
83
+ allowancesToUse: [],
84
+ authorizedOnBehalf: {
85
+ callingOnBehalf: vaultAddressAlias,
86
+ callingUser: ctx.callingUser
87
+ }
88
+ });
89
+
90
+ const sellingTokenClassKey = sale.fetchSellingTokenInstanceKey().getTokenClassKey();
91
+ const nativeTokenClassKey = sale.fetchNativeTokenInstanceKey().getTokenClassKey();
92
+
93
+ const { isChanged: areTokensSorted } = sortString(
94
+ [sellingTokenClassKey, nativeTokenClassKey].map(generateKeyFromClassKey)
95
+ );
96
+
97
+ const { sqrtPrice, finalPrice } = calculateFinalLaunchpadPrice(sale, areTokensSorted);
98
+ const poolDTO = new CreatePoolDto(
99
+ areTokensSorted ? nativeTokenClassKey : sellingTokenClassKey,
100
+ areTokensSorted ? sellingTokenClassKey : nativeTokenClassKey,
101
+ 3000,
102
+ sqrtPrice
103
+ );
104
+
105
+ // Check if a pool for this token already exists
106
+ const pool = await getPoolData(ctx, poolDTO);
107
+ if (!pool) {
108
+ await createPool(ctx, poolDTO);
109
+ }
110
+ const poolInfo = await getSlot0(ctx, poolDTO);
111
+
112
+ // Proceed normally if price in the pool is within an acceptable range
113
+ const priceCloseEnough = sqrtPrice.minus(poolInfo.sqrtPrice).abs().lte(sqrtPrice.multipliedBy(0.05));
114
+ const expectedNativeTokenRequired = new BigNumber(sale.nativeTokenQuantity).times(
115
+ liquidityAllocationPercentage
116
+ );
117
+ const isPriceGreaterThanExpected = poolInfo.sqrtPrice.isGreaterThan(sqrtPrice);
118
+ const expectedSaleTokenRequired = expectedNativeTokenRequired.times(finalPrice);
119
+
120
+ // Determine token amounts and token ordering
121
+ const token0 = areTokensSorted ? nativeTokenClassKey : sellingTokenClassKey;
122
+ const token1 = areTokensSorted ? sellingTokenClassKey : nativeTokenClassKey;
123
+
124
+ const liquidityAmount = priceCloseEnough
125
+ ? expectedNativeTokenRequired
126
+ : isPriceGreaterThanExpected
127
+ ? expectedSaleTokenRequired
128
+ : expectedNativeTokenRequired;
129
+
130
+ const zeroForOne = priceCloseEnough
131
+ ? areTokensSorted
132
+ : isPriceGreaterThanExpected
133
+ ? !areTokensSorted
134
+ : areTokensSorted;
135
+
136
+ const expectedTokenDTO = new GetAddLiquidityEstimationDto(
137
+ token0,
138
+ token1,
139
+ 3000,
140
+ liquidityAmount,
141
+ -887220,
142
+ 887220,
143
+ zeroForOne
144
+ );
145
+
146
+ const addLiquidityEstimate = await getAddLiquidityEstimation(ctx, expectedTokenDTO);
147
+
148
+ const amount0 = new BigNumber(addLiquidityEstimate.amount0.toString());
149
+ const amount1 = new BigNumber(addLiquidityEstimate.amount1.toString());
150
+
151
+ const positionDto = new AddLiquidityDTO(
152
+ token0,
153
+ token1,
154
+ 3000,
155
+ -887220,
156
+ 887220,
157
+ amount0,
158
+ amount1,
159
+ amount0.times(0.9999999),
160
+ amount1.times(0.9999999),
161
+ undefined
162
+ );
163
+ positionDto.uniqueKey = sale.vaultAddress.toString();
164
+
165
+ await addLiquidity(ctx, positionDto, vaultAddressAlias);
166
+
167
+ // Burn any extra meme tokens
168
+ const sellingTokenToBurn = await fetchOrCreateBalance(ctx, vaultAddressAlias, memeToken);
169
+ const burnSellingTokenQuantity = new BurnTokenQuantity();
170
+ burnSellingTokenQuantity.tokenInstanceKey = memeToken;
171
+ burnSellingTokenQuantity.quantity = sellingTokenToBurn.getQuantityTotal();
172
+
173
+ await burnTokens(ctx, {
174
+ owner: vaultAddressAlias,
175
+ toBurn: [burnSellingTokenQuantity],
176
+ preValidated: true
177
+ });
178
+
179
+ // Burn any extra GALA
180
+ const nativeTokenToBurn = await fetchOrCreateBalance(ctx, vaultAddressAlias, nativeToken);
181
+ const burnNativeTokenQuantity = new BurnTokenQuantity();
182
+ burnNativeTokenQuantity.tokenInstanceKey = nativeToken;
183
+ burnNativeTokenQuantity.quantity = nativeTokenToBurn.getQuantityTotal();
184
+
185
+ if (burnNativeTokenQuantity.quantity) {
186
+ await burnTokens(ctx, {
187
+ owner: vaultAddressAlias,
188
+ toBurn: [burnNativeTokenQuantity],
189
+ preValidated: true
190
+ });
191
+ }
192
+
193
+ // update sale status
194
+ sale.finalizeSale();
195
+ await putChainObject(ctx, sale);
196
+ }
197
+
198
+ function calculateFinalLaunchpadPrice(
199
+ sale: LaunchpadSale,
200
+ areTokensSorted: boolean
201
+ ): { sqrtPrice: BigNumber; finalPrice: BigNumber } {
202
+ const totalTokensSold = new Decimal(sale.fetchTokensSold());
203
+ const basePrice = new Decimal(sale.fetchBasePrice());
204
+ const { exponentFactor, euler, decimals } = getBondingConstants();
205
+
206
+ const exponent = exponentFactor.mul(totalTokensSold).div(decimals);
207
+ const multiplicand = euler.pow(exponent);
208
+ const finalPriceDecimal = multiplicand.mul(basePrice).div(decimals);
209
+
210
+ const priceDecimal = areTokensSorted ? new Decimal(1).dividedBy(finalPriceDecimal) : finalPriceDecimal;
211
+ const sqrtPriceDecimal = priceDecimal.pow("0.5");
212
+
213
+ return {
214
+ finalPrice: new BigNumber(new Decimal(1).dividedBy(finalPriceDecimal).toString()),
215
+ sqrtPrice: new BigNumber(sqrtPriceDecimal.toString())
216
+ };
217
+ }
@@ -0,0 +1,47 @@
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 { UnauthorizedError } from "@gala-chain/api";
16
+ import { GalaChainContext } from "@gala-chain/chaincode";
17
+ import { getObjectByKey, putChainObject } from "@gala-chain/chaincode";
18
+
19
+ import { FinalizeTokenAllocationDto, LaunchpadFinalizeFeeAllocation } from "../../api/types";
20
+ import { PreConditionFailedError } from "../../api/utils/error";
21
+ import { fetchLaunchpadFeeAddress } from "../utils";
22
+
23
+ export async function finalizeTokenAllocation(
24
+ ctx: GalaChainContext,
25
+ dto: FinalizeTokenAllocationDto
26
+ ): Promise<LaunchpadFinalizeFeeAllocation> {
27
+ const platformFeeAddress = await fetchLaunchpadFeeAddress(ctx);
28
+ if (!platformFeeAddress) {
29
+ throw new PreConditionFailedError(
30
+ "Platform fee configuration has yet to be defined. Platform fee configuration is not defined."
31
+ );
32
+ } else if (!platformFeeAddress.authorities.includes(ctx.callingUser)) {
33
+ throw new UnauthorizedError(`CallingUser ${ctx.callingUser} is not authorized to create or update`);
34
+ }
35
+
36
+ const key = ctx.stub.createCompositeKey(LaunchpadFinalizeFeeAllocation.INDEX_KEY, []);
37
+ let feeAllocation = await getObjectByKey(ctx, LaunchpadFinalizeFeeAllocation, key).catch(() => undefined);
38
+
39
+ if (!feeAllocation) {
40
+ feeAllocation = new LaunchpadFinalizeFeeAllocation(dto.platformFeePercentage, dto.ownerFeePercentage);
41
+ } else {
42
+ feeAllocation.setAllocation(dto.platformFeePercentage, dto.ownerFeePercentage);
43
+ }
44
+
45
+ await putChainObject(ctx, feeAllocation);
46
+ return feeAllocation;
47
+ }
@@ -0,0 +1,28 @@
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
+ export * from "./buyExactToken";
16
+ export * from "./buyWithNative";
17
+ export * from "./callMemeTokenIn";
18
+ export * from "./callMemeTokenOut";
19
+ export * from "./callNativeTokenIn";
20
+ export * from "./callNativeTokenOut";
21
+ export * from "./createSale";
22
+ export * from "./fetchSaleDetails";
23
+ export * from "./preMintCalculation";
24
+ export * from "./sellExactToken";
25
+ export * from "./finalizeTokenAllocation";
26
+ export * from "./sellWithNative";
27
+ export * from "./configureLaunchpadFeeConfig";
28
+ export * from "./fetchLaunchpadAdressConfig";