Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
af407c9
Add privy as custom connector and support multiple connection methods
feyyazcigim Nov 7, 2025
df46ae1
Refactor privt integration for modularity and performance improvements
feyyazcigim Nov 7, 2025
7168501
fix: deduplicate viem and ox dependencies to resolve build OOM
fr1jo Nov 7, 2025
53a39a0
update .env
fr1jo Nov 7, 2025
ef62557
feat: update silo convert
burr-nim Dec 1, 2025
3307dc1
feat: update deposit inputs
burr-nim Dec 1, 2025
30d2f02
feat: update deposit
burr-nim Dec 1, 2025
1981264
feat: update main2lp deposit strategy
burr-nim Dec 1, 2025
8c442fc
feat: update copy
burr-nim Dec 1, 2025
36dda25
Merge branch 'main' into burr/ui/deposit-convert
burr-nim Dec 1, 2025
c5ecbf7
feat: update tooltip
burr-nim Dec 3, 2025
7694f22
feat: update tooltip text
burr-nim Dec 4, 2025
d81842b
feat: update alt text
burr-nim Dec 4, 2025
cc07028
Add copy button and basescan redirect to wallet panel
feyyazcigim Dec 9, 2025
408c729
Merge branch 'main' into wallet-connection
burr-nim Dec 9, 2025
1d9caff
Remove handleConnect from useChainSelection hook
feyyazcigim Dec 9, 2025
51b38bc
Add privy funding and update no value alert
feyyazcigim Dec 13, 2025
8c5ae45
Rerun codegen
PintoPirate Dec 15, 2025
195c878
Merge branch 'main' into burr/ui/deposit-convert
burr-nim Dec 17, 2025
bbdf955
Merge pull request #315 from pinto-org/burr/ui/deposit-convert
burr-nim Dec 17, 2025
923feaf
Add fund button to wallet panel
feyyazcigim Dec 17, 2025
96f83c3
Change fund icon
feyyazcigim Dec 17, 2025
def5e1c
Add return keyword to hasBalanceOnBase updater
feyyazcigim Dec 18, 2025
f36296b
Change return type for hasBalanceOnBase mapping
feyyazcigim Dec 18, 2025
5fce64c
Comment out Coinbase SW SDK connection
feyyazcigim Dec 18, 2025
642fe49
Add todo explanation and referance to useWalletConnection hook
feyyazcigim Dec 18, 2025
71bd4c8
Fix privy modal <> Dialog conflict
feyyazcigim Dec 19, 2025
8099ec3
Revert z-index on Dialog component
feyyazcigim Dec 19, 2025
fb2a4b6
Merge pull request #304 from pinto-org/wallet-connection
fr1jo Dec 19, 2025
f0b3cdb
Disable exchange subgraph charts (revert this later)
PintoPirate Dec 22, 2025
1c43a55
Merge pull request #356 from pinto-org/pp/disable-exchange-charts
PintoPirate Dec 22, 2025
bd49413
Solve compo input field conflict
feyyazcigim Dec 29, 2025
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
3 changes: 2 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ VITE_WALLET_CONNECT_PROJECT_ID=""
VITE_ALCHEMY_API_KEY="" # alchemy api key. https://www.alchemy.com/
VITE_CHAINS="1337,8453" #comma separated list of chain ids. (ex: "1337,8543")
VITE_BASE_ENDPOINT="pinto.money" # base endpoint domain
VITE_TENDERLY_RPC_URL="" # rpc url for pointing towards tenderly RPC for testing. Add chainId 41337 to VITE_CHAINS
VITE_TENDERLY_RPC_URL="" # rpc url for pointing towards tenderly RPC for testing. Add chainId 41337 to VITE_CHAINS
VITE_PRIVY_APP_ID="" # privy app id from dashboard.privy.io
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ dist-ssr

