-
Notifications
You must be signed in to change notification settings - Fork 100
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Submit Tx] Add a Submit Tx Page (#879)
- Loading branch information
1 parent
ae1c715
commit d45c33d
Showing
9 changed files
with
328 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
75 changes: 75 additions & 0 deletions
75
src/app/(sidebar)/transaction/submit/components/ErrorResponse.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import { | ||
AccountRequiresMemoError, | ||
BadResponseError, | ||
NetworkError, | ||
} from "@stellar/stellar-sdk"; | ||
|
||
import { Box } from "@/components/layout/Box"; | ||
import { TxResponse } from "@/components/TxResponse"; | ||
import { ValidationResponseCard } from "@/components/ValidationResponseCard"; | ||
|
||
interface ErrorProps { | ||
error: NetworkError & { | ||
response: { | ||
data?: { | ||
extras?: { | ||
result_codes?: string; | ||
result_xdr?: string; | ||
}; | ||
}; | ||
}; | ||
}; | ||
} | ||
|
||
export const ErrorResponse = ({ error }: ErrorProps) => { | ||
let message = "", | ||
extras = null; | ||
if (error instanceof AccountRequiresMemoError) { | ||
message = "This destination requires a memo."; | ||
extras = ( | ||
<Box gap="xs"> | ||
<TxResponse label="Destination account:" value={error.accountId} /> | ||
<TxResponse label="Operation index:" value={error.operationIndex} /> | ||
</Box> | ||
); | ||
} else if ( | ||
error?.response && | ||
error.response.data?.extras?.result_codes && | ||
error.response.data?.extras.result_xdr | ||
) { | ||
const { result_codes, result_xdr } = error.response.data.extras; | ||
message = error.message; | ||
extras = ( | ||
<Box gap="xs"> | ||
<TxResponse | ||
label="extras.result_codes:" | ||
value={JSON.stringify(result_codes)} | ||
/> | ||
|
||
<TxResponse label="Result XDR:" value={result_xdr} /> | ||
</Box> | ||
); | ||
} else { | ||
message = | ||
error instanceof BadResponseError | ||
? "Received a bad response when submitting." | ||
: "An unknown error occurred."; | ||
extras = ( | ||
<Box gap="xs"> | ||
<TxResponse | ||
label="original error:" | ||
value={JSON.stringify(error, null, 2)} | ||
/> | ||
</Box> | ||
); | ||
} | ||
|
||
return ( | ||
<ValidationResponseCard | ||
variant="error" | ||
title="Transaction failed!" | ||
subtitle={message} | ||
response={extras} | ||
/> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,163 @@ | ||
"use client"; | ||
|
||
import { useState } from "react"; | ||
import { Button, Card, Text } from "@stellar/design-system"; | ||
import { Horizon, TransactionBuilder } from "@stellar/stellar-sdk"; | ||
|
||
import { useStore } from "@/store/useStore"; | ||
|
||
import * as StellarXdr from "@/helpers/StellarXdr"; | ||
|
||
import { useIsXdrInit } from "@/hooks/useIsXdrInit"; | ||
|
||
import { TransactionResponse, useSubmitTx } from "@/query/useSubmitTx"; | ||
|
||
import { Box } from "@/components/layout/Box"; | ||
import { PrettyJson } from "@/components/PrettyJson"; | ||
import { XdrPicker } from "@/components/FormElements/XdrPicker"; | ||
import { ValidationResponseCard } from "@/components/ValidationResponseCard"; | ||
import { TxResponse } from "@/components/TxResponse"; | ||
import { ErrorResponse } from "./components/ErrorResponse"; | ||
|
||
export default function SubmitTransaction() { | ||
return <div>Submit Transaction</div>; | ||
const { network, xdr } = useStore(); | ||
const { blob, updateXdrBlob } = xdr; | ||
|
||
const [txErr, setTxErr] = useState<any | null>(null); | ||
const [txResponse, setTxResponse] = useState<TransactionResponse | null>( | ||
null, | ||
); | ||
|
||
const isXdrInit = useIsXdrInit(); | ||
const submitTx = useSubmitTx(); | ||
|
||
const onSubmit = () => { | ||
const transaction = TransactionBuilder.fromXDR(blob, network.passphrase); | ||
|
||
const server = new Horizon.Server(network.horizonUrl, { | ||
appName: "Laboratory", | ||
}); | ||
|
||
submitTx.mutate( | ||
{ transaction, server }, | ||
{ | ||
onSuccess: (res) => setTxResponse(res), | ||
onError: (res) => setTxErr(res), | ||
}, | ||
); | ||
}; | ||
|
||
const getXdrJson = () => { | ||
const xdrType = "TransactionEnvelope"; | ||
|
||
if (!(isXdrInit && blob)) { | ||
return null; | ||
} | ||
|
||
try { | ||
const xdrJson = StellarXdr.decode(xdrType, blob); | ||
|
||
return { | ||
jsonString: xdrJson, | ||
error: "", | ||
}; | ||
} catch (e) { | ||
return { | ||
jsonString: "", | ||
error: `Unable to decode input as ${xdrType}`, | ||
}; | ||
} | ||
}; | ||
|
||
const xdrJson = getXdrJson(); | ||
|
||
return ( | ||
<Box gap="md"> | ||
<div className="PageHeader"> | ||
<Text size="md" as="h1" weight="medium"> | ||
Submit Transaction | ||
</Text> | ||
</div> | ||
<Card> | ||
<Box gap="md"> | ||
<XdrPicker | ||
id="submit-tx-xdr" | ||
label="Input a base-64 encoded TransactionEnvelope:" | ||
value={blob} | ||
error={xdrJson?.error || ""} | ||
onChange={(e) => { | ||
updateXdrBlob(e.target.value); | ||
}} | ||
note="Enter a base-64 encoded XDR blob to decode." | ||
hasCopyButton | ||
/> | ||
|
||
<div className="SignTx__CTA"> | ||
<Button | ||
disabled={!blob || Boolean(xdrJson?.error)} | ||
isLoading={submitTx.status === "pending"} | ||
size="md" | ||
variant={"secondary"} | ||
onClick={onSubmit} | ||
> | ||
Submit transaction | ||
</Button> | ||
</div> | ||
|
||
<Box gap="lg" direction="row" align="center" justify="end"> | ||
<div> | ||
{xdrJson?.jsonString ? ( | ||
<div className="Tabs"> | ||
<div className="Tab" data-is-active="true"> | ||
JSON | ||
</div> | ||
</div> | ||
) : null} | ||
</div> | ||
</Box> | ||
|
||
<> | ||
{xdrJson?.jsonString ? ( | ||
<div className="PageBody__content PageBody__scrollable"> | ||
<PrettyJson json={JSON.parse(xdrJson.jsonString)} /> | ||
</div> | ||
) : null} | ||
</> | ||
</Box> | ||
</Card> | ||
<> | ||
{submitTx.status === "success" && txResponse ? ( | ||
<ValidationResponseCard | ||
variant="success" | ||
title="Transaction submitted!" | ||
subtitle={`Transaction succeeded with ${txResponse.operation_count} operation(s)`} | ||
response={ | ||
<Box gap="xs"> | ||
<TxResponse label="Hash:" value={txResponse.hash} /> | ||
<TxResponse label="Ledger number:" value={txResponse.ledger} /> | ||
<TxResponse | ||
label="Paging token:" | ||
value={txResponse.paging_token} | ||
/> | ||
<TxResponse label="Result XDR:" value={txResponse.result_xdr} /> | ||
<TxResponse | ||
label="Result Meta XDR:" | ||
value={txResponse.result_meta_xdr} | ||
/> | ||
<TxResponse | ||
label="Fee Meta XDR:" | ||
value={txResponse.fee_meta_xdr} | ||
/> | ||
</Box> | ||
} | ||
/> | ||
) : null} | ||
</> | ||
<> | ||
{submitTx.status === "error" && txErr ? ( | ||
<ErrorResponse error={txErr} /> | ||
) : null} | ||
</> | ||
</Box> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import "./styles.scss"; | ||
|
||
import { Box } from "@/components/layout/Box"; | ||
|
||
export const TxResponse = ({ | ||
label, | ||
value, | ||
}: { | ||
label: string; | ||
value: string | number; | ||
}) => ( | ||
<Box gap="xs"> | ||
<div>{label}</div> | ||
<div className="TxResponse__value">{value}</div> | ||
</Box> | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
@use "../../styles/utils.scss" as *; | ||
|
||
.TxResponse { | ||
&__value { | ||
margin-left: var(--sds-gap-sm); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { useEffect, useState } from "react"; | ||
import * as StellarXdr from "@/helpers/StellarXdr"; | ||
|
||
export const useIsXdrInit = () => { | ||
const [isReady, setIsReady] = useState(false); | ||
|
||
useEffect(() => { | ||
// Stellar XDR init | ||
const init = async () => { | ||
await StellarXdr.init(); | ||
setIsReady(true); | ||
}; | ||
|
||
init(); | ||
}, []); | ||
|
||
return isReady; | ||
}; |
Oops, something went wrong.