Skip to content

Commit 2181b3a

Browse files
committed
Dev status - Working demo app for wallet direct mode, packages still need a ton more cleanup, demi app needs through check on, need to make more general utillities around the signing behaviour, need to add ed25119 signing method, need to fix the window.... calling for wallets, persitency for sessions works but local signer method with turnkey is still funky.
1 parent e40c285 commit 2181b3a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+2628
-2041
lines changed
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
"use client";
2+
import { useState, useEffect, useMemo } from "react";
3+
import {
4+
AbstraxionProvider,
5+
type AbstraxionConfig,
6+
type WalletConnectionMethods,
7+
useAbstraxionAccount,
8+
} from "@burnt-labs/abstraxion";
9+
import { WalletModal } from "../../components/WalletModal";
10+
11+
export default function DirectModeLayout({
12+
children,
13+
}: {
14+
children: React.ReactNode;
15+
}): JSX.Element {
16+
const [showWalletModal, setShowWalletModal] = useState(false);
17+
const [walletMethods, setWalletMethods] = useState<WalletConnectionMethods | null>(null);
18+
19+
// Direct mode configuration with treasury contract
20+
const directModeConfig: AbstraxionConfig = useMemo(() => ({
21+
// REQUIRED: Chain ID
22+
chainId: "xion-testnet-2",
23+
24+
// REQUIRED: RPC URL for blockchain connection
25+
rpcUrl: process.env.NEXT_PUBLIC_RPC_URL || "https://rpc.xion-testnet-2.burnt.com:443",
26+
27+
// REQUIRED: REST API endpoint
28+
restUrl: process.env.NEXT_PUBLIC_REST_URL || "https://api.xion-testnet-2.burnt.com",
29+
30+
// REQUIRED: Gas price
31+
gasPrice: "0.001uxion",
32+
33+
// Treasury contract address (optional - for dynamic grant configs)
34+
treasury: process.env.NEXT_PUBLIC_TREASURY_ADDRESS,
35+
36+
// Fee granter address (optional - pays transaction fees for grant creation)
37+
// Must match the FEE_GRANTER_ADDRESS used by your AA API
38+
feeGranter:
39+
process.env.NEXT_PUBLIC_FEE_GRANTER_ADDRESS ||
40+
"xion10y5pzqs0jn89zpm6va625v6xzsqjkm293efwq8",
41+
42+
// Enable direct mode for in-app wallet connections
43+
walletAuth: {
44+
mode: "direct" as const,
45+
46+
// Point to local AA API for development
47+
aaApiUrl: "http://localhost:8787",
48+
49+
// Use custom strategy to show our custom wallet modal
50+
walletSelectionStrategy: "custom" as const,
51+
52+
// Define wallets to support (optional - defaults to MetaMask + Keplr for auto mode)
53+
// You can add any Ethereum or Cosmos wallet by specifying its window key!
54+
wallets: [
55+
{ name: "MetaMask", windowKey: "ethereum", signingMethod: "ethereum" },
56+
{ name: "Keplr", windowKey: "keplr", signingMethod: "cosmos" },
57+
{ name: "OKX", windowKey: "okxwallet.keplr", signingMethod: "cosmos" },
58+
// Example: Add Leap wallet (Cosmos)
59+
// { name: "Leap", windowKey: "leap", signingMethod: "cosmos" },
60+
// Example: Add Rainbow wallet (Ethereum)
61+
// { name: "Rainbow", windowKey: "ethereum", signingMethod: "ethereum" },
62+
// Example: Add Compass wallet (Cosmos)
63+
// { name: "Compass", windowKey: "compass", signingMethod: "cosmos" },
64+
],
65+
66+
// Custom wallet selection callback - shows our modal
67+
onWalletSelectionRequired: (methods) => {
68+
setWalletMethods(methods);
69+
setShowWalletModal(true);
70+
},
71+
},
72+
}), []);
73+
74+
return (
75+
<AbstraxionProvider config={directModeConfig}>
76+
<ModalHandler
77+
showWalletModal={showWalletModal}
78+
setShowWalletModal={setShowWalletModal}
79+
walletMethods={walletMethods}
80+
>
81+
{children}
82+
</ModalHandler>
83+
</AbstraxionProvider>
84+
);
85+
}
86+
87+
// Component inside provider that can access context to close modal on connection
88+
function ModalHandler({
89+
children,
90+
showWalletModal,
91+
setShowWalletModal,
92+
walletMethods,
93+
}: {
94+
children: React.ReactNode;
95+
showWalletModal: boolean;
96+
setShowWalletModal: (show: boolean) => void;
97+
walletMethods: WalletConnectionMethods | null;
98+
}) {
99+
const { isConnected } = useAbstraxionAccount();
100+
101+
// Close modal when connection succeeds
102+
useEffect(() => {
103+
if (isConnected && showWalletModal) {
104+
setShowWalletModal(false);
105+
}
106+
}, [isConnected, showWalletModal, setShowWalletModal]);
107+
108+
return (
109+
<>
110+
{children}
111+
112+
{/* Custom Wallet Selection Modal */}
113+
<WalletModal
114+
isOpen={showWalletModal}
115+
onClose={() => setShowWalletModal(false)}
116+
connectionMethods={walletMethods}
117+
chainId="xion-testnet-1"
118+
/>
119+
</>
120+
);
121+
}
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
"use client";
2+
import { useState } from "react";
3+
import {
4+
useAbstraxionAccount,
5+
useAbstraxionSigningClient,
6+
} from "@burnt-labs/abstraxion";
7+
import { Button } from "@burnt-labs/ui";
8+
import "@burnt-labs/ui/dist/index.css";
9+
import "@burnt-labs/abstraxion/dist/index.css";
10+
import Link from "next/link";
11+
12+
export default function DirectModePage(): JSX.Element {
13+
const { data: account, login } = useAbstraxionAccount();
14+
const { client, logout } = useAbstraxionSigningClient();
15+
16+
// Send transaction state
17+
const [recipient, setRecipient] = useState("");
18+
const [amount, setAmount] = useState("");
19+
const [isSending, setIsSending] = useState(false);
20+
const [txHash, setTxHash] = useState<string | null>(null);
21+
const [txError, setTxError] = useState<string | null>(null);
22+
23+
const handleSendTokens = async () => {
24+
if (!client || !account.bech32Address || !recipient || !amount) {
25+
setTxError("Please fill in all fields");
26+
return;
27+
}
28+
29+
try {
30+
setIsSending(true);
31+
setTxError(null);
32+
setTxHash(null);
33+
34+
const amountInUxion = (parseFloat(amount) * 1_000_000).toString();
35+
36+
const result = await client.sendTokens(
37+
account.bech32Address,
38+
recipient,
39+
[{ denom: "uxion", amount: amountInUxion }],
40+
"auto",
41+
"Send XION via Abstraxion Direct Mode"
42+
);
43+
44+
setTxHash(result.transactionHash);
45+
setRecipient("");
46+
setAmount("");
47+
} catch (error: any) {
48+
console.error("Send error:", error);
49+
setTxError(error.message || "Failed to send tokens");
50+
} finally {
51+
setIsSending(false);
52+
}
53+
};
54+
55+
return (
56+
<main className="m-auto flex min-h-screen max-w-lg flex-col items-center justify-center gap-4 p-4">
57+
<h1 className="text-2xl font-bold tracking-tighter text-white">
58+
Direct Mode Abstraxion Example
59+
</h1>
60+
<p className="text-center text-gray-400">
61+
This example uses the new <strong>direct mode</strong> which allows in-app wallet
62+
connections without redirecting to the dashboard. Connect with MetaMask,
63+
Keplr, Leap, or OKX directly in this app.
64+
</p>
65+
66+
<div className="w-full space-y-4">
67+
{!account.bech32Address && (
68+
<Button fullWidth onClick={() => login()} structure="base">
69+
CONNECT WALLET (DIRECT MODE)
70+
</Button>
71+
)}
72+
73+
{account.bech32Address && (
74+
<>
75+
<div className="rounded border border-white/20 p-4">
76+
<h3 className="mb-2 font-semibold">Account Info</h3>
77+
<p className="text-sm text-gray-400 break-all">
78+
Address: {account.bech32Address}
79+
</p>
80+
<p className="text-sm text-gray-400">
81+
Client: {client ? "Connected" : "Not connected"}
82+
</p>
83+
</div>
84+
85+
<div className="rounded border border-green-500/20 bg-green-500/10 p-4">
86+
<p className="text-sm text-green-400">
87+
✓ Successfully connected using direct mode! No dashboard redirect needed.
88+
</p>
89+
</div>
90+
91+
{/* Send XION Section */}
92+
<div className="rounded border border-blue-500/20 bg-blue-500/5 p-4 space-y-3">
93+
<h3 className="font-semibold text-blue-400">Send XION</h3>
94+
95+
<div className="space-y-2">
96+
<label className="block text-sm text-gray-400">
97+
Recipient Address
98+
</label>
99+
<input
100+
type="text"
101+
value={recipient}
102+
onChange={(e) => setRecipient(e.target.value)}
103+
placeholder="xion1..."
104+
className="w-full rounded bg-black/30 border border-white/10 px-3 py-2 text-sm text-white placeholder-gray-500 focus:border-blue-500 focus:outline-none"
105+
/>
106+
</div>
107+
108+
<div className="space-y-2">
109+
<label className="block text-sm text-gray-400">
110+
Amount (XION)
111+
</label>
112+
<input
113+
type="number"
114+
value={amount}
115+
onChange={(e) => setAmount(e.target.value)}
116+
placeholder="0.001"
117+
step="0.001"
118+
min="0"
119+
className="w-full rounded bg-black/30 border border-white/10 px-3 py-2 text-sm text-white placeholder-gray-500 focus:border-blue-500 focus:outline-none"
120+
/>
121+
</div>
122+
123+
<Button
124+
fullWidth
125+
onClick={handleSendTokens}
126+
disabled={isSending || !recipient || !amount}
127+
structure="base"
128+
>
129+
{isSending ? "SENDING..." : "SEND TOKENS"}
130+
</Button>
131+
132+
{txHash && (
133+
<div className="rounded border border-green-500/20 bg-green-500/10 p-3">
134+
<p className="text-xs text-green-400 font-medium mb-1">
135+
✓ Transaction Successful
136+
</p>
137+
<p className="text-xs text-gray-400 break-all">
138+
Hash: {txHash}
139+
</p>
140+
</div>
141+
)}
142+
143+
{txError && (
144+
<div className="rounded border border-red-500/20 bg-red-500/10 p-3">
145+
<p className="text-xs text-red-400">
146+
{txError}
147+
</p>
148+
</div>
149+
)}
150+
</div>
151+
152+
<Button
153+
fullWidth
154+
onClick={() => {
155+
if (logout) logout();
156+
}}
157+
structure="outlined"
158+
>
159+
DISCONNECT
160+
</Button>
161+
</>
162+
)}
163+
</div>
164+
165+
<Link
166+
href="/"
167+
className="mt-4 inline-block text-sm text-gray-400 underline hover:text-gray-300"
168+
>
169+
← Back to examples
170+
</Link>
171+
</main>
172+
);
173+
}