# Editor directories and files
.vscode/*
.kiro
!.vscode/extensions.json
.idea
.DS_Store
Expand Down
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
"@hookform/resolvers": "^3.9.0",
"@netlify/functions": "^2.8.1",
"@number-flow/react": "^0.5.9",
"@privy-io/react-auth": "^3.6.0",
"@privy-io/wagmi": "^2.0.2",
"@privy-io/wagmi-connector": "^0.1.13",
"@radix-ui/react-accordion": "^1.2.3",
"@radix-ui/react-checkbox": "^1.1.2",
"@radix-ui/react-collapsible": "^1.1.1",
Expand Down Expand Up @@ -120,7 +123,9 @@
"@babel/runtime": "^7.26.10",
"brace-expansion": "^2.0.2",
"micromatch": "^4.0.8",
"cross-spawn": "7.0.6"
"cross-spawn": "7.0.6",
"viem": "^2.33.1",
"ox": "^0.8.1"
},
"packageManager": "yarn@4.5.0"
}
203 changes: 136 additions & 67 deletions src/Web3Provider.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { PrivyProvider, usePrivy, useWallets } from "@privy-io/react-auth";
import { createSyncStoragePersister } from "@tanstack/query-sync-storage-persister";
import { QueryClient } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
Expand All @@ -6,8 +7,13 @@ import { ConnectKitProvider } from "connectkit";
import { atom, useAtom } from "jotai";
import { ReactNode, useEffect, useMemo } from "react";
import { createTestClient } from "viem";
import { http, WagmiProvider, createConfig } from "wagmi";
import { http, WagmiProvider, createConfig, createStorage } from "wagmi";
import type { CreateConnectorFn } from "wagmi";
import { mock } from "wagmi/connectors";
import { useAutoReconnect } from "./hooks/wallet/useAutoReconnect";
import { usePrivySync } from "./hooks/wallet/usePrivySync";
import { privyConfig } from "./utils/privy/privy.config";
import { getPrivyEmbeddedWallet, getPrivyLogout } from "./utils/privy/privyRefs";
import { isValidAddress } from "./utils/string";
import { isLocalhost, isNetlifyPreview, isProd } from "./utils/utils";
import {
Expand All @@ -16,7 +22,8 @@ import {
localhostNetwork as localhost,
tenderlyTestnetNetwork as testnet,
} from "./utils/wagmi/chains";
import config from "./utils/wagmi/config";
import { buildBaseConfigParams } from "./utils/wagmi/config";
import { privy as privyConnector } from "./utils/wagmi/connectors/privy";

// biome-ignore lint/suspicious/noExplicitAny: React Query needs this to serialize BigInts
(BigInt.prototype as any).toJSON = function () {
Expand All @@ -38,10 +45,14 @@ const localStoragePersister = createSyncStoragePersister({
});

export const Web3Provider = ({ children }: { children: ReactNode }) => {
const config = useEnvConfig();
const privyAppId = import.meta.env.VITE_PRIVY_APP_ID;

if (!privyAppId) {
console.warn("VITE_PRIVY_APP_ID is not set. Privy email/social login will not work.");
}

return (
<WagmiProvider config={config}>
<PrivyProvider appId={privyAppId || ""} config={privyConfig}>
{/**
* If the cache that is found has a different buster string than what is set here, it will be discarded.
* Should be changed whenever there's a significant change in the subgraphs.
Expand All @@ -52,51 +63,103 @@ export const Web3Provider = ({ children }: { children: ReactNode }) => {
client={queryClient}
persistOptions={{ persister: localStoragePersister, buster: "20250501" }}
>
<MockConnectorManager />
<ConnectKitProvider
mode="light"
customTheme={{
"--ck-font-family": "Pinto",
"--ck-border-radius": "24px",
"--ck-body-color": "#404040",
"--ck-body-background": "#FCFCFC",
"--ck-modal-box-shadow": "0px 0px 0px 1px rgb(217, 217, 217)",
"--ck-overlay-backdrop-filter": "blur(2px)",
"--ck-overlay-background": "rgb(255 255 255 / 0.5)",
"--ck-primary-button-font-weight": "400",
"--ck-primary-button-box-shadow": "0px 0px 0px 1px rgb(217, 217, 217)",
"--ck-primary-button-background": "#FCFCFC",
"--ck-primary-button-hover-background": "#EBEBEB",
"--ck-secondary-button-background": "#FCFCFC",
"--ck-secondary-button-hover-background": "#EBEBEB",
"--ck-secondary-button-box-shadow": "0px 0px 0px 1px rgb(217, 217, 217)",
"--ck-spinner-color": "rgb(36 102 69)",
"--ck-qr-border-color": "rgb(217, 217, 217)",
"--ck-qr-dot-color": "rgb(36 102 69)",
"--ck-body-divider": "rgb(217, 217, 217)",
}}
options={{
initialChainId: getDefaultChainId(),
avoidLayoutShift: true,
hideNoWalletCTA: true,
hideQuestionMarkCTA: true,
enforceSupportedChains: true,
}}
>
{children}
</ConnectKitProvider>
<ReactQueryDevtools initialIsOpen={false} />
<WagmiProviderWrapper>{children}</WagmiProviderWrapper>
</PersistQueryClientProvider>
</WagmiProvider>
</PrivyProvider>
);
};

// New component to handle mock connector logic
function getDefaultChainId() {
if (isProd()) {
return base.id;
}
if (isNetlifyPreview()) {
return !!TENDERLY_RPC_URL ? testnet.id : base.id;
}
return localhost.id;
}

/**
* Stable wagmi config for non-localhost environments
* Config instance never changes to preserve wagmi's storage state
* Privy connector uses global refs that can be updated without recreating config
*/
const stableBaseConfig = createConfig(
buildBaseConfigParams({
additionalConnectors: [
privyConnector({
getEmbeddedWallet: getPrivyEmbeddedWallet,
logout: () => {
const logout = getPrivyLogout();
return logout ? logout() : Promise.resolve();
},
}),
],
}),
);

