Skip to content

Commit b6192df

Browse files
committed
Merge pull-request #1062
2 parents ea0e20d + 27faddf commit b6192df

File tree

8 files changed

+446
-1
lines changed

8 files changed

+446
-1
lines changed

.changeset/rotten-melons-strive.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
"@turnkey/react-wallet-kit": minor
3+
"@turnkey/sdk-types": minor
4+
---
5+
6+
- **Added `handleOnRamp()` helper** to simplify fiat-to-crypto on-ramping flows directly from the SDK.
7+
- Supports overriding defaults through optional parameters:
8+
- `network` (e.g., `FiatOnRampBlockchainNetwork.ETHEREUM`)
9+
- `cryptoCurrencyCode` (e.g., `FiatOnRampCryptoCurrency.ETHEREUM`)
10+
- `fiatCurrencyAmount`, `fiatCurrencyCode`, `paymentMethod`, and `onrampProvider`.
11+
- Integrates seamlessly with the `client.httpClient.initFiatOnRamp()` method to open a provider popup (Coinbase, MoonPay, etc.) and monitor transaction completion.

examples/with-sdk-js/src/app/page.tsx

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,32 @@ export default function AuthPage() {
160160
await httpClient?.getWhoami(),
161161
);
162162
};
163+
const handleOnRamp = async () => {
164+
try {
165+
if (!wallets.length || !wallets[0].accounts?.length) {
166+
console.error("No wallets available for on-ramp");
167+
return;
168+
}
169+
170+
const evmAccount: WalletAccount | undefined = wallets[0].accounts.find(
171+
(a) => a.addressFormat === "ADDRESS_FORMAT_ETHEREUM",
172+
);
173+
174+
if (!evmAccount) {
175+
console.error("No EVM-compatible account found for on-ramp");
176+
return;
177+
}
178+
179+
await turnkey.handleOnRamp({
180+
onrampProvider: "FIAT_ON_RAMP_PROVIDER_MOONPAY",
181+
walletAccount: evmAccount,
182+
cryptoCurrencyCode: "FIAT_ON_RAMP_CRYPTO_CURRENCY_ETH",
183+
sandboxMode: true,
184+
});
185+
} catch (err) {
186+
console.error("Fiat On-Ramp failed:", err);
187+
}
188+
};
163189

