Skip to content

Commit

Permalink
feat: fee estimate
Browse files Browse the repository at this point in the history
  • Loading branch information
0xKheops committed Aug 27, 2024
1 parent e6a8283 commit 16282f1
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 27 deletions.
30 changes: 30 additions & 0 deletions apps/extension/src/ui/domains/Staking/InlineStakingFeeEstimate.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { classNames } from "@talismn/util"
import { FC } from "react"

import { TokensAndFiat } from "../Asset/TokensAndFiat"
import { useInlineStakingWizard } from "./useInlineStakingWizard"

export const InlineStakingFeeEstimate: FC<{ noCountUp?: boolean }> = ({ noCountUp }) => {
const { feeEstimate, feeToken, isLoadingFeeEstimate, errorFeeEstimate } = useInlineStakingWizard()

return (
<>
{errorFeeEstimate ? (
<div className="text-alert-error truncate">Failed to estimate fee</div>
) : !!feeEstimate && !!feeToken ? (
<TokensAndFiat
tokenId={feeToken?.id}
planck={feeEstimate}
tokensClassName="text-body"
fiatClassName="text-body-secondary"
noCountUp={noCountUp}
className={classNames(isLoadingFeeEstimate && "animate-pulse")}
/>
) : isLoadingFeeEstimate ? (
<div className="text-body-disabled bg-body-disabled rounded-xs animate-pulse">
0.0000 TKN ($0.00)
</div>
) : null}
</>
)
}
37 changes: 27 additions & 10 deletions apps/extension/src/ui/domains/Staking/InlineStakingForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import Tokens from "../Asset/Tokens"
import { TokensAndFiat } from "../Asset/TokensAndFiat"
import { AccountPillButton } from "./AccountPillButton"
import { InlineStakingAccountPicker } from "./InlineStakingAccountPicker"
import { InlineStakingFeeEstimate } from "./InlineStakingFeeEstimate"
import { InlineStakingPoolPicker } from "./InlineStakingPoolPicker"
import { useNomPoolsBondingDuration } from "./useBondingDuration"
import { useInlineStakingWizard } from "./useInlineStakingWizard"
Expand Down Expand Up @@ -419,8 +420,16 @@ const durationFromMs = (ms: number): Duration => {

export const InlineStakingForm = () => {
const { t } = useTranslation()
const { account, accountPicker, token, pool, poolPicker, isFormValid, setStep } =
useInlineStakingWizard()
const {
account,
accountPicker,
token,
pool,
poolPicker,
isFormValid,

setStep,
} = useInlineStakingWizard()

return (
<div className="text-body-secondary flex size-full flex-col gap-4">
Expand Down Expand Up @@ -478,14 +487,22 @@ export const InlineStakingForm = () => {
<div className="flex items-center justify-between">
<div className="whitespace-nowrap">{t("Estimated Fee")}</div>
<div className="overflow-hidden">
<TokensAndFiat
isBalance
tokenId={token?.id}
planck={100000000n}
// className={classNames(balance.status !== "live" && "animate-pulse")}
tokensClassName="text-body"
fiatClassName="text-body-secondary"
/>
<InlineStakingFeeEstimate />
{/* {errorFeeEstimate ? (
<div className="text-alert-error truncate">Failed to estimate fee</div>
) : !!feeEstimate && !!feeToken ? (
<TokensAndFiat
tokenId={feeToken?.id}
planck={feeEstimate}
tokensClassName="text-body"
fiatClassName="text-body-secondary"
className={classNames(isLoadingFeeEstimate && "animate-pulse")}
/>
) : isLoadingFeeEstimate ? (
<div className="text-body-disabled bg-body-disabled rounded-xs animate-pulse">
0.0000 TKN ($0.00)
</div>
) : null} */}
</div>
</div>
</div>
Expand Down
13 changes: 2 additions & 11 deletions apps/extension/src/ui/domains/Staking/InlineStakingReview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { TokenLogo } from "../Asset/TokenLogo"
import { TokensAndFiat } from "../Asset/TokensAndFiat"
import { SapiSendButton } from "../Transactions/SapiSendButton"
import { InlineStakingAccount } from "./InlineStakingAccount"
import { InlineStakingFeeEstimate } from "./InlineStakingFeeEstimate"
import { useInlineStakingWizard } from "./useInlineStakingWizard"

export const InlineStakingReview = () => {
Expand Down Expand Up @@ -64,14 +65,7 @@ export const InlineStakingReview = () => {
<div className="flex items-center justify-between gap-8 pt-2 text-xs">
<div className="whitespace-nowrap">{t("Estimated fee")} </div>
<div>
<TokensAndFiat
isBalance
tokenId={token?.id}
planck={100000000n}
noCountUp
tokensClassName="text-body"
fiatClassName="text-body-secondary"
/>
<InlineStakingFeeEstimate noCountUp />
</div>
</div>
</div>
Expand All @@ -90,9 +84,6 @@ export const InlineStakingReview = () => {
txMetadata={txMetadata}
/>
)}
{/* <Button disabled={!isFormValid} onClick={submit} processing={isSubmitting} primary>
{t("Confirm")}
</Button> */}
</div>
)
}
39 changes: 39 additions & 0 deletions apps/extension/src/ui/domains/Staking/useInlineStakingWizard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { useAccountByAddress } from "@ui/hooks/useAccountByAddress"
import useToken from "@ui/hooks/useToken"
import { useTokenRates } from "@ui/hooks/useTokenRates"

import { useFeeToken } from "../SendFunds/useFeeToken"
import { useNominationPool } from "./useNominationPools"

type InlineStakingWizardStep = "form" | "review" | "follow-up"
Expand Down Expand Up @@ -72,6 +73,7 @@ export const useInlineStakingWizard = () => {

const account = useAccountByAddress(state.address)
const token = useToken(state.tokenId)
const feeToken = useFeeToken(token?.id)
const pool = useNominationPool(token?.chain?.id, state.poolId)
const tokenRates = useTokenRates(state.tokenId)
const formatter = useMemo(
Expand Down Expand Up @@ -154,6 +156,18 @@ export const useInlineStakingWizard = () => {
},
})

const {
data: feeEstimate,
isLoading: isLoadingFeeEstimate,
error: errorFeeEstimate,
} = useQuery({
queryKey: ["feeEstimate", payloadAndMetadata?.payload], // safe stringify because contains bigint
queryFn: () => {
if (!sapi || !payloadAndMetadata?.payload) return null
return sapi.getFeeEstimate(payloadAndMetadata.payload)
},
})

const onSubmitted = useCallback(
(hash: Hex) => {
if (hash) setState((prev) => ({ ...prev, step: "follow-up", hash }))
Expand Down Expand Up @@ -189,6 +203,24 @@ export const useInlineStakingWizard = () => {
// }
// }, [sapi, setState, state])

// useEffect(() => {
// console.log("[sapi] useInlineStakingWizard", {
// payload: payloadAndMetadata?.payload,
// feeEstimate,
// isLoadingFeeEstimate,
// isLoadingPayload,
// errorPayload,
// errorFeeEstimate,
// })
// }, [
// errorFeeEstimate,
// errorPayload,
// feeEstimate,
// isLoadingFeeEstimate,
// isLoadingPayload,
// payloadAndMetadata?.payload,
// ])

return {
account,
token,
Expand All @@ -203,6 +235,8 @@ export const useInlineStakingWizard = () => {
hash,
isSubmitting,
submitErrorMessage,
feeToken,

setAddress,
setTokenId,
setPoolId,
Expand All @@ -214,6 +248,11 @@ export const useInlineStakingWizard = () => {
...payloadAndMetadata,
isLoadingPayload,
errorPayload,

feeEstimate,
isLoadingFeeEstimate,
errorFeeEstimate,

onSubmitted,
}
}
55 changes: 49 additions & 6 deletions apps/extension/src/ui/util/scaleApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@ import {
} from "@polkadot-api/substrate-bindings"
import { mergeUint8, toHex } from "@polkadot-api/utils"
import { Metadata, TypeRegistry } from "@polkadot/types"
import { fromHex, getDynamicBuilder, getLookupFn, V14, V15 } from "@talismn/scale"
import { IRuntimeVersionBase } from "@polkadot/types/types"
import { getDynamicBuilder, getLookupFn, V14, V15 } from "@talismn/scale"
import { sleep } from "@talismn/util"
import { ChainId, SignerPayloadJSON } from "extension-core"
import { DEBUG, log } from "extension-shared"
import { Binary } from "polkadot-api"
import { Hex } from "viem"

import { api } from "@ui/api"

import { getSignedExtensionValues } from "./signedExtensions"
import { getExtrinsicDispatchInfo } from "./getExtrinsicDispatchInfo"

type ScaleMetadata = V14 | V15
type ScaleBuilder = ReturnType<typeof getDynamicBuilder>
Expand Down Expand Up @@ -75,6 +77,9 @@ export const getScaleApi = (
config: PayloadSignerConfig
) => getSignerPayloadJSON(chainId, metadata, builder, pallet, method, args, config, chainInfo),

getFeeEstimate: async (payload: SignerPayloadJSON) =>
getFeeEstimate(chainId, metadata, builder, payload, chainInfo),

submit: (payload: SignerPayloadJSON, signature?: Hex) => api.subSubmit(payload, signature),
}
}
Expand Down Expand Up @@ -132,17 +137,17 @@ const getPayloadWithMetadataHash = (
const metadataHash = toHex(merkleizedMetadata.digest()) as Hex

// TODO do this without PJS / registry
const { extra, additionalSigned } = getSignedExtensionValues(payload, metadata)
const badExtPayload = mergeUint8(fromHex(payload.method), ...extra, ...additionalSigned)
log.debug("[sapi] bad ExtPayload", { badExtPayload })
// const { extra, additionalSigned } = getSignedExtensionValues(payload, metadata)
// const badExtPayload = mergeUint8(fromHex(payload.method), ...extra, ...additionalSigned)
// log.debug("[sapi] bad ExtPayload", { badExtPayload })

const stop2 = log.timer("get ExtrinsicPayload using PJS")
const registry = new TypeRegistry()
registry.setMetadata(new Metadata(registry, metadataBytes), payload.signedExtensions)
const extPayload = registry.createType("ExtrinsicPayload", payload)
const barePayload = extPayload.toU8a(true)
stop2()
log.debug("[sapi] good ExtPayload", { badExtPayload })
log.debug("[sapi] good ExtPayload", { barePayload })

const txMetadata = merkleizedMetadata.getProofForExtrinsicPayload(barePayload)

Expand Down Expand Up @@ -219,6 +224,44 @@ const getSignerPayloadJSON = async (
return { payload, txMetadata }
}

const getFeeEstimate = async (
chainId: ChainId,
metadata: ScaleMetadata,
builder: ScaleBuilder,
payload: SignerPayloadJSON,
chainInfo: ChainInfo
) => {
const fullMetadata = {
magicNumber: 1635018093, // magic number for metadata
metadata: { tag: "v15" as const, value: metadata as V15 },
}
const metadataBytes = metadataCodec.enc(fullMetadata)

const stop = log.timer("[sapi] getFeeEstimate => create Extrinsic")
const registry = new TypeRegistry()
registry.setMetadata(new Metadata(registry, metadataBytes), payload.signedExtensions)
const extrinsic = registry.createType("Extrinsic", payload)
stop()

extrinsic.signFake(payload.address, {
nonce: payload.nonce,
blockHash: payload.blockHash,
genesisHash: payload.genesisHash,
//payload,
runtimeVersion: {
specVersion: chainInfo.specVersion,
transactionVersion: chainInfo.transactionVersion,
// other fields aren't necessary for signing
} as IRuntimeVersionBase,
})

await sleep(2000)

const { partialFee } = await getExtrinsicDispatchInfo(chainId, extrinsic)

return BigInt(partialFee)
}

const getConstantValue = <T>(
metadata: ScaleMetadata,
scaleBuilder: ScaleBuilder,
Expand Down

0 comments on commit 16282f1

Please sign in to comment.