apps/demo-app/src/app/layout.tsx

Lines changed: 6 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,37 +5,13 @@ import { AbstraxionProvider } from "@burnt-labs/abstraxion";
55

66
const inter = Inter({ subsets: ["latin"] });
77

8-
// Example XION seat contract
9-
const seatContractAddress =
10-
"xion1z70cvc08qv5764zeg3dykcyymj5z6nu4sqr7x8vl4zjef2gyp69s9mmdka";
11-
12-
const legacyConfig = {
13-
contracts: [
14-
// Usually, you would have a list of different contracts here
15-
seatContractAddress,
16-
{
17-
address: seatContractAddress,
18-
amounts: [{ denom: "uxion", amount: "1000000" }],
19-
},
20-
],
21-
stake: true,
22-
bank: [
23-
{
24-
denom: "uxion",
25-
amount: "1000000",
26-
},
27-
],
28-
// Optional params to activate mainnet config
29-
// rpcUrl: "https://rpc.xion-mainnet-1.burnt.com:443",
30-
// restUrl: "https://api.xion-mainnet-1.burnt.com:443",
31-
};
32-
8+
// Treasury configuration from environment variables
339
const treasuryConfig = {
34-
treasury: "xion13uwmwzdes7urtjyv7mye8ty6uk0vsgdrh2a2k94tp0yxx9vv3e9qazapyu", // Example XION treasury instance for instantiating smart contracts
35-
gasPrice: "0.001uxion", // If you feel the need to change the gasPrice when connecting to signer, set this value. Please stick to the string format seen in example
36-
// Optional params to activate mainnet config
37-
// rpcUrl: "https://rpc.xion-mainnet-1.burnt.com:443",
38-
// restUrl: "https://api.xion-mainnet-1.burnt.com:443",
10+
chainId: "xion-testnet-1",
11+
treasury: process.env.NEXT_PUBLIC_TREASURY_ADDRESS,
12+
rpcUrl: process.env.NEXT_PUBLIC_RPC_URL || "https://rpc.xion-testnet-2.burnt.com:443",
13+
restUrl: process.env.NEXT_PUBLIC_REST_URL || "https://api.xion-testnet-2.burnt.com",
14+
gasPrice: "0.001uxion",
3915
};
4016

4117
export default function RootLayout({

apps/demo-app/src/app/page.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ export default function Page(): JSX.Element {
1010
ABSTRAXION
1111
</h1>
1212
<div className="flex w-full flex-col gap-2">
13+
<Link href="/direct-mode">
14+
<Button fullWidth structure="base">
15+
DIRECT MODE (NEW!)
16+
</Button>
17+
</Link>
1318
<Link href="/ui-less">
1419
<Button fullWidth structure="outlined">
1520
CUSTOM UI EXAMPLE
@@ -21,6 +26,9 @@ export default function Page(): JSX.Element {
2126
</Button>
2227
</Link>
2328
</div>
29+
<p className="text-center text-xs text-gray-500 mt-4">
30+
<strong>Direct Mode</strong> allows wallet connections without dashboard redirect
31+
</p>
2432
</main>
2533
);
2634
}

0 commit comments

Comments
 (0)