164190
const signWithViem = async () => {
165191
const turnkeyAccount = await createAccount({
@@ -1007,6 +1033,19 @@ export default function AuthPage() {
10071033
>
10081034
Remove OAuth Provider
10091035
</button>
1036+
1037+
<button
1038+
data-testid="show-onramp-modal"
1039+
onClick={handleOnRamp}
1040+
style={{
1041+
backgroundColor: "purple",
1042+
borderRadius: "8px",
1043+
padding: "8px 16px",
1044+
color: "white",
1045+
}}
1046+
>
1047+
Onramp
1048+
</button>
10101049
</>
10111050
)}
10121051
</div>

packages/react-wallet-kit/src/components/design/Svg.tsx

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,51 @@ interface SVGProps {
33
style?: React.CSSProperties;
44
}
55

6+
export function CoinbaseLogo(props: SVGProps) {
7+
return (
8+
<svg
9+
viewBox="0 0 1024 1024"
10+
fill="none"
11+
xmlns="http://www.w3.org/2000/svg"
12+
{...props}
13+
>
14+
<rect width="1024" height="1024" fill="#0052FF" rx="100" ry="100"></rect>
15+
<path
16+
fill-rule="evenodd"
17+
clip-rule="evenodd"
18+
d="M152 512C152 710.823 313.177 872 512 872C710.823 872 872 710.823 872 512C872 313.177 710.823 152 512 152C313.177 152 152 313.177 152 512ZM420 396C406.745 396 396 406.745 396 420V604C396 617.255 406.745 628 420 628H604C617.255 628 628 617.255 628 604V420C628 406.745 617.255 396 604 396H420Z"
19+
fill="white"
20+
></path>
21+
</svg>
22+
);
23+
}
24+
25+
export function MoonPayLogo(props: SVGProps) {
26+
return (
27+
<svg
28+
viewBox="0 0 61 61"
29+
fill="none"
30+
xmlns="http://www.w3.org/2000/svg"
31+
{...props}
32+
>
33+
<g id="moonpay_symbol_wht 2">
34+
<rect
35+
x="1.3374"
36+
y="1"
37+
width="59"
38+
height="59"
39+
rx="11.5"
40+
fill="#7715F5"
41+
></rect>
42+
<path
43+
id="Vector"
44+
d="M43.8884 23.3258C45.0203 23.3258 46.1268 22.9901 47.068 22.3613C48.0091 21.7324 48.7427 20.8386 49.1759 19.7928C49.6091 18.747 49.7224 17.5962 49.5016 16.4861C49.2807 15.3759 48.7357 14.3561 47.9353 13.5557C47.1349 12.7553 46.1151 12.2102 45.0049 11.9893C43.8947 11.7685 42.7439 11.8819 41.6982 12.3151C40.6524 12.7482 39.7585 13.4818 39.1297 14.423C38.5008 15.3641 38.1651 16.4707 38.1651 17.6026C38.165 18.3542 38.3131 19.0985 38.6007 19.7929C38.8883 20.4873 39.3098 21.1182 39.8413 21.6496C40.3728 22.1811 41.0037 22.6027 41.6981 22.8903C42.3925 23.1778 43.1367 23.3259 43.8884 23.3258ZM26.3395 49.1017C23.5804 49.1017 20.8832 48.2836 18.5891 46.7507C16.295 45.2178 14.5069 43.039 13.4511 40.49C12.3952 37.9409 12.1189 35.1359 12.6572 32.4298C13.1955 29.7237 14.5241 27.238 16.4751 25.287C18.4262 23.336 20.9118 22.0074 23.6179 21.4691C26.324 20.9308 29.129 21.2071 31.6781 22.2629C34.2272 23.3189 36.406 25.1069 37.9389 27.401C39.4717 29.6952 40.2899 32.3923 40.2899 35.1514C40.2899 36.9835 39.9291 38.7975 39.2281 40.49C38.527 42.1826 37.4994 43.7205 36.204 45.0159C34.9086 46.3113 33.3707 47.3389 31.6781 48.04C29.9856 48.741 28.1715 49.1018 26.3395 49.1017Z"
45+
fill="white"
46+
></path>
47+
</g>
48+
</svg>
49+
);
50+
}
651
export function TurnkeyLogo(props: SVGProps) {
752
return (
853
<svg
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { useModal } from "../../providers/modal/Hook";
2+
import clsx from "clsx";
3+
import { ActionPage } from "../auth/Action";
4+
import { SuccessPage } from "../design/Success";
5+
6+
type OnRampPageProps = {
7+
icon: React.ReactNode;
8+
onrampProvider: string;
9+
action: () => Promise<void>;
10+
completed: boolean;
11+
successPageDuration?: number;
12+
sandboxMode?: boolean;
13+
onSuccess?: () => void;
14+
onError?: (error: any) => void;
15+
};
16+
17+
export function OnRampPage({
18+
icon,
19+
onrampProvider,
20+
action,
21+
completed,
22+
successPageDuration = 2000,
23+
sandboxMode = false,
24+
onSuccess,
25+
onError,
26+
}: OnRampPageProps) {
27+
const { closeModal, isMobile } = useModal();
28+
29+
if (completed) {
30+
return (
31+
<div
32+
className={clsx(
33+
"flex flex-col items-center justify-center py-5 transition-all duration-300 text-center",
34+
isMobile ? "w-full" : "w-72",
35+
)}
36+
>
37+
<SuccessPage
38+
text="On Ramp Transaction Complete!"
39+
duration={successPageDuration}
40+
onComplete={() => {
41+
onSuccess?.();
42+
closeModal();
43+
}}
44+
/>
45+
</div>
46+
);
47+
}
48+
49+
return (
50+
<div
51+
className={clsx(
52+
"flex flex-col items-center justify-center py-5 transition-all duration-300 text-center",
53+
isMobile ? "w-full" : "w-72",
54+
)}
55+
>
56+
<ActionPage
57+
title="Initiating On-Ramp"
58+
icon={icon}
59+
action={async () => {
60+
try {
61+
await action();
62+
} catch (err) {
63+
onError?.(err);
64+
throw err;
65+
}
66+
}}
67+
closeOnComplete={false}
68+
/>
69+
<div className="text-icon-text-light text-sm dark:text-icon-text-dark text-center !p-0">
70+
{onrampProvider === "FIAT_ON_RAMP_PROVIDER_COINBASE"
71+
? "Use the Coinbase popup to finish funding your account."
72+
: "Use the MoonPay popup to finish funding your account."}
73+
</div>
74+
{sandboxMode && (
75+
<div className="mt-2 text-xs text-blue-500 dark:text-blue-400">
76+
Sandbox mode — no real funds will be used.
77+
</div>
78+
)}
79+
</div>
80+
);
81+
}

0 commit comments

Comments
 (0)