
alchemy-embedded-wallets
by 2Rds
SKILL.md
name: Alchemy Embedded Wallets description: This skill should be used when the user asks about "Alchemy Account Kit", "embedded wallets", "MPC wallets", "Web Signer", "gas sponsorship", "iframe stamper", "wallet creation", "signTransaction", "signAndSendTransaction", "Alchemy signer", or needs to implement non-custodial embedded wallets with Alchemy's MPC infrastructure. Covers the complete Account Kit integration pattern used in BlockDrive. version: 0.1.0
Alchemy Embedded Wallets
Overview
Alchemy Account Kit provides MPC-based embedded wallets that allow users to have non-custodial Solana wallets without managing private keys. The wallet is created and controlled through multi-party computation (MPC), with the user authenticating via OIDC providers like Clerk.
When to Use
Activate this skill when:
- Setting up embedded wallet infrastructure
- Implementing wallet creation with Clerk JWT
- Configuring gas sponsorship policies
- Signing or sending transactions
- Troubleshooting wallet initialization issues
Core Architecture
┌─────────────────────────────────────────────────────────────────┐
│ MPC WALLET FLOW │
├─────────────────────────────────────────────────────────────────┤
│ 1. User authenticates with Clerk → Gets JWT │
│ 2. JWT submitted to AlchemySignerWebClient │
│ 3. Alchemy validates JWT with OIDC provider │
│ 4. MPC wallet created/retrieved for user │
│ 5. SolanaSigner wraps client for Solana operations │
│ 6. Transactions signed via MPC (user never sees private key) │
└─────────────────────────────────────────────────────────────────┘
Implementation Pattern
Required Packages
npm install @account-kit/signer @solana/web3.js
Initialize Web Signer Client
import { AlchemySignerWebClient, SolanaSigner } from '@account-kit/signer';
// Create the signer client with JWT authentication
const signerClient = new AlchemySignerWebClient({
connection: {
jwt: clerkSessionToken,
},
iframeConfig: {
iframeContainerId: 'alchemy-signer-iframe-container',
},
});
Authenticate with Clerk JWT
// Submit JWT for authentication
const authResponse = await signerClient.submitJwt({
jwt: clerkSessionToken,
authProvider: 'clerk',
});
// Complete authentication to get user info
const user = await signerClient.completeAuthWithBundle({
bundle: authResponse.bundle,
orgId: authResponse.orgId,
connectedEventName: 'connected',
authenticatingType: 'jwt',
});
Create Solana Signer
// Wrap the authenticated client for Solana operations
const solanaSigner = new SolanaSigner(signerClient);
// Get the wallet address
const walletAddress = solanaSigner.address;
console.log('MPC Wallet:', walletAddress);
Hidden Iframe Container
The MPC stamper requires a hidden iframe for secure key operations:
<div
id="alchemy-signer-iframe-container"
style={{
position: 'fixed',
top: '-9999px',
left: '-9999px',
width: '1px',
height: '1px',
opacity: 0,
pointerEvents: 'none'
}}
/>
Transaction Operations
Sign Transaction
Sign without sending (for inspection or multi-sig):
const signTransaction = async (
transaction: Transaction | VersionedTransaction
): Promise<Transaction | VersionedTransaction> => {
if (!solanaSigner) {
throw new Error('Wallet not initialized');
}
const signedTx = await solanaSigner.addSignature(transaction);
return signedTx;
};
Sign and Send with Gas Sponsorship
Send transaction with Alchemy paying gas fees:
const signAndSendTransaction = async (
transaction: Transaction | VersionedTransaction
): Promise<string> => {
if (!solanaSigner || !connection) {
throw new Error('Wallet not initialized');
}
let sponsoredTx: VersionedTransaction;
if (transaction instanceof Transaction) {
// Convert legacy to sponsored versioned transaction
const instructions = transaction.instructions;
sponsoredTx = await solanaSigner.addSponsorship(
instructions,
connection,
ALCHEMY_POLICY_ID
);
} else {
// Sign versioned transaction directly
sponsoredTx = await solanaSigner.addSignature(transaction);
}
// Send to network
const signature = await connection.sendRawTransaction(
sponsoredTx.serialize(),
{
skipPreflight: false,
preflightCommitment: 'confirmed',
}
);
return signature;
};
Sign Message
Sign arbitrary messages for verification:
const signMessage = async (message: Uint8Array): Promise<Uint8Array> => {
if (!solanaSigner) {
throw new Error('Wallet not initialized');
}
const signature = await solanaSigner.signMessage(message);
return signature as Uint8Array;
};
Gas Sponsorship
Policy Configuration
Gas sponsorship policies are configured in the Alchemy dashboard:
- Navigate to Account Kit → Gas Manager
- Create a new policy for Solana
- Set spending limits and allowed operations
- Copy the Policy ID
Using Sponsorship
// Policy ID from Alchemy dashboard
const ALCHEMY_POLICY_ID = 'your-policy-id';
// Add sponsorship to instructions
const sponsoredTx = await solanaSigner.addSponsorship(
instructions,
connection,
ALCHEMY_POLICY_ID
);
Sponsorship Limits
Configure policy limits in Alchemy dashboard:
- Daily spending cap (in SOL)
- Per-transaction limit
- Allowed transaction types
- Whitelisted programs
BlockDrive Implementation
Complete Provider Pattern
export function AlchemyProvider({ children }: { children: React.ReactNode }) {
const { session } = useSession();
const { isSignedIn, getToken } = useClerkAuth();
const [solanaAddress, setSolanaAddress] = useState<string | null>(null);
const [isInitialized, setIsInitialized] = useState(false);
const signerClientRef = useRef<AlchemySignerWebClient | null>(null);
const solanaSignerRef = useRef<SolanaSigner | null>(null);
const initializeWallet = useCallback(async () => {
if (!isSignedIn || !session) return;
const token = await getToken();
if (!token) throw new Error('Failed to get Clerk token');
// Initialize signer
const signerClient = new AlchemySignerWebClient({
connection: { jwt: token },
iframeConfig: { iframeContainerId: 'alchemy-signer-iframe-container' },
});
// Authenticate
const authResponse = await signerClient.submitJwt({
jwt: token,
authProvider: 'clerk',
});
await signerClient.completeAuthWithBundle({
bundle: authResponse.bundle,
orgId: authResponse.orgId,
connectedEventName: 'connected',
authenticatingType: 'jwt',
});
// Create Solana signer
const solanaSigner = new SolanaSigner(signerClient);
signerClientRef.current = signerClient;
solanaSignerRef.current = solanaSigner;
setSolanaAddress(solanaSigner.address);
setIsInitialized(true);
}, [isSignedIn, session, getToken]);
// Auto-initialize on sign in
useEffect(() => {
if (isSignedIn && !isInitialized) {
initializeWallet();
}
}, [isSignedIn, isInitialized, initializeWallet]);
// ... context provider
}
Troubleshooting
Wallet Not Initializing
- Verify Clerk session is active (
isSignedIn === true) - Check JWT token is valid and not expired
- Ensure iframe container exists in DOM before initialization
- Verify Alchemy API key is correct
Transaction Signing Fails
- Check wallet is initialized (
isInitialized === true) - Verify transaction is properly constructed
- For sponsored transactions, verify policy ID is correct
- Check network matches (devnet vs mainnet)
Gas Sponsorship Not Working
- Verify policy ID matches Alchemy dashboard
- Check policy hasn't exceeded spending limits
- Ensure transaction type is allowed by policy
- Verify policy is active for correct network
Additional Resources
Reference Files
For detailed patterns and troubleshooting:
references/account-kit-api.md- Complete Account Kit API referencereferences/gas-policies.md- Gas sponsorship configuration guide
Examples
Working implementations in examples/:
wallet-provider.tsx- Complete React providersign-transaction.ts- Transaction signing patterns
Score
Total Score
Based on repository quality metrics
SKILL.mdファイルが含まれている
ライセンスが設定されている
100文字以上の説明がある
GitHub Stars 100以上
3ヶ月以内に更新がある
10回以上フォークされている
オープンIssueが50未満
プログラミング言語が設定されている
1つ以上のタグが設定されている
Reviews
Reviews coming soon