Skip to content

Commit 74fdac0

Browse files
committed
fix: ibc shieled deposits with frontend fees
1 parent c08468b commit 74fdac0

File tree

14 files changed

+182
-68
lines changed

14 files changed

+182
-68
lines changed

apps/namadillo/public/config.toml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
[frontend_fee."*"]
99
transparent_target = "tnam1qrxsru5rdu4he400xny6p779fcw7xuftsgjnmzup"
1010
shielded_target = "znam1494fmm9qd4frr7jrydnxlcyt57nhngzutxnzgh6y70mkvh23d9z0jk2aasxh4p8dn2kczlgpans"
11-
percentage = 0.1
11+
percentage = 0.01
1212

13-
[frontend_fee.tnam1p54697ljxkw3ptffwy8xjhua9uwjc7kk9cc6fxnw]
14-
transparent_target = "tnam1qrku7yxdt6d23nhe826ntukygzkfyczuu5mm0yll"
15-
shielded_target = "znam175uaqlukleyjjyfcccehp2pw3g696a57skewlv0fenldlw702gtjd6qkpjs09zxl6jafsz3e73h"
16-
percentage = 0.5
13+
# [frontend_fee.tnam1p54697ljxkw3ptffwy8xjhua9uwjc7kk9cc6fxnw]
14+
# transparent_target = "tnam1qrku7yxdt6d23nhe826ntukygzkfyczuu5mm0yll"
15+
# shielded_target = "znam175uaqlukleyjjyfcccehp2pw3g696a57skewlv0fenldlw702gtjd6qkpjs09zxl6jafsz3e73h"
16+
# percentage = 0.5

