Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/api/types/LaunchpadDtos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {

import { BigNumberIsNotNegative, BigNumberLessThanOrEqualOther, BigNumberMax } from "../validators";
import { IsNonZeroBigNumber } from "../validators";
import { LaunchpadTradeData } from "./LaunchpadTradeData";

export class ReverseBondingCurveConfigurationChainObject extends ChainObject {
@BigNumberProperty()
Expand Down Expand Up @@ -322,6 +323,11 @@ export class TradeResDto {

@IsString()
public totalTokenSold: string;

@IsOptional()
@ValidateNested()
@Type(() => LaunchpadTradeData)
public tradeData?: LaunchpadTradeData;
}

export class FetchSaleDto extends ChainCallDTO {
Expand Down
67 changes: 67 additions & 0 deletions src/api/types/LaunchpadTradeData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright (c) Gala Games Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
BigNumberIsNotNegative,
BigNumberProperty,
ChainKey,
ChainObject,
IsUserAlias,
UserAlias
} from "@gala-chain/api";
import BigNumber from "bignumber.js";
import { Exclude } from "class-transformer";
import { IsInt, IsNotEmpty } from "class-validator";
import { JSONSchema } from "class-validator-jsonschema";

export interface ILaunchpadTradeData {
vaultAddress: UserAlias;
galaVolumeTraded: BigNumber;
createdAt: number;
lastUpdated: number;
}

@JSONSchema({
description:
"LaunchpadSale trade data, metrics, and/or analytics aggregated throughout the lifetime of the sale."
})
export class LaunchpadTradeData extends ChainObject {
@Exclude()
static INDEX_KEY = "GCLPTD"; //GalaChain LaunchPad Trade Data

@ChainKey({ position: 0 })
@IsUserAlias()
@IsNotEmpty()
public vaultAddress: UserAlias;

@BigNumberIsNotNegative()
@BigNumberProperty()
public galaVolumeTraded: BigNumber;

@IsInt()
public createdAt: number;

@IsInt()
public lastUpdated: number;

// constructor supports both new LaunchpadTradeData() and plainToInstance() / createValidChainObject()
// instance initialization styles
constructor(data: ILaunchpadTradeData) {
super();
this.vaultAddress = data?.vaultAddress ?? "";
this.galaVolumeTraded = data?.galaVolumeTraded ?? new BigNumber(0);
this.createdAt = data?.createdAt ?? 0;
this.lastUpdated = data?.lastUpdated ?? 0;
}
}
1 change: 1 addition & 0 deletions src/api/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ export * from "./LaunchpadDtos";
export * from "./LaunchpadFinalizeAllocation";
export * from "./LaunchpadFeeConfig";
export * from "./LaunchpadSale";
export * from "./LaunchpadTradeData";
export * from "./LaunchpadBatchSubmitAuthorities";
9 changes: 9 additions & 0 deletions src/chaincode/launchpad/buyExactToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
import { fetchAndValidateSale } from "../utils";
import { callNativeTokenIn } from "./callNativeTokenIn";
import { transferTransactionFees } from "./fees";
import { fetchOrCreateLaunchpadTradeData } from "./fetchLaunchpadTradeData";

Check warning on line 23 in src/chaincode/launchpad/buyExactToken.ts

View workflow job for this annotation

GitHub Actions / Run Tests

'fetchOrCreateLaunchpadTradeData' is defined but never used
import { finalizeSale } from "./finaliseSale";
import { writeTradeData } from "./writeTradeData";

/**
* Executes the purchase of an exact amount of tokens in a token sale.
Expand Down Expand Up @@ -100,6 +102,13 @@
sale.buyToken(tokensToBuy, nativeTokensRequired);
await putChainObject(ctx, sale);

const galaVolumeTraded = nativeTokensRequired.abs();

const tradeData = await writeTradeData(ctx, {

Check warning on line 107 in src/chaincode/launchpad/buyExactToken.ts

View workflow job for this annotation

GitHub Actions / Run Tests

'tradeData' is assigned a value but never used
vaultAddress: sale.vaultAddress,
galaVolumeTraded: galaVolumeTraded
});

// If the sale is finalized, create a V3 pool and add liquidity
if (isSaleFinalized) {
await finalizeSale(ctx, sale);
Expand Down
8 changes: 8 additions & 0 deletions src/chaincode/launchpad/buyWithNative.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import { callMemeTokenOut } from "./callMemeTokenOut";
import { transferTransactionFees } from "./fees";
import { finalizeSale } from "./finaliseSale";
import { writeTradeData } from "./writeTradeData";

/**
* Executes the purchase of tokens using a specified amount of native tokens.
Expand Down Expand Up @@ -104,6 +105,13 @@
sale.buyToken(tokensToBuy, nativeTokensRequired);
await putChainObject(ctx, sale);

const galaVolumeTraded = nativeTokensRequired.abs();

const tradeData = await writeTradeData(ctx, {

Check warning on line 110 in src/chaincode/launchpad/buyWithNative.ts

View workflow job for this annotation

GitHub Actions / Run Tests

'tradeData' is assigned a value but never used
vaultAddress: sale.vaultAddress,
galaVolumeTraded: galaVolumeTraded
});

// Finalize sale if it's complete
if (isSaleFinalized) {
await finalizeSale(ctx, sale);
Expand Down
5 changes: 5 additions & 0 deletions src/chaincode/launchpad/createSale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ConflictError, DefaultError, TokenInstanceKey, asValidUserAlias } from "@gala-chain/api";

Check warning on line 15 in src/chaincode/launchpad/createSale.ts

View workflow job for this annotation

GitHub Actions / Run Tests

'DefaultError' is defined but never used
import {
GalaChainContext,
createTokenClass,
Expand All @@ -32,6 +32,7 @@
} from "../../api/types";
import { PreConditionFailedError } from "../../api/utils/error";
import { buyWithNative } from "./buyWithNative";
import { fetchOrCreateLaunchpadTradeData } from "./fetchLaunchpadTradeData";

/**
* Creates a new token sale (Launchpad) in the GalaChain environment.
Expand Down Expand Up @@ -154,6 +155,10 @@
await putChainObject(ctx, launchpad);
}

const tradeData = await fetchOrCreateLaunchpadTradeData(ctx, { vaultAddress: launchpad.vaultAddress });

await putChainObject(ctx, tradeData);

// Return the response object
return {
image: launchpadDetails.tokenImage,
Expand Down
65 changes: 65 additions & 0 deletions src/chaincode/launchpad/fetchLaunchpadTradeData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright (c) Gala Games Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
ChainError,
ErrorCode,
NotFoundError,

Check warning on line 18 in src/chaincode/launchpad/fetchLaunchpadTradeData.ts

View workflow job for this annotation

GitHub Actions / Run Tests

'NotFoundError' is defined but never used
asValidUserAlias,
createValidChainObject
} from "@gala-chain/api";
import { GalaChainContext, getObjectByKey } from "@gala-chain/chaincode";
import BigNumber from "bignumber.js";

import { LaunchpadTradeData } from "../../api/types";

/**
* Fetches the trade data a specific token sale (LaunchpadSale) using the sale address.
*
* This function retrieves the trade data object from the chain using a composite key derived
* from the sale address. If the trade data record is not found, a newly initialized chain entry is returned.
*
* @param ctx - The context object providing access to the GalaChain environment.
* @param data - An object containing the sale address:
* - `vaultAddress`: The address of the sale to be fetched.
*
* @returns A promise that resolves to a `LaunchpadTradeData` object containing details about
* the specified token sale.
*
*/
export async function fetchOrCreateLaunchpadTradeData(
ctx: GalaChainContext,
data: { vaultAddress: string }
): Promise<LaunchpadTradeData> {
const { vaultAddress } = data;

const key = ctx.stub.createCompositeKey(LaunchpadTradeData.INDEX_KEY, [vaultAddress]);

const tradeData = await getObjectByKey(ctx, LaunchpadTradeData, key).catch((e) => {
const error = ChainError.from(e);

if (!error.matches(ErrorCode.NOT_FOUND)) {
throw error;
}

return createValidChainObject(LaunchpadTradeData, {
vaultAddress: asValidUserAlias(vaultAddress),
createdAt: ctx.txUnixTime,
lastUpdated: ctx.txUnixTime,
galaVolumeTraded: new BigNumber(0)
});
});

return tradeData;
}
20 changes: 17 additions & 3 deletions src/chaincode/launchpad/finaliseSale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import Decimal from "decimal.js";
import { LaunchpadFinalizeFeeAllocation, LaunchpadSale } from "../../api/types";
import { PreConditionFailedError } from "../../api/utils/error";
import { fetchLaunchpadFeeAddress, getBondingConstants } from "../utils";
import { fetchOrCreateLaunchpadTradeData } from "./fetchLaunchpadTradeData";

export async function finalizeSale(ctx: GalaChainContext, sale: LaunchpadSale): Promise<void> {
const key = ctx.stub.createCompositeKey(LaunchpadFinalizeFeeAllocation.INDEX_KEY, []);
Expand All @@ -59,13 +60,26 @@ export async function finalizeSale(ctx: GalaChainContext, sale: LaunchpadSale):
const memeToken = sale.fetchSellingTokenInstanceKey();
const vaultAddressAlias = await resolveUserAlias(ctx, sale.vaultAddress);

const creatorRewardsV1 = new BigNumber(sale.nativeTokenQuantity)
.times(ownerAllocationPercentage)
.decimalPlaces(LaunchpadSale.NATIVE_TOKEN_DECIMALS, BigNumber.ROUND_DOWN);

const tradeData = await fetchOrCreateLaunchpadTradeData(ctx, { vaultAddress: sale.vaultAddress });

const creatorRewardsV2 = tradeData.galaVolumeTraded
.times(ownerAllocationPercentage)
.decimalPlaces(LaunchpadSale.NATIVE_TOKEN_DECIMALS, BigNumber.ROUND_DOWN);

// todo: finalize business logic for pre-existing sales created prior to new total volume calculation
const creatorRewards = creatorRewardsV2.isGreaterThan(creatorRewardsV1)
? creatorRewardsV2
: creatorRewardsV1;

await transferToken(ctx, {
from: vaultAddressAlias,
to: sale.saleOwner,
tokenInstanceKey: nativeToken,
quantity: new BigNumber(sale.nativeTokenQuantity)
.times(ownerAllocationPercentage)
.decimalPlaces(LaunchpadSale.NATIVE_TOKEN_DECIMALS, BigNumber.ROUND_DOWN),
quantity: creatorRewards,
allowancesToUse: [],
authorizedOnBehalf: {
callingOnBehalf: vaultAddressAlias,
Expand Down
1 change: 1 addition & 0 deletions src/chaincode/launchpad/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export * from "./callNativeTokenIn";
export * from "./callNativeTokenOut";
export * from "./createSale";
export * from "./fetchSaleDetails";
export * from "./fetchLaunchpadTradeData";
export * from "./sellExactToken";
export * from "./finalizeTokenAllocation";
export * from "./sellWithNative";
Expand Down
8 changes: 8 additions & 0 deletions src/chaincode/launchpad/sellExactToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import { fetchAndValidateSale } from "../utils";
import { callNativeTokenOut } from "./callNativeTokenOut";
import { payReverseBondingCurveFee, transferTransactionFees } from "./fees";
import { writeTradeData } from "./writeTradeData";

/**
* Executes the sale of an exact amount of tokens for native tokens (e.g., GALA).
Expand Down Expand Up @@ -111,6 +112,13 @@
sale.sellToken(tokensBeingSold, nativeTokensPayout);
await putChainObject(ctx, sale);

const galaVolumeTraded = nativeTokensPayout.abs();

const tradeData = await writeTradeData(ctx, {

Check warning on line 117 in src/chaincode/launchpad/sellExactToken.ts

View workflow job for this annotation

GitHub Actions / Run Tests

'tradeData' is assigned a value but never used
vaultAddress: sale.vaultAddress,
galaVolumeTraded: galaVolumeTraded
});

const token = await fetchTokenClass(ctx, sale.sellingToken);
return {
inputQuantity: tokensBeingSold.toFixed(),
Expand Down
8 changes: 8 additions & 0 deletions src/chaincode/launchpad/sellWithNative.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { SlippageToleranceExceededError } from "../../api/utils/error";
import { fetchAndValidateSale } from "../utils";
import { callMemeTokenIn } from "./callMemeTokenIn";
import { payReverseBondingCurveFee, transferTransactionFees } from "./fees";
import { writeTradeData } from "./writeTradeData";

/**
* Executes a sale of tokens using native tokens (e.g., GALA) in exchange for the specified token amount.
Expand Down Expand Up @@ -127,6 +128,13 @@ export async function sellWithNative(
sale.sellToken(tokensToSell, nativeTokensPayout);
await putChainObject(ctx, sale);

const galaVolumeTraded = nativeTokensPayout.abs();

const tradeData = await writeTradeData(ctx, {
vaultAddress: sale.vaultAddress,
galaVolumeTraded: galaVolumeTraded
});

const token = await fetchTokenClass(ctx, sale.sellingToken);
return {
inputQuantity: tokensToSell.toFixed(),
Expand Down
40 changes: 40 additions & 0 deletions src/chaincode/launchpad/writeTradeData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright (c) Gala Games Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { GalaChainContext, putChainObject } from "@gala-chain/chaincode";
import BigNumber from "bignumber.js";

import { LaunchpadTradeData } from "../../api/types";
import { fetchOrCreateLaunchpadTradeData } from "./fetchLaunchpadTradeData";

export interface IWriteTradeData {
vaultAddress: string;
galaVolumeTraded: BigNumber;
}

export async function writeTradeData(
ctx: GalaChainContext,
data: IWriteTradeData
): Promise<LaunchpadTradeData> {
const { vaultAddress, galaVolumeTraded } = data;

const tradeData = await fetchOrCreateLaunchpadTradeData(ctx, { vaultAddress });

tradeData.galaVolumeTraded = tradeData.galaVolumeTraded.plus(galaVolumeTraded);
tradeData.lastUpdated = ctx.txUnixTime;

await putChainObject(ctx, tradeData);

return tradeData;
}
Loading