Skip to content
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

Text and small PNG image request works onchain, Xverse address fetched #34

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 42 additions & 15 deletions apps/web/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,24 @@ function App() {
setIsStarknetConnected(false)
}

// Bitcoin Wallet Connect
const [taprootAddress, setTaprootAddress] = useState<string | null>(null)

const connectBitcoinWalletHandler = async () => {
const addresses = await connectBitcoinWallet()
setBitcoinWallet(addresses)
// TODO: replace with the Ordinals address.
// Currently the sats connect lib fetches
// only the Payments address from Xverse.
if (addresses.paymentAddress) {
setTaprootAddress(addresses.paymentAddress)
setBitcoinWallet((prev) => ({
...prev,
paymentAddress: addresses.paymentAddress,
}))
} else {
console.error('Ordinals address not found in wallet connection')
}
}

// Bitcoin Wallet Disconnect

const disconnectBitcoinWallet = () => {
setBitcoinWallet({ paymentAddress: null, ordinalsAddress: null, stacksAddress: null })
}
Expand All @@ -113,21 +124,23 @@ function App() {
});

const [calls, setCalls] = useState([] as any[])
const requestInscriptionCall = async () => {

const requestInscriptionCall = async (dataToInscribe: string, taprootAddress: string) => {
if (!address || !orderbookContract) {
return
}

const calldata = CallData.compile([
byteArray.byteArrayFromString("message:Hello, Starknet!"),
byteArray.byteArrayFromString("tb1234567890123456789012345678901234567890"),
Number(100),
byteArray.byteArrayFromString(dataToInscribe),
byteArray.byteArrayFromString(taprootAddress),
Number(100), // TODO remove when contract is re-deployed
toHex("STRK"),
uint256.bnToUint256(2000)
]);
setCalls(
[orderbookContract.populate('request_inscription', calldata)]
)
uint256.bnToUint256(2000),
])

setCalls([await orderbookContract.populate('request_inscription', calldata)])
}

const { send, data, isPending } = useSendTransaction({
calls
});
Expand Down Expand Up @@ -174,14 +187,28 @@ function App() {
connectWallet: connectBitcoinWalletHandler,
disconnectWallet: disconnectBitcoinWallet
}}
/> <div className="h-[4.5rem]" />
/>
<div className="h-[4.5rem]" />
<Routes>
{tabs.map((tab) => (
<Route key={tab.path} path={tab.path} element={<tab.component {...tabProps} />} />
<Route
key={tab.path}
path={tab.path}
element={
<tab.component
taprootAddress={taprootAddress}
connectBitcoinWalletHandler={connectBitcoinWalletHandler}
disconnectBitcoinWallet={disconnectBitcoinWallet}
isBitcoinWalletConnected={!!taprootAddress}
{...tabProps}
/>
}
/>
))}
<Route path="/inscription/:id" element={<Inscription {...tabProps} />} />
<Route path="/request/:id" element={<Request {...tabProps} />} />
</Routes>

