Skip to content

Commit

Permalink
feat(ord-connect): Add support for magic eden (#218)
Browse files Browse the repository at this point in the history
* Add support for magic eden

* Update error handling for Magic Eden Wallet

Co-authored-by: Benedict Pak <10258208+Nanosync@users.noreply.github.com>

* Remove Unisat comment from Magic Eden Wallet

Co-authored-by: Benedict Pak <10258208+Nanosync@users.noreply.github.com>

* Update icons and wallet name

* Fix linting issues

* Fix border and margin styling

* Fix border styling

* Add support for segwit addresses on Magic Eden Wallet

* Fix modal title

* Minor fixes for PR comments

* Update README

* Update comment

Co-authored-by: Benedict Pak <10258208+Nanosync@users.noreply.github.com>

* Update variable name

Co-authored-by: KevinK <31565174+kevzzsk@users.noreply.github.com>

* Update variable name

Co-authored-by: KevinK <31565174+kevzzsk@users.noreply.github.com>

* Update variable naming and error messages

---------

Co-authored-by: Benedict Pak <10258208+Nanosync@users.noreply.github.com>
Co-authored-by: KevinK <31565174+kevzzsk@users.noreply.github.com>
  • Loading branch information
3 people authored Feb 26, 2024
1 parent f00703b commit b60593b
Show file tree
Hide file tree
Showing 9 changed files with 149 additions and 17 deletions.
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@

## Introduction

**ord-connect** is a React component library that allows you to easily integrate Bitcoin Ordinals & Inscriptions via [Sado Protocol Connections](https://sado.space) with your decentralized application (dApp). We currently support [Unisat](https://unisat.io) and [Xverse](https://www.xverse.app). We stand as the pioneering walletkit to support ordinal-aware transactions, ensuring you never inadvertently spend a rare ordinal!
**ord-connect** is a React component library that allows you to easily integrate Bitcoin Ordinals & Inscriptions via [Sado Protocol Connections](https://sado.space) with your decentralized application (dApp). We currently support [Unisat](https://unisat.io), [Xverse](https://www.xverse.app) and [Magic Eden Wallet](https://wallet.magiceden.io/). We stand as the pioneering walletkit to support ordinal-aware transactions, ensuring you never inadvertently spend a rare ordinal!

## Wallet Feature Support

| Wallet | Ordinal-safety | Inscription-safety |
| ------ | -------------- | ------------------ |
| Unisat |||
| Xverse |||
| Wallet | Ordinal-safety | Inscription-safety |
| ---------- | -------------- | ------------------ |
| Unisat |||
| Xverse |||
| Magic Eden |||

## Quick Start

Expand Down
12 changes: 12 additions & 0 deletions packages/ord-connect/src/assets/magiceden-wallet.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 8 additions & 3 deletions packages/ord-connect/src/components/PostConnectButton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,18 @@ import { Menu, Transition } from "@headlessui/react";

import ChevronDownIcon from "../../assets/chevron-down.svg";
import LogoutIcon from "../../assets/logout.svg";
import MagicEdenIcon from "../../assets/magiceden-wallet.svg";
import UnisatWalletIcon from "../../assets/unisat-wallet.svg";
import XverseWalletIcon from "../../assets/xverse-wallet.svg";
import { useOrdConnect, Wallet } from "../../providers/OrdConnectProvider";
import { truncateMiddle } from "../../utils/text-helper";

const WALLET_TO_ICON: Record<Wallet, string> = {
[Wallet.MAGICEDEN]: MagicEdenIcon,
[Wallet.UNISAT]: UnisatWalletIcon,
[Wallet.XVERSE]: XverseWalletIcon,
} as const;

interface PostConnectButtonProp {
address: string;
network: string;
Expand Down Expand Up @@ -46,9 +53,7 @@ export function PostConnectButton({
/>
)}
<img
src={
wallet === Wallet.XVERSE ? XverseWalletIcon : UnisatWalletIcon
}
src={WALLET_TO_ICON[wallet as Wallet]}
alt={`${wallet} is connected`}
/>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import { useOrdConnect, Wallet } from "../../providers/OrdConnectProvider";
import { truncateMiddle } from "../../utils/text-helper";

const WALLET_TO_NAME: Record<Wallet, string> = {
[Wallet.UNISAT]: "UniSat Wallet",
[Wallet.MAGICEDEN]: "Magic Eden",
[Wallet.UNISAT]: "UniSat",
[Wallet.XVERSE]: "Xverse",
} as const;

Expand Down
89 changes: 88 additions & 1 deletion packages/ord-connect/src/components/SelectWalletModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import {
BrowserWalletNotInstalledError,
BrowserWalletRequestCancelledByUserError,
} from "@ordzaar/ordit-sdk";
import { getAddresses as getMagicEdenAddress } from "@ordzaar/ordit-sdk/magiceden";
import { getAddresses as getUnisatAddresses } from "@ordzaar/ordit-sdk/unisat";
import { getAddresses as getXverseAddresses } from "@ordzaar/ordit-sdk/xverse";

import CloseModalIcon from "../../assets/close-modal.svg";
import MagicEdenWalletIcon from "../../assets/magiceden-wallet.svg";
import UnisatWalletIcon from "../../assets/unisat-wallet.svg";
import XverseWalletIcon from "../../assets/xverse-wallet.svg";
import { useOrdConnect, Wallet } from "../../providers/OrdConnectProvider";
Expand All @@ -24,6 +26,7 @@ interface SelectWalletModalProp {
}

const WALLET_CHROME_EXTENSION_URL: Record<Wallet, string> = {
[Wallet.MAGICEDEN]: "https://wallet.magiceden.io/",
[Wallet.UNISAT]: "https://unisat.io/download", // their www subdomain doesn't work
[Wallet.XVERSE]: "https://www.xverse.app/download",
};
Expand Down Expand Up @@ -72,6 +75,77 @@ export function SelectWalletModal({
[disconnectWallet],
);

const onConnectMagicEdenWallet = useCallback(async () => {
if (network === "testnet") {
const unsupportedNetworkError = new Error(
"Magic Eden wallet is not supported on testnet",
);
onError(Wallet.MAGICEDEN, unsupportedNetworkError);
return false;
}

try {
setErrorMessage("");
const magicEdenAddresses = await getMagicEdenAddress(network);
if (!magicEdenAddresses || magicEdenAddresses.length < 1) {
disconnectWallet();
throw new Error("Magic Eden via Ordit returned no addresses.");
}

// Magic Eden provides a segwit address by default for sending and receiving payments
// Imported xverse wallets will return a p2sh address for payments by default instead
const paymentAddress = magicEdenAddresses.find(
(walletAddress) =>
walletAddress.format === "segwit" ||
walletAddress.format === "p2sh-p2wpkh",
);

if (!paymentAddress) {
throw new Error(
"Magic Eden via Ordit did not return a P2SH or Segwit address.",
);
}

const ordinalsAddress = magicEdenAddresses.find(
(walletAddress) => walletAddress.format === "taproot",
);

if (!ordinalsAddress) {
throw new Error(
"Magic Eden via Ordit did not return a Taproot address.",
);
}

updateAddress({
ordinals: ordinalsAddress.address,
payments: paymentAddress.address,
});
updatePublicKey({
ordinals: ordinalsAddress.publicKey,
payments: paymentAddress.publicKey,
});
updateWallet(Wallet.MAGICEDEN);
updateFormat({
ordinals: ordinalsAddress.format,
payments: paymentAddress.format,
});
closeModal();
return true;
} catch (err) {
onError(Wallet.MAGICEDEN, err as Error);
return false;
}
}, [
closeModal,
disconnectWallet,
network,
onError,
updateAddress,
updateFormat,
updatePublicKey,
updateWallet,
]);

const onConnectUnisatWallet = useCallback(
async ({ readOnly }: { readOnly?: boolean } = {}) => {
try {
Expand Down Expand Up @@ -246,7 +320,7 @@ export function SelectWalletModal({
<section className="panel-title-container">
<Dialog.Title as="h3" className="panel-title">
{isSupportedDevice
? "Choose wallet to connect"
? "Choose Bitcoin wallet to connect"
: "Unsupported device"}
</Dialog.Title>
<button
Expand Down Expand Up @@ -285,6 +359,19 @@ export function SelectWalletModal({
isMobileDevice={isMobile}
renderAvatar={renderAvatar}
/>
<hr className="horizontal-separator" />
{!isMobile && (
<WalletButton
wallet={Wallet.MAGICEDEN}
subtitle="Coming soon on mobile browsing"
onConnect={onConnectMagicEdenWallet}
icon={MagicEdenWalletIcon}
setErrorMessage={setErrorMessage}
isDisabled={isMobile}
isMobileDevice={isMobile}
renderAvatar={renderAvatar}
/>
)}
</section>
) : (
<Dialog.Description className="unsupported-browser-message">
Expand Down
12 changes: 7 additions & 5 deletions packages/ord-connect/src/components/SelectWalletModal/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,6 @@
}

.ord-connect-wallet-modal .wallet-option-button {
margin-bottom: 8px;
display: flex;
align-items: center;
background: transparent;
Expand All @@ -132,7 +131,6 @@
font-size: 18px;
font-weight: 600;
line-height: 24px;
border-bottom: 0.5px solid rgba(255, 255, 255, 0.1);
width: 100%;
cursor: pointer;
}
Expand Down Expand Up @@ -203,14 +201,18 @@
}
}

.ord-connect-wallet-modal .wallet-option-button:nth-of-type(2) {
.ord-connect-wallet-modal .wallet-option-button:last-child {
padding: 8px 16px 16px 16px;
border: 0px;
margin-bottom: 0px;
border-radius: 0px 0px 20px 20px;
}

.ord-connect-wallet-modal .wallet-option-button:last-child {
padding: 8px 16px 16px 16px;
.ord-connect-wallet-modal .wallet-option-button:only-child {
padding: 8px 16px 8px 16px;
border: 0px;
margin-bottom: 0px;
border-radius: 20px 20px 20px 20px;
}

.ord-connect-wallet-modal .wallet-icon {
Expand Down
6 changes: 6 additions & 0 deletions packages/ord-connect/src/lib/signMessage.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { signMessage as signMagicEdenMessage } from "@ordzaar/ordit-sdk/magiceden";
import { signMessage as signUnisatMessage } from "@ordzaar/ordit-sdk/unisat";
import { signMessage as signXverseMessage } from "@ordzaar/ordit-sdk/xverse";

Expand All @@ -22,6 +23,11 @@ export default async function signMessage({
address,
network,
}: SignMessageParams): Promise<string | null> {
if (wallet === Wallet.MAGICEDEN) {
const { base64 } = await signMagicEdenMessage(message, address, network);
return base64;
}

if (wallet === Wallet.UNISAT) {
const { base64 } = await signUnisatMessage(message, "bip322-simple");
return base64;
Expand Down
21 changes: 19 additions & 2 deletions packages/ord-connect/src/lib/signPsbt.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { signPsbt as signMagicEdenPsbt } from "@ordzaar/ordit-sdk/magiceden";
import { signPsbt as signUnisatPsbt } from "@ordzaar/ordit-sdk/unisat";
import { signPsbt as signXversePsbt } from "@ordzaar/ordit-sdk/xverse";
import { Psbt } from "bitcoinjs-lib";
Expand Down Expand Up @@ -40,6 +41,24 @@ export default async function signPsbt({
}: SignPsbtParams): Promise<SerializedPsbt> {
const finalize = options?.finalize ?? true;
const extractTx = options?.extractTx ?? true;
const getAllInputIndices = () =>
psbt.data.inputs.map((value, index) => index);

if (wallet === Wallet.MAGICEDEN) {
const signedMagicEdenPsbt = await signMagicEdenPsbt(psbt, {
network,
inputsToSign: [
{
address,
signingIndexes: options?.signingIndexes ?? getAllInputIndices(), // If signingIndexes is not provided, just sign everything
sigHash: options?.sigHash,
},
],
finalize,
extractTx,
});
return signedMagicEdenPsbt;
}

if (wallet === Wallet.UNISAT) {
const signedUnisatPsbt = await signUnisatPsbt(psbt, {
Expand All @@ -50,8 +69,6 @@ export default async function signPsbt({
}

if (wallet === Wallet.XVERSE) {
const getAllInputIndices = () =>
psbt.data.inputs.map((value, index) => index);
const signedXversePsbt = await signXversePsbt(psbt, {
network,
inputsToSign: [
Expand Down
1 change: 1 addition & 0 deletions packages/ord-connect/src/providers/OrdConnectProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export enum Network {
export enum Wallet {

Check warning on line 18 in packages/ord-connect/src/providers/OrdConnectProvider.tsx

View workflow job for this annotation

GitHub Actions / Lint - Typescript and ESLint

Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components

Check warning on line 18 in packages/ord-connect/src/providers/OrdConnectProvider.tsx

View workflow job for this annotation

GitHub Actions / Build (Apps & Packages)

Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
UNISAT = "unisat",
XVERSE = "xverse",
MAGICEDEN = "magiceden",
}

export interface BiAddress<T> {
Expand Down

0 comments on commit b60593b

Please sign in to comment.