Skip to content

Commit

Permalink
Merge pull request #249 from gnoswap-labs/GSW-469-execute-a-reward-cl…
Browse files Browse the repository at this point in the history
…aim-contract

feat: [GSW-469] Execute a Reward Claim Contract
  • Loading branch information
jinoosss authored Dec 13, 2023
2 parents 9411e6a + d048ebc commit bad199a
Show file tree
Hide file tree
Showing 11 changed files with 226 additions and 5 deletions.
1 change: 1 addition & 0 deletions packages/web/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ NEXT_PUBLIC_PACKAGE_POOL_PATH="gno.land/r/pool"
NEXT_PUBLIC_PACKAGE_POOL_ADDRESS="g1ee305k8yk0pjz443xpwtqdyep522f9g5r7d63w"
NEXT_PUBLIC_PACKAGE_POSITION_PATH="gno.land/r/position"
NEXT_PUBLIC_PACKAGE_POSITION_ADDRESS="g1htpxzv2dkplvzg50nd8fswrneaxmdpwn459thx"
NEXT_PUBLIC_PACKAGE_STAKER_PATH="gno.land/r/staker"
NEXT_PUBLIC_WRAPPED_GNOT_PATH="gno.land/r/wugnot"
4 changes: 4 additions & 0 deletions packages/web/src/common/values/data-constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,7 @@ export enum NotificationType {
"UnWrap" = 10,
}
export type MathSymbolType = "NEGATIVE" | "POSITIVE" | "NAN";

export const DEFAULT_TRANSACTION_DEADLINE = "7282571140" as const;
export const DEFAULT_GAS_FEE = 1 as const;
export const DEFAULT_GAS_WANTED = 2000000 as const;
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@ interface MyLiquidityContentProps {
positions: PoolPositionModel[];
breakpoint: DEVICE_TYPE;
isDisabledButton: boolean;
claimAll: () => void;
}