const connectKitTheme = {
"--ck-font-family": "Pinto",
"--ck-border-radius": "24px",
"--ck-body-color": "#404040",
"--ck-body-background": "#FCFCFC",
"--ck-modal-box-shadow": "0px 0px 0px 1px rgb(217, 217, 217)",
"--ck-overlay-backdrop-filter": "blur(2px)",
"--ck-overlay-background": "rgb(255 255 255 / 0.5)",
"--ck-primary-button-font-weight": "400",
"--ck-primary-button-box-shadow": "0px 0px 0px 1px rgb(217, 217, 217)",
"--ck-primary-button-background": "#FCFCFC",
"--ck-primary-button-hover-background": "#EBEBEB",
"--ck-secondary-button-background": "#FCFCFC",
"--ck-secondary-button-hover-background": "#EBEBEB",
"--ck-secondary-button-box-shadow": "0px 0px 0px 1px rgb(217, 217, 217)",
"--ck-spinner-color": "rgb(36 102 69)",
"--ck-qr-border-color": "rgb(217, 217, 217)",
"--ck-qr-dot-color": "rgb(36 102 69)",
"--ck-body-divider": "rgb(217, 217, 217)",
};

const connectKitOptions = {
initialChainId: getDefaultChainId(),
avoidLayoutShift: true,
hideNoWalletCTA: true,
hideQuestionMarkCTA: true,
enforceSupportedChains: true,
};

function WagmiProviderWrapper({ children }: { children: ReactNode }) {
const wagmiConfig = useEnvConfig(stableBaseConfig);

return (
<WagmiProvider config={wagmiConfig}>
<MockConnectorManager />
<ConnectionManager />
<ConnectKitProvider mode="light" customTheme={connectKitTheme} options={connectKitOptions}>
{children}
</ConnectKitProvider>
<ReactQueryDevtools initialIsOpen={false} />
</WagmiProvider>
);
}

/**
* Manages wallet connection lifecycle
* Handles auto-reconnect and Privy sync
*/
function ConnectionManager() {
useAutoReconnect();
usePrivySync();
return null;
}

