Overview
Smart Account requires the @magic-ext/smart-account extension.
Magic’s Smart Account extension lets you send transactions through an EIP-7702 smart account powered by Alchemy’s Smart Wallet infrastructure. With EIP-7702, the user’s existing EOA is the smart account — there is no separate contract deployment or address change.
This enables:
- Gas sponsorship — sponsor transaction fees for your users via an Alchemy Gas Manager policy
- Batched calls — send multiple contract calls in a single atomic transaction
- Cross-chain support — use any EVM chain supported by Alchemy
Prerequisites
Setup
Install the Smart Account extension:
npm install magic-sdk @magic-ext/smart-account
Initialize Magic with the extension:
import { Magic } from 'magic-sdk';
import { SmartAccountExtension } from '@magic-ext/smart-account';
const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', {
extensions: [
new SmartAccountExtension({
apiKey: 'YOUR_ALCHEMY_API_KEY',
paymasterPolicyId: 'YOUR_POLICY_ID', // optional — enables gas sponsorship
}),
],
});
Configuration Options
| Parameter | Type | Required | Description |
|---|
apiKey | string | Yes | Your Alchemy API key |
paymasterPolicyId | string | No | Alchemy Gas Manager policy ID. When provided, transaction fees are sponsored |
Sending a Transaction
Use smartAccount.sendTransaction() to send one or more calls through the smart account:
const result = await magic.smartAccount.sendTransaction({
chainId: 11155111, // Sepolia
calls: [
{
to: '0x1234567890123456789012345678901234567890',
value: '1000000000000000', // 0.001 ETH in wei
data: '0x',
},
],
});
console.log('Transaction hash:', result.transactionHash);
console.log('Call ID:', result.id);
Parameters
| Parameter | Type | Required | Description |
|---|
chainId | number | Yes | The chain ID for the transaction |
calls | array | Yes | Array of calls to execute |
Each call in the calls array accepts:
| Field | Type | Required | Description |
|---|
to | string | Yes | The recipient or contract address |
data | string | No | Encoded calldata for contract interactions |
value | string | No | Value to send in wei |
Response
| Field | Type | Description |
|---|
id | string | The call bundle ID |
transactionHash | string | undefined | The on-chain transaction hash |
chainId | number | The chain ID the transaction was sent on |
Batched Transactions
Send multiple calls in a single atomic transaction. All calls execute together — if one fails, they all revert.
const result = await magic.smartAccount.sendTransaction({
chainId: 11155111,
calls: [
{
to: '0xContractA',
data: '0x...', // approve
},
{
to: '0xContractB',
data: '0x...', // swap
},
],
});
console.log('Transaction hash:', result.transactionHash);
When a paymasterPolicyId is provided in the extension config, transaction gas fees are automatically sponsored through Alchemy’s Gas Manager. Users don’t need any ETH to send transactions.
const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', {
extensions: [
new SmartAccountExtension({
apiKey: 'YOUR_ALCHEMY_API_KEY',
paymasterPolicyId: 'YOUR_POLICY_ID', // enables sponsorship
}),
],
});
// This transaction's gas is sponsored — user needs no ETH
const result = await magic.smartAccount.sendTransaction({
chainId: 11155111,
calls: [{ to: '0x...', data: '0x...' }],
});
You can configure sponsorship rules (per-user limits, allowed contracts, chain restrictions) in the Alchemy Gas Manager dashboard.
Complete Example
import { Magic } from 'magic-sdk';
import { SmartAccountExtension } from '@magic-ext/smart-account';
const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', {
extensions: [
new SmartAccountExtension({
apiKey: 'YOUR_ALCHEMY_API_KEY',
paymasterPolicyId: 'YOUR_POLICY_ID',
}),
],
});
async function sendSponsoredTransaction() {
try {
// Authenticate the user
await magic.auth.loginWithEmailOTP({ email: '[email protected]' });
// Send a sponsored transaction
const result = await magic.smartAccount.sendTransaction({
chainId: 11155111,
calls: [
{
to: '0x1234567890123456789012345678901234567890',
value: '0',
data: '0x',
},
],
});
console.log('Transaction hash:', result.transactionHash);
console.log('Call ID:', result.id);
} catch (error) {
console.error('Transaction failed:', error);
}
}
Resources