Fractal React SDK
Before Integrating
You will need to provide us with a list of allowed origins (e.g. http://localhost (opens in a new tab), https://your.game.com (opens in a new tab)). Please reach out to us at developers@fractal.is and let us know the origins you would like to allow when you are ready to start integrating.
Installation
npm install @fractalwagmi/react-sdk
Example
SDK Demo Preview (opens in a new tab)
Changelog
A full version changelog is available in the changelog.
Setup and Authentication
1. Import the global stylesheet
If using the provided default button rather than your own UI, import our global stylesheet to ensure that the component renders correctly:
import '@fractalwagmi/react-sdk/styles.css';
2. Set up the provider
Render the provider above any components that need access to data from the SDK.
import { FractalProvider } from "@fractalwagmi/react-sdk";
const App = () => {
return <FractalProvider clientId="YOUR_CLIENT_ID">...</FractalProvider>;
};
3A. Render the SignInWithFractal
component
This will display a button for logging in.
import {
Scope,
SignInWithFractal,
User,
FractalSDKError,
} from "@fractalwagmi/react-sdk";
export function YourSignInComponent() {
return (
<SignInWithFractal
// `scopes` defaults to [Scope.IDENTIFY].
scopes={[Scope.IDENTIFY, Scope.ITEMS_READ, Scope.COINS_READ]}
onError={(err: FractalSDKError) => {
console.log(err.getUserFacingErrorMessage());
}}
onSuccess={(user: User) => {
console.log("user = ", user);
}}
/>
);
}
SignInWithFractal Props
Prop | Type / Description | Default |
---|---|---|
buttonProps | HTMLAttributes<HTMLButtonElement> Any additional props for <button> that should be passed to the default sign-in button. | {} |
onError | (e: FractalSDKError) => void A callback function to call when an error occurs. | undefined |
onSuccess | (user: User) => void A callback function to call when a user successfully signs in. | undefined |
onSignOut | () => void A callback function to call when a sign out occurs. | undefined |
scopes | Scope[] The scope to assign to the access token. See src/types/scope.ts for a list of available scopes. | [Scope.IDENTIFY] |
variant | "light" | "dark" The button style variant to use. | "light" |
Customizations
By default, there are 2 button variants that we support:
const YourComponent = () => {
// There is a "light" (default) and "dark" variant:
return <SignInWithFractal variant="dark">;
}
You can make minor adjustments with your own class name to override any styles:
const YourComponent = () => {
// Using your own class name to override any default styles:
return <SignInWithFractal className="foobar">;
};
3B: Render Your Own Button
You can go headless and render your own button with the help of the
useAuthButtonProps
hook.
This option offers full control over your mark up and styles:
import { useAuthButtonProps, Scope } from "@fractalwagmi/react-sdk";
const YourButtonComponent = () => {
const { loading, signedIn, onClick } = useAuthButtonProps({
scopes: [Scope.IDENTIFY, Scope.ITEMS_READ, Scope.COINS_READ],
});
if (loading) {
return "...";
}
return <button onClick={onClick}>{signedIn ? "Sign Out" : "Sign In"}</button>;
};
useAuthButtonProps
supports the same props as SignInWithFractal
except for
the variant
and buttonProps
.
Be sure to add support for both signed in and signed out states (like in the
example above with the alternating button text) because the onClick
prop will
invoke different logic based on the signedIn
boolean.
General Error Handling
All exported error classes extends FractalSDKError
which extends the native JS
Error
class.
All exported error classes have a getUserFacingErrorMessage
method that
returns a UI-friendly fallback message, but we encourage you to handle each
error case individually and render UI text that is appropriate for your
application.
You may handle different error cases using instanceof
checks to infer the
meaning of the error state. Example:
import {
useSignTransaction,
FractalSDKSignTransactionDeniedError,
FractalSDKApprovalOccurringError,
} from "@fractalwagmi/react-sdk";
const MyComponent = () => {
const { signTransaction } = useSignTransaction();
const doSignTransaction = async () => {
try {
await signTransaction("some base58 transaction string");
} catch (err: unknown) {
if (error instanceof FractalSDKApprovalOccurringError) {
console.log("an approval is already occurring");
}
if (error instanceof FractalSDKSignTransactionDeniedError) {
console.log("transaction denied");
}
}
};
return <button onClick={() => doSignTransaction()}>...</button>;
};
Wallet Data Hooks
There are a variety of hooks that wrap our API functions to give you access to wallet hooks:
import {
useCoins,
useItems,
useUser,
useUserWallet,
} from "@fractalwagmi/react-sdk";
export function YourWalletComponent() {
// Returns user information like email, username, and id.
const { data: user } = useUser();
// Returns the user's wallet information like solana public keys.
const { data: userWallet } = useUserWallet();
// Returns the items in the user's wallet and whether each item is currently
// listed for sale or not.
const { data: items } = useItems();
// Returns the coins in the user's wallet.
const { data: coins } = useCoins();
return <div>...</div>;
}
Marketplace Hooks
The SDK has first-class support for marketplace functionality, like buying, listing, and cancelling listings on the Fractal marketplace.
Fetching Items For Sale
import { useItemsForSale } from "@fractalwagmi/react-sdk";
export function YourComponent() {
const { data, error, refetch } = useItemsForSale();
console.log("data = ", data);
return <div>...</div>;
}
Buying an Item
import { useBuyItem } from "@fractalwagmi/react-sdk";
interface Props {
tokenAddress: string;
}
export function YourBuyButton({ tokenAddress }: Props) {
const { buyItem } = useBuyItem();
return (
<button
onClick={async () => {
const { signature } = await buyItem({
tokenAddress,
});
console.log("signature = ", signature);
}}
>
Buy Item
</button>
);
}
This will generate a buy transaction and request user approval. Like the
useSignTransaction
hook, this hook returns the transaction signature and is
resolved as soon as the user approves the transaction, not when the transaction
is posted to the chain.
Listing an Item For Sale
import { useListItem } from "@fractalwagmi/react-sdk";
interface Props {
tokenAddress: string;
price: string;
}
export function YourListForSaleButton({
tokenAddress,
price,
quantity,
}: Props) {
const { listItem } = useListItem();
return (
<button
onClick={async () => {
const { signature } = await listItem({
tokenAddress,
// The price is a string like '0.02'.
price,
// The quantity defaults to `1` as it assumes the address being listed
// is an NFT. This prop is made available for when support for SFTs
// is more widely available.
quantity: 1,
});
console.log("signature = ", signature);
}}
>
List item for sale
</button>
);
}
This will generate a list-item-for-sale transaction and request user approval.
Like the useSignTransaction
hook, this hook returns the transaction signature
and is resolved as soon as the user approves the transaction, not when the
transaction is posted to the chain.
Cancelling an item listing
The useCancelListItem
hook can be used to cancel the action done in
useListItem
:
import { useCancelListItem } from "@fractalwagmi/react-sdk";
interface Props {
tokenAddress: string;
}
export function YourCancelListingButton({ tokenAddress }: Props) {
const { cancelListItem } = useCancelListItem();
return (
<button
onClick={async () => {
const { signature } = await cancelListItem({
tokenAddress,
// Like `useListItem`, this hook supports a `quantity` prop that
// defaults to 1. No need to set this unless you are dealing with an
// SFT.
quantity: 1,
});
console.log("signature = ", signature);
}}
>
Cancel item listing
</button>
);
}
This will generate a transaction for cancelling an item listing and request user
approval. Like the useSignTransaction
hook, this hook returns the transaction
signature and is resolved as soon as the user approves the transaction, not when
the transaction is posted to the chain.
Onramp
To expose onramp capability for end-users, you can use the useOnramp
hook:
import { useOnramp } from "@fractalwagmi/react-sdk";
export function YourOnrampButtonComponent() {
const { openOnrampWindow } = useOnramp();
return <button onClick={openOnrampWindow}>Buy Crypto</button>;
}
The useOnramp
hook accepts several arguments for (optional) configuration:
Prop | Type / Description | Default |
---|---|---|
onFulfillmentComplete | () => void A callback function to call when an onramp session is fulfilled. Fulfillment occurs when crypto has arrived in the user's wallet. | undefined |
onRejected | (err: OnrampErrors) => void A callback to invoke after an onramp session was rejected for a known reason. Possible reasons may include KYC failure, sanctions screening issues, fraud checks. | undefined |
theme | The theme variant to use for the onramp experience (can be light or dark ). | light |
For convenience, we have also exposed a Fractal-themed Button
component: <BuyCrypto />
:
import { BuyCrypto } from "@fractalwagmi/react-sdk";
export function YourOnrampButtonComponent() {
return <BuyCrypto />;
}
This component accepts the same set of props as described above, as well as buttonProps
, which support any additional
props you may want to use to configure the button component (HTMLAttributes<HTMLButtonElement>
).
One example use case of the optional onFulfillmentComplete
/ onRejected
callbacks could be to trigger a refetch of
displayed wallet funds, or to show a success/error message to users within a native experience.
Onramp Access
In order to use the onramp feature, users must be signed in to Fractal Account. If that is not the case, invoking the
openOnrampWindow
callback will throw an error and the <BuyCrypto />
component will render null
in place of the
button.
Our onramp provider (Stripe) is currently in early stages of pilot rollout, so it is worth noting that users in some regions will not be eligible to buy crypto, or may have restrictions to a subset of cryptos for their geographical region, which is based on IP detection + KYC verification. At the time of writing, only users in the US are eligible for the onramp feature, though we hope to expand on this in the near future.
If you have any feedback on the current implementation or new feature requests, please reach out to us. We'd love to hear from you!
Other Functional Hooks
Signing Out
If you need to programmatically sign the user out, you can use the useSignOut
hook:
import { useSignOut } from "@fractalwagmi/react-sdk";
export function YourWalletComponent() {
const { signOut } = useSignOut();
return <button onClick={signOut}>Your Sign Out Button Text</button>;
}
Approving a Generic Transaction
If you need the user to approve a generic transaction, you can create an unsigned transaction and initialize an approval popup flow for the user to approve the transaction:
import { useSignTransaction } from "@fractalwagmi/react-sdk";
interface YourComponentProps {
someTransactionB58: string | undefined;
}
export function YourComponent({ someTransactionB58 }: YourComponentProps) {
const {
// An async function to run which request's user approval to sign a
// transaction.
signTransaction,
} = useSignTransaction();
return (
<div>
<button
onClick={async () => {
try {
const { signature } = await signTransaction(someTransactionB58);
// This is the transaction signature for the signed transaction.
console.log("signature = ", signature);
} catch (err: unknown) {
// See memo below on error handling.
console.log("err = ", err);
}
}}
>
Request user approval for transaction
</button>
</div>
);
}
Keep in mind that a signed transaction does not mean that it has been posted to the chain yet. This hook only returns a transaction signature.
If you need to know when a transaction completes, use the
useWaitForTransaction
or useTransactionStatus
hooks described below.
Error handling for useSignTransaction
The signTransaction
function returned by useSignTransaction
will potentially
throw the following error classes:
Error class | Meaning |
---|---|
FractalSDKAuthenticationError | An authentication error occurred. This typically means that the user is not properly authenticated. |
FractalSDKApprovalOccurringError | An approval flow popup is already open for this hook instance. This error can occur if the unsignedTransactionB58 input changes while approving=true . |
FractalSDKInvalidTransactionError | The transaction input was invalid. |
FractalSDKSignTransactionDeniedError | The transaction was denied. |
FractalSDKSignTransactionUnknownError | An unknown error occurred (catch-all). |
Waiting for a Transaction To Post To The Chain
A signed transaction that is sent to the chain can take a variable amount of
time to post to the chain. All of the tranasction hooks we expose like
useSignTransaction
and useBuyItem
return a callback that resolves to a
transaction signature once the user has approved the transaction, not
when the transaction has posted to the chain.
There will be cases where you want to block, or want to add a UI affordance for a "pending" transaction state (like a loading spinner) while the transaction is being posted to the chain. For this, you can use one of the following hooks:
useWaitForTransaction
- returns an async callbackuseTransactionStatus
- returns an updating status object
import {
useWaitForTransaction,
TransactionStatus,
} from "@fractalwagmi/react-sdk";
interface Props {
tokenAddress: string;
onComplete: () => void;
onFail: () => void;
}
export function BuyItem({ tokenAddress, onComplete, onFail }: Props) {
const { buyItem } = useBuyItem();
const { waitForTransaction } = useWaitForTransaction();
const handleClick = async () => {
const { signature } = await buyItem(tokenAddress);
try {
const status = await waitForTransaction(signature);
if (status === TransactionStatus.SUCCESS) {
onComplete();
} else if (status === TransactionStatus.FAILED) {
onFail();
}
} catch (err: unknown) {
// See memo on error handling below.
}
};
return <button onClick={handleClick}>Buy Item</button>;
}
import {
TransactionStatus,
useTransactionStatus,
} from "@fractalwagmi/react-sdk";
interface Props {
transactionSignature: string;
}
export function TransactionStatusDisplay({ transactionSignature }: Props) {
const { status, error } = useTransactionStatus(transactionSignature);
if (error) {
// See memo on error handling below.
}
if (status === TransactionStatus.PENDING) {
return <div>Loading...</div>;
}
if (status === TransactionStatus.FAILED) {
return <div>Transaction Failed</div>;
}
return <div>Transaction Success</div>;
}
Error Handling for Transaction Statuses
Both the useTransactionStatus
hook and useWaitForTransaction
emit the same
errors:
Error Class | Meaning |
---|---|
FractalSDKAuthenticationError | An authentication error occurred. This typically means that the user is not properly authenticated. |
FractalSDKTransactionStatusFetchInvalidError | An invalid transaction signature was provided. |
FractalSDKTransactionStatusFetchUnknownError | Catch-all unknown error occurred during fetching of the transaction status. |