apps/namadillo/src/App/Ibc/IbcTransfer.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,18 +68,18 @@ export const IbcTransfer = ({
6868
);
6969
const { trackEvent } = useFathomTracker();
7070
const { storeTransaction } = useTransactionActions();
71+
const shielded = isShieldedAddress(destinationAddress ?? "");
7172

7273
const { transferToNamada, gasConfig, frontendFee } = useIbcTransaction({
7374
registry,
7475
sourceAddress,
7576
sourceChannel,
7677
destinationChannel,
77-
shielded: isShieldedAddress(destinationAddress ?? ""),
78+
shielded,
7879
selectedAsset: selectedAssetWithAmount?.asset,
7980
});
8081

8182
// DERIVED VALUES
82-
const shielded = isShieldedAddress(destinationAddress ?? "");
8383
const availableDisplayAmount = mapUndefined((baseDenom) => {
8484
return userAssets ? userAssets[baseDenom]?.amount : undefined;
8585
}, selectedAssetWithAmount?.asset?.address);
@@ -124,11 +124,16 @@ export const IbcTransfer = ({
124124
}: OnSubmitTransferParams): Promise<void> => {
125125
try {
126126
invariant(registry?.chain, "Error: Chain not selected");
127+
invariant(selectedAssetWithAmount?.asset, "Error: Asset not selected");
128+
invariant(displayAmount, "Error: Amount not specified");
129+
invariant(destinationAddress, "Error: Destination address not specified");
130+
127131
setGeneralErrorMessage("");
128132
setCurrentStatus("Submitting...");
133+
129134
const result = await transferToNamada.mutateAsync({
130-
destinationAddress: destinationAddress ?? "",
131-
displayAmount: new BigNumber(displayAmount ?? "0"),
135+
destinationAddress,
136+
displayAmount: BigNumber(displayAmount),
132137
memo,
133138
onUpdateStatus: setCurrentStatus,
134139
});

apps/namadillo/src/App/Transfer/TransferModule.tsx

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ import {
2525
useNavigate,
2626
useSearchParams,
2727
} from "react-router-dom";
28-
import { AssetWithAmountAndChain } from "types";
28+
import { AssetWithAmountAndChain, NamadaAsset } from "types";
2929
import { filterAvailableAssetsWithBalance } from "utils/assets";
30+
import { getFrontendFeeEntry } from "utils/frontendFee";
3031
import { getDisplayGasFee } from "utils/gas";
3132
import { isIbcAddress, isShieldedAddress } from "./common";
3233
import { IbcChannels } from "./IbcChannels";
@@ -133,7 +134,14 @@ export const TransferModule = ({
133134
}, [gasConfig]);
134135

135136
const availableAmountMinusFees = useMemo(() => {
136-
if (!availableAmount || !availableAssets || !displayGasFee) return;
137+
if (
138+
!availableAmount ||
139+
!availableAssets ||
140+
!displayGasFee ||
141+
!gasConfig ||
142+
!frontendFee
143+
)
144+
return;
137145
let amountMinusFees = availableAmount;
138146

139147
if (gasConfig?.gasToken === selectedAsset?.asset.address) {
@@ -142,9 +150,10 @@ export const TransferModule = ({
142150
.decimalPlaces(6);
143151
}
144152

145-
// TODO: move this code
146-
const frontendSusFee =
147-
frontendFee?.[selectedAsset.asset.address || "default"];
153+
const frontendSusFee = getFrontendFeeEntry(
154+
frontendFee,
155+
(selectedAsset.asset as NamadaAsset).address
156+
);
148157

149158
if (frontendSusFee && (isShielding || isUnshielding)) {
150159
amountMinusFees = amountMinusFees

apps/namadillo/src/atoms/fees/atoms.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,17 @@ import {
55
import { defaultAccountAtom } from "atoms/accounts";
66
import { indexerApiAtom } from "atoms/api";
77
import { namadaRegistryChainAssetsMapAtom } from "atoms/integrations";
8+
import { defaultServerConfigAtom } from "atoms/settings";
89
import { queryDependentFn } from "atoms/utils";
910
import BigNumber from "bignumber.js";
11+
import * as E from "fp-ts/Either";
12+
import { pipe } from "fp-ts/lib/function";
13+
import * as O from "fp-ts/Option";
14+
import * as R from "fp-ts/Record";
1015
import invariant from "invariant";
16+
import * as t from "io-ts";
17+
import { PathReporter } from "io-ts/PathReporter";
18+
import { atom } from "jotai";
1119
import { atomWithQuery } from "jotai-tanstack-query";
1220
import { atomFamily } from "jotai/utils";
1321
import { isPublicKeyRevealed } from "lib/query";
@@ -98,3 +106,41 @@ export const isPublicKeyRevealedAtom = atomWithQuery<boolean>((get) => {
98106
}, [defaultAccount]),
99107
};
100108
});
109+
110+
const FrontendFeeSchema = t.union([
111+
t.undefined,
112+
t.record(
113+
t.string,
114+
t.type({
115+
transparent_target: t.string,
116+
shielded_target: t.string,
117+
percentage: t.number,
118+
})
119+
),
120+
]);
121+
122+
export const frontendFeeAtom = atom((get) => {
123+
const maybeFrontendFee = get(defaultServerConfigAtom).data?.frontend_fee;
124+
const eitherFrontendFee = FrontendFeeSchema.decode(maybeFrontendFee);
125+
if (E.isLeft(eitherFrontendFee)) {
126+
console.warn(
127+
"Invalid frontend fee schema: ",
128+
PathReporter.report(eitherFrontendFee).join("\n")
129+
);
130+
return {};
131+
}
132+
// TODO: validate if targets are valid addresses
133+
134+
return pipe(
135+
O.fromNullable(eitherFrontendFee.right),
136+
O.fold(
137+
() => ({}),
138+
(fees) => fees
139+
),
140+
R.map((fee) => ({
141+
transparentTarget: fee.transparent_target,
142+
shieldedTarget: fee.shielded_target,
143+
percentage: BigNumber(fee.percentage),
144+
}))
145+
);
146+
});

apps/namadillo/src/atoms/fees/services.ts

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@ import {
33
GasEstimate,
44
GasPriceTableInner,
55
} from "@namada/indexer-client";
6-
import { FrontendSusFeeProps } from "@namada/sdk-multicore";
7-
import { assertNever } from "@namada/utils";
8-
import { FrontendFee } from "types";
96
import { TxKind } from "types/txKind";
107

118
export const fetchGasEstimate = async (
@@ -51,24 +48,3 @@ export const fetchTokensGasPrice = async (
5148
): Promise<GasPriceTableInner[]> => {
5249
return (await api.apiV1GasPriceGet()).data;
5350
};
54-
55-
export const frontendSusMsgFromConfig = (
56-
frontendFee: FrontendFee,
57-
token: string,
58-
whichTarget: "shielded" | "transparent"
59-
): FrontendSusFeeProps => {
60-
const { percentage, shieldedTarget, transparentTarget } =
61-
frontendFee[token] || frontendFee["*"];
62-
63-
const target =
64-
whichTarget === "shielded" ? shieldedTarget
65-
: whichTarget === "transparent" ? transparentTarget
66-
: assertNever(whichTarget);
67-
68-
const frontendSusFee = {
69-
percentage: percentage,
70-
target,
71-
};
72-
73-
return frontendSusFee;
74-
};

apps/namadillo/src/atoms/integrations/services.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ import {
1616
} from "@namada/indexer-client";
1717
import { getIndexerApi } from "atoms/api";
1818
import { chainParametersAtom } from "atoms/chain";
19-
import { frontendSusMsgFromConfig } from "atoms/fees";
20-
import { frontendFeeAtom, rpcUrlAtom } from "atoms/settings";
19+
import { frontendFeeAtom } from "atoms/fees";
20+
import { rpcUrlAtom } from "atoms/settings";
2121
import { queryForAck, queryForIbcTimeout } from "atoms/transactions";
2222
import BigNumber from "bignumber.js";
2323
import * as Comlink from "comlink";
@@ -36,6 +36,7 @@ import {
3636
TransferStep,
3737
TransferTransactionData,
3838
} from "types";
39+
import { frontendSusMsgFromConfig } from "utils/frontendFee";
3940
import { isError404 } from "utils/http";
4041
import { getKeplrWallet } from "utils/ibc";
4142
import { getSdkInstance } from "utils/sdk";

apps/namadillo/src/atoms/settings/atoms.ts

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
import { isUrlValid, sanitizeUrl } from "@namada/utils";
22
import { getCustomIndexerApi, indexerApiAtom } from "atoms/api";
33
import { chainParametersAtom, indexerRpcUrlAtom } from "atoms/chain";
4-
import BigNumber from "bignumber.js";
5-
import { pipe } from "fp-ts/lib/function";
6-
import * as R from "fp-ts/Record";
74
import { Getter, Setter, atom, getDefaultStore } from "jotai";
85
import { atomWithMutation, atomWithQuery } from "jotai-tanstack-query";
96
import { atomWithStorage } from "jotai/utils";
@@ -178,20 +175,6 @@ export const maspIndexerUrlAtom = atom((get) => {
178175
return "";
179176
});
180177

181-
// TODO: figure out where to have this atom
182-
export const frontendFeeAtom = atom((get) => {
183-
const frontendFee = get(defaultServerConfigAtom).data?.frontend_fee;
184-
// TODO: validate with schema
185-
return pipe(
186-
frontendFee ?? {},
187-
R.map((fee) => ({
188-
transparentTarget: fee.transparent_target,
189-
shieldedTarget: fee.shielded_target,
190-
percentage: BigNumber(fee.percentage),
191-
}))
192-
);
193-
});
194-
195178
export const updateIndexerUrlAtom = atomWithMutation(() => {
196179
return {
197180
mutationKey: ["update-indexer-url"],

apps/namadillo/src/atoms/transfer/services.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ import {
1313
AccountType,
1414
GenDisposableSignerResponse,
1515
} from "@namada/types";
16-
import { frontendSusMsgFromConfig } from "atoms/fees";
1716
import BigNumber from "bignumber.js";
1817
import * as Comlink from "comlink";
1918
import { NamadaKeychain } from "hooks/useNamadaKeychain";
2019
import { buildTx, EncodedTxData, isPublicKeyRevealed } from "lib/query";
2120
import { Address, ChainSettings, FrontendFee, GasConfig } from "types";
21+
import { frontendSusMsgFromConfig } from "utils/frontendFee";
2222
import { getSdkInstance } from "utils/sdk";
2323
import {
2424
IbcTransfer,

apps/namadillo/src/hooks/useIbcTransaction.tsx

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
} from "@tanstack/react-query";
88
import { TokenCurrency } from "App/Common/TokenCurrency";
99
import { chainParametersAtom } from "atoms/chain";
10+
import { frontendFeeAtom } from "atoms/fees";
1011
import {
1112
broadcastIbcTransactionAtom,
1213
createStargateClient,
@@ -19,8 +20,9 @@ import {
1920
createNotificationId,
2021
dispatchToastNotificationAtom,
2122
} from "atoms/notifications";
22-
import { frontendFeeAtom } from "atoms/settings";
2323
import BigNumber from "bignumber.js";
24+
import { pipe } from "fp-ts/lib/function";
25+
import * as O from "fp-ts/Option";
2426
import invariant from "invariant";
2527
import { useAtomValue, useSetAtom } from "jotai";
2628
import {
@@ -35,11 +37,16 @@ import {
3537
FrontendFee,
3638
GasConfig,
3739
IbcTransferStage,
40+
NamadaAsset,
3841
TransferStep,
3942
TransferTransactionData,
4043
} from "types";
4144
import { toBaseAmount } from "utils";
4245
import { sanitizeAddress } from "utils/address";
46+
import {
47+
calculateAmountWithFrontendFee,
48+
getFrontendFeeEntry,
49+
} from "utils/frontendFee";
4350
import { getKeplrWallet, sanitizeChannel } from "utils/ibc";
4451
import { useSimulateIbcTransferFee } from "./useSimulateIbcTransferFee";
4552

@@ -193,6 +200,11 @@ export const useIbcTransaction = ({
193200
gasConfigQuery.error?.message
194201
);
195202

203+
const frontendFeeEntry = getFrontendFeeEntry(
204+
frontendFee,
205+
(selectedAsset as NamadaAsset).address
206+
);
207+
196208
const baseAmount = toBaseAmount(selectedAsset, displayAmount);
197209

198210
const sourceChainAssets =
@@ -227,11 +239,25 @@ export const useIbcTransaction = ({
227239
const chainId = registry.chain.chain_id;
228240
const denomination = asset.base;
229241

242+
const amount = pipe(
243+
frontendFeeEntry,
244+
O.fromNullable,
245+
O.filter(() => !!shielded),
246+
O.fold(
247+
() => baseAmount,
248+
(fee) =>
249+
toBaseAmount(
250+
selectedAsset,
251+
calculateAmountWithFrontendFee(BigNumber(displayAmount), fee)
252+
)
253+
)
254+
);
255+
230256
const transferMsg = createIbcTransferMessage(
231257
sanitizeChannel(sourceChannel!),
232258
sanitizeAddress(sourceAddress),
233259
sanitizeAddress(maspCompatibleReceiver),
234-
baseAmount,
260+
amount,
235261
denomination,
236262
maspCompatibleMemo
237263
);
@@ -256,7 +282,8 @@ export const useIbcTransaction = ({
256282
chainId,
257283
destinationChainId || "",
258284
getIbcTransferStage(!!shielded),
259-
!!shielded
285+
!!shielded,
286+
baseAmount
260287
);
261288
dispatchPendingTxNotification(tx);
262289
setTxHash(tx.hash);

apps/namadillo/src/hooks/useTransactionFee/useTransactionFee.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ import { transparentBalanceAtom } from "atoms/accounts";
33
import { shieldedBalanceAtom } from "atoms/balance";
44
import { nativeTokenAddressAtom } from "atoms/chain";
55
import {
6+
frontendFeeAtom,
67
gasEstimateFamily,
78
GasPriceTable,
89
gasPriceTableAtom,
910
isPublicKeyRevealedAtom,
1011
} from "atoms/fees";
1112
import { tokenPricesFamily } from "atoms/prices/atoms";
12-
import { frontendFeeAtom } from "atoms/settings";
1313
import BigNumber from "bignumber.js";
1414
import invariant from "invariant";
1515
import { useAtomValue } from "jotai";

0 commit comments

Comments
 (0)