Skip to content

Commit

Permalink
Merge pull request #258 from gnoswap-labs/GSW-695-integrate-create-in…
Browse files Browse the repository at this point in the history
…centive

feat: [GSW-695] Integrate Create Incentive
  • Loading branch information
jinoosss authored Dec 15, 2023
2 parents 47af823 + e780f4c commit 39c10d5
Show file tree
Hide file tree
Showing 9 changed files with 240 additions and 27 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { makeApproveMessage, PACKAGE_POOL_ADDRESS } from "./common";
import {
makeApproveMessage,
makeTransactionMessage,
PACKAGE_POOL_ADDRESS,
PACKAGE_STAKER_ADDRESS,
PACKAGE_STAKER_PATH,
} from "./common";

export function makePoolTokenApproveMessage(
packagePath: string,
Expand All @@ -11,3 +17,52 @@ export function makePoolTokenApproveMessage(
caller,
);
}

export function makeCreateIncentiveMessage(
poolPath: string,
rewardTokenPath: string,
rewardAmount: string,
startTime: number,
endTime: number,
caller: string,
) {
return makeTransactionMessage({
send: "",
func: "CreateExternalIncentive",
packagePath: PACKAGE_STAKER_PATH,
args: [
poolPath,
rewardTokenPath,
rewardAmount,
`${startTime}`,
`${endTime}`,
],
caller,
});
}

export function makeRemoveIncentiveMessage(
poolPath: string,
rewardTokenPath: string,
caller: string,
) {
return makeTransactionMessage({
send: "",
func: "EndExternalIncentive",
packagePath: PACKAGE_STAKER_PATH,
args: [caller, poolPath, rewardTokenPath],
caller,
});
}

export function makeStakerApproveMessage(
tokenPath: string,
amount: string,
caller: string,
) {
return makeApproveMessage(
tokenPath,
[PACKAGE_STAKER_ADDRESS, amount],
caller,
);
}
8 changes: 8 additions & 0 deletions packages/web/src/common/errors/pool/pool-error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ const ERROR_VALUE = {
status: 404,
type: "Not found pool",
},
FAILED_TO_CREATE_INCENTIVE: {
status: 500,
type: "Failed to create incentive",
},
FAILED_TO_REMOVE_INCENTIVE: {
status: 500,
type: "Failed to remove incentive",
},
};

type ErrorType = keyof typeof ERROR_VALUE;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,47 @@ import { useClearModal } from "@hooks/common/use-clear-modal";
import React, { useCallback } from "react";
import { useAtom } from "jotai";
import { EarnState } from "@states/index";
import { useGnoswapContext } from "@hooks/common/use-gnoswap-context";
import { useRouter } from "next/router";

const DAY_TIME = 24 * 60 * 60;

const IncentivizePoolModalContainer = () => {
const router = useRouter();
const clearModal = useClearModal();
const { poolRepository } = useGnoswapContext();
const [period] = useAtom(EarnState.period);
const [startDate] = useAtom(EarnState.date);
const [dataModal] = useAtom(EarnState.dataModal);
const [pool] = useAtom(EarnState.pool);

const close = useCallback(() => {
clearModal();
}, [clearModal]);

const onSubmit = useCallback(() => {
clearModal();
}, [clearModal]);
if (!pool || !dataModal?.token) {
return null;
}
const startUTCDate = Date.UTC(startDate.year, startDate.month - 1, startDate.date, 0, 0, 0, 0);
const startTime = new Date(startUTCDate).getTime();
const endTime = startTime + period * DAY_TIME;

return poolRepository.createExternalIncentive({
poolPath: pool.path,
rewardToken: dataModal.token,
rewardAmount: dataModal.amount || "0",
startTime,
endTime
}).then(response => {
clearModal();
router.back();
return response;
}).catch(e => {
console.log(e);
return null;
});
}, [clearModal, dataModal, period, pool, poolRepository, router, startDate.date, startDate.month, startDate.year]);

return <IncentivizePoolModal close={close} onSubmit={onSubmit} data={dataModal} date={startDate} period={period} pool={pool} />;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const PoolIncentivizeContainer: React.FC = () => {
const tokenAmountInput = useTokenAmountInput(token);
const { updateTokenPrices } = useTokenData();
const { data: pools = [] } = useGetPoolList({ enabled: false });

useEffect(() => {
setDataModal(tokenAmountInput);
}, [tokenAmountInput.amount, token]);
Expand All @@ -57,7 +57,7 @@ const PoolIncentivizeContainer: React.FC = () => {
if (pool) {
setCurrentPool(pool);
}
}, [setCurrentPool]);
}, [pools, setCurrentPool]);

