Skip to content

Commit

Permalink
Add Fee Bump Tx
Browse files Browse the repository at this point in the history
  • Loading branch information
Jeesun Kim authored and Jeesun Kim committed Jun 21, 2024
1 parent b916ed0 commit 1eb2573
Show file tree
Hide file tree
Showing 4 changed files with 294 additions and 7 deletions.
197 changes: 196 additions & 1 deletion src/app/(sidebar)/transaction/fee-bump/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,200 @@
"use client";

import { useState } from "react";
import { Button, Card, Icon, Text } from "@stellar/design-system";

import { useStore } from "@/store/useStore";

import { validate } from "@/validate";

import { txHelper, FeeBumpedTxResponse } from "@/helpers/txHelper";

import { useRouter } from "next/navigation";

import { Box } from "@/components/layout/Box";
import { PositiveIntPicker } from "@/components/FormElements/PositiveIntPicker";
import { PubKeyPicker } from "@/components/FormElements/PubKeyPicker";
import { SdsLink } from "@/components/SdsLink";
import { TxResponse } from "@/components/TxResponse";
import { ValidationResponseCard } from "@/components/ValidationResponseCard";
import { XdrPicker } from "@/components/FormElements/XdrPicker";

export default function FeeBumpTransaction() {
return <div>Fee Bump Transaction</div>;
const router = useRouter();
const { network, transaction } = useStore();
const {
feeBump,
updateBaseFeeSource,
updateBaseFeeBase,
updateBaseFeeInnerXdr,
updateSignActiveView,
updateSignImportXdr,
resetBaseFee,
} = transaction;
const { source_account, fee, innerTxXdr } = feeBump;

// Sign tx status related
const [feeBumpedTx, setFeeBumpedTx] = useState<FeeBumpedTxResponse>({
errors: [],
xdr: "",
});
const [xdrError, setXdrError] = useState<string>("");
const [sourceErrorMessage, setSourceErrorMessage] = useState<string>("");
const [baseErrorMessage, setBaseErrorMessage] = useState<string>("");

const onXdrChange = (value: string) => {
// reset messages onChange
setXdrError("");
setFeeBumpedTx({
errors: [],
xdr: "",
});

updateBaseFeeInnerXdr(value);

if (value.length > 0) {
const validatedXDR = validate.xdr(value);

if (validatedXDR?.result && validatedXDR.message) {
if (validatedXDR.result === "success") {
const result = txHelper.buildFeeBumpTx({
innerTxXDR: value,
maxFee: fee,
sourceAccount: source_account,
networkPassphrase: network.passphrase,
});

setFeeBumpedTx(result);
} else {
setXdrError(validatedXDR.message);
}
}
}
};

return (
<Box gap="md">
<div className="PageHeader">
<Text size="md" as="h1" weight="medium">
Fee Bump
</Text>

<Button
size="md"
variant="error"
icon={<Icon.RefreshCw01 />}
iconPosition="right"
onClick={() => {
resetBaseFee();
}}
>
Clear and import new
</Button>
</div>
<Card>
<Box gap="lg">
<PubKeyPicker
id="source_account"
label="Source Account"
value={source_account}
error={sourceErrorMessage}
onChange={(e) => {
const error = validate.publicKey(e.target.value);
setSourceErrorMessage(error || "");
updateBaseFeeSource(e.target.value);
}}
note={<>The account responsible for paying the transaction fee.</>}
infoLink="https://developers.stellar.org/docs/learn/glossary#source-account"
/>

<PositiveIntPicker
id="fee"
label="Base Fee"
value={fee}
error={baseErrorMessage}
onChange={(e) => {
const error = validate.positiveInt(e.target.value);
setBaseErrorMessage(error || "");
updateBaseFeeBase(e.target.value);
}}
note={
<>
The{" "}
<SdsLink href="https://developers.stellar.org/docs/learn/fundamentals/fees-resource-limits-metering">
network base fee
</SdsLink>{" "}
fee is currently set to 100 stroops (0.00001 lumens). Based on
current network activity, we suggest setting it to 100 stroops.
Final transaction fee is equal to base fee times number of
operations in this transaction.
</>
}
infoLink="https://developers.stellar.org/docs/learn/glossary#base-fee"
/>

<XdrPicker
id="submit-tx-xdr"
label="Input a base-64 encoded TransactionEnvelope:"
value={innerTxXdr}
error={xdrError}
onChange={(e) => onXdrChange(e.target.value)}
note="Enter a base-64 encoded XDR blob to decode."
hasCopyButton
/>
</Box>
</Card>
<>
{feeBumpedTx.xdr ? (
<ValidationResponseCard
variant="success"
title="Success! Transaction Envelope XDR:"
response={
<Box gap="xs">
<TxResponse
label="Network Passphrase:"
value={network.passphrase}
/>
<TxResponse label="XDR:" value={feeBumpedTx.xdr} />
</Box>
}
note={<></>}
footerLeftEl={
<>
<Button
size="md"
variant="secondary"
onClick={() => {
updateSignImportXdr(feeBumpedTx.xdr);
updateSignActiveView("overview");

router.push("/transaction/sign");
}}
>
Sign in Transaction Signer
</Button>
<Button
size="md"
variant="tertiary"
onClick={() => {
alert("TODO: handle view in xdr flow");
}}
>
View in XDR viewer
</Button>
</>
}
/>
) : null}
</>
<>
{feeBumpedTx.errors.length && innerTxXdr ? (
<ValidationResponseCard
variant="error"
title="Transaction Sign Error:"
response={feeBumpedTx.errors}
/>
) : null}{" "}
</>
</Box>
);
}
10 changes: 5 additions & 5 deletions src/app/(sidebar)/transaction/sign/components/Overview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { FEE_BUMP_TX_FIELDS, TX_FIELDS } from "@/constants/signTransactionPage";