/**
* Manages mock connector for localhost development
* Impersonates accounts for testing
*/
function MockConnectorManager() {
const [mockAddress] = useAtom(mockAddressAtom);
const isLocal = isLocalhost();

// Impersonate account whenever mockAddress changes
useEffect(() => {
if (isLocal && isValidAddress(mockAddress)) {
anvilTestClient.impersonateAccount({ address: mockAddress });
Expand All @@ -106,51 +169,57 @@ function MockConnectorManager() {
return null;
}

// Add atom for mock address with stored value or default
export const mockAddressAtom = atom<`0x${string}`>(
// default to local storage
(localStorage.getItem("mockAddress") as `0x${string}`) ||
null ||
// if none in local storage, use env variable
"0x",
);
export const mockAddressAtom = atom<`0x${string}`>((localStorage.getItem("mockAddress") as `0x${string}`) || "0x");

export const anvilTestClient = createTestClient({
mode: "anvil",
chain: localhost,
transport: http(),
});

const getDefaultChainId = () => {
if (isProd()) {
return base.id;
}
if (isNetlifyPreview()) {
return !!TENDERLY_RPC_URL ? testnet.id : base.id;
}
return localhost.id;
};

// Create config with current mock address value
const useEnvConfig = () => {
/**
* Creates wagmi config based on environment
* - Localhost: Uses mock connector with impersonation
* - Other environments: Uses stable base config
*/
function useEnvConfig(stableBaseConfig: ReturnType<typeof createConfig>) {
const [mockAddress] = useAtom(mockAddressAtom);
const isLocal = isLocalhost();
const { logout: privyLogout } = usePrivy();
const wallets = useWallets();
const walletsArray = Array.isArray(wallets) ? wallets : wallets?.wallets || [];
const embeddedWallet = walletsArray.find((w) => w.walletClientType === "privy");

const localConfig = useMemo(() => {
if (!isValidAddress(mockAddress)) {
return undefined;
}

const connectors: CreateConnectorFn[] = [
mock({ accounts: [mockAddress], features: { defaultConnected: true, reconnect: true } }),
];

if (embeddedWallet && privyLogout) {
connectors.push(
privyConnector({
getEmbeddedWallet: () => embeddedWallet,
logout: privyLogout,
}),
);
}

return createConfig({
connectors: [mock({ accounts: [mockAddress], features: { defaultConnected: true, reconnect: true } })],
connectors,
chains: [localhost, base],
client() {
return anvilTestClient;
},
storage: createStorage({
storage: typeof window !== "undefined" ? window.localStorage : undefined,
}),
ssr: false,
});
}, [mockAddress]);

const envConfig = isLocal && localConfig ? localConfig : config;
}, [mockAddress, embeddedWallet, privyLogout]);

return envConfig;
};
return isLocal && localConfig ? localConfig : stableBaseConfig;
}
Binary file added src/assets/misc/Privy_Symbol_Black.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/misc/mail.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/wallets/coinbase.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/wallets/metamask.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/wallets/rabby.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/wallets/walletconnect.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
39 changes: 39 additions & 0 deletions src/components/AutoConnectFirstConnector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { isDev } from "@/utils/utils";
import { useEffect } from "react";
import { useConnect } from "wagmi";

/**
* AutoConnectFirstConnector Component
*
* Automatically connects to the first available wagmi connector when:
* - Status is "idle" (not connected)
* - Connectors array has at least one connector
* - Component is mounted
*
* This is useful for development/testing purposes.
* Can be conditionally rendered based on environment (e.g., only in dev mode).
*
* Usage:
* ```tsx
* {isDev() && <AutoConnectFirstConnector />}
* ```
*/
export default function AutoConnectFirstConnector() {
const { connectors, connectAsync, status } = useConnect();

useEffect(() => {
if (!isDev()) {
return;
}

if (status === "idle" && connectors.length > 0) {
const firstConnector = connectors[0];
connectAsync({ connector: firstConnector }).catch((error) => {
console.error("AutoConnectFirstConnector: Auto-connect failed", error);
});
}
}, [connectors, status, connectAsync]);

// This component doesn't render anything
return null;
}
Loading