const MyLiquidityContent: React.FC<MyLiquidityContentProps> = ({
connected,
positions,
breakpoint,
claimAll,
}) => {
const { tokenPrices } = useTokenData();

Expand Down Expand Up @@ -216,6 +218,7 @@ const MyLiquidityContent: React.FC<MyLiquidityContentProps> = ({
padding: "10px 16px",
fontType: "p1",
}}
onClick={claimAll}
/>
</div>
) : (
Expand Down Expand Up @@ -245,6 +248,7 @@ const MyLiquidityContent: React.FC<MyLiquidityContentProps> = ({
padding: "0px 16px",
fontType: "p1",
}}
onClick={claimAll}
/>
</div>
</>
Expand Down
3 changes: 3 additions & 0 deletions packages/web/src/components/pool/my-liquidity/MyLiquidity.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ interface MyLiquidityProps {
divRef: React.RefObject<HTMLDivElement>;
onScroll: () => void;
currentIndex: number;
claimAll: () => void;
}

const MyLiquidity: React.FC<MyLiquidityProps> = ({
Expand All @@ -28,6 +29,7 @@ const MyLiquidity: React.FC<MyLiquidityProps> = ({
divRef,
onScroll,
currentIndex,
claimAll,
}) => {
return (
<MyLiquidityWrapper>
Expand All @@ -43,6 +45,7 @@ const MyLiquidity: React.FC<MyLiquidityProps> = ({
positions={positions}
breakpoint={breakpoint}
isDisabledButton={isSwitchNetwork || !connected}
claimAll={claimAll}
/>
</div>
<PoolDivider />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,19 @@ import { useWallet } from "@hooks/wallet/use-wallet";
import { useRouter } from "next/router";
import { usePositionData } from "@hooks/common/use-position-data";
import { PoolPositionModel } from "@models/position/pool-position-model";
import { usePosition } from "@hooks/common/use-position";



const MyLiquidityContainer: React.FC = () => {
const router = useRouter();
const divRef = useRef<HTMLDivElement | null>(null);
const { breakpoint } = useWindowSize();
const { connected: connectedWallet, isSwitchNetwork, account } = useWallet();
const { getPositionsByPoolId } = usePositionData();
const router = useRouter();
const divRef = useRef<HTMLDivElement | null>(null);
const [currentIndex, setCurrentIndex] = useState(1);
const [positions, setPositions] = useState<PoolPositionModel[]>([]);
const { claimAll } = usePosition(positions);

useEffect(() => {
const poolPath = router.query["pool-path"] as string;
Expand All @@ -42,6 +44,10 @@ const MyLiquidityContainer: React.FC = () => {
}
};

const claimAllReward = useCallback(() => {
claimAll();
}, [claimAll]);

return (
<MyLiquidity
positions={positions}
Expand All @@ -53,6 +59,7 @@ const MyLiquidityContainer: React.FC = () => {
divRef={divRef}
onScroll={handleScroll}
currentIndex={currentIndex}
claimAll={claimAllReward}
/>
);
};
Expand Down
31 changes: 31 additions & 0 deletions packages/web/src/hooks/common/use-position.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { useWallet } from "@hooks/wallet/use-wallet";
import { PoolPositionModel } from "@models/position/pool-position-model";
import { useCallback } from "react";
import { useGnoswapContext } from "./use-gnoswap-context";

export const usePosition = (positions: PoolPositionModel[]) => {
const { positionRepository } = useGnoswapContext();
const { account } = useWallet();

const claimAll = useCallback(() => {
const address = account?.address;
if (!address) {
return null;
}

const lpTokenIds = positions
.filter(
position =>
position.unclaimedFee0Amount + position.unclaimedFee1Amount > 0,
)
.map(position => position.lpTokenId);
return positionRepository.claimAll({
lpTokenIds,
receipient: address,
});
}, [account?.address, positionRepository, positions]);

return {
claimAll,
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ const GnoswapServiceProvider: React.FC<React.PropsWithChildren> = ({
}, [localStorageClient, networkClient]);

const positionRepository = useMemo(() => {
return new PositionRepositoryImpl(networkClient);
}, [networkClient]);
return new PositionRepositoryImpl(networkClient, rpcProvider, walletClient);
}, [networkClient, rpcProvider, walletClient]);

async function initNetwork() {
const defaultChainId = process.env.NEXT_PUBLIC_DEFAULT_CHAIN_ID || DEFAULT_NETWORK_ID;
Expand Down
147 changes: 146 additions & 1 deletion packages/web/src/repositories/position/position-repository-impl.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,37 @@
import { NetworkClient } from "@common/clients/network-client";
import { WalletClient } from "@common/clients/wallet-client";
import { SendTransactionSuccessResponse } from "@common/clients/wallet-client/protocols";
import { CommonError } from "@common/errors";
import {
DEFAULT_GAS_FEE,
DEFAULT_GAS_WANTED,
DEFAULT_TRANSACTION_DEADLINE,
} from "@common/values";
import { GnoProvider } from "@gnolang/gno-js-client";
import { MAX_INT64 } from "@gnoswap-labs/swap-router";
import { PositionMapper } from "@models/position/mapper/position-mapper";
import { PositionModel } from "@models/position/position-model";
import { PositionRepository } from "./position-repository";
import { ClaimAllRequest } from "./request/claim-all-request";
import { DecreaseLiquidityReqeust } from "./request/decrease-liquidity-request";
import { PositionListResponse } from "./response";

const STAKER_PATH = process.env.NEXT_PUBLIC_PACKAGE_STAKER_PATH || "";
const POSITION_PATH = process.env.NEXT_PUBLIC_PACKAGE_POSITION_PATH || "";

export class PositionRepositoryImpl implements PositionRepository {
private networkClient: NetworkClient;
private rpcProvider: GnoProvider | null;
private walletClient: WalletClient | null;

constructor(networkClient: NetworkClient) {
constructor(
networkClient: NetworkClient,
rpcProvider: GnoProvider | null,
walletClient: WalletClient | null,
) {
this.networkClient = networkClient;
this.rpcProvider = rpcProvider;
this.walletClient = walletClient;
}

getPositionsByAddress = async (address: string): Promise<PositionModel[]> => {
Expand All @@ -17,4 +40,126 @@ export class PositionRepositoryImpl implements PositionRepository {
});
return PositionMapper.fromList(response.data);
};

claimAll = async (request: ClaimAllRequest): Promise<string | null> => {
if (this.walletClient === null) {
throw new CommonError("FAILED_INITIALIZE_WALLET");
}
const { lpTokenIds, receipient } = request;
const messages = lpTokenIds.flatMap(lpTokenId => {
const messages = [];
messages.push(
PositionRepositoryImpl.makeZeroDecreaseLiquidityMessage(
lpTokenId,
receipient,
),
);
messages.push(
PositionRepositoryImpl.makeCollectMessage(lpTokenId, receipient),
);
return messages;
});

// TODO: Need to check if a contract error occurred
// messages.push(PositionRepositoryImpl.makeCollectRewardMessage(receipient));

const result = await this.walletClient.sendTransaction({
messages,
gasFee: DEFAULT_GAS_FEE,
gasWanted: DEFAULT_GAS_WANTED,
});
const hash = (result.data as SendTransactionSuccessResponse)?.hash || null;
if (!hash) {
throw new Error(`${result}`);
}
return hash;
};

decreaseLiquidity = async (
request: DecreaseLiquidityReqeust,
): Promise<string | null> => {
if (this.walletClient === null) {
throw new CommonError("FAILED_INITIALIZE_WALLET");
}
const {
lpTokenId,
liquidity,
amountAMin,
amountBMax,
caller,
deadline = DEFAULT_TRANSACTION_DEADLINE,
} = request;
const messages = [];
messages.push(
PositionRepositoryImpl.makeDecreaseLiquidityMessage(
lpTokenId,
liquidity,
amountAMin,
amountBMax,
deadline,
caller,
),
);
const result = await this.walletClient.sendTransaction({
messages,
gasFee: DEFAULT_GAS_FEE,
gasWanted: DEFAULT_GAS_WANTED,
});
const hash = (result.data as SendTransactionSuccessResponse)?.hash || null;
if (!hash) {
throw new Error(`${result}`);
}
return hash;
};

private static makeCollectMessage(lpTokenId: string, receipient: string) {
return {
caller: receipient,
send: "",
pkg_path: POSITION_PATH,
func: "Collect",
args: [lpTokenId, receipient, MAX_INT64.toString(), MAX_INT64.toString()],
};
}

private static makeCollectRewardMessage(caller: string) {
return {
caller,
send: "",
pkg_path: STAKER_PATH,
func: "CollectReward",
args: [],
};
}

private static makeDecreaseLiquidityMessage(
lpTokenId: string,
liquidity: string,
amountAMin: string,
amountBMin: string,
deadeline: string,
caller: string,
) {
return {
caller,
send: "",
pkg_path: POSITION_PATH,
func: "DecreaseLiquidity",
args: [lpTokenId, liquidity, amountAMin, amountBMin, deadeline],
};
}

private static makeZeroDecreaseLiquidityMessage(
lpTokenId: string,
caller: string,
) {
return this.makeDecreaseLiquidityMessage(
lpTokenId,
"0",
"0",
"0",
DEFAULT_TRANSACTION_DEADLINE,
caller,
);
}
}
8 changes: 8 additions & 0 deletions packages/web/src/repositories/position/position-repository.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import { PositionModel } from "@models/position/position-model";
import { ClaimAllRequest } from "./request/claim-all-request";
import { DecreaseLiquidityReqeust } from "./request/decrease-liquidity-request";

export interface PositionRepository {
getPositionsByAddress: (address: string) => Promise<PositionModel[]>;

claimAll: (request: ClaimAllRequest) => Promise<string | null>;

decreaseLiquidity: (
request: DecreaseLiquidityReqeust,
) => Promise<string | null>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface ClaimAllRequest {
lpTokenIds: string[];

receipient: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export interface DecreaseLiquidityReqeust {
lpTokenId: string;

liquidity: string;

amountAMin: string;

amountBMax: string;

deadline?: string;

caller: string;
}

0 comments on commit bad199a

Please sign in to comment.