</div>
)
}
Expand Down
90 changes: 75 additions & 15 deletions apps/web/src/components/inscription/Form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,44 @@ function InscriptionForm(props: any) {

const handleSubmit = async (e: any) => {
e.preventDefault();
if (!uploadedImage) {
setErrorMessage("Please upload an image");

let dataToInscribe = "";

if (selectedOption === "Image") {
if (!uploadedImage) {
setErrorMessage("Please upload an image");
return;
}
const response = await fetch(uploadedImage);
const blob = await response.blob();
const base64Image = await new Promise<string>((resolve, reject) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result as string);
reader.onerror = reject;
reader.readAsDataURL(blob);
});
dataToInscribe = base64Image;
} else if (selectedOption === "Message") {
const textAreaElement = document.querySelector<HTMLTextAreaElement>(".Form__textarea");
dataToInscribe = textAreaElement?.value || "";
if (!dataToInscribe) {
setErrorMessage("Please enter a message to inscribe");
return;
}
}

const taprootAddress = props.taprootAddress;
if (!taprootAddress) {
setErrorMessage("Taproot address not found. Please connect Bitcoin wallet.");
return;
}
await props.requestInscriptionCall();

setErrorMessage("");

await props.requestInscriptionCall(dataToInscribe, taprootAddress);
props.setIsInscribing(true);
};

}
const handleImageUpload = (e: any) => {
e.preventDefault();
if (e.target.files && e.target.files[0]) {
Expand All @@ -39,28 +69,58 @@ function InscriptionForm(props: any) {
<div className="flex-grow Form__input">
{selectedOption === "Image" ? (
<div>
<label className={`text-lg Form__image ${uploadedImage ? "Form__image--grid" : ""}`} htmlFor="image" onDrop={handleImageUpload} onDragOver={handleImgDrag}>
{uploadedImage ? "Image to Inscribe →" : "Upload an Image..."} {uploadedImage && <img className="Form__image__up" src={uploadedImage} alt="uploaded" />}
<label
className={`text-lg Form__image ${uploadedImage ? "Form__image--grid" : ""}`}
htmlFor="image"
onDrop={handleImageUpload}
onDragOver={handleImgDrag}
>
{uploadedImage ? "Image to Inscribe →" : "Upload an Image..."}{" "}
{uploadedImage && <img className="Form__image__up" src={uploadedImage} alt="uploaded" />}
</label>
<input style={{display: 'none'}} type="file" name="image" id="image" accept="image/*" onChange={handleImageUpload}/>
<input
style={{ display: "none" }}
type="file"
name="image"
id="image"
accept="image/*"
onChange={handleImageUpload}
/>
</div>
) : (
<textarea className="text-lg Form__textarea" placeholder="Enter a message to inscribe..." />
<textarea
className="text-lg Form__textarea"
placeholder="Enter a message to inscribe..."
/>
)}
</div>
<div className="flex flex-row items-center justify-center relative">
<DropButton options={dropOptions} selectedOption={selectedOption} setSelectedOption={setSelectedOption} />
<button type="submit" className={`button--gradient button__primary ml-4 ${uploadedImage ? "button__primary--pinging": "button__primary--disabled"}`}>
Inscribe
<DropButton
options={dropOptions}
selectedOption={selectedOption}
setSelectedOption={setSelectedOption}
/>
<button
type="submit"
disabled={
(selectedOption === "Image" && !uploadedImage) || !props.taprootAddress
}
className={`button--gradient button__primary ml-4 ${
(selectedOption === "Image" && uploadedImage) || selectedOption === "Message"
? "button__primary--pinging"
: "button__primary--disabled"
}`}
>
Request Inscription
</button>
{errorMessage &&
{errorMessage && (
<p className="absolute right-0 translate-x-[110%] text-red-500 text-xs">
{errorMessage}
</p>
}
)}
</div>
</form>
);
);
}

export default InscriptionForm;
32 changes: 27 additions & 5 deletions apps/web/src/pages/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,25 @@ import InscriptionStatus from "../components/inscription/Status";
import { Pagination } from "../components/Pagination";
import { getNewInscriptions } from "../api/inscriptions";

function Home(props: any) {
function Home(props: {
requestInscriptionCall: (dataToInscribe: string, taprootAddress: string) => Promise<void>;
taprootAddress: string | null;
connectBitcoinWalletHandler: () => Promise<void>;
disconnectBitcoinWallet: () => void;
isBitcoinWalletConnected: boolean;
}) {
const [isInscribing, setIsInscribing] = useState(false);

const defaultInscription: any[] = [];
const [recentInscriptions, setRecentInscriptions] = useState(defaultInscription);
const [recentsPagination, setRecentsPagination] = useState({
pageLength: 16,
page: 1
page: 1,
});

useEffect(() => {
const fetchInscriptions = async () => {
// TODO fetch real new inscriptions from smart contract
let result = await getNewInscriptions(recentsPagination.pageLength, recentsPagination.page);
if (result.data) {
if (recentsPagination.page === 1) {
Expand All @@ -29,20 +36,35 @@ function Home(props: any) {
setRecentInscriptions([...recentInscriptions, ...newInscriptions]);
}
}
}
};
try {
fetchInscriptions();
} catch (error) {
console.error(error);
}
}, [recentsPagination]);
}, [recentsPagination, isInscribing]);

return (
<div className="w-full flex flex-col h-max">
<div className="bg__color--tertiary w-full flex flex-col items-center justify-center py-8">
<h1 className="text-4xl font-bold">Inscribe on Bitcoin</h1>
<h2 className="text-lg mb-8">Starknet's Decentralized Inscriptor Network</h2>
<InscriptionForm isInscribing={isInscribing} setIsInscribing={setIsInscribing} requestInscriptionCall={props.requestInscriptionCall} />
{!props.isBitcoinWalletConnected && (
<button
className="button--gradient button__primary"
onClick={props.connectBitcoinWalletHandler}
>
Connect Bitcoin Wallet
</button>
)}
{props.isBitcoinWalletConnected && (
<InscriptionForm
isInscribing={isInscribing}
setIsInscribing={setIsInscribing}
requestInscriptionCall={props.requestInscriptionCall}
taprootAddress={props.taprootAddress}
/>
)}
{isInscribing && <InscriptionStatus />}
</div>
<div className="w-full flex flex-col items-center py-2 bg__color--primary h-full border-t-2 border-[var(--color-primary-light)]">
Expand Down
Loading