diff --git a/packages/keychain/__image_snapshots__/components-deploycontroller--default-chromium.png b/packages/keychain/__image_snapshots__/components-deploycontroller--default-chromium.png
index a89e22673..5068ffb7b 100644
Binary files a/packages/keychain/__image_snapshots__/components-deploycontroller--default-chromium.png and b/packages/keychain/__image_snapshots__/components-deploycontroller--default-chromium.png differ
diff --git a/packages/keychain/__image_snapshots__/components-funding--default-chromium.png b/packages/keychain/__image_snapshots__/components-funding--default-chromium.png
index 91093c2d2..dcef7caa3 100644
Binary files a/packages/keychain/__image_snapshots__/components-funding--default-chromium.png and b/packages/keychain/__image_snapshots__/components-funding--default-chromium.png differ
diff --git a/packages/keychain/__image_snapshots__/components-funding-purchasecredits--default-chromium.png b/packages/keychain/__image_snapshots__/components-funding-purchasecredits--default-chromium.png
index c6f291024..3d04380bd 100644
Binary files a/packages/keychain/__image_snapshots__/components-funding-purchasecredits--default-chromium.png and b/packages/keychain/__image_snapshots__/components-funding-purchasecredits--default-chromium.png differ
diff --git a/packages/keychain/src/components/app.tsx b/packages/keychain/src/components/app.tsx
index 0f580bbef..80a27a623 100644
--- a/packages/keychain/src/components/app.tsx
+++ b/packages/keychain/src/components/app.tsx
@@ -7,6 +7,7 @@ import { Success } from "./success";
import { Pending } from "./pending";
import { Consent, Slot } from "./slot";
import { OcclusionDetector } from "./OcclusionDetector";
+import { Fund } from "./slot/fund";
export function App() {
return (
@@ -19,6 +20,7 @@ export function App() {
} />
}>
} />
+ } />
} />
} />
diff --git a/packages/keychain/src/components/funding/Balance.tsx b/packages/keychain/src/components/funding/Balance.tsx
index 341ee94fb..33797d083 100644
--- a/packages/keychain/src/components/funding/Balance.tsx
+++ b/packages/keychain/src/components/funding/Balance.tsx
@@ -16,8 +16,10 @@ import {
} from "@cartridge/utils";
import { useController } from "@/hooks/controller";
+export type BalanceType = "credits" | "eth" | "strk";
+
type BalanceProps = {
- showBalances: ("credits" | "eth" | "strk")[];
+ showBalances: BalanceType[];
};
export function Balance({ showBalances }: BalanceProps) {
@@ -26,6 +28,7 @@ export function Balance({ showBalances }: BalanceProps) {
username: controller?.username(),
interval: 3000,
});
+
const {
data: [eth],
} = useERC20Balance({
diff --git a/packages/keychain/src/components/funding/PurchaseCredits.tsx b/packages/keychain/src/components/funding/PurchaseCredits.tsx
index 61db5c63c..d73e55c12 100644
--- a/packages/keychain/src/components/funding/PurchaseCredits.tsx
+++ b/packages/keychain/src/components/funding/PurchaseCredits.tsx
@@ -14,6 +14,7 @@ import { Elements } from "@stripe/react-stripe-js";
import { Appearance, loadStripe } from "@stripe/stripe-js";
import { Balance } from "./Balance";
import CheckoutForm from "./StripeCheckout";
+import { isIframe } from "@cartridge/utils";
const STRIPE_API_PUBKEY =
"pk_test_51Kr6IXIS6lliDpf33KnwWDtIjRPWt3eAI9CuSLR6Vvc3GxHEwmSU0iszYbUlgUadSRluGKAFphe3JzltyjPAKiBK00al4RAFQu";
@@ -25,10 +26,11 @@ enum PurchaseState {
}
type PurchaseCreditsProps = {
+ isSlot?: boolean;
onBack?: () => void;
};
-export function PurchaseCredits({ onBack }: PurchaseCreditsProps) {
+export function PurchaseCredits({ isSlot, onBack }: PurchaseCreditsProps) {
const { controller, closeModal } = useConnection();
const [clientSecret, setClientSecret] = useState("");
@@ -82,24 +84,6 @@ export function PurchaseCredits({ onBack }: PurchaseCreditsProps) {
},
} as Appearance;
- // For when we need to support Payment Links
- // useStripePaymentQuery(
- // { referenceId },
- // {
- // enabled: !!referenceId && !error,
- // refetchInterval: REFETCH_INTERVAL,
- // retry: MAX_RETRIES,
- // onSuccess: () => setState(PurchaseState.SUCCESS),
- // onError: () => {
- // setError(
- // new Error(
- // `Payment not received. Please try again. Reference ID: ${referenceId}`,
- // ),
- // );
- // },
- // },
- // );
-
if (state === PurchaseState.STRIPE_CHECKOUT) {
return (
@@ -150,7 +139,7 @@ export function PurchaseCredits({ onBack }: PurchaseCreditsProps) {
/>
)}
- {state === PurchaseState.SUCCESS && (
+ {state === PurchaseState.SUCCESS && isIframe() && (
diff --git a/packages/keychain/src/components/funding/index.tsx b/packages/keychain/src/components/funding/index.tsx
index b03a4ffb0..b86551430 100644
--- a/packages/keychain/src/components/funding/index.tsx
+++ b/packages/keychain/src/components/funding/index.tsx
@@ -10,7 +10,7 @@ import {
} from "@cartridge/ui-next";
import { DepositEth } from "./DepositEth";
import { PurchaseCredits } from "./PurchaseCredits";
-import { Balance } from "./Balance";
+import { Balance, BalanceType } from "./Balance";
const enum FundingState {
SHOW_OPTIONS,
@@ -19,15 +19,18 @@ const enum FundingState {
}
export type FundingProps = {
- title?: React.ReactElement;
+ title?: React.ReactElement | string;
+ isSlot?: boolean;
onComplete?: (deployHash?: string) => void;
};
-export function Funding({ onComplete, title }: FundingProps) {
+export function Funding({ title, isSlot, onComplete }: FundingProps) {
const { controller } = useConnection();
const [state, setState] = useState(FundingState.SHOW_OPTIONS);
+ const showBalances: BalanceType[] = isSlot ? ["credits"] : ["credits", "eth"];
const showCredits =
- typeof document !== "undefined" && document.cookie.includes("credits=");
+ (typeof document !== "undefined" && document.cookie.includes("credits=")) ||
+ isSlot;
if (state === FundingState.FUND_ETH) {
return (
@@ -40,7 +43,10 @@ export function Funding({ onComplete, title }: FundingProps) {
if (state === FundingState.FUND_CREDITS) {
return (
- setState(FundingState.SHOW_OPTIONS)} />
+ setState(FundingState.SHOW_OPTIONS)}
+ />
);
}
@@ -49,9 +55,10 @@ export function Funding({ onComplete, title }: FundingProps) {
title={title || (controller ? `Fund ${controller.username()}` : "")}
description={controller && }
icon={}
+ hideNetwork
>
-
+
);
diff --git a/packages/keychain/src/components/slot/consent.tsx b/packages/keychain/src/components/slot/consent.tsx
index 29bc8b803..14194e1a8 100644
--- a/packages/keychain/src/components/slot/consent.tsx
+++ b/packages/keychain/src/components/slot/consent.tsx
@@ -2,10 +2,11 @@ import Controller from "@/utils/controller";
import { Button } from "@cartridge/ui-next";
import { Container, Footer } from "@/components/layout";
import { useCallback, useEffect } from "react";
-import { useNavigate, useSearchParams } from "react-router-dom";
+import { useLocation, useNavigate, useSearchParams } from "react-router-dom";
export function Consent() {
const navigate = useNavigate();
+ const { pathname } = useLocation();
const [searchParams] = useSearchParams();
const callback_uri = searchParams.get("callback_uri")!;
@@ -25,9 +26,18 @@ export function Consent() {
useEffect(() => {
if (!Controller.fromStore(import.meta.env.VITE_ORIGIN!)) {
- navigate("/slot", { replace: true });
+ navigate(
+ `/slot?returnTo=${encodeURIComponent(pathname)}${
+ callback_uri
+ ? `&callback_uri=${encodeURIComponent(callback_uri)}`
+ : ""
+ }`,
+ {
+ replace: true,
+ },
+ );
}
- }, [navigate]);
+ }, [navigate, callback_uri, pathname]);
return (
{
+ if (!Controller.fromStore(import.meta.env.VITE_ORIGIN!)) {
+ navigate(`/slot?returnTo=${encodeURIComponent(pathname)}`, {
+ replace: true,
+ });
+ }
+ }, [navigate, pathname]);
+
+ return ;
+}
diff --git a/packages/keychain/src/components/slot/index.tsx b/packages/keychain/src/components/slot/index.tsx
index 84da58f69..afcb117bd 100644
--- a/packages/keychain/src/components/slot/index.tsx
+++ b/packages/keychain/src/components/slot/index.tsx
@@ -23,6 +23,7 @@ export function Slot() {
case "/slot/auth/failure":
return ;
case "/slot/consent":
+ case "/slot/fund":
return ;
default:
return ;
@@ -37,13 +38,17 @@ function Auth() {
useEffect(() => {
if (user && controller) {
- const query = Array.from(searchParams.entries()).reduce(
- (prev, [key, val], i) =>
- i === 0 ? `?${key}=${val}` : `${prev}&${key}=${val}`,
- "",
- );
+ const returnTo = searchParams.get("returnTo");
+ const otherParams = Array.from(searchParams.entries())
+ .filter(([key]) => key !== "returnTo")
+ .reduce(
+ (prev, [key, val], i) =>
+ i === 0 ? `?${key}=${val}` : `${prev}&${key}=${val}`,
+ "",
+ );
- navigate(`/slot/consent${query}`, { replace: true });
+ const target = returnTo ? `${returnTo}${otherParams}` : "/slot";
+ navigate(target, { replace: true });
}
}, [user, controller, navigate, searchParams]);
diff --git a/packages/keychain/src/hooks/connection.ts b/packages/keychain/src/hooks/connection.ts
index 449735ede..5de35e9ef 100644
--- a/packages/keychain/src/hooks/connection.ts
+++ b/packages/keychain/src/hooks/connection.ts
@@ -107,8 +107,13 @@ export function useConnectionValue() {
useEffect(() => {
const urlParams = new URLSearchParams(window.location.search);
- // Set rpc and origin if we're not embedded (eg Slot auth/session)
+ // if we're not embedded (eg Slot auth/session) load controller from store and set origin/rpcUrl
if (!isIframe()) {
+ const controller = Controller.fromStore(import.meta.env.VITE_ORIGIN!);
+ if (controller) {
+ setController(controller);
+ }
+
setOrigin(urlParams.get("origin") || import.meta.env.VITE_ORIGIN);
const rpcUrl = urlParams.get("rpc_url");
if (rpcUrl) {