Skip to content

Commit

Permalink
fee on transfer
Browse files Browse the repository at this point in the history
  • Loading branch information
denis-orbs committed May 15, 2024
1 parent aeaa8fb commit f291b91
Show file tree
Hide file tree
Showing 8 changed files with 243 additions and 10 deletions.
2 changes: 1 addition & 1 deletion packages/dapp-example/src/QuickSwap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ const TWAPComponent = ({ limit }: { limit?: boolean }) => {

useEffect(() => {
if (!dappTokens) return;
if (!fromToken) setFromToken(Object.values(dappTokens).find((it: any) => it.symbol === "WMATIC"));
if (!fromToken) setFromToken(Object.values(dappTokens).find((it: any) => it.symbol === "WBTC"));
if (!toToken) setToToken(Object.values(dappTokens).find((it: any) => it.symbol === "USDC"));
}, [dappTokens, fromToken, toToken]);

Expand Down
133 changes: 133 additions & 0 deletions packages/lib/src/abi/FEE_ON_TRANSFER.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
[
{
"inputs": [
{
"internalType": "address",
"name": "_factoryV2",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "PairLookupFailed",
"type": "error"
},
{
"inputs": [],
"name": "SameToken",
"type": "error"
},
{
"inputs": [
{
"internalType": "address[]",
"name": "tokens",
"type": "address[]"
},
{
"internalType": "address",
"name": "baseToken",
"type": "address"
},
{
"internalType": "uint256",
"name": "amountToBorrow",
"type": "uint256"
}
],
"name": "batchValidate",
"outputs": [
{
"components": [
{
"internalType": "uint256",
"name": "buyFeeBps",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "sellFeeBps",
"type": "uint256"
}
],
"internalType": "struct TokenFees[]",
"name": "fotResults",
"type": "tuple[]"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount0",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
}
],
"name": "uniswapV2Call",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "baseToken",
"type": "address"
},
{
"internalType": "uint256",
"name": "amountToBorrow",
"type": "uint256"
}
],
"name": "validate",
"outputs": [
{
"components": [
{
"internalType": "uint256",
"name": "buyFeeBps",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "sellFeeBps",
"type": "uint256"
}
],
"internalType": "struct TokenFees",
"name": "fotResult",
"type": "tuple"
}
],
"stateMutability": "nonpayable",
"type": "function"
}
]
9 changes: 9 additions & 0 deletions packages/lib/src/components/Components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import {
useSetSrcAmountUi,
useDebounce,
usePriceDisplay,
useFeeOnTranserWarning,
} from "../hooks";
import { useLimitPriceStore, useTwapStore } from "../store";
import {
Expand Down Expand Up @@ -512,6 +513,14 @@ export const SubmitButton = ({ className = "", isMain }: { className?: string; i
);
};

export const FeeOnTranferWarning = ({className = ''}:{className?: string}) => {
const { hasFeeOnTransfer } = useFeeOnTranserWarning();

if (!hasFeeOnTransfer) return null;

return <StyledText className={`${className} twap-fee-on-transfer-warning`}>Fee on transfer tokens are not supported</StyledText>;
};

export const useLimitPriceComponents = ({
placeholder = "0.00",
showDefault,
Expand Down
11 changes: 11 additions & 0 deletions packages/lib/src/consts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,14 @@ export const QUERY_PARAMS = {
};

export const SUGGEST_CHUNK_VALUE = 100;
export const AMOUNT_TO_BORROW = 10000; // smallest amount that has full precision over bps

export const feeOnTransferDetectorAddresses = {
1: "0xe9200516a475b9e6FD4D1c452858097F345A6760",
56: "0x003BD52f589F23346E03fA431209C29cD599d693",
42161: "0xD8b14F915b1b4b1c4EE4bF8321Bea018E72E5cf3",
1101: "0xe9200516a475b9e6FD4D1c452858097F345A6760",
8453: "0xD8b14F915b1b4b1c4EE4bF8321Bea018E72E5cf3",
324: "0xED87D01674199355CEfC05648d17E037306d7962",
59144: "0xD8b14F915b1b4b1c4EE4bF8321Bea018E72E5cf3",
};
92 changes: 84 additions & 8 deletions packages/lib/src/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,24 @@ import {
parsebn,
} from "@defi.org/web3-candies";
import { TimeResolution, useLimitPriceStore, useOrdersStore, useTwapStore, useWizardStore, WizardAction, WizardActionStatus } from "./store";
import { MIN_NATIVE_BALANCE, QUERY_PARAMS, REFETCH_BALANCE, REFETCH_GAS_PRICE, REFETCH_ORDER_HISTORY, REFETCH_USD, STALE_ALLOWANCE, SUGGEST_CHUNK_VALUE } from "./consts";
import {
AMOUNT_TO_BORROW,
feeOnTransferDetectorAddresses,
MIN_NATIVE_BALANCE,
QUERY_PARAMS,
REFETCH_BALANCE,
REFETCH_GAS_PRICE,
REFETCH_ORDER_HISTORY,
REFETCH_USD,
STALE_ALLOWANCE,
SUGGEST_CHUNK_VALUE,
} from "./consts";
import { QueryKeys } from "./enums";
import { useNumericFormat } from "react-number-format";
import moment from "moment";
import { amountBN, amountBNV2, amountUi, amountUiV2, devideCurrencyAmounts, getTokenFromTokensList, safeInteger, setQueryParam, supportsTheGraphHistory } from "./utils";
import { getOrderFills, getUserOrders } from "./helper";

import { getOrderFills } from "./helper";
import FEE_ON_TRANSFER_ABI from "./abi/FEE_ON_TRANSFER.json";
/**
* Actions
*/
Expand Down Expand Up @@ -586,11 +597,14 @@ const useTokenList = () => {

export const useOrdersHistoryQuery = () => {
const tokenList = useTokenList();
const { lib, updateState, showConfirmation } = useTwapStore((state) => ({
const { lib, updateState, showConfirmation, srcToken } = useTwapStore((state) => ({
lib: state.lib,
updateState: state.updateState,
showConfirmation: state.showConfirmation,
srcToken: state.srcToken,
}));
const { error, data } = useFeeOnTransfer(srcToken?.address);
console.log({ error, data });
const QUERY_KEY = [QueryKeys.GET_ORDER_HISTORY, lib?.maker, lib?.config.chainId];

const query = useQuery<OrdersData>(
Expand Down Expand Up @@ -917,8 +931,8 @@ export const useDappRawSelectedTokens = () => {

export const useSubmitButton = (isMain?: boolean, _translations?: Translations) => {
const translations = useTwapContext()?.translations || _translations;
const { maker, shouldWrap, shouldUnwrap, wrongNetwork, disclaimerAccepted, setShowConfirmation, showConfirmation, createOrderLoading, srcAmount, srcUsd, dstUsd } = useTwapStore(
(store) => ({
const { maker, shouldWrap, shouldUnwrap, wrongNetwork, disclaimerAccepted, setShowConfirmation, srcToken, showConfirmation, createOrderLoading, srcAmount, srcUsd, dstUsd } =
useTwapStore((store) => ({
maker: store.lib?.maker,
shouldWrap: store.shouldWrap(),
shouldUnwrap: store.shouldUnwrap(),
Expand All @@ -930,8 +944,8 @@ export const useSubmitButton = (isMain?: boolean, _translations?: Translations)
srcAmount: store.getSrcAmount().toString(),
srcUsd: store.srcUsd?.toString(),
dstUsd: store.dstUsd?.toString(),
})
);
srcToken: store.srcToken,
}));
const reset = useResetStore();
const outAmountLoading = useDstAmount().isLoading;
const { mutate: approve, isLoading: approveLoading } = useApproveToken();
Expand Down Expand Up @@ -1373,11 +1387,15 @@ export const useFillWarning = () => {
const deadline = useDeadline();

const maxSrcInputAmount = useMaxSrcInputAmount();
const { isLoading: feeOnTraferLoading, hasFeeOnTransfer } = useFeeOnTranserWarning();

const isNativeTokenAndValueBiggerThanMax = maxSrcInputAmount && srcAmount?.gt(maxSrcInputAmount);
return useMemo(() => {
if (!translation) return;
if (!srcToken || !dstToken || lib?.validateTokens(srcToken, dstToken) === TokensValidation.invalid) return translation.selectTokens;
if (hasFeeOnTransfer) {
return translation.feeOnTranferWarning;
}
if (srcAmount.isZero()) return translation.enterAmount;
if ((srcBalance && srcAmount.gt(srcBalance)) || isNativeTokenAndValueBiggerThanMax) return translation.insufficientFunds;
if (chunkSize.isZero()) return translation.enterTradeSize;
Expand All @@ -1395,6 +1413,10 @@ export const useFillWarning = () => {
if (fillDelayWarning) {
return translation.fillDelayWarning;
}
if (feeOnTraferLoading) {
return translation.loading;
}

}, [
srcToken,
dstToken,
Expand All @@ -1414,6 +1436,8 @@ export const useFillWarning = () => {
maxSrcInputAmount,
lib,
dstAmountOut,
feeOnTraferLoading,
hasFeeOnTransfer,
]);
};

Expand Down Expand Up @@ -1761,3 +1785,55 @@ export const usePriceDisplay = () => {
isLoading,
};
};

const useFeeOnTransferContract = () => {
const provider = useTwapContext().provider;
const lib = useTwapStore((s) => s.lib);

const address = useMemo(() => {
const chainId = lib?.config.chainId;
if (!chainId) return undefined;
return feeOnTransferDetectorAddresses[chainId as keyof typeof feeOnTransferDetectorAddresses];
}, [lib?.config.chainId]);

return useMemo(() => {
if (!provider || !address) return;

const web3 = new Web3(provider);

return new web3.eth.Contract(FEE_ON_TRANSFER_ABI as any, address);
}, [provider, address]);
};

const useFeeOnTransfer = (tokenAddress?: string) => {
const lib = useTwapStore((s) => s.lib);
const contract = useFeeOnTransferContract();

return useQuery({
queryFn: async () => {
const res = await contract?.methods.validate(tokenAddress, lib?.config.wToken.address, AMOUNT_TO_BORROW).call();
return {
buyFee: res.buyFeeBps,
sellFee: res.sellFeeBps,
hasFeeOnTranfer: BN(res.buyFeeBps).gt(0) || BN(res.sellFeeBps).gt(0),
};
},
queryKey: ["useFeeOnTransfer", tokenAddress, lib?.config.chainId],
enabled: !!contract && !!tokenAddress && !!lib,
});
};

export const useFeeOnTranserWarning = () => {
const { srcToken, dstToken } = useTwapStore((s) => ({
srcToken: s.srcToken,
dstToken: s.dstToken,
}));

const srcTokenFeeOnTranfer = useFeeOnTransfer(srcToken?.address);
const dstTokenFeeOnTranfer = useFeeOnTransfer(dstToken?.address);

return {
isLoading: srcTokenFeeOnTranfer.isLoading || dstTokenFeeOnTranfer.isLoading,
hasFeeOnTransfer: srcTokenFeeOnTranfer.data?.hasFeeOnTranfer || dstTokenFeeOnTranfer.data?.hasFeeOnTranfer,
};
};
3 changes: 2 additions & 1 deletion packages/lib/src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -96,5 +96,6 @@
"viewOrders": "View Order History",
"view": "View",
"noLiquidity": "Insufficient liquidity for this trade",
"outAmountLoading": "Searching for the best price"
"outAmountLoading": "Searching for the best price",
"feeOnTranferWarning":"Fee on transfer tokens are not supported"
}
1 change: 1 addition & 0 deletions packages/lib/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ export interface Translations {
estimate: string;
noLiquidity: string;
outAmountLoading: string;
feeOnTranferWarning: string;
}

export interface BaseComponentProps {
Expand Down
2 changes: 2 additions & 0 deletions packages/pancake/src/styles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -925,3 +925,5 @@ export const StyledModalHeaderTitle = styled(Typography)(({ theme }) => {
color: darkMode ? "#f4eeff" : "#280d5f",
};
});


0 comments on commit f291b91

Please sign in to comment.