Skip to content

Commit 874776c

Browse files
Adds ledger hash to the UI & warning (#44)
1 parent 2b36baa commit 874776c

File tree

6 files changed

+67
-4
lines changed

6 files changed

+67
-4
lines changed

app/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"@mysten/dapp-kit": "0.19.5",
1919
"@mysten/sagat": "workspace:*",
2020
"@mysten/sui": "^1.43.0",
21+
"@noble/hashes": "^2.0.1",
2122
"@radix-ui/react-accordion": "^1.2.12",
2223
"@radix-ui/react-dialog": "^1.1.15",
2324
"@radix-ui/react-slot": "^1.2.3",

app/src/components/ProposalSheet.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ export function ProposalSheet({
237237
{isDryRunSuccessful ? (
238238
<EffectsPreview
239239
output={dryRunMutation.data}
240+
bytes={transactionData}
240241
/>
241242
) : (
242243
<p className="text-sm text-red-600">

app/src/components/preview-effects/EffectsPreview.tsx

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
import { type DryRunTransactionBlockResponse } from '@mysten/sui/client';
2-
import { useState } from 'react';
2+
import { messageWithIntent } from '@mysten/sui/cryptography';
3+
import { fromBase64, toHex } from '@mysten/sui/utils';
4+
import { blake2b } from '@noble/hashes/blake2.js';
5+
import { useMemo, useState } from 'react';
36

47
import { Label } from '@/components/ui/label';
58

69
import { cn } from '../../lib/utils';
10+
import { Alert } from '../ui/Alert';
711
import { Textarea } from '../ui/textarea';
812
import { BalanceChanges } from './partials/BalanceChanges';
913
import { Events } from './partials/Events';
@@ -13,15 +17,35 @@ import { Transactions } from './partials/Transactions';
1317

1418
export function EffectsPreview({
1519
output,
20+
bytes,
1621
}: {
1722
output: DryRunTransactionBlockResponse;
23+
bytes?: string;
1824
}) {
1925
const [activeTab, setActiveTab] = useState(
2026
'balance-changes',
2127
);
2228

2329
const { objectChanges, balanceChanges } = output;
2430

31+
// Compute the blake2b hash (ledger transaction hash)
32+
const ledgerTransactionHash = useMemo(() => {
33+
if (!bytes) return null;
34+
// Decode the base64-encoded transaction bytes
35+
const decodedBytes = fromBase64(bytes);
36+
const intentMessage = messageWithIntent(
37+
'TransactionData',
38+
decodedBytes,
39+
);
40+
const intentMessageDigest = blake2b(intentMessage, {
41+
dkLen: 32,
42+
});
43+
const intentMessageDigestHex = toHex(
44+
intentMessageDigest,
45+
);
46+
return `0x${intentMessageDigestHex}`;
47+
}, [bytes]);
48+
2549
const tabs = [
2650
{
2751
id: 'balance-changes',
@@ -79,6 +103,28 @@ export function EffectsPreview({
79103
<div className="space-y-4">
80104
<Overview output={output} />
81105

106+
{/* Ledger Hash */}
107+
{ledgerTransactionHash && (
108+
<div className="border rounded p-3 bg-gray-50">
109+
<div className="flex items-center justify-between gap-2">
110+
<span className="text-sm font-medium text-gray-700">
111+
Ledger Hash:
112+
</span>
113+
<span className="font-mono text-xs break-all">
114+
{ledgerTransactionHash}
115+
</span>
116+
</div>
117+
</div>
118+
)}
119+
120+
{/* Warning Alert */}
121+
<Alert variant="warning">
122+
<strong>Important:</strong> You should always
123+
validate the transaction details in your wallet
124+
before signing. Your wallet is the ultimate source
125+
of truth for what you're approving.
126+
</Alert>
127+
82128
{/* Tab Navigation */}
83129
<div className="w-full">
84130
<div className="flex overflow-x-auto border-b">

app/src/components/proposals/ProposalPreview.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,10 @@ export function ProposalPreview({
126126
: 'border-red-200 bg-white'
127127
}`}
128128
>
129-
<EffectsPreview output={dryRunMutation.data} />
129+
<EffectsPreview
130+
output={dryRunMutation.data}
131+
bytes={proposal.transactionBytes}
132+
/>
130133
</div>
131134
)}
132135

app/src/components/tools/SigningTool.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,13 @@ function PreviewResult({
5858
data,
5959
error,
6060
isLoading,
61+
bytes,
6162
}: {
6263
isSuccess: boolean;
6364
data: DryRunTransactionBlockResponse | undefined;
6465
error: Error | null;
6566
isLoading: boolean;
67+
bytes?: string;
6668
}) {
6769
if (isLoading) {
6870
return (
@@ -103,7 +105,7 @@ function PreviewResult({
103105
)}
104106
</div>
105107
{isSuccess && data ? (
106-
<EffectsPreview output={data} />
108+
<EffectsPreview output={data} bytes={bytes} />
107109
) : (
108110
<div className="space-y-3">
109111
<p className="text-sm text-red-600 whitespace-pre-wrap">
@@ -295,6 +297,7 @@ export default function SigningTool() {
295297
data={dryRunMutation.data}
296298
error={dryRunMutation.error}
297299
isLoading={dryRunMutation.isPending}
300+
bytes={transactionData}
298301
/>
299302
)}
300303

bun.lock

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
"@mysten/dapp-kit": "0.19.5",
4040
"@mysten/sagat": "workspace:*",
4141
"@mysten/sui": "^1.43.0",
42+
"@noble/hashes": "^2.0.1",
4243
"@radix-ui/react-accordion": "^1.2.12",
4344
"@radix-ui/react-dialog": "^1.1.15",
4445
"@radix-ui/react-slot": "^1.2.3",
@@ -268,7 +269,7 @@
268269

269270
"@noble/curves": ["@noble/[email protected]", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-2bKONnuM53lINoDrSmK8qP8W271ms7pygDhZt4SiLOoLwBtoHqeCFi6RG42V8zd3mLHuJFhU/Bmaqo4nX0/kBw=="],
270271

271-
"@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="],
272+
"@noble/hashes": ["@noble/hashes@2.0.1", "", {}, "sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw=="],
272273

273274
"@nodelib/fs.scandir": ["@nodelib/[email protected]", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
274275

@@ -1066,6 +1067,14 @@
10661067

10671068
"@isaacs/cliui/wrap-ansi": ["[email protected]", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="],
10681069

1070+
"@mysten/sui/@noble/hashes": ["@noble/[email protected]", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="],
1071+
1072+
"@noble/curves/@noble/hashes": ["@noble/[email protected]", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="],
1073+
1074+
"@scure/bip32/@noble/hashes": ["@noble/[email protected]", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="],
1075+
1076+
"@scure/bip39/@noble/hashes": ["@noble/[email protected]", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="],
1077+
10691078
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/[email protected]", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-zq/ay+9fNIJJtJiZxdTnXS20PllcYMX3OE23ESc4HK/bdYu3cOWYVhsOhVnXALfU/uqJIxn5NBPd9z4v+SfoSg=="],
10701079

10711080
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/[email protected]", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-obtUmAHTMjll499P+D9A3axeJFlhdjOWdKUNs/U6QIGT7V5RjcUW1xToAzjvmgTSQhDbYn/NwfTRoJcQ2rNBxA=="],

0 commit comments

Comments
 (0)