@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,128 @@
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 { GalaChainContext, fetchTokenClass, putChainObject, transferToken } from "@gala-chain/chaincode";
16
+ import { BigNumber } from "bignumber.js";
17
+
18
+ import { ExactTokenQuantityDto, LaunchpadSale, TradeResDto } from "../../api/types";
19
+ import { SlippageToleranceExceededError } from "../../api/utils/error";
20
+ import { fetchAndValidateSale } from "../utils";
21
+ import { callNativeTokenIn } from "./callNativeTokenIn";
22
+ import { finalizeSale } from "./finaliseSale";
23
+
24
+ BigNumber.config({
25
+ ROUNDING_MODE: BigNumber.ROUND_UP
26
+ });
27
+
28
+ /**
29
+ * Executes the purchase of an exact amount of tokens in a token sale.
30
+ *
31
+ * This function validates the sale, calculates the required native tokens,
32
+ * and performs the token transfer to complete the purchase. If the purchase
33
+ * consumes all tokens in the sale, the sale is marked as finalized.
34
+ *
35
+ * @param ctx - The context object providing access to the GalaChain environment.
36
+ * @param buyTokenDTO - The data transfer object containing the sale address,
37
+ * token amount to buy, and optional expected native token amount.
38
+ *
39
+ * @returns A promise that resolves to a `TradeResDto` containing the updated
40
+ * balances of the purchased token and the native token for the buyer.
41
+ *
42
+ * @throws DefaultError if the expected native tokens are insufficient to complete the purchase.
43
+ */
44
+ export async function buyExactToken(
45
+ ctx: GalaChainContext,
46
+ buyTokenDTO: ExactTokenQuantityDto
47
+ ): Promise<TradeResDto> {
48
+ let isSaleFinalized = false;
49
+
50
+ // Fetch and validate the sale based on the provided vault address
51
+ const sale = await fetchAndValidateSale(ctx, buyTokenDTO.vaultAddress);
52
+ const tokenLeftInVault = new BigNumber(sale.sellingTokenQuantity);
53
+
54
+ // Calculate the required amount of native tokens to buy the specified token amount
55
+ const callNativeTokenInResult1 = await callNativeTokenIn(ctx, buyTokenDTO);
56
+ let nativeTokensToBuy = new BigNumber(callNativeTokenInResult1.calculatedQuantity);
57
+ const nativeToken = sale.fetchNativeTokenInstanceKey();
58
+ const memeToken = sale.fetchSellingTokenInstanceKey();
59
+
60
+ // If the requested token amount exceeds what's available, adjust it and recalculate native tokens needed
61
+ if (tokenLeftInVault.lte(buyTokenDTO.tokenQuantity)) {
62
+ buyTokenDTO.tokenQuantity = tokenLeftInVault;
63
+ const callNativeTokenInResult2 = await callNativeTokenIn(ctx, buyTokenDTO);
64
+ nativeTokensToBuy = new BigNumber(callNativeTokenInResult2.calculatedQuantity);
65
+ isSaleFinalized = true;
66
+ }
67
+
68
+ // Check if the native tokens used exceed the market cap, finalizing the sale if true
69
+ if (
70
+ nativeTokensToBuy
71
+ .plus(new BigNumber(sale.nativeTokenQuantity))
72
+ .gte(new BigNumber(LaunchpadSale.MARKET_CAP))
73
+ ) {
74
+ isSaleFinalized = true;
75
+ }
76
+
77
+ // Ensure the expected native token amount is not less than the actual amount required
78
+ if (buyTokenDTO.expectedNativeToken && buyTokenDTO.expectedNativeToken.comparedTo(nativeTokensToBuy) < 0) {
79
+ throw new SlippageToleranceExceededError(
80
+ "Gala tokens expected to perform this operation are less than the actual amount required."
81
+ );
82
+ }
83
+
84
+ // Transfer native tokens from the buyer to the vault
85
+ await transferToken(ctx, {
86
+ from: ctx.callingUser,
87
+ to: buyTokenDTO.vaultAddress,
88
+ tokenInstanceKey: nativeToken,
89
+ quantity: nativeTokensToBuy,
90
+ allowancesToUse: [],
91
+ authorizedOnBehalf: undefined
92
+ });
93
+
94
+ // Transfer meme tokens from the vault to the buyer
95
+ await transferToken(ctx, {
96
+ from: buyTokenDTO.vaultAddress,
97
+ to: ctx.callingUser,
98
+ tokenInstanceKey: memeToken,
99
+ quantity: buyTokenDTO.tokenQuantity,
100
+ allowancesToUse: [],
101
+ authorizedOnBehalf: {
102
+ callingOnBehalf: buyTokenDTO.vaultAddress,
103
+ callingUser: ctx.callingUser
104
+ }
105
+ });
106
+
107
+ // Update the sale record with the purchased token details
108
+ sale.buyToken(buyTokenDTO.tokenQuantity, nativeTokensToBuy);
109
+ await putChainObject(ctx, sale);
110
+
111
+ // If the sale is finalized, create a V3 pool and add liquidity
112
+ if (isSaleFinalized) {
113
+ await finalizeSale(ctx, sale);
114
+ }
115
+
116
+ // Return the updated balance response
117
+ const token = await fetchTokenClass(ctx, sale.sellingToken);
118
+ return {
119
+ inputQuantity: nativeTokensToBuy.toFixed(),
120
+ outputQuantity: buyTokenDTO.tokenQuantity.toFixed(),
121
+ tokenName: token.name,
122
+ tradeType: "Buy",
123
+ vaultAddress: buyTokenDTO.vaultAddress,
124
+ userAddress: ctx.callingUser,
125
+ isFinalized: isSaleFinalized,
126
+ functionName: "BuyExactToken"
127
+ };
128
+ }
@@ -0,0 +1,120 @@
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 { GalaChainContext, fetchTokenClass, putChainObject, transferToken } from "@gala-chain/chaincode";
16
+ import { BigNumber } from "bignumber.js";
17
+
18
+ import { ExactTokenQuantityDto, LaunchpadSale, NativeTokenQuantityDto, TradeResDto } from "../../api/types";
19
+ import { SlippageToleranceExceededError } from "../../api/utils/error";
20
+ import { fetchAndValidateSale } from "../utils";
21
+ import { callMemeTokenOut } from "./callMemeTokenOut";
22
+ import { callNativeTokenIn } from "./callNativeTokenIn";
23
+ import { finalizeSale } from "./finaliseSale";
24
+
25
+ BigNumber.config({
26
+ ROUNDING_MODE: BigNumber.ROUND_UP
27
+ });
28
+
29
+ /**
30
+ * Executes the purchase of tokens using a specified amount of native tokens.
31
+ *
32
+ * This function validates the sale, calculates the amount of tokens that can
33
+ * be purchased with the provided native tokens, and performs the token transfer
34
+ * to complete the purchase. If the purchase consumes all tokens in the sale,
35
+ * the sale is marked as finalized.
36
+ *
37
+ * @param ctx - The context object providing access to the GalaChain environment.
38
+ * @param buyTokenDTO - The data transfer object containing the sale address,
39
+ * native token amount to spend, and optional expected token amount.
40
+ *
41
+ * @returns A promise that resolves to a `TradeResDto` containing the updated
42
+ * balances of the purchased token and the native token for the buyer.
43
+ *
44
+ * @throws DefaultError if the expected tokens to be received are less than the
45
+ * actual amount provided by the operation.
46
+ */
47
+ export async function buyWithNative(
48
+ ctx: GalaChainContext,
49
+ buyTokenDTO: NativeTokenQuantityDto
50
+ ): Promise<TradeResDto> {
51
+ let isSaleFinalized = false;
52
+ const sale = await fetchAndValidateSale(ctx, buyTokenDTO.vaultAddress);
53
+ const tokensLeftInVault = new BigNumber(sale.sellingTokenQuantity);
54
+ const callMemeTokenOutResult = await callMemeTokenOut(ctx, buyTokenDTO);
55
+ let tokensToBuy = new BigNumber(callMemeTokenOutResult.calculatedQuantity);
56
+
57
+ const nativeToken = sale.fetchNativeTokenInstanceKey();
58
+ const memeToken = sale.fetchSellingTokenInstanceKey();
59
+
60
+ if (tokensLeftInVault.comparedTo(tokensToBuy) <= 0) {
61
+ tokensToBuy = tokensLeftInVault;
62
+ const nativeTokensrequiredToBuyDto = new ExactTokenQuantityDto(buyTokenDTO.vaultAddress, tokensToBuy);
63
+ const callNativeTokenInResult = await callNativeTokenIn(ctx, nativeTokensrequiredToBuyDto);
64
+ buyTokenDTO.nativeTokenQuantity = new BigNumber(callNativeTokenInResult.calculatedQuantity);
65
+ isSaleFinalized = true;
66
+ }
67
+
68
+ if (
69
+ buyTokenDTO.nativeTokenQuantity
70
+ .plus(new BigNumber(sale.nativeTokenQuantity))
71
+ .gte(new BigNumber(LaunchpadSale.MARKET_CAP))
72
+ )
73
+ isSaleFinalized = true;
74
+
75
+ if (buyTokenDTO.expectedToken && buyTokenDTO.expectedToken.comparedTo(tokensToBuy) > 0) {
76
+ throw new SlippageToleranceExceededError(
77
+ "Tokens expected from this operation are more than the the actual amount that will be provided."
78
+ );
79
+ }
80
+
81
+ await transferToken(ctx, {
82
+ from: ctx.callingUser,
83
+ to: buyTokenDTO.vaultAddress,
84
+ tokenInstanceKey: nativeToken,
85
+ quantity: buyTokenDTO.nativeTokenQuantity,
86
+ allowancesToUse: [],
87
+ authorizedOnBehalf: undefined
88
+ });
89
+
90
+ await transferToken(ctx, {
91
+ from: buyTokenDTO.vaultAddress,
92
+ to: ctx.callingUser,
93
+ tokenInstanceKey: memeToken,
94
+ quantity: tokensToBuy,
95
+ allowancesToUse: [],
96
+ authorizedOnBehalf: {
97
+ callingOnBehalf: buyTokenDTO.vaultAddress,
98
+ callingUser: ctx.callingUser
99
+ }
100
+ });
101
+
102
+ sale.buyToken(tokensToBuy, buyTokenDTO.nativeTokenQuantity);
103
+ await putChainObject(ctx, sale);
104
+
105
+ if (isSaleFinalized) {
106
+ await finalizeSale(ctx, sale);
107
+ }
108
+
109
+ const token = await fetchTokenClass(ctx, sale.sellingToken);
110
+ return {
111
+ inputQuantity: buyTokenDTO.nativeTokenQuantity.toFixed(),
112
+ outputQuantity: tokensToBuy.toFixed(),
113
+ tokenName: token.name,
114
+ tradeType: "Buy",
115
+ vaultAddress: buyTokenDTO.vaultAddress,
116
+ userAddress: ctx.callingUser,
117
+ isFinalized: isSaleFinalized,
118
+ functionName: "BuyWithNative"
119
+ };
120
+ }
@@ -0,0 +1,82 @@
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 { ValidationFailedError } from "@gala-chain/api";
16
+ import { GalaChainContext } from "@gala-chain/chaincode";
17
+ import { BigNumber } from "bignumber.js";
18
+ import Decimal from "decimal.js";
19
+
20
+ import { LaunchpadSale, NativeTokenQuantityDto } from "../../api/types";
21
+ import { fetchAndValidateSale, getBondingConstants } from "../utils";
22
+ import { calculateReverseBondingCurveFee } from "./fees";
23
+
24
+ BigNumber.config({
25
+ ROUNDING_MODE: BigNumber.ROUND_UP
26
+ });
27
+
28
+ function calculateMemeTokensRequired(sale: LaunchpadSale, requestedNativeTokenQuantity: BigNumber) {
29
+ const totalTokensSold = new Decimal(sale.fetchTokensSold()); // current tokens sold / x
30
+ let nativeTokens = new Decimal(requestedNativeTokenQuantity.toString()); // native tokens used to buy / y
31
+ const basePrice = new Decimal(sale.fetchBasePrice()); // base price / a
32
+ const { exponentFactor, euler, decimals } = getBondingConstants();
33
+
34
+ const nativeTokenInVault = new Decimal(sale.nativeTokenQuantity);
35
+ if (nativeTokens.greaterThan(nativeTokenInVault)) {
36
+ nativeTokens = nativeTokenInVault;
37
+ }
38
+
39
+ const exponent = exponentFactor.mul(totalTokensSold).div(decimals);
40
+ const exp1 = euler.pow(exponent);
41
+ const constantFactor = nativeTokens.mul(exponentFactor).div(basePrice);
42
+
43
+ if (exp1.lte(constantFactor)) {
44
+ throw new ValidationFailedError("Cannot sell more tokens than have been bought in this sale.");
45
+ }
46
+
47
+ const adjustedExp = exp1.minus(constantFactor);
48
+ const lnAdjustedExp = adjustedExp.ln();
49
+ const tokensSent = totalTokensSold.minus(lnAdjustedExp.mul(decimals).div(exponentFactor));
50
+ const roundedTokenSent = tokensSent.toDecimalPlaces(18, Decimal.ROUND_UP);
51
+
52
+ return roundedTokenSent.toFixed();
53
+ }
54
+
55
+ /**
56
+ * Calculates the number of tokens that can be sold in exchange for a specified amount
57
+ * of native tokens using a bonding curve mechanism.
58
+ *
59
+ * This function retrieves the sale details and applies the bonding curve formula
60
+ * to determine the number of tokens the user can sell based on the provided native token amount.
61
+ *
62
+ * @param ctx - The context object that provides access to the GalaChain environment.
63
+ * @param sellTokenDTO - An object containing the sale details:
64
+ * - `vaultAddress`: The address of the sale.
65
+ * - `nativeTokenAmount`: The amount of native tokens to be recieved from sale.
66
+ * - `expectedToken` (optional): The expected amount of tokens to be sold.
67
+ *
68
+ * @returns A promise that resolves to a string representing the calculated amount of
69
+ * tokens to be sent, rounded up to 18 decimal places.
70
+ *
71
+ * @throws Error if the calculation results in an invalid amount (e.g., `InvalidAmountError`).
72
+ */
73
+ export async function callMemeTokenIn(ctx: GalaChainContext, sellTokenDTO: NativeTokenQuantityDto) {
74
+ const sale = await fetchAndValidateSale(ctx, sellTokenDTO.vaultAddress);
75
+
76
+ return {
77
+ calculatedQuantity: calculateMemeTokensRequired(sale, sellTokenDTO.nativeTokenQuantity),
78
+ extraFees: {
79
+ reverseBondingCurve: calculateReverseBondingCurveFee(sale, sellTokenDTO.nativeTokenQuantity).toString()
80
+ }
81
+ };
82
+ }
@@ -0,0 +1,80 @@
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 { GalaChainContext } from "@gala-chain/chaincode";
16
+ import { BigNumber } from "bignumber.js";
17
+ import Decimal from "decimal.js";
18
+
19
+ import { LaunchpadSale, NativeTokenQuantityDto } from "../../api/types";
20
+ import { fetchAndValidateSale, getBondingConstants } from "../utils";
21
+
22
+ BigNumber.config({
23
+ ROUNDING_MODE: BigNumber.ROUND_UP
24
+ });
25
+
26
+ /**
27
+ * Calculates the number of tokens that can be purchased using a specified amount
28
+ * of native tokens based on a bonding curve mechanism.
29
+ *
30
+ * This function retrieves the sale details and applies the bonding curve formula
31
+ * to determine the number of tokens the user can buy with the provided native token amount.
32
+ *
33
+ * @param ctx - The context object providing access to the GalaChain environment.
34
+ * @param buyTokenDTO - The data transfer object containing the sale address
35
+ * and the amount of native tokens to spend for the purchase.
36
+ *
37
+ * @returns A promise that resolves to a string representing the calculated amount of
38
+ * tokens to be received, rounded down to 18 decimal places.
39
+ *
40
+ * @throws Error if the calculation results in an invalid state.
41
+ */
42
+ export async function callMemeTokenOut(ctx: GalaChainContext, buyTokenDTO: NativeTokenQuantityDto) {
43
+ const sale = await fetchAndValidateSale(ctx, buyTokenDTO.vaultAddress);
44
+ const totalTokensSold = new Decimal(sale.fetchTokensSold()); // current tokens sold / x
45
+ let nativeTokens = new Decimal(buyTokenDTO.nativeTokenQuantity.toString()); // native tokens used to buy / y
46
+ const basePrice = new Decimal(sale.fetchBasePrice()); // base price / a
47
+ const { exponentFactor, euler, decimals } = getBondingConstants();
48
+
49
+ if (
50
+ nativeTokens.add(new Decimal(sale.nativeTokenQuantity)).greaterThan(new Decimal(LaunchpadSale.MARKET_CAP))
51
+ ) {
52
+ nativeTokens = new Decimal(LaunchpadSale.MARKET_CAP).minus(new Decimal(sale.nativeTokenQuantity));
53
+ }
54
+
55
+ const constant = nativeTokens.mul(exponentFactor).div(basePrice);
56
+
57
+ const exponent1 = exponentFactor.mul(totalTokensSold).div(decimals);
58
+
59
+ const eResult1 = euler.pow(exponent1);
60
+
61
+ const ethScaled = constant.add(eResult1);
62
+
63
+ const lnEthScaled = ethScaled.ln().mul(decimals);
64
+
65
+ const lnEthScaledBase = lnEthScaled.div(exponentFactor);
66
+
67
+ const result = lnEthScaledBase.minus(totalTokensSold);
68
+ let roundedResult = result.toDecimalPlaces(18, Decimal.ROUND_DOWN);
69
+
70
+ if (roundedResult.add(totalTokensSold).greaterThan(new Decimal("1e+7"))) {
71
+ roundedResult = new Decimal("1e+7").minus(new Decimal(totalTokensSold));
72
+ }
73
+
74
+ return {
75
+ calculatedQuantity: roundedResult.toFixed(),
76
+ extraFees: {
77
+ reverseBondingCurve: "0"
78
+ }
79
+ };
80
+ }
@@ -0,0 +1,71 @@
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 { GalaChainContext } from "@gala-chain/chaincode";
16
+ import { BigNumber } from "bignumber.js";
17
+ import Decimal from "decimal.js";
18
+
19
+ import { ExactTokenQuantityDto } from "../../api/types";
20
+ import { fetchAndValidateSale, getBondingConstants } from "../utils";
21
+
22
+ BigNumber.config({
23
+ ROUNDING_MODE: BigNumber.ROUND_UP
24
+ });
25
+
26
+ /**
27
+ * Calculates the amount of native tokens required to purchase a specified amount
28
+ * of tokens using a bonding curve mechanism.
29
+ *
30
+ * This function retrieves the sale details and applies the bonding curve formula
31
+ * to determine the cost in native tokens for the given token amount.
32
+ *
33
+ * @param ctx - The context object providing access to the GalaChain environment.
34
+ * @param buyTokenDTO - The data transfer object containing the sale address
35
+ * and the exact amount of tokens to be purchased.
36
+ *
37
+ * @returns A promise that resolves to a string representing the calculated amount of
38
+ * native tokens required for the purchase, rounded up to 8 decimal places.
39
+ *
40
+ * @throws Error if the calculation encounters an invalid state or data.
41
+ */
42
+ export async function callNativeTokenIn(ctx: GalaChainContext, buyTokenDTO: ExactTokenQuantityDto) {
43
+ const sale = await fetchAndValidateSale(ctx, buyTokenDTO.vaultAddress);
44
+ const totalTokensSold = new Decimal(sale.fetchTokensSold());
45
+
46
+ let tokensToBuy = new Decimal(buyTokenDTO.tokenQuantity.toString());
47
+ const basePrice = new Decimal(sale.fetchBasePrice());
48
+ const { exponentFactor, euler, decimals } = getBondingConstants();
49
+
50
+ if (tokensToBuy.add(totalTokensSold).greaterThan(new Decimal("1e+7"))) {
51
+ tokensToBuy = new Decimal(sale.sellingTokenQuantity);
52
+ }
53
+
54
+ const exponent1 = exponentFactor.mul(totalTokensSold.add(tokensToBuy)).div(decimals);
55
+ const exponent2 = exponentFactor.mul(totalTokensSold).div(decimals);
56
+
57
+ const eResult1 = euler.pow(exponent1);
58
+ const eResult2 = euler.pow(exponent2);
59
+
60
+ const constantFactor = basePrice.div(exponentFactor);
61
+ const differenceOfExponentials = eResult1.minus(eResult2);
62
+
63
+ const price = constantFactor.mul(differenceOfExponentials);
64
+
65
+ return {
66
+ calculatedQuantity: price.toDecimalPlaces(8, Decimal.ROUND_UP).toFixed(),
67
+ extraFees: {
68
+ reverseBondingCurve: "0"
69
+ }
70
+ };
71
+ }
@@ -0,0 +1,81 @@
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 { GalaChainContext } from "@gala-chain/chaincode";
16
+ import { BigNumber } from "bignumber.js";
17
+ import Decimal from "decimal.js";
18
+
19
+ import { ExactTokenQuantityDto, LaunchpadSale } from "../../api/types";
20
+ import { fetchAndValidateSale, getBondingConstants } from "../utils";
21
+ import { calculateReverseBondingCurveFee } from "./fees";
22
+
23
+ BigNumber.config({
24
+ ROUNDING_MODE: BigNumber.ROUND_UP
25
+ });
26
+
27
+ function calculateNativeTokensReceived(sale: LaunchpadSale, tokensToSellBn: BigNumber) {
28
+ const totalTokensSold = new Decimal(sale.fetchTokensSold());
29
+
30
+ let tokensToSell = new Decimal(tokensToSellBn.toString());
31
+ const basePrice = new Decimal(sale.fetchBasePrice());
32
+ const { exponentFactor, euler, decimals } = getBondingConstants();
33
+
34
+ let newTotalTokensSold = totalTokensSold.minus(tokensToSell);
35
+
36
+ if (newTotalTokensSold.comparedTo(0) < 0) {
37
+ tokensToSell = totalTokensSold;
38
+ newTotalTokensSold = new Decimal(0);
39
+ }
40
+
41
+ const exponent1 = exponentFactor.mul(newTotalTokensSold.add(tokensToSell)).div(decimals);
42
+ const exponent2 = exponentFactor.mul(newTotalTokensSold).div(decimals);
43
+
44
+ const eResult1 = euler.pow(exponent1);
45
+ const eResult2 = euler.pow(exponent2);
46
+
47
+ const constantFactor = basePrice.div(exponentFactor);
48
+ const differenceOfExponentials = eResult1.minus(eResult2);
49
+ const price = constantFactor.mul(differenceOfExponentials);
50
+ const roundedPrice = price.toDecimalPlaces(8, Decimal.ROUND_DOWN);
51
+
52
+ return roundedPrice.toFixed();
53
+ }
54
+
55
+ /**
56
+ * Calculates the amount of native tokens a user would receive when selling
57
+ * a specified amount of tokens based on a bonding curve mechanism.
58
+ *
59
+ * This function retrieves the sale details and applies the bonding curve formula
60
+ * to determine the value in native tokens for the given token amount.
61
+ *
62
+ * @param ctx - The context object providing access to the GalaChain environment.
63
+ * @param sellTokenDTO - The data transfer object containing the sale address
64
+ * and the exact amount of tokens to be sold.
65
+ *
66
+ * @returns A promise that resolves to a string representing the calculated amount of
67
+ * native tokens to be received, rounded down to 8 decimal places.
68
+ *
69
+ * @throws DefaultError if the calculated new total tokens sold is less than zero
70
+ * or if the input amount is invalid.
71
+ */
72
+ export async function callNativeTokenOut(ctx: GalaChainContext, sellTokenDTO: ExactTokenQuantityDto) {
73
+ const sale = await fetchAndValidateSale(ctx, sellTokenDTO.vaultAddress);
74
+ const nativeTokensReceived = calculateNativeTokensReceived(sale, sellTokenDTO.tokenQuantity);
75
+ return {
76
+ calculatedQuantity: nativeTokensReceived,
77
+ extraFees: {
78
+ reverseBondingCurve: calculateReverseBondingCurveFee(sale, BigNumber(nativeTokensReceived)).toString()
79
+ }
80
+ };
81
+ }
@@ -0,0 +1,69 @@
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, ErrorCode, UnauthorizedError, ValidationFailedError } from "@gala-chain/api";
16
+ import { GalaChainContext } from "@gala-chain/chaincode";
17
+ import { getObjectByKey, putChainObject } from "@gala-chain/chaincode";
18
+
19
+ import { ConfigureLaunchpadFeeAddressDto, LaunchpadFeeConfig } from "../../api/types";
20
+
21
+ export async function configureLaunchpadFeeAddress(
22
+ ctx: GalaChainContext,
23
+ dto: ConfigureLaunchpadFeeAddressDto
24
+ ): Promise<LaunchpadFeeConfig> {
25
+ if (!dto.newPlatformFeeAddress && !dto.newAuthorities?.length) {
26
+ throw new ValidationFailedError("None of the input fields are present.");
27
+ }
28
+
29
+ const curatorOrgMsp = process.env.CURATOR_ORG_MSP ?? "CuratorOrg";
30
+
31
+ const key = ctx.stub.createCompositeKey(LaunchpadFeeConfig.INDEX_KEY, []);
32
+ let platformFeeAddress = await getObjectByKey(ctx, LaunchpadFeeConfig, key).catch((e) => {
33
+ const chainError = ChainError.from(e);
34
+ if (chainError.matches(ErrorCode.NOT_FOUND)) {
35
+ return undefined;
36
+ } else {
37
+ throw chainError;
38
+ }
39
+ });
40
+
41
+ if (ctx.clientIdentity.getMSPID() !== curatorOrgMsp) {
42
+ if (!platformFeeAddress || !platformFeeAddress.authorities.includes(ctx.callingUser)) {
43
+ throw new UnauthorizedError(`CallingUser ${ctx.callingUser} is not authorized to create or update`);
44
+ }
45
+ }
46
+
47
+ if (!platformFeeAddress) {
48
+ if (!dto.newPlatformFeeAddress) {
49
+ throw new ValidationFailedError(
50
+ "Must provide a platform fee address in the initial setup of the configuration."
51
+ );
52
+ }
53
+ platformFeeAddress = new LaunchpadFeeConfig(
54
+ dto.newPlatformFeeAddress,
55
+ dto.newAuthorities ?? [ctx.callingUser]
56
+ );
57
+ } else if (platformFeeAddress && platformFeeAddress.authorities.includes(ctx.callingUser)) {
58
+ platformFeeAddress.setNewFeeAddress(
59
+ dto.newPlatformFeeAddress ?? platformFeeAddress.feeAddress,
60
+ dto.newAuthorities ?? platformFeeAddress.authorities
61
+ );
62
+ } else {
63
+ throw new UnauthorizedError(`CallingUser ${ctx.callingUser} is not authorized to create or update`);
64
+ }
65
+
66
+ await putChainObject(ctx, platformFeeAddress);
67
+
68
+ return platformFeeAddress;
69
+ }