-
Notifications
You must be signed in to change notification settings - Fork 36
feat: demo features script deloyer #466
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
8aeff65
chore: demo features script deloyer
ashuralyk c821a4f
fix(lint): solve lint issue
ashuralyk 20a5c52
feat: list all type_id cells and display them inside a big button per…
ashuralyk b0e5e67
chore: make big button display elegancely
ashuralyk eaa1868
chore: tiny changes
ashuralyk cb548bd
chore: seperate file uploader and tx confirmation modal from script d…
ashuralyk b4d84b3
refact: refactor module
ashuralyk cbc806f
chore: refresh big buttons after transaction confirmed
ashuralyk 972136f
bug: modify FileUpload and TxConfigm with default modifier
ashuralyk 00547a5
chore: format file name
ashuralyk 925a80e
chore: make message box automatically unfolded
ashuralyk d305adc
chore: fix issues reported by gemini
ashuralyk 4515bab
chore: solve lint error
ashuralyk 88d24b8
Solve ugly code logic
ashuralyk 0aafb2f
feat(demo): redesign UI of script deployer
Hanssen0 c99230f
refactor(demo): migrate to @ckb-ccc/type-id
Hanssen0 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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
198 changes: 198 additions & 0 deletions
198
packages/demo/src/app/connected/(tools)/DeployScript/deployComponents.tsx
This file contains hidden or 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,198 @@ | ||
| "use client"; | ||
|
|
||
| import { Button } from "@/src/components/Button"; | ||
| import { Message } from "@/src/components/Message"; | ||
| import { useGetExplorerLink } from "@/src/utils"; | ||
| import { ccc } from "@ckb-ccc/connector-react"; | ||
| import { FileCode, X } from "lucide-react"; | ||
| import type { DeployResult } from "./deployLogic"; | ||
|
|
||
| function formatCellCreationDate(timestampMs: number): string { | ||
| try { | ||
| return new Date(timestampMs).toLocaleString(undefined, { | ||
| year: "numeric", | ||
| month: "short", | ||
| day: "numeric", | ||
| hour: "2-digit", | ||
| minute: "2-digit", | ||
| }); | ||
| } catch { | ||
| return ""; | ||
| } | ||
| } | ||
|
|
||
| export function TypeIdCellListItem({ | ||
| cell, | ||
| index, | ||
| onSelect, | ||
| isSelected, | ||
| creationTimestamp, | ||
| }: { | ||
| cell: ccc.Cell; | ||
| index: number; | ||
| onSelect: () => void; | ||
| isSelected: boolean; | ||
| creationTimestamp?: number; | ||
| }) { | ||
| const outPoint = `${cell.outPoint.txHash}:${cell.outPoint.index}`; | ||
|
|
||
| return ( | ||
| <button | ||
| type="button" | ||
| onClick={onSelect} | ||
| className={`flex w-full flex-col gap-2 rounded-lg border p-4 text-left text-sm transition-colors hover:border-purple-300 hover:bg-purple-50/50 ${ | ||
| isSelected | ||
| ? "border-purple-400 bg-purple-50" | ||
| : "border-gray-200 bg-white" | ||
| }`} | ||
| > | ||
| <div className="flex flex-wrap items-center justify-between gap-2"> | ||
| <div className="flex items-center gap-2 font-semibold text-gray-700"> | ||
| <span>#{index + 1}</span> | ||
| <span>Type ID</span> | ||
| </div> | ||
| <span className="text-xs text-gray-600"> | ||
| <span className="font-medium">Occupied / Capacity:</span>{" "} | ||
| {ccc.fixedPointToString(ccc.fixedPointFrom(cell.occupiedSize))} /{" "} | ||
| {ccc.fixedPointToString(cell.cellOutput.capacity)} CKB | ||
| </span> | ||
| </div> | ||
| <div className="flex flex-wrap items-center justify-between gap-x-4 gap-y-1"> | ||
| <span | ||
| className="min-w-0 truncate font-mono text-xs text-gray-600" | ||
| title={outPoint} | ||
| > | ||
| <span className="font-sans font-medium">Out Point:</span> {outPoint} | ||
| </span> | ||
| <span className="shrink-0 text-xs text-gray-500"> | ||
| {creationTimestamp == null | ||
| ? "Unavailable" | ||
| : formatCellCreationDate(creationTimestamp)} | ||
| </span> | ||
| </div> | ||
| </button> | ||
| ); | ||
| } | ||
|
|
||
| export function CellFoundSection({ | ||
| foundCell, | ||
| onClear, | ||
| }: { | ||
| foundCell: ccc.Cell; | ||
| onClear: () => void; | ||
| }) { | ||
| const { explorerTransaction } = useGetExplorerLink(); | ||
| const typeScript = foundCell.cellOutput.type; | ||
| const typeId = typeScript?.args; | ||
|
|
||
| return ( | ||
| <div> | ||
| <div className="flex items-center justify-between text-gray-800"> | ||
| <div className="flex items-center gap-2"> | ||
| <FileCode className="h-5 w-5 text-purple-500" /> | ||
| <p className="font-semibold">Cell to Update</p> | ||
| </div> | ||
| <button | ||
| type="button" | ||
| onClick={onClear} | ||
| className="rounded-full p-1 text-gray-400 transition-colors hover:text-gray-700" | ||
| aria-label="Clear selected cell" | ||
| title="Clear selection" | ||
| > | ||
| <X className="h-5 w-5" /> | ||
| </button> | ||
| </div> | ||
| <div className="mt-3 space-y-1 text-sm text-gray-700"> | ||
| <p> | ||
| <span className="font-medium">Out Point:</span>{" "} | ||
| {explorerTransaction( | ||
| foundCell.outPoint.txHash, | ||
| `${foundCell.outPoint.txHash}:${foundCell.outPoint.index}`, | ||
| )} | ||
| </p> | ||
| <p> | ||
| <span className="font-medium">Occupied / Capacity:</span>{" "} | ||
| {ccc.fixedPointToString(ccc.fixedPointFrom(foundCell.occupiedSize))} /{" "} | ||
| {ccc.fixedPointToString(foundCell.cellOutput.capacity)} CKB | ||
| </p> | ||
| <p> | ||
| <span className="font-medium">Data Hash:</span>{" "} | ||
| <span className="font-mono break-all"> | ||
| {ccc.hashCkb(foundCell.outputData ?? "0x")} | ||
| </span> | ||
| </p> | ||
| {typeScript && ( | ||
| <p> | ||
| <span className="font-medium">Type Hash:</span>{" "} | ||
| <span className="font-mono break-all">{typeScript.hash()}</span> | ||
| </p> | ||
| )} | ||
| {typeId && typeId !== "0x" && ( | ||
| <p> | ||
| <span className="font-medium">Type ID:</span>{" "} | ||
| <span className="font-mono break-all">{typeId}</span> | ||
| </p> | ||
| )} | ||
| </div> | ||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| export function DeploymentResultSection({ | ||
| result, | ||
| }: { | ||
| result: DeployResult & { | ||
| immutable: boolean; | ||
| action: "deployed" | "updated"; | ||
| }; | ||
| }) { | ||
| const { explorerTransaction } = useGetExplorerLink(); | ||
| const outPoint = `${result.txHash}:0`; | ||
|
|
||
| return ( | ||
| <Message | ||
| title={`Cell ${result.action === "deployed" ? "Deployed" : "Updated"}`} | ||
| type="success" | ||
| expandable={false} | ||
| > | ||
| <div className="space-y-1 text-sm text-gray-700"> | ||
| <p> | ||
| <span className="font-medium">Out Point:</span>{" "} | ||
| {explorerTransaction(result.txHash, outPoint)} | ||
| </p> | ||
| <p> | ||
| <span className="font-medium">Type ID:</span>{" "} | ||
| <span className="font-mono break-all">{result.typeId}</span> | ||
| </p> | ||
| <p> | ||
| <span className="font-medium">Data Hash:</span>{" "} | ||
| <span className="font-mono break-all">{result.dataHash}</span> | ||
| </p> | ||
| {result.immutable && ( | ||
| <p className="text-green-700"> | ||
| This cell is immutable and can never be updated. | ||
| </p> | ||
| )} | ||
| </div> | ||
| </Message> | ||
| ); | ||
| } | ||
|
|
||
| export function BurnButton({ | ||
| onClick, | ||
| disabled, | ||
| }: { | ||
| onClick: () => void; | ||
| disabled?: boolean; | ||
| }) { | ||
| return ( | ||
| <Button | ||
| variant="danger" | ||
| className="ml-2" | ||
| onClick={onClick} | ||
| disabled={disabled} | ||
| > | ||
| Burn | ||
| </Button> | ||
| ); | ||
| } | ||
81 changes: 81 additions & 0 deletions
81
packages/demo/src/app/connected/(tools)/DeployScript/deployLogic.ts
This file contains hidden or 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,81 @@ | ||
| import { readFileAsBytes } from "@/src/app/utils/(tools)/FileUpload/page"; | ||
| import { ccc } from "@ckb-ccc/connector-react"; | ||
| import { createTypeId, transferTypeId } from "@ckb-ccc/type-id"; | ||
| import { ReactNode } from "react"; | ||
| import { createImmutableLock } from "./helpers"; | ||
|
|
||
| export type Logger = (...args: ReactNode[]) => void; | ||
| export type DeployResult = { | ||
| txHash: string; | ||
| typeId: string; | ||
| dataHash: string; | ||
| }; | ||
|
|
||
| export async function runDeploy( | ||
| signer: ccc.Signer, | ||
| file: File, | ||
| immutable: boolean, | ||
| foundCell: ccc.Cell | null, | ||
| log: Logger, | ||
| ): Promise<DeployResult> { | ||
| const fileBytes = (await readFileAsBytes(file)) as ccc.Bytes; | ||
|
|
||
| let tx: ccc.Transaction; | ||
| let typeIdArgsValue: string; | ||
|
|
||
| if (foundCell) { | ||
| const typeId = foundCell.cellOutput.type?.args; | ||
| if (!typeId) { | ||
| throw new Error("Selected cell does not have a Type ID"); | ||
| } | ||
| log("Updating existing Type ID cell..."); | ||
|
|
||
| ({ tx } = await transferTypeId({ | ||
| client: signer.client, | ||
| id: typeId, | ||
| receiver: immutable ? createImmutableLock() : foundCell.cellOutput.lock, | ||
| data: fileBytes, | ||
| })); | ||
| typeIdArgsValue = typeId; | ||
| } else { | ||
| log("Building transaction..."); | ||
| const created = await createTypeId({ | ||
| signer, | ||
| data: fileBytes, | ||
| receiver: immutable ? createImmutableLock() : undefined, | ||
| }); | ||
| tx = created.tx; | ||
| typeIdArgsValue = created.id; | ||
| log("Type ID created:", typeIdArgsValue); | ||
| } | ||
|
|
||
| await tx.completeFeeBy(signer); | ||
| log("Sending transaction..."); | ||
| const txHash = await signer.sendTransaction(tx); | ||
| log("Transaction sent:", txHash); | ||
| return { | ||
| txHash, | ||
| typeId: typeIdArgsValue, | ||
| dataHash: ccc.hashCkb(fileBytes), | ||
| }; | ||
| } | ||
|
|
||
| /** Burn the selected type_id cell: consume it and send capacity back to the lock (no type script). */ | ||
| export async function runBurn( | ||
| signer: ccc.Signer, | ||
| foundCell: ccc.Cell, | ||
| log: Logger, | ||
| ): Promise<string | null> { | ||
| const { lock } = foundCell.cellOutput; | ||
| const tx = ccc.Transaction.from({ | ||
| inputs: [{ previousOutput: foundCell.outPoint }], | ||
| outputs: [{ lock, capacity: ccc.Zero }], | ||
| outputsData: ["0x"], | ||
| }); | ||
| await tx.addCellDepsOfKnownScripts(signer.client, ccc.KnownScript.TypeId); | ||
| await tx.completeFeeChangeToOutput(signer, 0); | ||
| log("Sending burn transaction..."); | ||
| const txHash = await signer.sendTransaction(tx); | ||
| log("Transaction sent:", txHash); | ||
| return txHash; | ||
| } |
16 changes: 16 additions & 0 deletions
16
packages/demo/src/app/connected/(tools)/DeployScript/helpers.ts
This file contains hidden or 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 { ccc } from "@ckb-ccc/connector-react"; | ||
|
|
||
| /** Normalize Type ID args (strip 0x, trim). */ | ||
| export function normalizeTypeIdArgs(args: string): string { | ||
| const s = (args || "").trim(); | ||
| return s.startsWith("0x") ? s.slice(2) : s; | ||
| } | ||
|
|
||
| /** Create an unspendable lock script for immutable cells. */ | ||
| export function createImmutableLock(): ccc.Script { | ||
| return ccc.Script.from({ | ||
| codeHash: `0x${"00".repeat(32)}`, | ||
| hashType: "data", | ||
| args: "0x", | ||
| }); | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Add
aria-pressedto the cell selection button.TypeIdCellListItemsignals selection only through border and background colors. Screen-reader users receive no selection state. The list is the primary control for choosing which cell to update or burn, so the state matters.♿ Proposed change
<button type="button" onClick={onSelect} + aria-pressed={isSelected} className={`flex w-full flex-col gap-2 rounded-lg border p-4 text-left text-sm transition-colors hover:border-purple-300 hover:bg-purple-50/50 ${📝 Committable suggestion
🤖 Prompt for AI Agents