import { useStore } from "@/store/useStore";

import { txSigner } from "@/helpers/txSigner";
import { txHelper } from "@/helpers/txHelper";

import { validate } from "@/validate";

Expand Down Expand Up @@ -102,7 +102,7 @@ export const Overview = () => {
networkPassphrase: string,
hardWalletSigs: xdr.DecoratedSignature[],
) => {
const { xdr, message } = txSigner.signTx({
const { xdr, message } = txHelper.signTx({
txXdr,
signers,
networkPassphrase,
Expand All @@ -128,7 +128,7 @@ export const Overview = () => {

try {
if (selectedHardware === "ledger") {
const { signature, error } = await txSigner.signWithLedger({
const { signature, error } = await txHelper.signWithLedger({
bipPath: sign.bipPath,
transaction: sign.importTx as FeeBumpTransaction | Transaction,
isHash: false,
Expand All @@ -139,7 +139,7 @@ export const Overview = () => {
}

if (selectedHardware === "ledger_hash") {
const { signature, error } = await txSigner.signWithLedger({
const { signature, error } = await txHelper.signWithLedger({
bipPath: sign.bipPath,
transaction: sign.importTx as FeeBumpTransaction | Transaction,
isHash: true,
Expand All @@ -152,7 +152,7 @@ export const Overview = () => {
if (selectedHardware === "trezor") {
const path = `m/${sign.bipPath}`;

const { signature, error } = await txSigner.signWithTrezor({
const { signature, error } = await txHelper.signWithTrezor({
bipPath: path,
transaction: sign.importTx as Transaction,
});
Expand Down
63 changes: 62 additions & 1 deletion src/helpers/txSigner.ts → src/helpers/txHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,66 @@ import { LedgerErrorResponse } from "@/types/types";

import { validate } from "@/validate";

/* Build Transaction related */
export type FeeBumpedTxResponse = {
errors: string[];
xdr: string;
};

const buildFeeBumpTx = ({
innerTxXDR,
maxFee,
sourceAccount,
networkPassphrase,
}: {
innerTxXDR: string;
maxFee: string;
sourceAccount: string;
networkPassphrase: string;
}): FeeBumpedTxResponse => {
const result = {
errors: [] as string[],
xdr: "",
};

let innerTx: Transaction;

try {
innerTx = TransactionBuilder.fromXDR(
innerTxXDR,
networkPassphrase,
) as Transaction;
} catch (e) {
result.errors.push("Invalid inner transaction XDR.");
return result;
}

if (typeof innerTx?.operations === "undefined") {
result.errors.push("Inner transaction must be a regular transaction.");
return result;
}

let feeBumpTx;

try {
feeBumpTx = TransactionBuilder.buildFeeBumpTransaction(
sourceAccount,
maxFee,
innerTx,
networkPassphrase,
);
result.xdr = feeBumpTx.toEnvelope().toXDR("base64");
} catch (err) {
if (err instanceof Error) {
result.errors.push(err.message);
} else {
result.errors.push("Unknown error in fee bump tx");
}
}
return result;
};

/* Sign Transaction related */
interface LedgerApi {
getPublicKey(path: string): Promise<{ publicKey: string }>;
signHash(path: string, hash: Buffer): Promise<{ signature: Buffer }>;
Expand Down Expand Up @@ -219,7 +279,8 @@ const getTrezorDecoratedSignature = (
return [decorated];
};

export const txSigner = {
export const txHelper = {
buildFeeBumpTx,
signTx,
signWithLedger,
signWithTrezor,
Expand Down
31 changes: 31 additions & 0 deletions src/store/createStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ export interface Store {
bipPath: string;
hardWalletSigs: xdr.DecoratedSignature[] | [];
};
feeBump: {
source_account: string;
fee: string;
innerTxXdr: string;
};
// [Transaction] Build Transaction actions
updateBuildActiveTab: (tabId: string) => void;
updateBuildParams: (params: TransactionBuildParamsObj) => void;
Expand All @@ -121,6 +126,10 @@ export interface Store {
updateHardWalletSigs: (signer: xdr.DecoratedSignature[]) => void;
resetSign: () => void;
resetSignHardWalletSigs: () => void;
updateBaseFeeSource: (source: string) => void;
updateBaseFeeBase: (feeBase: string) => void;
updateBaseFeeInnerXdr: (xdr: string) => void;
resetBaseFee: () => void;
};

// XDR
Expand Down Expand Up @@ -175,6 +184,11 @@ const initTransactionState = {
bipPath: "44'/148'/0'",
hardWalletSigs: [],
},
feeBump: {
source_account: "",
fee: "",
innerTxXdr: "",
},
};

const initAccountState = {
Expand Down Expand Up @@ -359,6 +373,22 @@ export const createStore = (options: CreateStoreOptions) =>
state.transaction.sign.hardWalletSigs =
initTransactionState.sign.hardWalletSigs;
}),
updateBaseFeeSource: (source: string) =>
set((state) => {
state.transaction.feeBump.source_account = source;
}),
updateBaseFeeBase: (feeBase: string) =>
set((state) => {
state.transaction.feeBump.fee = feeBase;
}),
updateBaseFeeInnerXdr: (xdr: string) =>
set((state) => {
state.transaction.feeBump.innerTxXdr = xdr;
}),
resetBaseFee: () =>
set((state) => {
state.transaction.feeBump = initTransactionState.feeBump;
}),
},
xdr: {
...initXdrState,
Expand Down Expand Up @@ -395,6 +425,7 @@ export const createStore = (options: CreateStoreOptions) =>
importXdr: true,
bipPath: true,
},
feeBump: true,
},
xdr: {
blob: true,
Expand Down

0 comments on commit 1eb2573

Please sign in to comment.