const selectToken = useCallback((path: string) => {
const token = tokenBalances.find(token => token.path === path);
Expand Down
123 changes: 104 additions & 19 deletions packages/web/src/repositories/pool/pool-repository-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ import { makeRawTokenAmount } from "@utils/token-utils";
import { tickToSqrtPriceX96 } from "@gnoswap-labs/swap-router";
import { PoolDetailModel } from "@models/pool/pool-detail-model";
import { makeDepositMessage } from "@common/clients/wallet-client/transaction-messages/token";
import { CreateExternalIncentiveRequest } from "./request/create-external-incentive-request";
import { RemoveExternalIncentiveRequest } from "./request/remove-external-incentive-request";
import { makeCreateIncentiveMessage, makeRemoveIncentiveMessage, makeStakerApproveMessage } from "@common/clients/wallet-client/transaction-messages/pool";

const POOL_PATH = process.env.NEXT_PUBLIC_PACKAGE_POOL_PATH || "";
const POSITION_PATH = process.env.NEXT_PUBLIC_PACKAGE_POSITION_PATH || "";
Expand Down Expand Up @@ -264,15 +267,107 @@ export class PoolRepositoryImpl implements PoolRepository {
return response.hash;
};

getPoolDetailByPath = async (
poolPath: string,
): Promise<IPoolDetailResponse> => {
const response = await this.networkClient.get<IPoolDetailResponse>({
url: "/pool_details/" + poolPath,
});
return response.data;
};

createExternalIncentive = async (request: CreateExternalIncentiveRequest): Promise<string | null> => {
if (this.walletClient === null) {
throw new CommonError("FAILED_INITIALIZE_WALLET");
}
const account = await this.walletClient.getAccount();
if (!account.data ) {
throw new CommonError("FAILED_INITIALIZE_PROVIDER");
}
const { address } = account.data;
const {
poolPath,
rewardToken,
rewardAmount,
startTime,
endTime
} = request;

const rewardAmountRaw = makeRawTokenAmount(rewardToken, rewardAmount) || "0";

const messages = [];
let tokenPath = rewardToken.path;
if (isNativeToken(rewardToken)) {
tokenPath = rewardToken.wrappedPath;
messages.push(
makeDepositMessage(tokenPath, rewardAmountRaw, "ugnot", address),
);
}
messages.push(makeStakerApproveMessage(tokenPath, rewardAmountRaw, address));
messages.push(makeCreateIncentiveMessage(poolPath, tokenPath, rewardAmountRaw, startTime, endTime, address));

const response = await this.walletClient.sendTransaction({
messages,
gasWanted: 2000000,
gasFee: 1,
memo: "",
});
if (response.code !== 0 || !response.data) {
throw new PoolError("FAILED_TO_CREATE_INCENTIVE");
}
const data = response?.data as SendTransactionSuccessResponse<string>;
return data?.hash || null;
};

removeExternalIncentive = async (request: RemoveExternalIncentiveRequest): Promise<string | null> => {
if (this.walletClient === null) {
throw new CommonError("FAILED_INITIALIZE_WALLET");
}
const account = await this.walletClient.getAccount();
if (!account.data ) {
throw new CommonError("FAILED_INITIALIZE_PROVIDER");
}
const { address } = account.data;
const {
poolPath,
rewardToken
} = request;

const messages = [];
let tokenPath = rewardToken.path;
if (isNativeToken(rewardToken)) {
tokenPath = rewardToken.wrappedPath;
}
messages.push(makeRemoveIncentiveMessage(poolPath, tokenPath, address));

const response = await this.walletClient.sendTransaction({
messages,
gasWanted: 2000000,
gasFee: 1,
memo: "",
});
if (response.code !== 0 || !response.data) {
throw new PoolError("FAILED_TO_CREATE_INCENTIVE");
}
const data = response?.data as SendTransactionSuccessResponse<string>;
return data?.hash || null;
};

private static makeCreatePoolMessage(
tokenA: TokenModel,
tokenB: TokenModel,
feeTier: SwapFeeTierType,
startPrice: string,
caller: string,
) {
const tokenAPath = tokenA.priceId;
const tokenBPath = tokenB.priceId;
let tokenAPath = tokenA.path;
let tokenBPath = tokenB.path;
if (isNativeToken(tokenA) ) {
tokenAPath = tokenA.wrappedPath;
}
if (isNativeToken(tokenB) ) {
tokenBPath = tokenB.wrappedPath;
}
const fee = `${SwapFeeTierInfoMap[feeTier].fee}`;
const startPriceSqrt = tickToSqrtPriceX96(priceToNearTick(Number(startPrice), SwapFeeTierInfoMap[feeTier].tickSpacing));

Expand Down Expand Up @@ -314,19 +409,18 @@ export class PoolRepositoryImpl implements PoolRepository {
slippage: string,
caller: string,
) {
const tokenAPath = tokenA.priceId;
const tokenBPath = tokenB.priceId;
const fee = `${SwapFeeTierInfoMap[feeTier].fee}`;
const slippageRatio = 0;
const deadline = "7282571140";
const sendItems = [];
if (tokenA.type === "native" && BigNumber(tokenAAmount).isGreaterThan(0) ) {
sendItems.push(`${tokenAAmount}ugnot`);
let tokenAPath = tokenA.path;
let tokenBPath = tokenB.path;
if (isNativeToken(tokenA) ) {
tokenAPath = tokenA.wrappedPath;
}
if (tokenB.type === "native" && BigNumber(tokenAAmount).isGreaterThan(0)) {
sendItems.push(`${tokenBAmount}ugnot`);
if (isNativeToken(tokenB) ) {
tokenBPath = tokenB.wrappedPath;
}
const sendAmount = sendItems.join(",");
const sendAmount = "";
return {
caller,
send: sendAmount,
Expand All @@ -346,13 +440,4 @@ export class PoolRepositoryImpl implements PoolRepository {
],
};
}

getPoolDetailByPath = async (
poolPath: string,
): Promise<IPoolDetailResponse> => {
const response = await this.networkClient.get<IPoolDetailResponse>({
url: "/pool_details/" + poolPath,
});
return response.data;
};
}
14 changes: 12 additions & 2 deletions packages/web/src/repositories/pool/pool-repository-mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,19 @@ export class PoolRepositoryMock implements PoolRepository {
return "hash";
};

getPoolDetailByPath = async (poolPath: string): Promise<IPoolDetailResponse> => {
getPoolDetailByPath = async (
poolPath: string,
): Promise<IPoolDetailResponse> => {
console.log(poolPath);

return PoolDetailDataByPath as IPoolDetailResponse;
};

createExternalIncentive = async (): Promise<string> => {
return "hash";
};

removeExternalIncentive = async (): Promise<string> => {
return "hash";
};
}
9 changes: 9 additions & 0 deletions packages/web/src/repositories/pool/pool-repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { IPoolDetailResponse, PoolModel } from "@models/pool/pool-model";
import { PoolRPCModel } from "@models/pool/pool-rpc-model";
import { AddLiquidityRequest } from "./request/add-liquidity-request";
import { CreatePoolRequest } from "./request/create-pool-request";
import { CreateExternalIncentiveRequest } from "./request/create-external-incentive-request";
import { RemoveExternalIncentiveRequest } from "./request/remove-external-incentive-request";

export interface PoolRepository {
getPools: () => Promise<PoolModel[]>;
Expand All @@ -20,4 +22,11 @@ export interface PoolRepository {

getPoolDetailByPath: (poolPath: string) => Promise<IPoolDetailResponse>;

createExternalIncentive: (
request: CreateExternalIncentiveRequest,
) => Promise<string | null>;

removeExternalIncentive: (
request: RemoveExternalIncentiveRequest,
) => Promise<string | null>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { TokenModel } from "@models/token/token-model";

export interface CreateExternalIncentiveRequest {
poolPath: string;

rewardToken: TokenModel;

rewardAmount: string;

startTime: number;

endTime: number;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { TokenModel } from "@models/token/token-model";

export interface RemoveExternalIncentiveRequest {
poolPath: string;

rewardToken: TokenModel;
}

0 comments on commit 39c10d5

Please sign in to comment.