# Custom Email Template Source: https://docs.magic.link/embedded-wallets/authentication/customization/custom-email-template ## Overview Magic's Custom Email Template feature allows you to fully tailor your app's login email using your own custom HTML. With this feature, developers can customize emails that use one-time passcodes. **Note: Custom email templates require using your own existing email provider.** You can register a custom email provider through the Settings page in the Magic Dashboard. This feature is available to developers on our Growth or Enterprise Plans - you can head [here](https://magic.link/pricing) to learn more. ### Configuration Custom Email Templates can be figured in the [Magic Dashboard](https://dashboard.magic.link). Navigate to **Customization**, then **Email** to create and edit templates. The following is a list of variables that can be used when creating a custom email template in Magic. From the dashboard editor, variables must be wrapped in `{{}}` in order to be resolved to their value. For example, to use the one-time passcode, insert `{{otp}}` into the template. * `otp`: One-time passcode. Example: 412320 * `app.name`: The name of the application. Example: "My test app" * `user.email`: Email address of the user receiving the login email. Example: [joe@magic.link](mailto:joe@magic.link) * `template.locale`: Language of the user receiving the login email. Example: en-US * `login.device.browser`: Browser of the user receiving the login email. Example: Chrome * `login.device.os`: Operating system of the user receiving the login email. Example: MacOS * `login.timestamp`: Timestamp of when email was requested ## Localization Custom email templates support Jinja-like conditional syntax for creating localized content based on the user's locale. This allows you to display different text depending on the `template.locale` value. ### Conditional Syntax The template parser supports the following conditional structures: ```Jinja theme={null} {% if condition %} content {% endif %} {% if condition %} content {% else %} fallback content {% endif %} {% if condition %} content {% elif condition %} alternative {% else %} fallback {% endif %} ``` ### Supported Operators | Operator | Description | Example | | -------------- | --------------------- | ---------------------------------------------------- | | `==` | Equality comparison | `template.locale == 'ja'` | | `!=` | Inequality comparison | `template.locale != 'en-US'` | | `startswith()` | Prefix matching | `template.locale.startswith('zh')` | | `endswith()` | Suffix matching | `template.locale.endswith('-US')` | | `and` | Logical AND | `template.locale == 'ja' and app.name` | | `or` | Logical OR | `template.locale == 'ja' or template.locale == 'ko'` | ### Examples **Basic locale switching:** ```Jinja theme={null} {% if template.locale == 'es' %} Código de inicio de sesión {% else %} Login Code {% endif %} ``` **Matching locale variants with `startswith()`:** ```Jinja theme={null} {% if template.locale == 'zh-CN' or template.locale.startswith('zh-') %} 登录验证码 {% elif template.locale == 'ja' %} ログインコード {% else %} Your login code is: {{otp}} {% endif %} ``` **Multiple language support:** ```Jinja theme={null} {% if template.locale == 'es' %} Tu código de inicio de sesión es: {{otp}} {% elif template.locale == 'fr' %} Votre code de connexion est: {{otp}} {% elif template.locale == 'de' %} Ihr Anmeldecode lautet: {{otp}} {% else %} Your login code is: {{otp}} {% endif %} ``` * `af` - Afrikaans * `az` - Azerbaijani * `bg` - Bulgarian * `ca` - Catalan * `cs` - Czech * `cy` - Welsh * `cy-GB` - Welsh (UK) * `da` - Danish * `de` - German * `el` - Greek * `en-US` - English (US) * `es` - Spanish * `et` - Estonian * `fi` - Finnish * `fr` - French * `hr` - Croatian * `hu` - Hungarian * `id` - Indonesian * `it` - Italian * `ja` - Japanese * `ko` - Korean * `lt-LT` - Lithuanian * `lv-LV` - Latvian * `mk-MK` - Macedonian * `nl` - Dutch * `no` - Norwegian * `pl` - Polish * `pt` - Portuguese * `ro` - Romanian * `ru` - Russian * `sk` - Slovak * `sl-SI` - Slovenian * `sr` - Serbian * `sv` - Swedish * `th` - Thai * `tr` - Turkish * `vi` - Vietnamese * `zh-CN` - Chinese (Simplified) * `zh-TW` - Chinese (Traditional) ### Preview with Locale Selector When editing your template in the Magic Dashboard, you can preview how your email will appear in different languages using the locale selector in the preview panel. The selector will automatically detect which locales are used in your template's conditional statements and show only those options. ## Usage > ⁠**ℹ️** **This feature is supported in Magic SDK 21.1.0 and above.** ### Testing Once a template has been created, you can use the **Send Test** button at the bottom of the customization page to send an email to the account that is currently logged in. Note that test emails will contain placeholder values for `otp` and `magic_link`. You can also view a live preview of your email in a separate browser window by selecting **Preview** from the overflow menu at the top of the page. Testing the full end-to-end user experience will require publishing your template to a live development environment. To ensure the best results, we recommend utilizing a dedicated email testing service such as [Litmus](https://www.litmus.com/). ### Production In order to use the newly created template, the `template name` must be passed in as an argument into the login method. ```javascript theme={null} //one-time passcode magic.auth.loginWithEmailOTP({ email: 'john@example.com', overrides: { variation: 'template name', } }) ``` # Custom Email Provider Source: https://docs.magic.link/embedded-wallets/authentication/customization/custom-smtp ## Overview Custom Email Provider is a premium feature that allows developers to configure the sender of login emails by routing emails through their own SMTP servers. Emails sent from our servers come from the default `noreply@trymagic.com`. Enabling a custom SMTP gives you full control over where your application's login emails are sent from, as well as the name of the sender. Custom email providers are configured at the application level by visiting the Settings section of the [Dashboard](https://dashboard.magic.link). ## Configuration Once Custom Email Provider has been unlocked for your workspace, you can configure your own custom SMTP server. Currently, only the Microsoft provider is supported for OAuth authentication. Refer to your SMTP provider to determine whether to use Basic Auth or OAuth. ### Basic Auth SMTP-Basic-Auth * **Sender email**: The email address your app's login emails will be sent from. E.g. `example@your-domain.com` * ⁠**Note:** Do **not** to use your dashboard user account email as the `Sender Email`, or try to login with the `Sender Email` * **Sender name (optional)**: The name that appears on the subject line of the login email. Defaults to sender email address if not provided. * **Host**: Hostname for your SMTP server. E.g. `smtp.postmarkapp.com` * **Port**: Port of your SMTP server * **Username, Password**: Credentials to authenticate into your SMTP server ### OAuth SMTP-OAuth * **Sender email**: The email address your app's login emails will be sent from. E.g. `example@your-domain.com` * ⁠**Note:** Do **not** to use your dashboard user account email as the `Sender Email`, or try to login with the `Sender Email` * **Sender name (optional)**: The name that appears on the subject line of the login email. Defaults to sender email address if not provided. * **Provider**: OAuth Provider. E.g. `Microsoft` * **Client ID**: The Client ID of your provider’s OAuth App * **Client Secret**: The Client Secret of your provider’s OAuth App * **Tenant ID**: Your Microsoft Tenant ID⁠ * **Note:** This field is required because, currently, only the `Microsoft` provider is supported ### How to Set Up the Microsoft OAuth Provider You need an Office 365 subscription to use Microsoft SMTP Server. 1. Log in to the Microsoft Azure portal * Use your organization credentials to access the [Microsoft Azure registration portal](https://learn.microsoft.com/en-us/azure). Refer to the official Microsoft documentation for more details. 2. Register a new application * Provide an application name * Select the supported account type * Enter the redirect URL (optional) * Click **Register** 3. Copy the Client ID 4. Create a Client Secret * In Microsoft Azure, go to **Manage -> Certificates & secrets** * Click **New client secret** * Provide a **description** and set an **expiration period** * Select **Add** * Copy the **Secret** value 5. Enable the `SMTP.SendAsApp` permission * In Microsoft Azure, go to **Manage -> API Permissions** * Select **Add a permission** * Choose the *APIs my organization uses* tile * Select **Office 365 Exchange Online** * Choose **Application permissions** * In the *Select permissions* field, search for *SMTP.SendAsApp* * Check the box for *SMTP.SendAsApp* * Click **Add permissions** * Select **Grant admin consent for ``**, then click **Yes** to confirm 6. Register service principals in Microsoft Exchange using PowerShell * Run the following commands ```powershell PowerShell theme={null} Install-Module -Name ExchangeOnlineManagement Import-module ExchangeOnlineManagement Connect-ExchangeOnline -Organization ``` ⁠For more information, see [Authenticate SMTP connection using OAuth](https://learn.microsoft.com/en-us/exchange/client-developer/legacy-protocols/how-to-authenticate-an-imap-pop-smtp-application-by-using-oauth) * Register the service principal of a Microsoft Entra ID application by running the following command ```powershell PowerShell theme={null} New-ServicePrincipal -AppId -ObjectId ``` **Important**: To obtain the `YOUR_APPLICATION_ID` value, navigate to **Overview -> Managed applications** in local directory and copy the Object ID.  ## Send a Test Email The **Send test email** button will be enabled after saving a valid custom SMTP configuration. It will attempt to send a test email from the specified custom SMTP configuration to the email of the Magic Dashboard user who is currently logged in. If you do not receive your test email within 1-2 minutes, please check your configuration settings and try again. If you run into any issues during setup, please reach out via our help widget in the bottom-right corner of this page. # Custom Twilio Configuration Source: https://docs.magic.link/embedded-wallets/authentication/customization/custom-twilio-configuration ## Introduction We understand that every business has unique communication needs, including specific country restrictions, compliance requirements, and cost optimizations. With our Custom Twilio Configuration integration, you can connect your own Twilio account to our system and send SMS messages using your own Twilio credentials. This guide walks you through the process of setting up and activating your Twilio integration in a few simple steps. **Why Use Your Own Twilio Account?** By connecting your Twilio account, you can: * Control which countries your SMS messages can be sent to * Manage your own Twilio usage and billing directly * Ensure messages are sent in compliance with your own business policies * Monitor and troubleshoot SMS delivery from your Twilio Console ## Step 1: Get Your Twilio Credentials Before you can enable your Twilio integration, you need to collect a few key pieces of information from your Twilio account. ### Where to Find Your Twilio Credentials 1. Log in to your [Twilio Console](https://www.twilio.com/console) 2. Navigate to **Account Dashboard** to find your **Account SID** twilio-account-dashboard 3. Go to **API keys & tokens** and create a new **API Key** (recommended: [Standard API Key](https://www.twilio.com/docs/iam/api-keys#types-of-api-keys)) twilio-create-api-key 4. Navigate to **Messaging → Services** and **Create Messaging Service** twilio-create-messaging-service 5. Copy the **Messaging Service SID** after creation ### How to Add a Sender to the Sender Pool in Messaging Service Currently, we support the following Sender Types: * Phone Number * WhatsApp Number\* \*WhatsApp Number requires a specific **Content Template** approved by WhatsApp. 1. Navigate to **Messaging → Services →** your **Messaging Service → Sender Pool** and **Add Senders** twilio-add-senders 2. Choose the **Phone Number** Sender Type. The account must have an SMS-capable Twilio number to use this sender type. ### How to Add a Twilio Verify Service A Twilio Verify Service enables additional verification channels required to enable the [account recovery](embedded-wallets/authentication/features/account-recovery#account-recovery) feature. 1. Navigate to **Verify → Services** in your Twilio Console and click **Create new** 2. Enter a **Friendly Name** for your service (e.g., "My App Verification") 3. Configure the verification channels (SMS, Email) for your service 4. Click **Create** to create the service 5. Copy the **Service SID** after creation ### How to Add a WhatsApp Number Sender WhatsApp [requires authentication templates](https://developers.facebook.com/docs/whatsapp/business-management-api/authentication-templates) to include one-time password (OTP) buttons as part of the approval process. These templates must adhere to predefined formats set by WhatsApp and cannot be customized by businesses. These authentication template formats are supported in Content Template Builder which supports basic authentication message, and optional expiry code and security advisory 1. Navigate to **Messaging → Content Template Builder → Create New** 2. Choose the **Authentication** Content Type twilio-whatsapp-auth-type 3. Configure **Content Fields** as shown in the Screenshot. Click **Save and submit for WhatsApp approval** twilio-whatsapp-content-template 4. Copy the **Content Template SID** after creation and receiving WhatsApp approval ## Step 2: Enter Your Twilio Credentials in the Dashboard Now that you have your Twilio credentials, you need to enter them in your account settings in our Dashboard. 1. Log in to your Dashboard 2. Go to **Settings → Custom Twilio Configuration** 3. Fill in the following details: * Account SID * API Key * API Secret * Messaging Service SID * Content Template SID (optional) dashboard-fields 4. **Save** Configuration Once saved, our system will automatically start using your Twilio settings to send SMS messages on your behalf. ## Step 3: Country Restrictions Your Twilio account allows you to control which countries your messages can be sent to. To check and update this: 1. Log in to your **Twilio Console** 2. Go to **Messaging → Settings → Geo Permissions** 3. Select the countries you want to allow or block twilio-geo-permissions ## Step 4: Managing & Updating Your Twilio Configuration ### Updating Your Twilio Credentials If you ever need to update your Twilio credentials (e.g., changing API keys for security reasons), simply: 1. Go to **Settings → Custom Twilio Configuration** in the Dashboard 2. Edit the fields with your new credentials 3. Click **Save** Configuration ### Disabling Your Twilio Integration If you want to temporarily disable your Twilio integration and switch back to our global SMS provider: 1. Go to **Settings → Custom Twilio Configuration** 2. Toggle off the Twilio configuration twilio-disable-integration Your messages will now be sent using our default SMS provider. ## Conclusion By following these steps, you have successfully connected your Twilio account to our system. You now have full control over your SMS messaging while leveraging the power of Twilio's infrastructure. If you need further assistance, feel free to reach out to our support team. ## Frequently Asked Questions (FAQ) **What happens if I enter incorrect Twilio credentials?** If the credentials are incorrect, SMS messages will fail to send. You can check for errors in your Twilio Console under [Messaging Logs](https://www.twilio.com/docs/messaging/features/messaging-logs). **Can I use multiple Twilio accounts?** Currently, only one Twilio account can be linked to your dashboard at a time. If you need to switch accounts, update the credentials in **Settings → Custom Twilio Configuration**. **What if my SMS messages are not delivering?** * Check your Twilio Console [Messaging Logs](https://www.twilio.com/docs/messaging/features/messaging-logs) for errors * Ensure the destination country is allowed in **Twilio Geo Permissions** * Verify that your **Messaging Service SID** is correct **What happens if I do not configure Twilio?** A Twilio configuration is required to utilize SMS features like logging in with SMS. If you do not setup and activate your Twilio integration SMS messages will fail to send and users will not be able to login with SMS. # Localization Source: https://docs.magic.link/embedded-wallets/authentication/customization/localization Magic allows you to customize the popup modal, email, and confirmation screen to any of our 30+ supported languages. ## Usage To initialize magic with a specific language, pass the `locale` value to the `Magic` instance upon creation. ```javascript JavaScript icon="square-js" theme={null} import { Magic } from "magic-sdk"; const magic = new Magic('API_KEY', { locale: 'es' }); const did = await magic.auth.loginWithMagicLink({ email }); ``` email modal ## Supported Languages | | | | --------------------- | --------------- | | **Language** | **Locale Code** | | Afrikaans | `af` | | Azerbaijani | `az` | | Bulgarian | `bg` | | Catalan | `ca` | | Chinese (Simplified) | `zh_CN` | | Chinese (Traditional) | `zh_TW` | | Croatian | `hr` | | Czech | `cs` | | Danish | `da` | | Dutch | `nl` | | English | `en` | | Estonian | `et` | | Finnish | `fi` | | French | `fr` | | German | `de` | | Greek | `el` | | Hungarian | `hu` | | Indonesian | `id` | | Italian | `it` | | Japanese | `ja` | | Korean | `ko` | | Latvian | `lv` | | Lithuanian | `lt` | | Macedonian | `mk` | | Norwegian | `no` | | Polish | `pl` | | Portuguese | `pt` | | Romanian | `ro` | | Russian | `ru` | | Serbian | `sr` | | Slovak | `sk` | | Slovenian | `sl` | | Spanish | `es` | | Swedish | `sv` | | Thai | `th` | | Turkish | `tr` | | Vietnamese | `vi` | | Welsh | `cy` | # Login UI Source: https://docs.magic.link/embedded-wallets/authentication/customization/login-ui ## Overview Magic's authentication can enable passwordless Web3 onboarding (no seed phrases) using multiple configurable methods. Each method either creates an account address for the user (non-custodial) or utilizes an existing account address. This is handled completely by Magic with out-of-the-box UI, with no lift from the integrating dApp. ### Compatibility The Login UI is available via the following client-side SDKs: * [Web](/embedded-wallets/sdk/client-side/javascript) * [React Native](/embedded-wallets/sdk/client-side/react-native) * [iOS](/embedded-wallets/sdk/client-side/ios) * [Android](/embedded-wallets/sdk/client-side/android) ## Use Cases * Allow your users to log in and/or sign up to your dApp using their preferred authentication method and gain access to their public wallet address on the network you are connected to. * You can collect a signed token for wallet address verification and skip the need to explicitly request a personal signature which would prompt an additional screen. dedicated-widget-login ## Authentication Methods ### Email One-Time Passcode If a user chooses to authenticate through their email, they will receive a unique code to their email that is generated per attempt and the user will be required to enter it to authenticate. OTPs for email provide a simple and effective way to increase security and ensure the safety of user assets. ## Usage ### Login ```javascript JavaScript icon="square-js" theme={null} import { Magic } from "magic-sdk" const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: "sepolia", }); const accounts = await magic.wallet.connectWithUI(); ``` The example above authenticates the user and connects to the Ethereum Sepolia Testnet. To connect to a different network, simply replace the configuration with another of [the 30+ chains supported by Magic](/embedded-wallets/blockchains/overview). ## Device Registration Find out more about Device Registration [here](/embedded-wallets/authentication/security/device-registration). ## Configuration * See how to brand this experience with your own logo and colors in the [customization section](https://dashboard.magic.link/app/branding). ## Resources * [`magic.wallet.connectWithUI()`](/embedded-wallets/sdk/client-side/javascript#connectwithui) # Data Export Source: https://docs.magic.link/embedded-wallets/authentication/data/data-export ## Overview Data for which users are using your application can be found in the **Users** tab in your [Magic Dashboard](https://dashboard.magic.link). Basic attributes are provided per user, including their email address, when they signed up and when they last logged in. Customers who are Startup and Growth subscribers will also have the ability to export data into a CSV format. This is available via the `Export CSV` button in the top right hand corner of the page. # GDPR Deletion Request Source: https://docs.magic.link/embedded-wallets/authentication/data/deletion-request Once a deletion request is submitted, the user will permanently lose access to their wallet and cannot be recovered. Available to users whose primary login is an email. ## Overview The **Deletion Request API** enables developers to delete users' information in compliance with GDPR. This API is designed for applications that require bulk deletion operations and supports authentication using the application's secret key. ## Usage ### Request The bulk deletion endpoint supports deleting multiple user accounts in a single API call. To use this endpoint, send a POST request to the following URL with a payload that includes the list of email addresses associated with the accounts you want to delete. ```bash theme={null} curl --location 'https://api.magic.link/v1/admin/user/deletion/request' \ --header 'X-Magic-Secret-Key: ' \ --header 'Content-Type: application/json' \ --data-raw '{ "emails": ["user1@example.com", "user2@example.com"] }' ``` ### Response #### Success Upon successful request processing, the API returns a response with lists of processed and unprocessed emails. * `processed`: An array of emails that have been queued for deletion. * `unprocessed`: An array of emails that were not found and hence not processed. ```bash theme={null} { "processed": ["user1@example.com"], "unprocessed": ["user2@example.com"] } ``` #### Failure In the event of an error, the API will return an error code and message. * `400 Bad Request`: Your request is invalid. This often occurs due to missing the `emails` field in the request body. * `401 Unauthorized`: Your authorization token is missing or invalid. * `429 Too Many Requests`: You have hit the rate limit for deletion requests. Please wait before sending more requests. # Email Logs Source: https://docs.magic.link/embedded-wallets/authentication/data/email-logs ## Overview Email logs allows developers to get visibility into the lifecycle of an email login. It provides granular information like when a login was initiated and when it was completed for email OTPs. This is useful for debugging customer support tickets and understanding basic insights. To access it, navigate to the **Email Logs** tab in your [Magic Dashboard](https://dashboard.magic.link). This is a beta feature and only includes email based login events - additional methods like social logins are coming soon. For feedback, please reach out [here](https://magic-fortmatic.typeform.com/event-feedback). Events in the table can take up to a minute to populate from the time the event has taken place. Events can be filtered by email address from the search bar at the top of the table. Click on any event to reveal more information about the event, including the associated IP address and User Agent. ## Events Below is a list of all possible events and their definitions | | | | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Email Login Initiated | A user has requested an OTP | | Login Email Sent | Email provider has sent the login email | | Login Email Delivered | The login email has been received by the mail server | | Login Email Opened | The user has opened the login email | | Login Success | The user has successfully logged in | | Login Email Bounced | Email was not delivered. See here for more information:

[https://docs.sendgrid.com/glossary/bounces](https://docs.sendgrid.com/glossary/bounces)

[https://postmarkapp.com/support/article/815-what-are-bounces-and-spam-complaints](https://postmarkapp.com/support/article/815-what-are-bounces-and-spam-complaints) | | Error | There was an error during the login. See the dashboard for more details. | | New Device Detected | The user is logging in from a new device. | | Device Registration Email Sent | Email provider has sent the device registration email. | | Device Registration Email Delivered | The device registration email has been received by the mail server. | | Device Registration Email Opened | The user has opened the device registration email. | | Device Registration Approved | User has approved the device from the email. | | Device Registration Rejected | User has rejected the device from the email. | | Device Registration Email Bounced | Email was not delivered. See here for more information:

[https://docs.sendgrid.com/glossary/bounces](https://docs.sendgrid.com/glossary/bounces)

[https://postmarkapp.com/support/article/815-what-are-bounces-and-spam-complaints](https://postmarkapp.com/support/article/815-what-are-bounces-and-spam-complaints) | # Account Recovery Source: https://docs.magic.link/embedded-wallets/authentication/features/account-recovery ## Overview This feature requires two prerequisites: 1. The primary login factor is an email (this includes `loginWithMagicLink` and `loginWithEmailOTP` implementations) 2. The user can prove ownership of a phone number, which will be used as the recovery factor ### Compatibility Account recovery methods are available on the following client-side SDKs: * [Web](/embedded-wallets/sdk/client-side/javascript) * [React Native](/embedded-wallets/sdk/client-side/react-native) * [iOS](/embedded-wallets/sdk/client-side/ios) * [Android](/embedded-wallets/sdk/client-side/android) ## Usage ## Enabling Account Recovery enable-recovery-method To enable SMS recovery for your users: ```javascript JavaScript icon="square-js" theme={null} ⁠magic.user.showSettings(); ``` This will display a settings modal where users will be able to add a recovery factor. Additionally, Magic supports a deep linking flow where you can send users straight to adding a recovery factor. [The flow can also be whitelabeled](/embedded-wallets/sdk/client-side/javascript#show-settings-show-ui-false-page-recovery): ```javascript JavaScript icon="square-js" theme={null} magic.user.showSettings({ page: 'recovery' }); ``` ## Recover Account use-recovery-method Once a recovery factor is a set up, a user can recover their account. To enable this, the developer should call the following: ```javascript JavaScript icon="square-js" theme={null} magic.user.recoverAccount({ email: email }) ``` It’s important to note that updating the recovery factor is a security sensitive operation so users will first be prompted to demonstrate account ownership by authenticating their email via a one-time-passcode before they are able to add a recovery phone number. # Decentralized ID (DID) Tokens Source: https://docs.magic.link/embedded-wallets/authentication/features/decentralized-id ## Overview Decentralized ID (DID) tokens are used as cryptographically-generated proofs that are used to manage user access to your application's resource server. By adapting W3C's [Decentralized Identifiers](https://w3c-ccg.github.io/did-primer/) (DID) protocol, the **DID token** created by the Magic client-side SDK (see [`getIdToken`](/embedded-wallets/sdk/client-side/javascript#getidtoken)) leverages the [Ethereum](https://ethereum.org/) blockchain and [elliptic curve cryptography](https://en.wikipedia.org/wiki/Elliptic-curve_cryptography) to generate verifiable proofs of identity and authorization. These proofs are encoded in a lightweight, digital signature that can be shared between client and server to manage permissions, protect routes and resources, or authenticate users. The DID token is encoded as a Base64 JSON string tuple representing `**[proof, claim]**`: * `proof`: A digital signature that proves the validity of the given `claim`. * `claim`: Unsigned data the user asserts. This should equal the `proof` after Elliptic Curve recovery. ```javascript JavaScript icon="square-js" theme={null} const claim = javascript.stringify({ ... }); // Data representing the user's access const proof = sign(claim); // Sign data with Ethereum's `personal_sign` method const DIDToken = btoa(JSON.stringify([proof, claim])); ``` ### Use Cases * Users have control over their own identity without relying on a central authority * DIDs can be used for secure authentication and authorization processes ### Usage ### Generating a DID Token (Pseudo-code) ```javascript JavaScript icon="square-js" theme={null} // Construct the user's claim const claim = JSON.stringify({ iat: Math.floor(Date.now() / 1000), ext: Math.floor(Date.now() / 1000) + lifespan, iss: `did:ethr:${user_public_address}`, sub: subject, aud: audience, nbf: Math.floor(Date.now() / 1000), tid: uuid(), }); // Sign the claim with the user's private key // (this way the claim is verifiable and impossible to forge). const proof = sign(claim); // Encode the DIDToken so it can be transported over HTTP. const DIDToken = btoa(JSON.stringify([proof, claim])); ``` ### Token Specification | | | | ----- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Key | Description | | `iat` | Issued at timestamp (UTC in seconds). | | `ext` | Expiration timestamp (UTC in seconds). | | `nbf` | Not valid before timestamp (UTC in seconds). | | `iss` | Issuer (the signer, the "user"). This field is represented as a [Decentralized Identifier](https://w3c-ccg.github.io/did-primer/#the-format-of-a-did) populated with the user's Ethereum public key. | | `sub` | The "subject" of the request. This field is populated with the user's *Magic entity ID*. Note: this is separate from the user's Ethereum public key. | | `aud` | Identifies the project space. This field is populated with the *application's Magic entity ID.* | | `add` | An encrypted signature of arbitrary, serialized data. The usage of this field is up to the developer and use-case dependent. It's handy for validating information passed between client and server. *The raw data must already be known to the developer in order to recover the token!* | | `tid` | Unique token identifier. | ## Resources * [Decentralized ID - (W3C)](https://www.w3.org/TR/did-core/) # Multi-factor Authentication Source: https://docs.magic.link/embedded-wallets/authentication/features/mfa Multi-factor authentication is a common technique used to add an additional layer of security to an account. This means a secondary factor is validated along with the existing primary factor in order to login to an account. ## Overview Typically, the primary factor is an email and the secondary factor is a phone number or mobile device authenticator. The idea is that both factors must be compromised in order for an account to be breached. There are many forms of both primary and secondary factors. Magic offers end-user MFA through mobile authenticator apps like Authy or Google Authenticator. This is currently supported for email, SMS, and social login primary factors. ### Compatibility Cannot use with white-label login flows. Multi-factor auth is currently compatible with end-user accounts created via email OTP or SMS login. Multi-factor auth SDK methods are available via the following client-side SDKs: * [Web](/embedded-wallets/sdk/client-side/javascript) * [React Native](/embedded-wallets/sdk/client-side/react-native) * [iOS](/embedded-wallets/sdk/client-side/ios) * [Android](/embedded-wallets/sdk/client-side/android) ### Benefits of MFA The most obvious benefit of MFA is increased security. Magic's MFA increases your users' security by requiring additional proof of ownership to their wallet. If a bad actor has found a way to compromise a user’s primary email or SMS, Magic's MFA provides a second layer of protection to prevent account compromise. Enabling MFA can also help you meet regulatory requirements. If you are required to meet HIPAA, PCI, or CJIS compliance standards, then MFA should be enabled for your users. This does come with some drawbacks, specifically a lengthier sign-in process for end users. Requiring MFA during initial registration can also decrease user conversion rates by increasing friction. A common way to mitigate this is by nudging users to enable MFA after initial registration. Magic was developed to give you maximum flexibility when you want to enroll your users into MFA. You can nudge them to enroll at the end of user registration or when they have hit a milestone on their user journey that may benefit from the enhanced security of MFA - for example, once they start to hold assets in their wallet. ## Usage ### Implementation **Unlocking MFA** Multi-factor auth is a premium feature available to all customers for an additional monthly charge. To unlock MFA for your workspace, please upgrade to the [**Startup Plan**](https://dashboard.magic.link/checkout/upgrade-to-startup) within your developer dashboard. **Pre-requisites** * [Client-side SDK](/embedded-wallets/sdk/client-side/javascript#installation) * Feature unlocked by upgrading to the Startup * Feature enabled through Multi-factor Auth Dashboard page MFA-Disabled **Add MFA to your app** You can allow users to add MFA via one of several SDK method options * `magicClient.user.showSettings()` - this will bring up the entire settings modal, and one of those settings options will allow users to enable or disable MFA. * `magicClient.user.enableMFA()` - this will deeplink the user to the start of the enable MFA flow. The counter part to this is `magicClient.user.disableMFA()` Calling the SDK method [`magic.user.getInfo()`](/embedded-wallets/sdk/client-side/javascript#getinfo) will return whether the user has MFA enabled or not. This can be used to progressively introduce MFA to users via a banner reminding them to enable it or similar. ### Disabling MFA To disable MFA, remove any settings implementation for the Magic SDK or Login Form. Existing users with MFA enabled will still be able to use their second factor to login. ### End-user account recovery **MFA recovery codes** When registering for MFA, end users are given one-time use recovery codes. If an end-user loses access to their MFA, they can use their one-time recovery code to self-service recover their account. Using the recovery code will authenticate the user and deactivate MFA in the process for the given device. The user will be given a new recovery code after they complete MFA enrollment. recovery-code **Admin reset** If a user loses their recovery code, you can reset their MFA via the Users section in the Magic Dashboard. Simply search for the user, and use the action menu on the right side to disable MFA. You will be asked to confirm the user again before MFA will be disabled. Admin-Reset # Email OTP Source: https://docs.magic.link/embedded-wallets/authentication/login/email-otp With Magic, you can use Email one-time codes as an authentication mechanism, giving users a simple way to log in using their emails. ### Compatibility Email OTP is available on all [client-side SDKs](/embedded-wallets/sdk/overview#client-side-sdk). ## Use Cases * Log in and create wallets for end users with a one-time code sent to their email address that they will input for authentication ## Usage You can use Email OTP login by creating a project with our [CLI tool](/embedded-wallets/quickstart/overview) and picking 'Quickstart' when prompted. Alternatively, you can integrate Email OTP directly into your existing projects using the sample below. **Refer to the** [**API documentation**](/embedded-wallets/sdk/overview) **for information on how to install and initialize Magic for your existing project.** Once you've created a Magic instance, log users in using `loginWithEmailOTP`. In addition to the user's email address, you can provide a boolean indicating whether or not to show a pre-built modal interface directing the user to enter their one-time passcode. When `false`, you can implement a custom UI to continue the email OTP flow. Once logged in, you will receive a DID token that can be used with our [Admin SDK](/embedded-wallets/sdk/server-side/node#token-module) to verify the user's information and wallet address on the backend. You can also retrieve the user's wallet address and email using [`getInfo`](/embedded-wallets/sdk/client-side/javascript#getinfo) for Web/React Native and `getMetadata` for iOS, Android, and Flutter. ```javascript JavaScript icon="square-js" theme={null} const login = async (emailAddress, showUI) => { try { const did = await magic.auth.loginWithEmailOTP({ email: emailAddress, showUI: showUI}); console.log(`DID Token: ${did}`); const userInfo = await magic.user.getInfo(); console.log(`UserInfo: ${userInfo}`); // Handle user information as needed } catch { // Handle errors if required! } } ``` ## Resources * [Quickstart](/embedded-wallets/quickstart/overview) * [Email OTP Wallets on EVM chains](https://magic.link/posts/magic-evm-nextjs-guide) * [Web API documentation](/embedded-wallets/sdk/client-side/javascript#loginwithemailotp) * [React Native API documentation](/embedded-wallets/sdk/client-side/react-native#loginwithemailotp) * [iOS API documentation](/embedded-wallets/sdk/client-side/ios#loginwithemailotp) * [Android API documentation](/embedded-wallets/sdk/client-side/android#loginwithemailotp) * [Flutter API documentation](/embedded-wallets/sdk/client-side/flutter#loginwithemailotp) # Farcaster Login Source: https://docs.magic.link/embedded-wallets/authentication/login/farcaster ## Overview Provide users the ability to log into your app via Farcaster. This login method allows Farcaster users to login with their account and create a Magic wallet associated to their digital Farcaster Identifier (FID). Developers can access Farcaster user information such as `fid` and `username`, along with Magic metadata. * [⁠Test out Farcaster login here!](https://farcaster-example.vercel.app/magic-sdk) ### Compatibility Farcaster Login is only available as of `magic-sdk@28.0.7`. ## Use Cases * Authenticate and create wallets for end users by prompting users to login via Farcaster native app. ## Usage You can implement Farcaster Login by including the Farcaster extension to your Magic instance. Once you've created a Magic client-side instance with the extension, log users in by calling `magicClient.farcaster.login()`. You can pass a `showUI` parameter boolean indicating whether or not to show a pre-built modal interface displaying the QR code to the user. When `false`, you can implement a custom UI to continue the Farcaster login. Once logged in, you can retrieve the user's Magic wallet address using [`getInfo`](/embedded-wallets/sdk/client-side/javascript#getinfo) for Web/React Native. ## Login Farcaster Login works as an extension to Magic SDK. To add Farcaster Login to your Magic integration, start by installing the **Farcaster Extension**: ```bash NPM icon="npm" theme={null} npm install magic-sdk @magic-ext/farcaster ``` ```bash Yarn icon="yarn" theme={null} yarn add magic-sdk @magic-ext/farcaster ``` ## Initialization When creating your Magic instance, you'll need to add an instance of `FarcasterExtension` to the Magic constructor: ```javascript JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { FarcasterExtension } from '@magic-ext/farcaster'; const magic = new Magic('YOUR_API_KEY', { extensions: [new FarcasterExtension()], }); ``` ### Arguments * `showUI?` (Boolean): If `true`, show an out-of-the-box UI to display the QR code to the user. Defaults to `true`. ### Returns * `PromiEvent`: The promise resolves upon authentication request success and rejects with a specific error code if the request fails. The resolved value is a Decentralized ID token with a default 15-minute lifespan. ### Example ```javascript JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { FarcasterExtension } from '@magic-ext/farcaster'; const magic = new Magic('YOUR_API_KEY', { extensions: [new FarcasterExtension()], }); try { await magic.farcaster.login(); } catch { // Handle errors or reject if required! } try { const didToken = await magic.farcaster.login({ showUI: false }); } catch { // Handle errors if required! } ``` ### Event Handling A white-label Farcaster Login flow is available when passing `showUI: false` to this login method. Here's a short example to illustrate listening for and emitting events during the login flow: ```javascript JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { FarcasterExtension } from '@magic-ext/farcaster'; const magic = new Magic('YOUR_API_KEY', { extensions: [new FarcasterExtension()], }); try { const handle = magic.farcaster.login({ showUI: false }); handle .on("channel", (channel) => { console.log("channel URL", channel.url) // display custom UI with QR code }) .on("success", (data) => { // get Farcaster user information }) .on('done', (did) => { // get DID Token }) .on("failed", (e) => { // user rejects }) .on('error', (e) => { // handle error }) .on('settled', (e) => { // handle resolve or reject }); } catch (err) { // handle errors } ``` ## Resources * [Quickstart](/embedded-wallets/quickstart/overview) * [Web API documentation](/embedded-wallets/sdk/client-side/javascript#loginwithemailotp) * [React Native API documentation](/embedded-wallets/sdk/client-side/react-native#loginwithemailotp) # Implementation Source: https://docs.magic.link/embedded-wallets/authentication/login/oauth/implementation With Magic, you can use OAuth as an authentication mechanism, giving users a simple way to log in with supported social providers. ## Overview Applications can be integrated with the following social providers: [Google](/embedded-wallets/authentication/login/oauth/social-providers/google), [Facebook](/embedded-wallets/authentication/login/oauth/social-providers/facebook), [Twitter](/embedded-wallets/authentication/login/oauth/social-providers/twitter), [Apple](/embedded-wallets/authentication/login/oauth/social-providers/apple), [Discord](/embedded-wallets/authentication/login/oauth/social-providers/discord), [GitHub](/embedded-wallets/authentication/login/oauth/social-providers/github), [LinkedIn](/embedded-wallets/authentication/login/oauth/social-providers/linkedin), [Twitch](/embedded-wallets/authentication/login/oauth/social-providers/twitch), [Bitbucket](/embedded-wallets/authentication/login/oauth/social-providers/bitbucket) and [Microsoft](/embedded-wallets/authentication/login/oauth/social-providers/microsoft). ### Compatibility OAuth SDK methods are available via the following client-side SDKs: * [Web](/embedded-wallets/sdk/client-side/javascript) * [React Native](/embedded-wallets/sdk/client-side/react-native) * [iOS](/embedded-wallets/sdk/client-side/ios) * [Android](/embedded-wallets/sdk/client-side/android) ## Use Cases * Log in and create wallets for end users with OAuth for authentication ## Usage You can use OAuth by creating a project with our [CLI tool](/embedded-wallets/quickstart/overview) and selecting your preferred social providers when prompted. Alternatively, you can directly integrate it into your existing projects using the instructions below. **Refer to the **[**API documentation**](/embedded-wallets/sdk/overview)** for information on how to install and initialize Magic for your existing project.** ## Installation Social Logins work as an extension to Magic SDK. To add Social Login to your Magic integration, start by installing the **OAuth Extension**: ```bash NPM icon="npm" theme={null} npm install magic-sdk @magic-ext/oauth2 ``` ```bash Yarn icon="yarn" theme={null} yarn add magic-sdk @magic-ext/oauth2 ``` ## Initialization When creating your Magic instance, you'll need to add an instance of `OAuthExtension` to the Magic constructor: ```javascript JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { OAuthExtension } from '@magic-ext/oauth2'; const magic = new Magic('YOUR_API_KEY', { extensions: [new OAuthExtension()], }); ``` ## Login Once you've created a Magic instance, kick off the OAuth login with `loginWithRedirect` on the web and `loginWithPopup` on mobile. Once logged in, you will have access to a DID token that can be used with our [Admin SDK](/embedded-wallets/sdk/server-side/node#token-module) to verify the user's information and wallet address on the backend. On mobile SDKs, this is provided as the result of `loginWithPopup`, while on the web SDK you can retrieve this and other information about the OAuth result with `getRedirectResult`. The `getRedirectResult` function returns an object that includes user information. On mobile, you can get similar user info, such as wallet address and email, with `getMetadata`. ```javascript JavaScript icon="square-js" theme={null} const beginOAuthFlow = () => { await magic.oauth2.loginWithRedirect({ // provider: '...' /* 'google', 'facebook', 'apple', or 'github' */, redirectURI: 'https://your-app.com/your/oauth/callback', scope: ['user:email'] /* optional */, }); } // Call this upon redirect back to application const handleOAuthResult = () => { const result = await magic.oauth2.getRedirectResult(); console.log(`OAuth result: ${result}`); // Handle result information as needed } ``` The interface for the result of `getRedirectResult` is as follows: ```typescript theme={null} interface OAuthRedirectResult { magic: { idToken: string; userMetadata: MagicUserMetadata; }, oauth: { provider: string; scope: string[]; accessToken: string; userHandle: string; userInfo: ...; } }; ``` ## Resources * [OAuth Demo](https://magic-oauth-v2-dun.vercel.app) * [Web API documentation](/embedded-wallets/sdk/client-side/javascript#loginwithredirect) * [React Native API documentation](/embedded-wallets/sdk/client-side/react-native#loginwithpopup) # Apple Social Login with Magic Source: https://docs.magic.link/embedded-wallets/authentication/login/oauth/social-providers/apple You can allow your users to sign up & log in to your web app with their Apple ID. ## Usage ### Prerequisites * You will need an [Apple developer](https://developer.apple.com/) account * You will need to have [created a primary **App ID**](https://developer.apple.com/library/archive/documentation/ToolsLanguages/Conceptual/DevPortalGuide/CreatingandConfiguringAppIDs/CreatingandConfiguringAppIDs.html) for your Apple developer account * You will need to have the [Magic SDK installed](/embedded-wallets/sdk/overview) into your web app * You will need to have the [Magic SDK - OAuth Extension](/embedded-wallets/authentication/login/oauth/oauth-implementation) installed into your web app ### Apple Setup After installing the OAuth extension, you can now enable Sign in with Apple for your Magic app. You'll need to collect credentials from Apple first, then configure Magic. **Step 1: Collect Apple Credentials** 1. Go to your [Apple developer dashboard](https://developer.apple.com/account) 2. Navigate to [Membership details](https://developer.apple.com/account/#/membership) 3. Find and copy your **Team ID** (you'll need this later for Magic Dashboard) 4. If you have not already created an Apple key, under **Certificates, IDs & Profiles** select **Keys** 5. On the Keys page, click the blue plus button to create a new key. Make sure to enable **Sign in with Apple**. Apple developer portal Keys page showing blue plus button to create a new key 6. Complete the key setup and securely store your **Private Key** file and **Key ID** (you'll need both for Magic Dashboard) 7. In Apple's dashboard, navigate to **Identifiers** under [Certificates, IDs, & Profiles](https://developer.apple.com/account/resources/identifiers/list/serviceId) 8. Add a new [Services ID](https://developer.apple.com/account/resources/identifiers/add/serviceId) for your Magic authentication connection. Click **Continue**, then click **Register** Apple developer portal showing Services ID creation form 9. Click your newly created Services ID to enable it for **Sign in with Apple**. Then, click **Configure** Apple Services ID settings with Sign in with Apple configuration option 10. In the configuration modal, whitelist your app's domain in **Domains & Subdomains**, and add your **Redirect URI** in the **Return URLs** field: Add the **Redirect URI** you are passing as the **redirectURI** argument to the **loginWithRedirect** method: ```javascript JavaScript icon="square-js" theme={null} await magic.oauth2.loginWithRedirect({ provider: 'apple', redirectURI: 'https://your-app.com/your/oauth/callback', // whitelist this with Apple }); ``` Apple Services ID configuration modal showing Domains and Return URLs input fields 11. Click **Continue**, then **Save** to save your Services ID configuration **Step 2: Configure Magic Dashboard** 12. Go to your [Magic Dashboard](https://dashboard.magic.link/login) 13. Select the Magic app for which you'd like to enable Sign in with Apple, or create a new app 14. Navigate to **Social Login** from the sidebar 15. Click the toggle for **Apple** 16. Input your Apple credentials: **Team ID**, **Services ID**, **Key ID**, and paste the contents of your **Private Key** file 17. Click **Save** ## Resources * [Apple Social Login Demo](https://magic-oauth-v2-nine.vercel.app/) # Bitbucket Social Login with Magic Source: https://docs.magic.link/embedded-wallets/authentication/login/oauth/social-providers/bitbucket You can allow your users to sign up & log in to your web app with their Bitbucket account. ## Usage ### Prerequisites * You will need a [Bitbucket](https://bitbucket.org/) account * You will need to have the [Magic SDK installed](/embedded-wallets/sdk/overview) into your web app * You will need to have the [Magic SDK - OAuth Extension](/embedded-wallets/authentication/login/oauth/oauth-implementation) installed into your web app ### Bitbucket Setup (v1 extension) After installing the OAuth extension, you can now enable Bitbucket Login for your Magic app: 1. Go to your [Magic Dashboard](https://dashboard.magic.link/login) 2. Select the Magic app for which you’d like to enable bitbucket Login, or create a new app 3. Navigate to **Authentication ->** **Social Logins** from the sidebar 4. Click the toggle for **Bitbucket** 5. Copy the **Redirect URI** field from your Magic Dashboard 6. Go to Bitbucket App Dashboard. In the top right corner, click the profile avatar and select **Workspace settings.** Alternatively you can click on the profile avatar in the top right and choose **All workspaces** to open an entire list where you can then select **Manage** on the workspace you need. ⁠ bitbucket-step-1.png 7. On the left sidebar, scroll down to the **Apps and Features** header and click **OAuth consumers**, then click the **Add consumer** button bitbucket-step-2.png 8. Add the **Name** along with the **Redirect URI** you obtained from your Magic Dashboard into the **Callback URL** and click **Save** 9. Please make sure at least to grant **Read** permission in the account section. Otherwise, authentication will not complete. bitbucket-step-3.png 10. Obtain the **"Key"** and **"Secret"** bitbucket-step-4.png 11. Return to your Magic Dashboard and input the **Key** and **Secret** for your bitbucket OAuth app 12. In Magic Dashboard, click **“Save”** – Done! ### Bitbucket Setup (v2 extension) * Follow above steps, but disregard Magic's **Redirect URI** in step 5 * In step 8, paste the **Redirect URI** you are passing in as the **redirectURI** argument to the **loginWithRedirect** method ```javascript JavaScript icon="square-js" theme={null} await magic.oauth2.loginWithRedirect({ provider: 'bitbucket', redirectURI: 'https://your-app.com/your/oauth/callback', // <== whitelist this with Bitbucket }); ``` ## Resources * [Bitbucket Social Login Demo](https://magic-oauth-v2-dun.vercel.app) # Discord Social Login with Magic Source: https://docs.magic.link/embedded-wallets/authentication/login/oauth/social-providers/discord You can allow your users to sign up & log in to your web app with their Discord account. ## Usage ### Prerequisites * You will need a [Discord](https://discord.com/developers) account * You will need to have the [Magic SDK installed](/embedded-wallets/sdk/overview) into your web app * You will need to have the [Magic SDK - OAuth Extension](/embedded-wallets/authentication/login/oauth/oauth-implementation) installed into your web app ### Discord Setup After installing the OAuth extension, you can now enable Discord Login for your Magic app: 1. Create a Discord [API application](https://discord.com/developers/applications) 2. Go to your [Magic Dashboard](https://dashboard.magic.link/login) 3. Select the Magic app for which you’d like to enable Discord Login, or create a new app 4. Navigate to **Social Login** from the sidebar 5. Click the toggle for **Discord** 6. Return to your Discord developer Dashboard, navigate to **OAuth2** from the sidebar, and add your **Redirect URI**: Add the **Redirect URI** you are passing as the **redirectURI** argument to the **loginWithRedirect** method: ```javascript JavaScript icon="square-js" theme={null} await magic.oauth2.loginWithRedirect({ provider: 'discord', redirectURI: 'https://your-app.com/your/oauth/callback', // whitelist this with Discord }); ``` Discord OAuth2 settings page with Redirect URI input field 7. Save your changes in the Discord OAuth2 settings 8. Next, obtain the **Client ID** and **Client Secret** from the current page Discord application settings showing Client ID and Client Secret 9. Return to your Magic Dashboard and input the **Client ID** and **Client Secret** for your Discord OAuth app 10. In Magic Dashboard, click **Save** ## Resources * [Discord Social Login Demo](https://magic-oauth-v2-dun.vercel.app) # Facebook Social Login with Magic Source: https://docs.magic.link/embedded-wallets/authentication/login/oauth/social-providers/facebook You can allow your users to sign up & log in to your web app with their Facebook account. ## Usage ### Prerequisites * You will need a [Facebook Developer](https://developers.facebook.com/) account * You will need to have the [Magic SDK installed](/embedded-wallets/sdk/overview) into your web app * You will need to have the [Magic SDK - OAuth Extension](/embedded-wallets/authentication/login/oauth/oauth-implementation) installed into your web app ### Facebook Setup After installing the OAuth extension, you can now enable Facebook Login for your Magic app: 1. Follow Facebook's instructions to [register a Facebook app](https://developers.facebook.com/docs/apps#register) 2. When creating your Facebook app, select **Authenticate and request data from users with Facebook Login**, click Next and follow the prompts to add app information Facebook app creation screen with authentication option selected 3. Complete your Facebook app setup 4. In your Facebook app's Advanced Settings page, add your **Redirect URI** in the **Share Redirect Allow List** input: Facebook Advanced Settings showing Share Redirect Allow List input field Add the **Redirect URI** you are passing as the **redirectURI** argument to the **loginWithRedirect** method: ```javascript JavaScript icon="square-js" theme={null} await magic.oauth2.loginWithRedirect({ provider: 'facebook', redirectURI: 'https://your-app.com/your/oauth/callback', // whitelist this with Facebook }); ``` 5. Save the changes in your Facebook settings 6. From the main Facebook dashboard, click on **App Settings -> Basic** 7. Copy the **App Id** and **App Secret** Facebook App Settings showing App ID and App Secret credentials 8. Go to your [Magic Dashboard](https://dashboard.magic.link/login) 9. Select the Magic app for which you'd like to enable Facebook Login, or create a new app 10. Navigate to **Social Login** from the sidebar 11. Click the toggle for **Facebook** 12. Input the **App Id** and **App Secret** from your Facebook app 13. In Magic Dashboard, click **Save** ## Resources * [Facebook Social Login Demo](https://magic-oauth-v2-dun.vercel.app) # GitHub Social Login with Magic Source: https://docs.magic.link/embedded-wallets/authentication/login/oauth/social-providers/github You can allow your users to sign up & log in to your web app with their GitHub account. ## Usage ### Prerequisites * You will need a [GitHub](https://github.com/) account * You will need to have the [Magic SDK installed](/embedded-wallets/sdk/overview) into your web app * You will need to have the [Magic SDK - OAuth Extension](/embedded-wallets/authentication/login/oauth/oauth-implementation) installed into your web app ### GitHub Setup After installing the OAuth extension, you can now enable GitHub Login for your Magic app: 1. Follow GitHub's instructions to [create an OAuth app](https://docs.github.com/en/developers/apps/creating-an-oauth-app) 2. Go to your [Magic Dashboard](https://dashboard.magic.link/login) 3. Select the Magic app for which you’d like to enable GitHub Login, or create a new app 4. Navigate to **Social Login** from the sidebar 5. Click the toggle for **GitHub** 6. Return to your GitHub Dashboard and add your **Redirect URI** in the **Authorization callback URL** field: Add the **Redirect URI** you are passing as the **redirectURI** argument to the **loginWithRedirect** method: ```javascript JavaScript icon="square-js" theme={null} await magic.oauth2.loginWithRedirect({ provider: 'github', redirectURI: 'https://your-app.com/your/oauth/callback', // whitelist this with GitHub }); ``` GitHub OAuth application settings with Authorization callback URL input field 7. Click **Register App** to complete your GitHub OAuth app setup 8. On the next page, obtain the **Client ID** and **Client Secret** GitHub application showing Client ID and Client Secret credentials 9. Return to your Magic Dashboard and input the **Client ID** and **Client Secret** for your GitHub OAuth app 10. In Magic Dashboard, click **Save** ## Resources * [GitHub Social Login Demo](https://magic-oauth-v2-dun.vercel.app) # GitLab Social Login with Magic Source: https://docs.magic.link/embedded-wallets/authentication/login/oauth/social-providers/gitlab You can allow your users to sign up & login into your web application with their GitLab account. ## Usage ### Prerequisites * You will need a [GitLab](https://gitlab.com/) account * You will need to have the [Magic SDK installed](/embedded-wallets/sdk/overview) into your web app * You will need to have the [Magic SDK - OAuth Extension](/embedded-wallets/authentication/login/oauth/oauth-implementation) installed into your web app ### GitLab Setup After installing the OAuth extension, you can now enable GitLab Login for your Magic app: 1. Go to your [Magic Dashboard](https://dashboard.magic.link/login) 2. Select the Magic app for which you’d like to enable GitLab Login, or create a new app 3. Navigate to **Social Login** from the sidebar 4. Click the toggle for **GitLab** 5. Go to GitLab App Dashboard 6. On the left sidebar, select your avatar 7. Select **Edit Profile** 8. On the left sidebar, select **Applications** 9. Select **Add new application** 10. In the application form, enter a **Name** (arbitrary), and add your **Redirect URI**: Add the **Redirect URI** you are passing as the **redirectURI** argument to the **loginWithRedirect** method: ```javascript JavaScript icon="square-js" theme={null} await magic.oauth2.loginWithRedirect({ provider: 'gitlab', redirectURI: 'https://your-app.com/your/oauth/callback', // whitelist this with GitLab }); ``` 11. Make sure to tick **openid**, **profile**, **email** in the Scopes GitLab application form showing openid, profile, and email scope checkboxes 12. Click **Save application** to save your GitLab app 13. You will be provided with the **Application ID** and **Secret** 14. Return to your Magic Dashboard and input the **Application ID** and **Secret** for your GitLab OAuth app 15. In Magic Dashboard, click **Save** ## Resources * [GitLab Social Login Demo](https://magic-oauth-v2-dun.vercel.app) # Google Social Login with Magic Source: https://docs.magic.link/embedded-wallets/authentication/login/oauth/social-providers/google You can allow your users to sign up & login into your web application with their Google account. ## Usage If your users are encountering an "Access blocked: magic.link has not completed the Google verification process" error, please [read below](/embedded-wallets/authentication/login/oauth/social-providers/google#verification-process). ### Prerequisites * You will need a [Google Developer](https://console.developers.google.com/) account * You will need to have the [Magic SDK installed](/embedded-wallets/sdk/overview) into your web app * You will need to have the [Magic SDK - OAuth Extension](/embedded-wallets/authentication/login/oauth/oauth-implementation) installed into your web app ### Google Setup After installing the OAuth extension, you can now enable Google Login for your Magic app: 1. Follow Google's instructions to [set up an OAuth 2.0 app](https://support.google.com/cloud/answer/6158849?hl=en) 2. Obtain the OAuth **Client ID** and **Client Secret** from your [Google Developer dashboard](https://console.developers.google.com/) 3. Go to your [Magic Dashboard](https://dashboard.magic.link/login) 4. Select the Magic app for which you'd like to enable Google Login, or create a new app 5. Navigate to **Social Login** from the sidebar 6. Click the toggle for **Google / Gmail** 7. Input the **Client ID** and **Client Secret** for your OAuth app 8. Add your **Redirect URI** to your Google Dashboard's OAuth app configuration: If you're using `loginWithRedirect`, add the **Redirect URI** you are passing as the **redirectURI** argument: ```javascript JavaScript icon="square-js" theme={null} await magic.oauth2.loginWithRedirect({ provider: 'google', redirectURI: 'https://your-app.com/your/oauth/callback', // whitelist this with Google }); ``` Choose "Magic Login Widget" in your Magic Dashboard's Google OAuth settings and copy the **Redirect URI** field from your Magic Dashboard. Add this Redirect URI to your Google Dashboard's OAuth app configuration: Google OAuth Redirect URI configuration in Magic Dashboard 9. In Magic Dashboard, click **"Save"** 10. Click "**Test Connection**" to give your new Google OAuth flow a try! ### Verification Process If your users are encountering an `Access blocked: magic.link has not completed the Google verification process` error, you will need to navigate to your Google developer dashboard and change your app's "Publishing status" from "Testing" to "In production". Google OAuth consent screen showing publishing status options To do this, please follow these steps: 1. Go to your app within the Google developer dashboard and select "OAuth consent screen" on the nav menu. 2. Under "Publishing status", select "publish app". 3. Once the status is set to "In production", you should no longer be encountering the error. If your app meets certain criteria, you might be required to go through a verification process. To avoid this, we recommend removing any icons or logos you have added in the Google developer dashboard. The full list of criteria that requires verification from Google is as follows: * You want your application to display an icon or app name * Your app requests authorization of any sensitive or restricted scopes * The number of authorized domains for your apps [exceeds the domain count limit](https://support.google.com/cloud/answer/7650096) for a project * There are changes to the OAuth consent screen after your app has been approved Google OAuth consent screen with app published to production ## Gmail Linking A user's email login and Google social login can automatically be linked so that logging in with either auth factor will resolve to a single wallet. Each login counts towards a unique monthly active wallet for billing purposes. This is most useful when end users forget whether they signed up to your app using Google login or via email login. This prevents the user seeing 2 unique wallet addresses and confusion around which wallet holds their assets. By default, this account linking is enabled in the dashboard when you enable Google Sign-In. This will ensure that users who log in with an email and Google social login using the same email address will resolve to the same wallet. Turning this feature off will prevent automatic linking for new users. It **will not unlink** existing users' social and email logins. ## Resources * [Google Social Login Demo](https://magic-oauth-v2-dun.vercel.app) # LinkedIn Social Login with Magic Source: https://docs.magic.link/embedded-wallets/authentication/login/oauth/social-providers/linkedin You can allow your users to sign up & log in to your web app with their LinkedIn account. ## Usage ### Prerequisites * You will need a [LinkedIn](https://www.linkedin.com/) account * You will need to have the [Magic SDK installed](/embedded-wallets/sdk/overview) into your web app * You will need to have the [Magic SDK - OAuth Extension](/embedded-wallets/authentication/login/oauth/oauth-implementation) installed into your web app ### LinkedIn Setup (v1 extension) After installing the OAuth extension, you can now enable LinkedIn Login for your Magic app: 1. Follow LinkedIn's instructions to [create an app](https://www.linkedin.com/developers/apps/new) 2. Go to your [Magic Dashboard](https://dashboard.magic.link/login) 3. Select the Magic app for which you’d like to enable LinkedIn Login, or create a new app 4. Navigate to **Social Login** from the sidebar 5. Click the toggle for **LinkedIn** 6. Copy the **Redirect URI** field from your Magic Dashboard 7. Return to your LinkedIn App Dashboard and select **"Sign in with LinkedIn"** in the **"Products"** tab 8. Select **Auth** tab, add the **Redirect URI** you obtained from your Magic Dashboard into the **"Authorized redirect URLs for your app"** field 9. Click **"Update"** to save 10. Obtain the **"Client ID"** and **"Client Secret"** 11. Return to your Magic Dashboard and input the **Client ID** and **Client Secret** for your LinkedIn OAuth app 12. In Magic Dashboard, click **“Save”** – Done! ### LinkedIn Setup (v2 extension) * Follow above steps, but disregard Magic's **Redirect URI** in step 6 * In step 8, paste the **Redirect URI** you are passing in as the **redirectURI** argument to the **loginWithRedirect** method ```javascript JavaScript icon="square-js" theme={null} await magic.oauth2.loginWithRedirect({ provider: 'linkedin', redirectURI: 'https://your-app.com/your/oauth/callback', // <== whitelist this with LinkedIn }); ``` ## Resources * [LinkedIn Social Login Demo](https://magic-oauth-v2-dun.vercel.app) # Microsoft Social Login with Magic Source: https://docs.magic.link/embedded-wallets/authentication/login/oauth/social-providers/microsoft You can allow your users to sign up & log in to your web app with their Microsoft account. ## Usage ### Prerequisites * You will need a [Microsoft Azure](https://portal.azure.com/) account * You will need to have the [Magic SDK installed](/embedded-wallets/sdk/overview) into your web app * You will need to have the [Magic SDK - OAuth Extension](/embedded-wallets/authentication/login/oauth/oauth-implementation) installed into your web app ### Microsoft Setup (v1 extension) After installing the OAuth extension, you can now enable Microsoft Login for your Magic app: 1. Go to your [Magic Dashboard](https://dashboard.magic.link/login) 2. Select the Magic app for which you’d like to enable Microsoft Login, or create a new app 3. Navigate to **Social Login** from the sidebar 4. Click the toggle for **Microsoft** 5. Copy the **Redirect URI** field from your Magic Dashboard 6. Follow Microsoft's [registration instructions](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app) to register your app 7. Search and navigate to **App registrations** section app-registration.png 8. Click **New registration** new-registration.png 9. Make sure to select **personal Microsoft accounts** to allow your users to access your oauth app. Paste redirect link from Magic Dashboard into the **Redirect URI**. 10. After register your app, select **Certificates & secrets** in the sidebar, click **New client secret**, and obtain the client secret **Value** create-secret.png 11. Navigate back to **Overview** in the sidebar, obtain the Client ID client-id.png 12. Return to your Magic Dashboard and input the **Client ID** and **Client Secret** for your Microsoft OAuth app and click **“Save”** 13. Click **"Test Connection"** to give your new Microsoft OAuth flow a try! ### Microsoft Setup (v2 extension) * Follow above steps, but disregard Magic's **Redirect URI** in step 5 * In step 9, paste the **Redirect URI** you are passing in as the **redirectURI** argument to the **loginWithRedirect** method ```javascript JavaScript icon="square-js" theme={null} await magic.oauth2.loginWithRedirect({ provider: 'microsoft', redirectURI: 'https://your-app.com/your/oauth/callback', // <== whitelist this with Microsoft }); ``` ## Resources * [Microsoft Social Login Demo](https://magic-oauth-v2-dun.vercel.app) # Telegram Social Login with Magic Source: https://docs.magic.link/embedded-wallets/authentication/login/oauth/social-providers/telegram You can allow your users to sign up & log in to your web app with their Telegram account. ## Usage ### Prerequisites * You will need to have the [Magic SDK installed](/embedded-wallets/sdk/overview) into your web app * You will need to have the [Magic SDK - OAuth Extension](/embedded-wallets/authentication/login/oauth/oauth-implementation) installed into your web app ### Telegram Setup After installing the OAuth extension, you can now enable Telegram Login for your Magic app: 1. Follow Telegram instructions to create a bot 2. Provide Bot name and Bot Token in your Magic developer dashboard 3. Optionally enable Seamless login with Mini App 4. Copy the Redirect URI field from your Magic Dashboard and set it as your bot domain using command `/setdomain` in @BotFather If you have CSP enforcement, allow the following script-src: * [https://telegram.org](https://telegram.org) * [https://web.telegram.org](https://web.telegram.org) ## Implementation ```javascript JavaScript icon="square-js" theme={null} await magic.oauth2.loginWithPopup({ provider: 'telegram' }); ``` The result interface: ```typescript theme={null} interface OAuthRedirectResult { magic: { idToken: string; userMetadata: MagicUserMetadata; }, oauth: { provider: string; scope: string[]; accessToken: string; userHandle: string; userInfo: ...; } }; ``` Keep your token secure and store it safely, it can be used by anyone to control your bot ## Resources * [Telegram Login Demo](https://github.com/magiclabs/telegram-oauth-demo) # Twitch Social Login with Magic Source: https://docs.magic.link/embedded-wallets/authentication/login/oauth/social-providers/twitch You can allow your users to sign up & log in to your web app with their Twitch account. ## Usage ### Prerequisites * You will need a [Twitch](https://www.twitch.com/) account * You will need to have the [Magic SDK installed](/embedded-wallets/sdk/overview) into your web app * You will need to have the [Magic SDK - OAuth Extension](/embedded-wallets/authentication/login/oauth/oauth-implementation) installed into your web app ### Twitch Setup (v1 extension) After installing the OAuth extension, you can now enable Twitch Login for your Magic app: 1. Go to your [Magic Dashboard](https://dashboard.magic.link/login) 2. Select the Magic app for which you’d like to enable Twitch Login, or create a new app 3. Navigate to **Social Login** from the sidebar 4. Click the toggle for **Twitch** 5. Copy the **Redirect URI** field from your Magic Dashboard 6. Follow Twitch's [registration instructions](https://dev.twitch.tv/embedded-wallets/authentication/#registration) to [register your app](https://dev.twitch.tv/console/apps/create) 7. Paste **Redirect URI** into the **OAuth Redirect URLs** register-your-application 8. After creation of your Twitch app, click **Manage** manage-your-app.png 9. In the manage application section, click **New Secret** generate-client-secret.png 10. Obtain the **Client ID** and **Client Secret** 11. Return to your Magic Dashboard and input the **Client ID** and **Client Secret** for your Twitch OAuth app 12. In Magic Dashboard, click **“Save”** 13. Click **"Test Connection"** to give your new Twitch OAuth flow a try! ### Twitch Setup (v2 extension) * Follow above steps, but disregard Magic's **Redirect URI** in step 5 * In step 7, paste the **Redirect URI** you are passing in as the **redirectURI** argument to the **loginWithRedirect** method ```javascript JavaScript icon="square-js" theme={null} await magic.oauth2.loginWithRedirect({ provider: 'twitch', redirectURI: 'https://your-app.com/your/oauth/callback', // <== whitelist this with Twitch }); ``` ## Resources * [Twitch Social Login Demo](https://magic-oauth-v2-dun.vercel.app) # Twitter(X) Social Login with Magic Source: https://docs.magic.link/embedded-wallets/authentication/login/oauth/social-providers/twitter You can allow your users to sign up & log in to your web application with their Twitter account. ## Usage ### Prerequisites * You will need a [Twitter](https://developer.twitter.com/en/apply-for-access) Developer account (only free access is required) * You will need to have the [Magic SDK installed](/embedded-wallets/sdk/overview) into your web application * You will need to have the [Magic SDK - OAuth Extension](/embedded-wallets/authentication/login/oauth/oauth-implementation) installed into your web application ### Twitter Setup 1. Go to your [Magic Dashboard](https://dashboard.magic.link/login) 2. Select the Magic application for which you’d like to enable Twitter Login, or create a new application 3. Navigate to **Social Login** from the sidebar 4. Click the toggle for **Twitter** 5. Go to Twitter App Dashboard and navigate to **Projects & Apps** 6. In the **Overview** section, click on **Add Project** twitter-step-1.png 7. Add your project name, use case and description twitter-step-1.png 8. Do **NOT** use the **API Key** and **Secret** under Consumer Keys. Make sure to copy the **Client ID** and **Client Secret** to the Magic Dashboard, which you can then use with your application that connects to Twitter. Complete flow if OAuth 2.0 **Client ID** and **Client Secret** are not visible 9. Navigate to the application on the sidebar and click **Set up** under **User authentication settings** twitter-step-4.png 10. Fill out all of the required permissions and app type information 11. Paste the **Redirect URI** you will pass as the **redirectURI** argument to the **loginWithRedirect** method, in the **Callback URI / Redirect URL** and add your applications **Website URL** twitter-step-5.png 12. After saving these details for the first time, Twitter will then display an OAuth 2.0 Client ID and Client Secret. **IMPORTANT:** These are the keys that Magic requires to use v2. Do **NOT** use the **API Key** and **API Key Secret** for v2. 13. **"Test Connection" function in dashboard will not work for v2.** ## Resources * [Twitter Social Login Demo](https://magic-oauth-v2-nine.vercel.app) # External Wallets (SIWE) Source: https://docs.magic.link/embedded-wallets/authentication/login/siwe ## Overview Sign-In with Ethereum (SIWE) is a standard for passwordless authentication using Ethereum accounts. It allows users to authenticate to your application by signing a message with their Ethereum address, providing a self-custodied alternative to centralized identity providers. Learn more about the [SIWE standard (EIP-4361)](https://eips.ethereum.org/EIPS/eip-4361). ### Compatibility SIWE SDK methods are available via the `siwe` module of the [Web](/embedded-wallets/sdk/client-side/javascript) and [React Native](/embedded-wallets/sdk/client-side/react-native) client-side SDKs. **Available Methods**: Users authenticated through the SIWE extension can use the following `user` methods: * `user.isLoggedIn()` * `user.getInfo()` * `user.logout()` SIWE authentication is designed for authentication purposes, so wallet operations are handled through the user's third-party wallet provider. ## Usage ### Installation ```bash NPM icon="npm" theme={null} npm install magic-sdk @magic-ext/siwe ``` ```bash Yarn icon="yarn" theme={null} yarn add magic-sdk @magic-ext/siwe ``` ### Sign in with a Third-Party Wallet To authenticate users with a third-party wallet (e.g., MetaMask, Coinbase Wallet), follow these steps: 1. Generate a SIWE message using the wallet's address 2. Sign the message with the third-party wallet 3. Complete authentication with Magic using the message and signature ```javascript JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { SiweExtension } from '@magic-ext/siwe'; const magic = new Magic('PUBLISHABLE_API_KEY', { extensions: [new SiweExtension()], }); const signInWithWallet = async (walletProvider, walletAddress) => { try { // 1. Generate the SIWE message const message = await magic.siwe.generateMessage({ address: walletAddress, chainId: 1, // Optional: defaults to Ethereum mainnet }); // 2. Sign the message with the third-party wallet const signature = await walletProvider.request({ method: 'personal_sign', params: [message, walletAddress], }); // 3. Complete authentication with Magic const publicAddress = await magic.siwe.login({ message, signature }); console.log('SIWE authentication complete! Public Address:', publicAddress); return publicAddress; } catch (error) { console.error('SIWE authentication error:', error); throw error; } }; ``` ### Sign in with Worldcoin Mini App To authenticate users with Worldcoin Mini Apps, you can use World App's native wallet integration. Follow these steps: 1. Generate a nonce from Magic 2. Request wallet authentication from World App 3. Extract the signed message and signature 4. Complete authentication with Magic This example requires the `@worldcoin/minikit-js` package. See the [Worldcoin Mini Apps documentation](https://docs.world.org/mini-apps) for setup instructions. ```javascript JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { SiweExtension } from '@magic-ext/siwe'; import { MiniKit } from '@worldcoin/minikit-js'; const magic = new Magic('PUBLISHABLE_API_KEY', { extensions: [new SiweExtension()], }); const signInWithWorldcoin = async () => { try { // 1. Generate a nonce from Magic const nonce = await magic.siwe.generateNonce(); // 2. Request wallet authentication from World App const { finalPayload } = await MiniKit.commandsAsync.walletAuth({ nonce, }); if (!finalPayload || finalPayload.status !== 'success') { throw new Error('World App wallet authentication failed'); } // 3. Extract the signed message and signature const { message, signature } = finalPayload; // 4. Complete authentication with Magic const publicAddress = await magic.siwe.login({ message, signature }); console.log('SIWE authentication complete! Public Address:', publicAddress); return publicAddress; } catch (error) { console.error('SIWE authentication error:', error); throw error; } }; ``` # SMS Source: https://docs.magic.link/embedded-wallets/authentication/login/sms With Magic, you can use SMS one-time codes as an authentication mechanism, giving users a simple way to log in using their phones. ### Compatibility **NOTE** See our [FAQs](/home/faq#is-my-country-code-supported) on supported country codes. SMS Login SDK methods are available on all [client-side SDKs](/embedded-wallets/sdk/overview#client-side-sdk). ## Use Cases * Log in and create wallets for end users with a one-time code sent by SMS to their phone number. ## Usage You can use SMS login by creating a project with our [CLI tool](/embedded-wallets/quickstart/overview) and picking 'SMS' when prompted. Alternatively, you can directly integrate it into your existing projects using the sample below. **Refer to the** [**API documentation**](/embedded-wallets/sdk/overview) **for information on how to install and initialize Magic for your existing project.** Once you've created a Magic instance, log users in using `loginWithSMS`. Once logged in, you will receive a DID token that can be used with our [Admin SDK](/embedded-wallets/sdk/server-side/node#token-module) to verify the user's information and wallet address on the backend. You can also retrieve the user's wallet address and email using the `user` module's [`getInfo`](/embedded-wallets/sdk/client-side/javascript#getinfo) function for Web/React Native and `getMetadata` for iOS, Android and Flutter. ```javascript JavaScript icon="square-js" theme={null} const login = async (phoneNumber) => { try { const did = await magic.auth.loginWithSMS({ phoneNumber: phoneNumber, }); console.log(`DID Token: ${did}`); const userInfo = await magic.user.getInfo(); console.log(`UserInfo: ${userInfo}`); ⁠ // Handle user information as needed } catch { // Handle errors as required } } ``` ## Resources * [Web API documentation](/embedded-wallets/sdk/client-side/javascript#loginwithsms) * [React Native API documentation](/embedded-wallets/sdk/client-side/react-native#loginwithsms) * [iOS API documentation](/embedded-wallets/sdk/client-side/ios#loginwithsms) * [Android API documentation](/embedded-wallets/sdk/client-side/android#loginwithsms) * [Flutter API documentation](/embedded-wallets/sdk/client-side/flutter#loginwithsms) # Wallet Kit Source: https://docs.magic.link/embedded-wallets/authentication/login/wallet-kit With Magic, you can use a pre-built React component that provides a complete authentication UI with support for email, OAuth, and external wallet login. ### Compatibility The Wallet Kit widget requires React 18+ and is available for web applications using the [JavaScript SDK](/embedded-wallets/sdk/client-side/javascript). ## Use Cases * Add a complete login UI to your React app without building custom components * Support multiple authentication methods (email OTP, OAuth, Farcaster, external wallets) from a single widget ## Getting Started ### Installation ```bash NPM icon="npm" theme={null} npm install magic-sdk @magic-ext/wallet-kit ``` ```bash Yarn icon="yarn" theme={null} yarn add magic-sdk @magic-ext/wallet-kit ``` ### Usage Initialize Magic with the `WalletKitExtension`, then render the `MagicWidget` component in your app. ```jsx JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { MagicWidget, WalletKitExtension } from '@magic-ext/wallet-kit'; const magic = new Magic('YOUR_API_KEY', { extensions: [new WalletKitExtension()], }); export default function LoginPage() { const handleSuccess = (result) => { switch (result.method) { case 'email': // result.didToken for backend verification console.log('Email login:', result.didToken); break; case 'oauth': // result.magic.idToken for backend verification console.log('OAuth login:', result.magic.idToken); break; case 'farcaster': // result.didToken for backend verification console.log('Farcaster login:', result.didToken, result.farcaster); break; case 'wallet': // result.walletAddress for wallet connections console.log('Wallet connected:', result.walletAddress); break; } }; return ( console.error(error)} /> ); } ``` ### Configuration The widget displays login options based on two sources: **Dashboard-configured options** — Email OTP and OAuth providers (Google, Apple, GitHub, etc.) are configured in your [Magic Dashboard](https://dashboard.magic.link). Enable the authentication methods you want, and they will automatically appear in the widget. **Code-configured options** — External wallets and Farcaster are configured via props. Pass `wallets` to specify which external wallets to display, and `enableFarcaster` to show a Farcaster login button. For example, if you enable Email and Google OAuth in your dashboard and pass `wallets={['metamask']}` and `enableFarcaster`, the widget will show all four options: email input, Google sign-in button, Farcaster, and MetaMask. ### Farcaster To enable Farcaster login, pass the `enableFarcaster` prop. This adds a Farcaster button alongside your OAuth social providers. When clicked, users see a QR code they can scan with the Warpcast app on their phone. On mobile devices, the widget automatically redirects to the Farcaster app. ```jsx JavaScript icon="square-js" theme={null} { if (result.method === 'farcaster') { console.log('DID token:', result.didToken); console.log('Farcaster user:', result.farcaster.username); } }} /> ``` ### WalletConnect If you include `'walletconnect'` in the `wallets` prop, the widget uses [Reown](https://reown.com/) (formerly WalletConnect) under the hood. A default project ID is included for development, but for production apps you should provide your own to avoid rate limiting: ```jsx JavaScript icon="square-js" theme={null} const magic = new Magic('YOUR_API_KEY', { extensions: [new WalletKitExtension({ projectId: 'YOUR_REOWN_PROJECT_ID' })], }); ``` You can get a project ID by creating a free account at [dashboard.walletconnect.com](https://dashboard.walletconnect.com). ### Account Switching When a user switches to a different account in their wallet (e.g. MetaMask), the widget automatically re-runs SIWE authentication for the new account in the background. The user will see their wallet's native signing prompt — no widget UI changes occur. Use `onAccountChanged` to respond when the new session is ready: ```jsx JavaScript icon="square-js" theme={null} { if (result.method === 'wallet') { setAddress(result.walletAddress); } }} onAccountChanged={(result) => { // New SIWE session is verified — safe to update your app state setAddress(result.walletAddress); }} onError={(error) => { // Fires for both initial login failures and account switch re-auth failures console.error(error); }} /> ``` ## API Reference ### Props | Prop | Type | Default | Description | | --------------------- | ------------------------------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `displayMode` | `'inline' \| 'modal'` | `'inline'` | How the widget is displayed. `'inline'` renders in document flow, `'modal'` shows as an overlay. | | `isOpen` | `boolean` | `true` | Whether the widget is visible. | | `onClose` | `() => void` | — | Called when the user closes the widget. | | `closeOnSuccess` | `boolean` | `false` | Automatically close after successful login (shows success screen for 2 seconds first). | | `closeOnClickOutside` | `boolean` | `false` | Close when clicking the backdrop. Only applies in modal mode. | | `wallets` | `Array` | `[]` | External wallets to display: `'metamask'`, `'coinbase'`, `'phantom'`, `'rabby'`, `'walletconnect'`. | | `enableFarcaster` | `boolean` | `false` | Show a Farcaster login button alongside social providers. | | `onSuccess` | `(result: LoginResult) => void` | — | Called on successful login. See [Login Results](#login-results) for result structure. | | `onAccountChanged` | `(result: WalletLoginResult) => void` | — | Called when a wallet user switches to a different account and re-authentication completes. Fires after the new SIWE session is verified, so `result.walletAddress` reflects the active account. | | `onError` | `(error: Error) => void` | — | Called when login fails, or when a re-authentication triggered by an account switch fails (e.g. the user rejects the signing prompt). | | `onReady` | `() => void` | — | Called when the widget has initialized and applied theme settings. | ### Login Results The `onSuccess` callback receives a result object with a `method` property indicating the authentication type: ```typescript theme={null} // Email OTP login { method: 'email', didToken: string } // OAuth login (Google, Apple, etc.) { method: 'oauth', oauth: { provider, scope, userHandle, userInfo }, magic: { idToken, userMetadata } } // Farcaster login { method: 'farcaster', didToken: string, farcaster: { fid?, username?, displayName?, pfpUrl?, bio? } } // External wallet login { method: 'wallet', walletAddress: string } ``` For email and Farcaster logins, use `result.didToken` for backend verification. For OAuth logins, use `result.magic.idToken`. Farcaster logins also include the user's Farcaster profile data in `result.farcaster`. ## Examples ### Modal Mode Use modal mode to display the widget as an overlay: ```jsx JavaScript icon="square-js" theme={null} import { useState } from 'react'; import { MagicWidget } from '@magic-ext/wallet-kit'; function App() { const [isOpen, setIsOpen] = useState(false); return ( <> setIsOpen(false)} closeOnClickOutside closeOnSuccess onSuccess={(result) => console.log(result)} /> ); } ``` ### Loading State The widget fetches configuration from your Magic dashboard before rendering. During this time, it returns `null`. To show a loading indicator, render the widget with `isOpen={false}` and wait for `onReady`: ```jsx JavaScript icon="square-js" theme={null} import { useState } from 'react'; import { Magic } from 'magic-sdk'; import { MagicWidget, WalletKitExtension } from '@magic-ext/wallet-kit'; // Create Magic instance once, outside the component const magic = new Magic('YOUR_API_KEY', { extensions: [new WalletKitExtension()], }); function LoginPage() { const [isLoading, setIsLoading] = useState(true); const [isOpen, setIsOpen] = useState(false); const handleReady = () => { setIsLoading(false); setIsOpen(true); }; return ( <> {isLoading &&
Loading...
} console.log(result)} /> ); } ``` Create the Magic instance outside your component or use `useMemo` to prevent recreating it on every render. ## Theming The widget automatically applies your Magic dashboard theme settings including colors and light/dark mode. Configure these in your [Magic Dashboard](https://dashboard.magic.link). ## Resources * [JavaScript SDK](/embedded-wallets/sdk/client-side/javascript) * [Email OTP Login](/embedded-wallets/authentication/login/email-otp) * [OAuth Implementation](/embedded-wallets/authentication/login/oauth/implementation) * [Farcaster Login](/embedded-wallets/authentication/login/farcaster) # Authentication Source: https://docs.magic.link/embedded-wallets/authentication/overview Tailor your authentication experience to perfection by selecting from a diverse array of passwordless login options, ensuring an ideal fit for your audience. All users are provisioned with a non-custodial wallet on signup, **enabling one-click web3 onboarding experiences**. ## Login } href="/embedded-wallets/authentication/login/farcaster" /> ## OAuth ## Features ## Customization ## Data ## Security # Access Control Source: https://docs.magic.link/embedded-wallets/authentication/security/access-control ## Overview The Access Control feature allows you to easily manage who is and isn't allowed to log in to your application. Access can be gated in 2 ways: explicitly allowing only certain emails and domains through with [Allow List](#allow-list), or blocking certain emails and domains with [Block List](#block-list). This feature is compatible with some OAuth providers (see [Social Login](#social-login)). ### Allow List The Allow List lets you specify emails and domains that can access your site. The max list size for Allow List is 20k entries. Sample use cases are mailing lists, private organizations or message boards. By default this list is empty, which means that everyone is allowed access, however once valid entries are specified in this list, only those emails + domains will be allowed to access your application. ### Block List The Block List lets you specify emails and domains you wish to block from accessing your application. The max list size for Block List is also 20k entries. Useful for most applications, forums, message boards, social media, etc. This will not end a user's currently active session, but will block a user from starting a new session. The entries in this list take precedence over the entries in the Allow List. If an email (or domain wildcard) is specified in the Block List would otherwise be granted access by the Allow List, the email would still be blocked. ### Formatting * Accepts email addresses or domain wildcards * Separate entries with spaces, commas, or line breaks * Pulling from a CRM? Export emails as a single-column CSV; copy + paste ### Social Login We are only able to gate email access for OAuth providers that return a user's email inside the OAuth user info response. The following is a list of OAuth providers that require additional steps or verification for your application to support email access. * Twitter * Microsoft * Bitbucket * LinkedIn # Domain Allowlist Source: https://docs.magic.link/embedded-wallets/authentication/security/allowlists/domain-allowlist Domain Allowlist allows your application to be secure-by-default, meaning that it establishes a strong security posture right from the start, and only permits communication with specific domains that have been explicitly approved. ## Overview The Domain Allowlist is a security feature that restricts which domains can make requests to your Magic application. When enabled, Magic will reject all requests from domains that are not explicitly included in your allowlist. ### Security Benefits Prevents unauthorized usage of your public API keys by restricting access to approved domains only. Blocks potential attacks from malicious domains attempting to use your Magic integration. ### How It Works When a request is made to your Magic application: 1. **Domain Check**: Magic verifies the requesting domain against your allowlist 2. **Allow/Block**: Requests from allowed domains proceed normally; blocked domains receive an error 3. **User Notification**: Blocked requests show a user-friendly error message explaining the restriction **Important**: Once enabled, the Domain Allowlist applies to ALL requests to your Magic application. Make sure to add all legitimate domains before enabling this feature. ## Usage ### Dashboard Configuration Navigate to the [Magic Dashboard](https://dashboard.magic.link) and select the application you want to configure. Go to the **Settings** tab of your selected application. Scroll down to the **"Allowed Origins & Redirects"** section. Toggle the **Domain** switch to enable it. In the text input field that appears, add the domains you want to allow. You can add multiple domains by separating them with commas or line breaks. Click **Save** to apply your domain allowlist configuration. Domain allowlist configuration interface in Magic Dashboard ### Domain Formatting When adding domains to your allowlist, follow these formatting rules: * **Full domains**: `example.com`, `app.example.com` * **Subdomains**: `*.example.com` (wildcard for all subdomains) * **Localhost**: `localhost`, `localhost:3000` * **IP addresses**: `192.168.1.1`, `192.168.1.1:8080` * **Protocols**: Include `https://` or `http://` if needed ``` example.com app.example.com *.example.com localhost:3000 https://staging.myapp.com ``` * Use specific domains when possible instead of wildcards * Include both `www` and non-`www` versions if needed * Add staging and production domains separately * Test with `localhost` during development ### New Projects For new applications created after December 15, 2023, the Domain Allowlist feature **is mandatory**. By default, the `localhost` domain is included for convenience during development. **Development Tip**: The `localhost` domain is automatically included to help with local development. Make sure to add your production domains before deploying to production. ### Existing Projects For applications created before December 15, 2023, the Domain Allowlist feature is **optional but highly recommended**. While not enforced, warning messages will appear in your dashboard if the feature is not enabled. **Security Risk**: Without enabling the Domain Allowlist, your API keys remain vulnerable to unauthorized usage from any domain. We strongly recommend enabling this feature for all applications. Domain allowlist entry point in Magic Dashboard If a project attempts to utilize a public key on a domain not included in the application's allowlist, a modal will appear. This modal will notify the user that an unauthorized domain is being used and that access has not been approved for that specific domain by the application. Error message shown to users when accessing from unauthorized domains ### Programmatic Configuration You can manage your domain allowlist programmatically using the Magic API. This is useful for: * **Automated deployments**: Add domains as part of your CI/CD pipeline * **Bulk management**: Add or remove multiple domains at once * **Integration**: Manage domains from your own admin interface #### Prerequisites To use the programmatic API, you'll need your **Secret Key**: Navigate to a Magic app from the main dashboard landing page On the app home page, scroll to the **API Keys** section Copy your **Secret Key** from the API Keys section **Security**: Keep your Secret Key secure and never expose it in client-side code or public repositories. #### API Operations ```bash List Domains icon="list" theme={null} curl -X GET 'https://api.dashboard.magic.link/v1/admin/access_whitelist' \ -H 'X-Magic-Secret-Key: sk_live_XXXXXXXX' ``` ```bash Add Domains icon="plus" theme={null} curl -X POST 'https://api.dashboard.magic.link/v1/admin/access_whitelist' \ -H 'X-Magic-Secret-Key: sk_live_XXXXXXXX' \ -H 'Content-Type: application/json' \ -d '[ { "access_type": "domain", "value": "example.com" } ]' ``` ```bash Remove Domains icon="minus" theme={null} curl -X DELETE 'https://api.dashboard.magic.link/v1/admin/access_whitelist' \ -H 'X-Magic-Secret-Key: sk_live_XXXXXXXX' \ -H 'Content-Type: application/json' \ -d '{ "access_type": "domain", "value": "example.com" }' ``` # Mobile Allowlist Source: https://docs.magic.link/embedded-wallets/authentication/security/allowlists/mobile-allowlist Mobile Allowlist ensures secure mobile app access by restricting which mobile applications can use your Magic application, preventing unauthorized usage and protecting your API keys. ## Overview The Mobile Allowlist is a security feature that restricts which mobile applications can access your Magic application. When enabled, Magic will only allow requests from mobile apps whose App IDs are explicitly included in your allowlist. ### Security Benefits Prevents unauthorized mobile apps from using your Magic application and API keys. Blocks all traffic from unknown mobile sources, ensuring only approved apps can authenticate. ### How It Works When a mobile app makes a request to your Magic application: 1. **App ID Check**: Magic verifies the app's Bundle ID (iOS) or Application ID (Android) against your allowlist 2. **Allow/Block**: Requests from allowed apps proceed normally; blocked apps receive an error 3. **User Notification**: Blocked apps show a user-friendly error message explaining the restriction **Mobile Only**: The Mobile Allowlist only applies to mobile applications using the Magic SDK. It doesn't affect web applications or server-side integrations. ## Usage ### Prerequisites Ensure your mobile SDK has been updated to the required version to enable this feature: **Version**: 3.2.0 or later **Version**: 2.2.0 or later **Version**: 1.1.0 or later **Version**: 0.2.0 or later **Version**: 13.0.0 or later (Bare / Expo) ### Dashboard Configuration Navigate to the [Magic Dashboard](https://dashboard.magic.link) and select the application you want to configure. Go to the **Settings** tab of your selected application. Scroll down to the **"Allowed Origins & Redirects"** section. Toggle the **Mobile App** switch to enable it. In the text input field that appears, add the App IDs you want to allow. You can add multiple App IDs by separating them with commas or line breaks. Click **Save** to apply your mobile allowlist configuration. Mobile allowlist configuration interface in Magic Dashboard After adding App IDs, traffic from unauthorized mobile sources is blocked. Users from unauthorized apps will see an error message when they try to log in. If this happens unexpectedly, add the App ID from the error message to unblock these users. Error message shown to users from unauthorized mobile apps ### App ID Formatting When adding App IDs to your allowlist, follow these formatting rules: * **iOS Bundle ID**: `com.example.app`, `link.magic.ios` * **Android Application ID**: `com.example.app`, `link.magic.android` * **Reverse DNS notation**: Usually written as `domain.company.app` * **Minimum segments**: Must have at least two segments (one or more dots) * **Valid characters**: Alphanumeric \[a-zA-Z0-9] or `-` or `_` ``` com.example.myapp link.magic.ios link.magic.android com.company.staging.app io.github.username.project ``` * **Case sensitive**: App IDs are case-sensitive * **Exact match**: Must match exactly as defined in your app * **No wildcards**: Specific App IDs only, no wildcard patterns * **Platform specific**: iOS and Android App IDs are separate entries ### Finding Your App ID The App ID is represented by the bundle identifier on iOS and the applicationId on Android: Apple uses bundle identifiers (bundle IDs) to uniquely identify an application in Apple's ecosystem. These IDs are usually written in reverse DNS notation. **Location**: **Target → Signing & Capabilities → Bundle Identifier** iOS Bundle Identifier in Xcode Your Android App ID is defined by the `applicationId` property in your module's `build.gradle` file: ```kotlin Android icon="android" theme={null} android { defaultConfig { applicationId = "link.magic.android" minSdk = 24 targetSdk = 31 versionCode = 1 versionName = "1.0" } ... } ``` For more details, refer to the [Official Android documentation](https://developer.android.com/studio/build/configure-app-module#set_the_application_id). ### Programmatic Configuration You can manage your mobile allowlist programmatically using the Magic API. This is useful for: * **Automated deployments**: Add App IDs as part of your CI/CD pipeline * **Bulk management**: Add or remove multiple App IDs at once * **Integration**: Manage App IDs from your own admin interface #### Prerequisites To use the programmatic API, you'll need your **Secret Key**: Navigate to a Magic app from the main dashboard landing page On the app home page, scroll to the **API Keys** section Copy your **Secret Key** from the API Keys section **Security**: Keep your Secret Key secure and never expose it in client-side code or public repositories. #### API Operations ```bash List App IDs icon="list" theme={null} curl -X GET 'https://api.dashboard.magic.link/v1/admin/access_whitelist' \ -H 'X-Magic-Secret-Key: sk_live_XXXXXXXX' ``` ```bash Add App IDs icon="plus" theme={null} curl -X POST 'https://api.dashboard.magic.link/v1/admin/access_whitelist' \ -H 'X-Magic-Secret-Key: sk_live_XXXXXXXX' \ -H 'Content-Type: application/json' \ -d '[ { "access_type": "bundle", "value": "com.example.myapp" } ]' ``` ```bash Remove App IDs icon="minus" theme={null} curl -X DELETE 'https://api.dashboard.magic.link/v1/admin/access_whitelist' \ -H 'X-Magic-Secret-Key: sk_live_XXXXXXXX' \ -H 'Content-Type: application/json' \ -d '{ "access_type": "bundle", "value": "com.example.myapp" }' ``` # Redirect Allowlist Source: https://docs.magic.link/embedded-wallets/authentication/security/allowlists/redirect-allowlist Redirect Allowlist ensures secure OAuth authentication by restricting which URLs users can be redirected to after completing authentication, preventing phishing attacks and unauthorized redirects. ## Overview The Redirect Allowlist is a security feature that restricts which URLs users can be redirected to after completing OAuth authentication. When enabled, Magic will only allow redirects to URLs that are explicitly included in your allowlist. ### Security Benefits Prevents attackers from redirecting users to malicious websites or phishing pages during OAuth flows. Protects sensitive authentication data by ensuring users are only redirected to trusted destinations. ### How It Works When a user completes OAuth authentication: 1. **URL Validation**: Magic checks the redirect URL against your allowlist 2. **Security Check**: Only pre-approved URLs are allowed for redirection 3. **User Safety**: Users are protected from being redirected to malicious sites **OAuth Only**: The Redirect Allowlist is only relevant when using OAuth providers (like Google, GitHub, etc.) as your authentication method. It doesn't apply to non-OAuth authentication methods. ## Usage ### Dashboard Configuration Navigate to the [Magic Dashboard](https://dashboard.magic.link) and select the application you want to configure. Go to the **Settings** tab of your selected application. Scroll down to the **"Allowed Origins & Redirects"** section. Toggle the **Redirect** switch to enable it. In the text input field that appears, add the redirect URLs you want to allow. You can add multiple URLs by separating them with commas or line breaks. Click **Save** to apply your redirect allowlist configuration. ### URL Formatting When adding redirect URLs to your allowlist, follow these formatting rules: * **Web URLs**: `https://example.com`, `https://app.example.com/path` * **Custom schemes**: `myapp://`, `myapp://callback` * **Mobile deep links**: `myapp://open`, `myapp://auth/callback` * **Universal Links**: `https://example.com/app-link` * **Localhost**: `http://localhost:3000`, `http://localhost:3000/callback` ``` https://example.com https://app.example.com/callback https://staging.myapp.com/auth/success myapp://auth/callback expo:// http://localhost:3000 ``` * **Explicit paths**: `https://example.com` does not include `https://example.com/path` * **No query strings**: Query parameters and hash fragments are ignored during validation * **Wildcards**: Use `https://*.example.com` for subdomain wildcards * **Protocols**: Include the full protocol (`https://` or `http://`) for web URLs ### Mobile Applications For mobile applications, you can use both **Deep Links** and **Universal Links** (App Links on Android). We recommend **Universal Links/App Links** for increased security. **Mobile Deep Links**: Use custom URL schemes like `myapp://` for deep linking within your mobile app. **Universal Links**: Use HTTPS URLs that can open your mobile app when available, falling back to web if the app isn't installed. ### Programmatic Configuration You can manage your redirect allowlist programmatically using the Magic API. This is useful for: * **Automated deployments**: Add redirect URLs as part of your CI/CD pipeline * **Bulk management**: Add or remove multiple redirect URLs at once * **Integration**: Manage redirect URLs from your own admin interface #### Prerequisites To use the programmatic API, you'll need your **Secret Key**: Navigate to a Magic app from the main dashboard landing page On the app home page, scroll to the **API Keys** section Copy your **Secret Key** from the API Keys section **Security**: Keep your Secret Key secure and never expose it in client-side code or public repositories. #### API Operations ```bash List Redirects icon="list" theme={null} curl -X GET 'https://api.dashboard.magic.link/v1/admin/access_whitelist' \ -H 'X-Magic-Secret-Key: sk_live_XXXXXXXX' ``` ```bash Add Redirects icon="plus" theme={null} curl -X POST 'https://api.dashboard.magic.link/v1/admin/access_whitelist' \ -H 'X-Magic-Secret-Key: sk_live_XXXXXXXX' \ -H 'Content-Type: application/json' \ -d '[ { "access_type": "redirect_url", "value": "https://example.com/callback" } ]' ``` ```bash Remove Redirects icon="minus" theme={null} curl -X DELETE 'https://api.dashboard.magic.link/v1/admin/access_whitelist' \ -H 'X-Magic-Secret-Key: sk_live_XXXXXXXX' \ -H 'Content-Type: application/json' \ -d '{ "access_type": "redirect_url", "value": "https://example.com/callback" }' ``` # Device Registration Source: https://docs.magic.link/embedded-wallets/authentication/security/device-registration ## Overview Device Registration is a security feature that helps protect end-users from sophisticated phishing techniques. When a returning user initiates a login from an unrecognized device or browser, they’ll receive an email or text message to review and confirm the login request. You can learn more about Magic’s commitment to security in [this blog post](https://magic.link/posts/magic-commitment-product-security). ## Usage Device Registration applies to returning users logging in via Email OTP or SMS on an unrecognized device. Device Registration is not enforced at time of initial account creation. ### Email Login If a returning user attempts to log in via email from an unrecognized device or browser, they will be shown a prompt to register their device. The user will receive a themed email containing information about the login request, with a button to approve the login. Clicking the **Approve this login** button navigates users to a secure domain owned by Magic, which compares the user’s current device profile to the device profile used to initiate the login request. If the profiles match, the user’s new device will be registered automatically. They can then return to the application and continue with their standard login process. However, if the confirming device profile does not match the device profile used to initiate a login request, Magic will display a secondary confirmation with information about login request. Users can then choose to approve or reject the login. This will most commonly occur for end-users that initiate a login on one device (laptop) and check their email on a different device (phone). ### SMS Login When a returning user attempts to log in via SMS on an unrecognized device or browser, they’ll receive an SMS containing information about the device profile that initiated the login. Users can then respond via SMS to either approve (`1`) or deny (`2`) the login request. ### Customization For security purposes, Magic’s Device Registration offering supports limited customization options. As with all widget UI, app name, logo, brand color, and theme will be applied to each step of the device registration flow. Additionally, you may use `deviceCheckUI=false` to customize the messaging shown to users when an unrecognized device is detected. You must use this option if you are also passing `showUI=false` to your email login method call, otherwise the user may not see a UI to continue registering their device. See [API Reference](/embedded-wallets/sdk/client-side/javascript#loginwithemailotp) for more info. Device Registration also supports [Custom SMTP](/embedded-wallets/authentication/customization/custom-smtp), allowing you to customize the domain and sender that the device registration email is sent from. ### Disabling Device Registration While we highly recommend enforcing device registration, this feature can be disabled on a per-app basis. To disable device registration, head to the Settings page in Magic’s developer dashboard. ## SDK Version Support For the best user experience, please make sure you’re using the minimum version of Magic’s SDK: | | | | | ----------------- | ------------------------------ | ----------- | | **Framework** | **SDK** | **Version** | | Web | `magic-sdk` | v20.1.0+ | | React Native Bare | `@magic-sdk/react-native-bare` | v20.1.0+ | | React Native Expo | `@magic-sdk/react-native-expo` | v20.1.0+ | | Flutter | `magic_sdk` | v4.1.1+ | | iOS | `MagicSDK` | v9.1.0+ | | Android | `link.magic:magic-android` | v9.1.0+ | # Custom Session Management Source: https://docs.magic.link/embedded-wallets/authentication/security/session-management ## Overview The Session Management feature allows you to control the maximum duration of your user's sessions with Magic. Default sessions will allow users to remain authenticated with Magic for **up to 7 days** (or until they logout or browser data is cleared). Auto Refresh sessions will allow users to stay authenticated with Magic for a duration of your choosing, up to 90 days (or until they logout or browser data is cleared). When Auto Refresh is enabled, each successful authentication will be issued a refresh token along with the session token. The refresh token will be valid for the configured number of days and while valid, will allow the user to bypass re-authentication. Once the refresh token expires, the user will need to re-authenticate with Magic. # How to Integrate with the Ethereum Blockchain using Android Source: https://docs.magic.link/embedded-wallets/blockchains/ethereum/android ## Installation To interact with the Ethereum blockchain, Magic Android SDK integrates [`Web3j`](https://github.com/web3j/web3j) as sub dependency. Add the following dependencies in `build.gradle` ```kotlin Kotlin icon="android" theme={null} dependencies { implementation 'link.magic:magic-android:4.0.0' implementation 'org.web3j:core:4.8.8-android' implementation 'org.web3j:geth:4.8.8-android' } ``` ## Initialization The Magic class is the entry-point to the Magic SDK. It must be instantiated with a Magic **publishable** key. **Note** The following example uses **Kotlin 1.3 and Magic Android 4.x**. Android demo will be open-sourced soon. You may use Android Studio to convert Java to Kotlin or vice versa. ```kotlin Kotlin icon="android" theme={null} class MagicDemoApp: Application() { lateinit var magic: Magic override fun onCreate() { magic = Magic(this, "YOUR_PUBLISHABLE_KEY") super.onCreate() } } // Initialize web3j class MagicActivity: AppCompatActivity() { lateinit var web3j: Web3j lateinit var gethWeb3j: Geth override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) web3j = Web3j.build(magic.rpcProvider) gethWeb3j = Geth.build(magic.rpcProvider) } } ``` ## Use Different Networks ### Testnet Goerli Block Explorer: [https://goerli.etherscan.io](https://etherscan.io) ⁠Goerli Testnet Faucet: [https://goerlifaucet.com](https://goerlifaucet.com) ```kotlin Kotlin icon="android" theme={null} magic = Magic(this, "YOUR_PUBLISHABLE_API_KEY", Magic.Network.goerli) ``` ### Custom Node You can allow specific URLs to interact with the Magic SDK, such as a custom RPC URL to send transactions to your node. The Content Security Policy (CSP) of a browser dictates what resources can be loaded. You can update the policy in the settings page of the [dashboard](https://dashboard.magic.link) with your custom URL. **Important** The use of a custom node will require the RPC URL to the project's Content Security Policy from your [Magic Dashboard](https://dashboard.magic.link). Refer to the [CSP documentation](/embedded-wallets/wallets/security/content-security-policy). ```kotlin Kotlin icon="android" theme={null} magic = Magic(this, "YOUR_PUBLISHABLE_API_KEY", CustomNodeConfiguration("https://alchemy.io")) ``` **Important** Do **not** set the custom nodes to local IP address (E.x. [http://127.0.0.1](http://127.0.0.1)), because local IP will point to the network environment inside mobile device / simulator. Try accessible IP address in the same Wifi/Internet Environment (E.x. [http://10.0.0.93:3000](http://10.0.0.93:3000)). ### Associated Class `CustomNodeConfiguration(rpcUrl: String, chainId: Int?)` * `rpcUrl`: Your own node URL * `chainId`: Your own node's chainId `Magic.EthNetwork` ```kotlin Kotlin icon="android" theme={null} enum class EthNetwork { Mainnet, Goerli } ``` ## Common Methods ### Send Transaction ```kotlin Kotlin icon="android" theme={null} class MagicActivity: AppCompatActivity() { lateinit var magic: Magic lateinit var web3j: Web3j override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) magic = (applicationContext as MagicDemoApp).magic web3j = Web3j.build(magic.rpcProvider) } // After user is successfully authenticated fun sendTransaction(v: View) { try { val value: BigInteger = Convert.toWei("0.5", Convert.Unit.ETHER).toBigInteger() val transaction = createEtherTransaction(account, BigInteger("1"), BigInteger("21000"), BigInteger("21000"), account, value) val receipt = web3j.ethSendTransaction(transaction).send() Log.d("Transaction complete: " + receipt.transactionHash) } catch (e: Exception) { Log.e("Error", e.localizedMessage) } } } ``` ### Sign Message Magic Android SDK extends the functionality from Web3j to allow developers to sign Typed Data. You may find it in `magic.web3jSigExt`. #### Personal Sign ```kotlin Kotlin icon="android" theme={null} class MagicActivity: AppCompatActivity() { lateinit var magic: Magic lateinit var web3j: Web3j lateinit var gethWeb3j: Geth // After user is successfully authenticated private var account: String? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) magic = (applicationContext as MagicDemoApp).magic web3j = Web3j.build(magic.rpcProvider) gethWeb3j = Geth.build(magic.rpcProvider) } fun personSign(view: View) { val message = "Hello from Magic!!!" val personalSign: PersonalSign = gethWeb3j.personalSign( message, account, "password") .send() Log.d("Magic", "Signed Message: " + personalSign.signedMessage) // Recover Message val recovered = gethWeb3j.personalEcRecover(message, personalSign.signedMessage).send() Log.d("Magic", "Recovered Address: " + recovered.recoverAccountId) } } ``` #### Sign TypedData Legacy (V1) ```kotlin Kotlin icon="android" theme={null} class MagicActivity: AppCompatActivity() { lateinit var magic: Magic // After user is successfully authenticated private var account: String? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) magic = (applicationContext as MagicDemoApp).magic } // Sign with EIP712 Data Field fun signTypedDataLegacy(v: View) { val list = listOf( EIP712TypedDataLegacyFields("string", "Hello from Magic", "This message will be signed by you"), EIP712TypedDataLegacyFields("uint32", "Here is a number", "90210") ) val signature = magic.web3jSigExt.signTypedDataLegacy(account, list).send() Log.d("Magic", signature.result) } // Sign with JSON String fun signTypedDataLegacyJson(v: View) { val jsonString = "[{\"type\":\"string\",\"name\":\"Hello from Magic\",\"value\":\"This message will be signed by you\"},{\"type\":\"uint32\",\"name\":\"Here is a number\",\"value\":\"90210\"}]" val signature = magic.web3jSigExt.signTypedDataLegacy(account, jsonString).send() Log.d("Magic", signature.result) } } ``` #### Sign Typed Data v3 ```kotlin Kotlin icon="android" theme={null} class MagicActivity: AppCompatActivity() { lateinit var magic: Magic lateinit var web3j: Web3j lateinit var gethWeb3j: Geth // After user is successfully authenticated private var account: String? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) magic = (applicationContext as MagicDemoApp).magic } fun signTypedData(v: View) { val jsonString = "{\"types\":{\"EIP712Domain\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"version\",\"type\":\"string\"},{\"name\":\"verifyingContract\",\"type\":\"address\"}],\"Order\":[{\"name\":\"makerAddress\",\"type\":\"address\"},{\"name\":\"takerAddress\",\"type\":\"address\"},{\"name\":\"feeRecipientAddress\",\"type\":\"address\"},{\"name\":\"senderAddress\",\"type\":\"address\"},{\"name\":\"makerAssetAmount\",\"type\":\"uint256\"},{\"name\":\"takerAssetAmount\",\"type\":\"uint256\"},{\"name\":\"makerFee\",\"type\":\"uint256\"},{\"name\":\"takerFee\",\"type\":\"uint256\"},{\"name\":\"expirationTimeSeconds\",\"type\":\"uint256\"},{\"name\":\"salt\",\"type\":\"uint256\"},{\"name\":\"makerAssetData\",\"type\":\"bytes\"},{\"name\":\"takerAssetData\",\"type\":\"bytes\"}]},\"domain\":{\"name\":\"0x Protocol\",\"version\":\"2\",\"verifyingContract\":\"0x35dd2932454449b14cee11a94d3674a936d5d7b2\"},\"message\":{\"exchangeAddress\":\"0x35dd2932454449b14cee11a94d3674a936d5d7b2\",\"senderAddress\":\"0x0000000000000000000000000000000000000000\",\"makerAddress\":\"0x338be8514c1397e8f3806054e088b2daf1071fcd\",\"takerAddress\":\"0x0000000000000000000000000000000000000000\",\"makerFee\":\"0\",\"takerFee\":\"0\",\"makerAssetAmount\":\"97500000000000\",\"takerAssetAmount\":\"15000000000000000\",\"makerAssetData\":\"0xf47261b0000000000000000000000000d0a1e359811322d97991e03f863a0c30c2cf029c\",\"takerAssetData\":\"0xf47261b00000000000000000000000006ff6c0ff1d68b964901f986d4c9fa3ac68346570\",\"salt\":\"1553722433685\",\"feeRecipientAddress\":\"0xa258b39954cef5cb142fd567a46cddb31a670124\",\"expirationTimeSeconds\":\"1553808833\"},\"primaryType\":\"Order\"}" val signature = magic.web3jSigExt.signTypedData(account, jsonString).send() Log.d("Magic", "Signature: " + signature.result) } } ``` #### Sign Typed Data v4 ```kotlin Kotlin icon="android" theme={null} class MagicActivity: AppCompatActivity() { lateinit var magic: Magic lateinit var web3j: Web3j lateinit var gethWeb3j: Geth // After user is successfully authenticated private var account: String? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) magic = (applicationContext as MagicDemoApp).magic } fun signTypedDataV4(v: View) { val jsonString = "{\"domain\":{\"chainId\":1,\"name\":\"Ether Mail\",\"verifyingContract\":\"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC\",\"version\":\"1\"},\"message\":{\"contents\":\"Hello, Bob!\",\"from\":{\"name\":\"Cow\",\"wallets\":[\"0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826\",\"0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF\"]},\"to\":[{\"name\":\"Bob\",\"wallets\":[\"0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB\",\"0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57\",\"0xB0B0b0b0b0b0B000000000000000000000000000\"]}]},\"primaryType\":\"Mail\",\"types\":{\"EIP712Domain\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"version\",\"type\":\"string\"},{\"name\":\"chainId\",\"type\":\"uint256\"},{\"name\":\"verifyingContract\",\"type\":\"address\"}],\"Group\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"members\",\"type\":\"Person[]\"}],\"Mail\":[{\"name\":\"from\",\"type\":\"Person\"},{\"name\":\"to\",\"type\":\"Person[]\"},{\"name\":\"contents\",\"type\":\"string\"}],\"Person\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"wallets\",\"type\":\"address[]\"}]}}" val signature = magic.web3jSigExt.signTypedDataV4(account, jsonString).send() Log.d("Magic", "Signature: " + signature.result) } } ``` ### Get User Info ```kotlin Kotlin icon="android" theme={null} class MagicActivity: AppCompatActivity() { lateinit var web3j: Web3j override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) magic = (applicationContext as MagicDemoApp).magic web3j = Web3j.build(magic.rpcProvider) } // After user is successfully authenticated fun getAccount(){ try { val accounts = web3j.ethAccounts().sendAsync() accounts.whenComplete { accRepsonse: EthAccounts?, error: Throwable? -> if (error != null) { Log.e("MagicError", error.localizedMessage) } if (accRepsonse != null && !accRepsonse.hasError()) { account = accRepsonse.accounts[0] Log.d("Magic", "Your address is $account") } } } catch (e: Exception) { Log.e("Error", e.localizedMessage) } } } ``` ## Smart Contract In this example, we'll be demonstrating how to use Magic with Web3j to interact with Solidity smart contracts. The simple Hello World contract allows anyone to read and write a message to it. ```solidity Solidity icon="ethereum" theme={null} pragma solidity ^0.5.10; contract HelloWorld { string public message; constructor(string memory initMessage) public { message = initMessage; } function update(string memory newMessage) public { message = newMessage; } } ``` ### Create a Kotlin/Java Contract Class from ABI Web3j supports the auto-generation of smart contract function wrappers in Java from Solidity ABI files. To get started, you must have two files * ABI JSON file `.json` * ByteCode file `.bin` ### Install web3j cli-tool ```bash Bash icon="terminal" theme={null} curl -L https://get.web3j.io | sh ``` You may need to install a JDK to support this library ethereum-android-jdk After it has been installed to your computer, you may run the following command to check ```bash Bash icon="terminal" theme={null} web3j version ``` ### Create the contract class ```bash Bash icon="terminal" theme={null} web3j solidity generate -a=./path/to/.json -b=./path/to/.bin -o=/output/path/ -p={packageName} ``` You’ll find a `Contract.java` file created in your output directory above. Put this file in your project, and no more changes are needed. For more details about this section, please [click here](https://web3j.readthedocs.io/en/latest/smart_contracts.html#solidity-smart-contract-wrappers). ### Deploy Contract When deploying contract, building or interacting with a contract using web3j library, Magic offers `MagicTxnManager` class as a default `TransactionManager` that helps you to avoid dealing with private keys or credentials that Contract class requires. ```kotlin Kotlin icon="android" theme={null} import link.magic.demo.contract.Contract // This is the contract class you created above class MagicActivity: AppCompatActivity() { lateinit var magic: Magic lateinit var web3j: Web3j // After user is successfully authenticated private var account: String? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) magic = (applicationContext as MagicDemoApp).magic web3j = Web3j.build(magic.rpcProvider) } fun deployContract(view: View) { try { val price = BigInteger.valueOf(22000000000L) val limit = BigInteger.valueOf(4300000) val gasProvider = StaticGasProvider(price, limit) val contract = Contract.deploy( web3j, account?.let { MagicTxnManager(web3j, it) }, gasProvider, "HELLO_WORLD_FROM_ANDROID" ).send() Log.d("Magic", "Deploy to" + contract.contractAddress) } catch (e: Exception) { Log.e("E", "error", e) } } } ``` ### Read From Contract ```kotlin Kotlin icon="android" theme={null} fun contractRead(view: View) { try { val price = BigInteger.valueOf(22000000000L) val limit = BigInteger.valueOf(4300000) val gasProvider = StaticGasProvider(price, limit) // Contract in testnet val contract = ExampleContract.load("0x6a2d321a3679b1b3c8a19b84e41abd11763a8ab5", web3j, account?.let { MagicTxnManager(web3j, it) }, gasProvider) if (contract.isValid) { val ethCall = contract.message().send() Log.d("Magic", ethCall.toString()) } else { throw Error("contract not valid") } } catch (e: Exception) { Log.e("E", "error", e) } } ``` ### Write to Contract ```kotlin Kotlin icon="android" theme={null} fun contractWrite(view: View) { try { val price = BigInteger.valueOf(22000000000L) val limit = BigInteger.valueOf(4300000) val gasProvider = StaticGasProvider(price, limit) // Contract in testnet val contract = ExampleContract.load("0x6a2d321a3679b1b3c8a19b84e41abd11763a8ab5", web3j, account?.let { MagicTxnManager(web3j, it) }, gasProvider) if (contract.isValid) { val ethCall = contract.update("NEW_MESSAGE_FROM_ANDROID").send() Log.d("Magic", ethCall.toString()) } else { throw Error("contract not valid") } } catch (e: Exception) { Log.e("E", "error", e) } } ``` ## Resources * [Magic Android SDK](https://github.com/magiclabs/magic-android) # How to Integrate with the Ethereum Blockchain using Flutter Source: https://docs.magic.link/embedded-wallets/blockchains/ethereum/flutter ## Installation To interact with the Ethereum blockchain, Magic Flutter SDK embeds [`web3dart`](https://pub.dev/packages/web3dart) as sub dependency. No more extra dependency is needed. ## Initialization The Magic class is the entry-point to the Magic SDK. It must be instantiated with a Magic **publishable** key. ```dart Dart icon="flutter" theme={null} import 'package:magic_sdk/magic_sdk.dart'; void main() { runApp(const MyApp()); Magic.instance = Magic("YOUR_PUBLISHABLE_KEY"); } ``` ```dart Dart icon="flutter" theme={null} import 'package:magic_sdk/magic_sdk.dart'; import 'package:magic_sdk/modules/web3/magic_credential.dart'; import 'package:web3dart/web3dart.dart'; class Web3Page extends StatefulWidget { const Web3Page({Key? key}) : super(key: key); @override State createState() => _Web3PageState(); } class _Web3PageState extends State { final magic = Magic.instance; final client = Web3Client.custom(Magic.instance.provider); final credential = MagicCredential(Magic.instance.provider); @override Widget build(BuildContext context) { //Your page } } ``` ## Use Different Networks ### Testnet Goerli Block Explorer: [https://goerli.etherscan.io](https://etherscan.io) Goerli Testnet Faucet: [https://goerlifaucet.com](https://goerlifaucet.com) ```dart Dart icon="flutter" theme={null} import 'package:magic_sdk/magic_sdk.dart'; import 'package:magic_sdk/modules/web3/eth_network.dart'; void main() { runApp(const MyApp()); Magic.instance = Magic.eth("YOUR_PUBLISHABLE_KEY", network: EthNetwork.goerli); } ``` ### Custom Node You can allow specific URLs to interact with the Magic SDK, such as a custom RPC URL to send transactions to your node. The Content Security Policy (CSP) of a browser dictates what resources can be loaded. You can update the policy in the settings page of the dashboard with your custom URL. **Note** The use of a custom node will require the RPC URL to the project's Content Security Policy from your [Magic Dashboard](https://dashboard.magic.link). Refer to the [CSP documentation](/embedded-wallets/wallets/security/content-security-policy). ```dart Dart icon="flutter" theme={null} import 'package:magic_sdk/magic_sdk.dart'; void main() { runApp(const MyApp()); Magic.instance = Magic.custom("YOUR_PUBLISHABLE_KEY", rpcUrl: "https://your.own.node", chainId: 1011); // your own node } ``` **Important** Do **not** set the custom nodes to local IP address (E.x. "[http://127.0.0.1"](http://127.0.0.1")). Local IP will point to the network environment inside the mobile device / simulator ## Magic Credential `web3dart` supports custodial design where the library will ask for user's private key to sign the payloads. To integrate `web3dart` in non-custodial, We recommend you to use `MagicCredential` to optimize security. ```dart Dart icon="flutter" theme={null} class MagicCredential { Future getAccount() Future personalSign({required Uint8List payload}) Future ethSign({required Uint8List payload}) Future signTypedDataLegacy({required Map payload}) Future signTypedData({required Map payload}) Future sendTransaction(Transaction transaction) } ``` **Important** You will need to call `MagicCredential getAccount()` before other signing functions to get the public address that will be used from the current authenticated user. ## Common Methods ### Send Transaction ```dart Dart icon="flutter" theme={null} import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:magic_sdk/magic_sdk.dart'; import 'package:magic_sdk/modules/web3/magic_credential.dart'; class _Web3PageState extends State { final magic = Magic.instance; final credential = MagicCredential(Magic.instance.provider); final client = Web3Client.custom(Magic.instance.provider); @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [ /// send Transaction ElevatedButton( onPressed: () async { var hash = await client.sendTransaction( credential, Transaction( to: EthereumAddress.fromHex( '0x01568bf1c1699bb9d58fac67f3a487b28ab4ab2d'), gasPrice: EtherAmount.inWei(BigInt.two), maxGas: 100000, value: EtherAmount.fromUnitAndValue(EtherUnit.gwei, 2), ), ); debugPrint("transaction, $hash"); }, child: const Text('sendTransaction'), ), ]))); } } ``` ### Sign Message `Magic Credential` offers non-custodial signing apis #### Eth Sign ```dart Dart icon="flutter" theme={null} import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:magic_sdk/magic_sdk.dart'; import 'package:magic_sdk/modules/web3/magic_credential.dart'; class _Web3PageState extends State { final magic = Magic.instance; final credential = MagicCredential(Magic.instance.provider); @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [ /// eth sign ElevatedButton( onPressed: () async { List list = utf8.encode("hello world"); Uint8List payload = Uint8List.fromList(list); var signature = await credential.ethSign(payload: payload); debugPrint(context, "eth_sign signature, $signature"); }, child: const Text('eth sign'), ), ]))); } } ``` #### Sign Typed Data Legacy (V1) ```dart Dart icon="flutter" theme={null} import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:magic_sdk/magic_sdk.dart'; import 'package:magic_sdk/modules/web3/magic_credential.dart'; class _Web3PageState extends State { final magic = Magic.instance; final credential = MagicCredential(Magic.instance.provider); @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [ /// Sign Typed Data V1 ElevatedButton( onPressed: () async { var payload = { "type": "string", "name": "Hello from Magic Labs", "value": "This message will be assigned by you" }; var signature = await credential.signTypedDataLegacy(payload: payload); debugPrint(context, "sign Typed Data V1 signature, $signature"); }, child: const Text('sign Typed Data V1'), ), ]))); } } ``` #### Sign Typed Data (EIP 712) ```dart Dart icon="flutter" theme={null} import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:magic_sdk/magic_sdk.dart'; import 'package:magic_sdk/modules/web3/magic_credential.dart'; class _Web3PageState extends State { final magic = Magic.instance; final credential = MagicCredential(Magic.instance.provider); @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [ /// Sign Typed Data V3 ElevatedButton( onPressed: () async { var signature = await credential.signTypedData(payload: signTypedDataV3Payload); showResult(context, "sign Typed Data V3 signature, ${(signature)}"); }, child: const Text('sign Typed Data V3'), ), ]))); } } var signTypedDataV3Payload = { "types": { "EIP712Domain": [ {"name": "name", "type": "string"}, {"name": "version", "type": "string"}, {"name": "verifyingContract", "type": "address"} ], "Order": [ {"name": "makerAddress", "type": "address"}, {"name": "takerAddress", "type": "address"}, {"name": "feeRecipientAddress", "type": "address"}, {"name": "senderAddress", "type": "address"}, {"name": "makerAssetAmount", "type": "uint256"}, {"name": "takerAssetAmount", "type": "uint256"}, {"name": "makerFee", "type": "uint256"}, {"name": "takerFee", "type": "uint256"}, {"name": "expirationTimeSeconds", "type": "uint256"}, {"name": "salt", "type": "uint256"}, {"name": "makerAssetData", "type": "bytes"}, {"name": "takerAssetData", "type": "bytes"} ] }, "domain": { "name": "0x Protocol", "version": "2", "verifyingContract": "0x35dd2932454449b14cee11a94d3674a936d5d7b2" }, "message": { "exchangeAddress": "0x35dd2932454449b14cee11a94d3674a936d5d7b2", "senderAddress": "0x0000000000000000000000000000000000000000", "makerAddress": "0x338be8514c1397e8f3806054e088b2daf1071fcd", "takerAddress": "0x0000000000000000000000000000000000000000", "makerFee": "0", "takerFee": "0", "makerAssetAmount": "97500000000000", "takerAssetAmount": "15000000000000000", "makerAssetData": "0xf47261b0000000000000000000000000d0a1e359811322d97991e03f863a0c30c2cf029c", "takerAssetData": "0xf47261b00000000000000000000000006ff6c0ff1d68b964901f986d4c9fa3ac68346570", "salt": "1553722433685", "feeRecipientAddress": "0xa258b39954cef5cb142fd567a46cddb31a670124", "expirationTimeSeconds": "1553808833" }, "primaryType": "Order" }; ``` ### Get User Info ```dart Dart icon="flutter" theme={null} import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:magic_sdk/magic_sdk.dart'; import 'package:magic_sdk/modules/web3/magic_credential.dart'; class _Web3PageState extends State { final magic = Magic.instance; final credential = MagicCredential(Magic.instance.provider); @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [ /// get account ElevatedButton( onPressed: () async { var cred = await credential.getAccount(); debugPrint("account, ${cred.hex}"); }, child: const Text('getAccount'), ), ]))); } } ``` ## Smart Contract In this example, we'll be demonstrating how to use Magic with `web3dart` to interact with Solidity smart contracts. The simple Hello World contract allows anyone to read and write a message to it. ```solidity Solidity icon="ethereum" theme={null} pragma solidity ^0.5.10; contract HelloWorld { string public message; constructor(string memory initMessage) public { message = initMessage; } function update(string memory newMessage) public { message = newMessage; } } ``` ```dart Dart icon="flutter" theme={null} class TestContract { static final deployedAddress = EthereumAddress.fromHex("0x8b211dfebf490a648f6de859dfbed61fa22f35e0"); static const contractAbi = '[{"constant":false,"inputs":[{"name":"newMessage","type":"string"}],"name":"update","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"message","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"initMessage","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]'; static const byteCode = "0x608060405234801561001057600080fd5b5060405161047f38038061047f8339818101604052602081101561003357600080fd5b81019080805164010000000081111561004b57600080fd5b8281019050602081018481111561006157600080fd5b815185600182028301116401000000008211171561007e57600080fd5b5050929190505050806000908051906020019061009c9291906100a3565b5050610148565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100e457805160ff1916838001178555610112565b82800160010185558215610112579182015b828111156101115782518255916020019190600101906100f6565b5b50905061011f9190610123565b5090565b61014591905b80821115610141576000816000905550600101610129565b5090565b90565b610328806101576000396000f3fe608060405234801561001057600080fd5b5060043610610053576000357c0100000000000000000000000000000000000000000000000000000000900480633d7403a314610058578063e21f37ce14610113575b600080fd5b6101116004803603602081101561006e57600080fd5b810190808035906020019064010000000081111561008b57600080fd5b82018360208201111561009d57600080fd5b803590602001918460018302840111640100000000831117156100bf57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050610196565b005b61011b6101b0565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561015b578082015181840152602081019050610140565b50505050905090810190601f1680156101885780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b80600090805190602001906101ac92919061024e565b5050565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156102465780601f1061021b57610100808354040283529160200191610246565b820191906000526020600020905b81548152906001019060200180831161022957829003601f168201915b505050505081565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061028f57805160ff19168380011785556102bd565b828001600101855582156102bd579182015b828111156102bc5782518255916020019190600101906102a1565b5b5090506102ca91906102ce565b5090565b6102f091905b808211156102ec5760008160009055506001016102d4565b5090565b9056fea265627a7a7230582003ae1ef5a63bf058bfd2b31398bdee39d3cbfbb7fbf84235f4bc2ec352ee810f64736f6c634300050a0032"; } ``` ### Deploy Contract ```dart Dart icon="flutter" theme={null} final credential = MagicCredential(Magic.instance.provider); final client = Web3Client.custom(Magic.instance.provider); var list = utf8.encode(TestContract.byteCode); Uint8List payload = Uint8List.fromList(list); final Transaction transaction = Transaction( to: null, from: credential.address, data: payload, maxGas: 2000000); final String transactionId = await client.sendTransaction(credential, transaction); ``` ### Read from Contract ```dart Dart icon="flutter" theme={null} final credential = MagicCredential(Magic.instance.provider); final client = Web3Client.custom(Magic.instance.provider); final contract = DeployedContract( ContractAbi.fromJson(TestContract.contractAbi, ''), TestContract.deployedAddress); final messageFunction = contract.function('message'); var message = await client.call( contract: contract, function: messageFunction, params: []); debugPrint("Contract Read Message, $message"); ``` ### Write to Contract ```dart Dart icon="flutter" theme={null} final credential = MagicCredential(Magic.instance.provider); final client = Web3Client.custom(Magic.instance.provider); final contract = DeployedContract( ContractAbi.fromJson(TestContract.contractAbi, ''), TestContract.deployedAddress); final updateFunction = contract.function('update'); var transactionId = await client.sendTransaction( credential, Transaction.callContract( contract: contract, function: updateFunction, parameters: ["NEW_MESSAGE"])); ``` ## Resources * [Magic Flutter SDK](https://github.com/magiclabs/magic-flutter) # How to Integrate with the Ethereum Blockchain using iOS Source: https://docs.magic.link/embedded-wallets/blockchains/ethereum/ios ## Installation To interact with the Ethereum blockchain, Magic iOS SDK embeds [`Web3.swift`](https://github.com/Boilertalk/Web3.swift) as a sub-dependency. No more extra dependency is needed. ## Initialization The Magic class is the entry-point to the Magic SDK. It must be instantiated with a Magic **publishable** key. **Note** Following example is using Swift 5 with Xcode 11. iOS demo will be open-sourced soon. ```swift Swift icon="swift" theme={null} // AppDelegate.swift import MagicSDK import UIKit @UIApplicationMain func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // assign the newly created Magic instance to shared property Magic.shared = Magic("YOUR_PUBLISHABLE_API_KEY"); return true } ``` ```swift Swift icon="swift" theme={null} // ViewController.swift import UIKit import MagicSDK import MagicSDK_Web3 class Web3ViewController: UIViewController { let web3 = Web3(provider: Magic.shared.rpcProvider) } ``` ## Use Different Networks ### Testnet Goerli Block Explorer: [https://goerli.etherscan.io](https://goerli.etherscan.io) Goerli Testnet Faucet: [https://goerlifaucet.com](https://goerlifaucet.com) ```swift Swift icon="swift" theme={null} // AppDelegate.swift import MagicSDK func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // assign to Magic singleton Magic.shared = Magic("YOUR_PUBLISHABLE_API_KEY", EthNetwork.rinkeby); return true } ``` ### Custom Node You can allow specific URLs to interact with the Magic SDK, such as a custom RPC URL to send transactions to your node. The Content Security Policy (CSP) of a browser dictates what resources can be loaded. You can update the policy in the settings page of the dashboard with your custom URL. **Note** Note: the use of a custom node will require the RPC URL to the project's Content Security Policy from your [Magic Dashboard](https://dashboard.magic.link). Refer to the [CSP documentation](/embedded-wallets/wallets/security/content-security-policy). ```swift Swift icon="swift" theme={null} // AppDelegate.swift import MagicSDK // assign to Magic singleton func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { let config = CustomNodeConfiguration(rpcUrl: "https://alchemy.io", chainId: 1) Magic.shared = Magic(apiKey: "API_KEY", customNode: config); return true } ``` **Important** Do **not** set the custom nodes to local IP address (E.x. "[http://127.0.0.1"](http://127.0.0.1")). Local IP will point to the network environment inside mobile device / simulator #### Associated Class `CustomNodeConfiguration(rpcUrl: String, chainId: Int?)` * `rpcUrl` :Your own node URL * `chainId` : Your own node's chainId `EthNetwork` ```swift Swift icon="swift" theme={null} public enum EthNetwork: String { case mainnet case goerli } ``` ## Common Methods ### Send Transaction ```swift Swift icon="swift" theme={null} import MagicSDK import MagicSDK_Web3 class Web3ViewController: UIViewController { var web3 = Web3(provider: Magic.shared.rpcProvider) var account: EthereumAddress? // After user is successfully authenticated func sendTransaction() { guard let account = self.account else { return } // Construct a transaction let transaction = EthereumTransaction( from: account, // from Get User Info section to: EthereumAddress(hexString: "0xE0cef4417a772512E6C95cEf366403839b0D6D6D"), value: EthereumQuantity(quantity: 1.gwei) ) // Submit transaction to the blockchain web3.eth.sendTransaction(transaction: transaction).done { (transactionHash) in print(transactionHash.hex()) }.catch { error in print(error.localizedDescription) } } } ``` ### Sign Message Magic iOS SDK extends the functionality from Web3.swift to allow developers to sign Typed Data. Make sure to `import MagicSDK` while using these functions. #### Eth Sign ```swift Swift icon="swift" theme={null} import MagicSDK import MagicSDK_Web3 import PromiseKit class ViewController: UIViewController { let web3 = Web3(provider: Magic.shared.rpcProvider) var account: EthereumAddress? func ethSign() { guard let account = self.account else { return } let message = try! EthereumData("Hello World".data(using: .utf8)!) web3.eth.sign(from: account, message: message).done({ result in print(result.hex()) }) } } ``` #### Sign Typed Data Legacy (V1) ```swift Swift icon="swift" theme={null} import MagicSDK import MagicSDK_Web3 import PromiseKit class Web3ViewController: UIViewController { let web3 = Web3(provider: Magic.shared.rpcProvider) var account: EthereumAddress? func SignTypedDataLegacy() { guard let account = self.account else { return } let payload = EIP712TypedDataLegacyFields(type: "string", name: "Hello from Magic Labs", value: "This message will be signed by you") web3.eth.signTypedDataLegacy(account: account, data: [payload]).done({ result in print(result.hex()) }) } } ``` #### Sign Typed Data v3(EIP 712) ```swift Swift icon="swift" theme={null} import MagicSDK import MagicSDK_Web3 class Web3ViewController: UIViewController { let web3 = Web3(provider: Magic.shared.rpcProvider) var account: EthereumAddress? func SignTypedData() { guard let account = self.account else { return } do { let json = """ {"types":{"EIP712Domain":[{"name":"name","type":"string"},{"name":"version","type":"string"},{"name":"verifyingContract","type":"address"}],"Greeting":[{"name":"contents","type":"string"}]},"primaryType":"Greeting","domain":{"name":"Magic","version":"1","verifyingContract":"0xE0cef4417a772512E6C95cEf366403839b0D6D6D"},"message":{"contents":"Hello, from Magic!"}} """.data(using: .utf8)! let typedData = try JSONDecoder().decode(EIP712TypedData.self, from: json) web3.eth.signTypedData(account: account, data: typedData).done({ result in print(result.hex()) }) } catch { print(error.localizedDescription) } } } ``` #### Sign Typed Data V4 (EIP 712) ```swift Swift icon="swift" theme={null} import MagicSDK import MagicSDK_Web3 class Web3ViewController: UIViewController { let web3 = Web3(provider: Magic.shared.rpcProvider) var account: EthereumAddress? func SignTypedData() { guard let account = self.account else { return } do { let json = """ {"domain":{"chainId":1,"name":"Ether Mail","verifyingContract":"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC","version":"1"},"message":{"contents":"Hello, Bob!","from":{"name":"Cow","wallets":["0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826","0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF"]},"to":[{"name":"Bob","wallets":["0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB","0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57","0xB0B0b0b0b0b0B000000000000000000000000000"]}]},"primaryType":"Mail","types":{"EIP712Domain":[{"name":"name","type":"string"},{"name":"version","type":"string"},{"name":"chainId","type":"uint256"},{"name":"verifyingContract","type":"address"}],"Group":[{"name":"name","type":"string"},{"name":"members","type":"Person[]"}],"Mail":[{"name":"from","type":"Person"},{"name":"to","type":"Person[]"},{"name":"contents","type":"string"}],"Person":[{"name":"name","type":"string"},{"name":"wallets","type":"address[]"}]}} """.data(using: .utf8)! let typedData = json.data(using: .utf8)! let typedDataV4 = try JSONDecoder().decode(EIP712TypedData.self, from: typedData) web3.eth.signTypedDataV4(account: address, data: typedDataV4).done({ response in print(response.hex()) }) // Catch decoding error } catch let DecodingError.typeMismatch(type, context) { print("Type '\(type)' mismatch:", context.debugDescription) print("codingPath:", context.codingPath) } catch { self.showResult(error.localizedDescription) } } } ``` ### Get User Info ```swift Swift icon="swift" theme={null} import MagicSDK import MagicSDK_Web3 import PromiseKit class Web3ViewController: UIViewController { var web3 = Web3(provider: Magic.shared.rpcProvider) // After user is successfully authenticated @IBOutlet weak var accountLabel: UILabel! func getAccount() { firstly { // Get user's Ethereum public address web3.eth.accounts() }.done { accounts -> Void in if let account = accounts.first { // Set to UILa self.accountLabel.text = account.hex(eip55: false) } else { print("No Account Found") } }.catch { error in print("Error loading accounts and balance: \(error)") } } } ``` ## Smart Contract In this example, we'll be demonstrating how to use Magic with Web3.swift to interact with Solidity smart contracts. The simple Hello World contract allows anyone to read and write a message to it. ```solidity Solidity icon="ethereum" theme={null} pragma solidity ^0.5.10; contract HelloWorld { string public message; constructor(string memory initMessage) public { message = initMessage; } function update(string memory newMessage) public { message = newMessage; } } ``` ### Deploy Contract ```swift Swift icon="swift" theme={null} import MagicSDK import MagicSDK_Web3 class Web3ViewController: UIViewController { let web3 = Web3(provider: Magic.shared.rpcProvider) var account: EthereumAddress? // After user is successfully authenticated func deployContract() { guard let account = self.account else { return } do { let contractABI = """ [{"constant":false,"inputs":[{"name":"newMessage","type":"string"}],"name":"update","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"message","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"initMessage","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}] """.data(using: .utf8)! let contractByteCode = try EthereumData("0x608060405234801561001057600080fd5b5060405161047f38038061047f8339818101604052602081101561003357600080fd5b81019080805164010000000081111561004b57600080fd5b8281019050602081018481111561006157600080fd5b815185600182028301116401000000008211171561007e57600080fd5b5050929190505050806000908051906020019061009c9291906100a3565b5050610148565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100e457805160ff1916838001178555610112565b82800160010185558215610112579182015b828111156101115782518255916020019190600101906100f6565b5b50905061011f9190610123565b5090565b61014591905b80821115610141576000816000905550600101610129565b5090565b90565b610328806101576000396000f3fe608060405234801561001057600080fd5b5060043610610053576000357c0100000000000000000000000000000000000000000000000000000000900480633d7403a314610058578063e21f37ce14610113575b600080fd5b6101116004803603602081101561006e57600080fd5b810190808035906020019064010000000081111561008b57600080fd5b82018360208201111561009d57600080fd5b803590602001918460018302840111640100000000831117156100bf57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050610196565b005b61011b6101b0565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561015b578082015181840152602081019050610140565b50505050905090810190601f1680156101885780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b80600090805190602001906101ac92919061024e565b5050565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156102465780601f1061021b57610100808354040283529160200191610246565b820191906000526020600020905b81548152906001019060200180831161022957829003601f168201915b505050505081565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061028f57805160ff19168380011785556102bd565b828001600101855582156102bd579182015b828111156102bc5782518255916020019190600101906102a1565b5b5090506102ca91906102ce565b5090565b6102f091905b808211156102ec5760008160009055506001016102d4565b5090565b9056fea265627a7a7230582003ae1ef5a63bf058bfd2b31398bdee39d3cbfbb7fbf84235f4bc2ec352ee810f64736f6c634300050a0032") /// Create Contract instance let contract = try web3.eth.Contract(json: contractABI, abiKey: nil, address: nil) /// Deploy contract guard let invocation = contract.deploy(byteCode: contractByteCode) else { return } invocation.send(from: self.account!, gas: 1025256, gasPrice: 0) { (hash, error) in print(hash?.hex() ?? "Missing Hash") print(error?.localizedDescription ?? "Error") } } catch { print(error.localizedDescription) } } } ``` ### Read From Contract ```swift Swift icon="swift" theme={null} import MagicSDK import MagicSDK_Web3 class MagicViewController: UIViewController { let web3 = Web3(provider: Magic.shared.rpcProvider) var account: EthereumAddress? // After user is successfully authenticated func getMessage() { do { /// Construct contract instance let contractABI = """ [{"constant":false,"inputs":[{"name":"newMessage","type":"string"}],"name":"update","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"message","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"initMessage","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}] """.data(using: .utf8)! let contract = try web3.eth.Contract(json: contractABI, abiKey: nil, address: EthereumAddress(ethereumValue: "0x8b211dfebf490a648f6de859dfbed61fa22f35e0")) /// contract call contract["message"]?().call() { response, error in if let response = response, let message = response[""] as? String { print(message.description) } else { print(error?.localizedDescription ?? "Failed to get response") } } } catch { /// Error handling print(error.localizedDescription) } } } ``` ### Write to Contract ```swift Swift icon="swift" theme={null} import MagicSDK import MagicSDK_Web3 class MagicViewController: UIViewController { let web3 = Web3(provider: Magic.shared.rpcProvider) var account: EthereumAddress? // After user is successfully authenticated func writeMessage() { guard let account = self.account else { return } do { /// contract instance let contractABI = """ [{"constant":false,"inputs":[{"name":"newMessage","type":"string"}],"name":"update","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"message","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"initMessage","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}] """.data(using: .utf8)! let contract = try web3.eth.Contract(json: contractABI, abiKey: nil, address: EthereumAddress(ethereumValue: "0x8b211dfebf490a648f6de859dfbed61fa22f35e0")) /// contract call guard let transaction = contract["update"]?("NEW_MESSAGE").createTransaction( nonce: 0, from: account, value: 0, gas: EthereumQuantity(150000), gasPrice: EthereumQuantity(quantity: 21.gwei) ) else { return } web3.eth.sendTransaction(transaction: transaction).done({ txHash in print(txHash.hex()) }).catch{ error in print(error.localizedDescription) } } catch { print(error.localizedDescription) } } } ``` ## Resources * [Magic iOS SDK](https://github.com/magiclabs/magic-ios) * [Magic iOS Demo](https://github.com/magiclabs/magic-ios-demo) # How to Integrate with the Ethereum Blockchain using JavaScript Source: https://docs.magic.link/embedded-wallets/blockchains/ethereum/javascript ## Installation To interact with the EVM network, you will need to use a web3 provider library, such as [`ethers.js`](https://docs.ethers.io/v5/getting-started/), with Magic. ⁠To get started, install the following dependencies for your project: ### Ethers.js ```bash NPM icon="npm" theme={null} npm install ethers magic-sdk ``` ```bash Yarn icon="yarn" theme={null} yarn add ethers magic-sdk ``` ```javascript CDN icon="code" theme={null} ``` ## Initialization The Magic class is the entry-point to the Magic SDK. It must be instantiated with a Magic **publishable** key. **Important** Ethereum provider is only supported in **magic-sdk\@1.0.1 or later** versions. ### Ethers.js **ES Modules/TypeScript** ```javascript Ethers (v6) icon="square-js" theme={null} import { Magic } from "magic-sdk"; import { ethers } from "ethers"; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.BrowserProvider(magic.rpcProvider); ``` ```javascript Ethers (v5) icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.providers.Web3Provider(magic.rpcProvider); ``` **CommonJS** ```javascript Ethers (v6) icon="square-js" theme={null} const { Magic } = require('magic-sdk'); const ethers = require('ethers'); const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.BrowserProvider(magic.rpcProvider); ``` ```javascript Ethers (v5) icon="square-js" theme={null} const { Magic } = require('magic-sdk'); const ethers = require('ethers'); const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.providers.Web3Provider(magic.rpcProvider); ``` ### Mainnet Mainnet Block Explorer: [https://etherscan.io](https://etherscan.io) ```javascript JavaScript icon="square-js" theme={null} const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: 'mainnet', }); // Magic's node infrastructure maps 'mainnet' and 'testnet' to 2 unique set of addresses. ``` ### Testnet Sepolia Block Explorer: [https://sepolia.etherscan.io/](https://sepolia.etherscan.io/) Sepolia Testnet Faucet: [https://sepoliafaucet.com/](https://sepoliafaucet.com/) ```javascript JavaScript icon="square-js" theme={null} const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: 'sepolia', }); // Magic's node infrastructure maps 'mainnet' and 'sepolia' to 2 unique set of addresses. ``` ### Custom Node You can allow specific URLs to interact with the Magic SDK, such as a custom RPC URL to send transactions to your node. The Content Security Policy (CSP) of a browser dictates what resources can be loaded. You can update the policy in the settings page of the [dashboard](https://dashboard.magic.link) with your custom URL. **Important** The use of a custom node will require the RPC URL to the project's Content Security Policy from your [Magic Dashboard](https://dashboard.magic.link). Refer to the [CSP documentation](/embedded-wallets/wallets/security/content-security-policy). ```javascript JavaScript icon="square-js" theme={null} const customNodeOptions = { rpcUrl: 'http://127.0.0.1:7545', // Your own node URL chainId: 1011, // Your own node's chainId }; // Setting network to localhost blockchain const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: customNodeOptions, }); // This configuration will map addresses in line with Magic's 'mainnet' setup. ``` ## Common Methods **Note** All Web.js examples are using **web3\@1.2.0** or later version. ## Send Transaction ### Ethers.js ```javascript Ethers (v6) icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.BrowserProvider(magic.rpcProvider); // ⭐️ After user is successfully authenticated const signer = await provider.getSigner(); const destination = '0xE0cef4417a772512E6C95cEf366403839b0D6D6D'; const amount = ethers.parseEther('1.0'); // Convert 1 ether to wei // Submit transaction to the blockchain const tx = await signer.sendTransaction({ to: destination, value: amount, }); // Wait for transaction to be mined const receipt = await tx.wait(); ``` ```javascript Ethers (v5) icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.providers.Web3Provider(magic.rpcProvider); // ⭐️ After user is successfully authenticated const signer = provider.getSigner(); const destination = '0xE0cef4417a772512E6C95cEf366403839b0D6D6D'; const amount = ethers.utils.parseEther('1.0'); // Convert 1 ether to wei // Submit transaction to the blockchain const tx = await signer.sendTransaction({ to: destination, value: amount, }); // Wait for transaction to be mined const receipt = await tx.wait(); ``` ## Sign Message ### Ethers.js #### Personal Sign ```javascript Ethers (v6) icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.BrowserProvider(magic.rpcProvider); // ⭐️ After user is successfully authenticated const signer = await provider.getSigner(); const originalMessage = 'YOUR_MESSAGE'; const signedMessage = await signer.signMessage(originalMessage); ``` ```javascript Ethers (v5) icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.providers.Web3Provider(magic.rpcProvider); await provider.send('eth_requestAccounts', []); // ⭐️ After user is successfully authenticated const signer = provider.getSigner(); const originalMessage = 'YOUR_MESSAGE'; const signedMessage = await signer.signMessage(originalMessage); ``` #### Sign Typed Data v1 ```javascript Ethers (v6) icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.BrowserProvider(magic.rpcProvider); // ⭐️ After user is successfully authenticated const signer = await provider.getSigner(); // Get user's Ethereum public address const fromAddress = await signer.getAddress(); const originalMessage = [ { type: 'string', name: 'fullName', value: 'John Doe', }, { type: 'uint32', name: 'userId', value: '1234', }, ]; const params = [originalMessage, fromAddress]; const method = 'eth_signTypedData'; const signedMessage = await signer.provider.send(method, params); ``` ```javascript Ethers (v5) icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.providers.Web3Provider(magic?.rpcProvider); await provider.send('eth_requestAccounts', []); // ⭐️ After user is successfully authenticated const signer = provider.getSigner(); // Get user's Ethereum public address const fromAddress = await signer.getAddress(); const originalMessage = [ { type: 'string', name: 'fullName', value: 'John Doe', }, { type: 'uint32', name: 'userId', value: '1234', }, ]; const params = [originalMessage, fromAddress]; const method = 'eth_signTypedData'; const signedMessage = await signer.provider.send(method, params); ``` #### Sign Typed Data v3 ```javascript Ethers (v6) icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.BrowserProvider(magic.rpcProvider); // ⭐️ After user is successfully authenticated const signer = await provider.getSigner(); // Get user's Ethereum public address const fromAddress = await signer.getAddress(); const originalMessage = { types: { EIP712Domain: [ { name: 'name', type: 'string', }, { name: 'version', type: 'string', }, { name: 'verifyingContract', type: 'address', }, ], Greeting: [ { name: 'contents', type: 'string', }, ], }, primaryType: 'Greeting', domain: { name: 'Magic', version: '1', verifyingContract: '0xE0cef4417a772512E6C95cEf366403839b0D6D6D', }, message: { contents: 'Hello, from Magic!', }, }; const params = [fromAddress, originalMessage]; const method = 'eth_signTypedData_v3'; const signedMessage = await signer.provider.send(method, params); ``` ```javascript Ethers (v5) icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.providers.Web3Provider(magic?.rpcProvider); await provider.send('eth_requestAccounts', []); // ⭐️ After user is successfully authenticated const signer = provider.getSigner(); // Get user's Ethereum public address const fromAddress = await signer.getAddress(); const originalMessage = { types: { EIP712Domain: [ { name: 'name', type: 'string', }, { name: 'version', type: 'string', }, { name: 'verifyingContract', type: 'address', }, ], Greeting: [ { name: 'contents', type: 'string', }, ], }, primaryType: 'Greeting', domain: { name: 'Magic', version: '1', verifyingContract: '0xE0cef4417a772512E6C95cEf366403839b0D6D6D', }, message: { contents: 'Hello, from Magic!', }, }; const params = [fromAddress, originalMessage]; const method = 'eth_signTypedData_v3'; const signedMessage = await signer.provider.send(method, params); ``` #### Sign Typed Data v4 ```javascript Ethers (v6) icon="square-js" theme={null} /* Sign Typed Data v4 adds support for arrays and recursive data types. Otherwise, it works the same as Sign Typed Data v3. \*/ import { Magic } from "magic-sdk"; import { ethers } from "ethers"; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.BrowserProvider(magic.rpcProvider); // ⭐️ After user is successfully authenticated const signer = await provider.getSigner(); // Get user's Ethereum public address const fromAddress = await signer.getAddress(); const originalMessage = { types: { EIP712Domain: [ { name: 'name', type: 'string', }, { name: 'version', type: 'string', }, { name: 'verifyingContract', type: 'address', }, ], Greeting: [ { name: 'contents', type: 'string', }, ], }, primaryType: 'Greeting', domain: { name: 'Magic', version: '1', verifyingContract: '0xE0cef4417a772512E6C95cEf366403839b0D6D6D', }, message: { contents: 'Hello, from Magic!', }, }; const params = [fromAddress, originalMessage]; const method = 'eth_signTypedData_v4'; const signedMessage = await signer.provider.send(method, params); ``` ```javascript Ethers (v5) icon="square-js" theme={null} /* Sign Typed Data v4 adds support for arrays and recursive data types. Otherwise, it works the same as Sign Typed Data v3. */ import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.providers.Web3Provider(magic.rpcProvider); // ⭐️ After user is successfully authenticated const signer = provider.getSigner(); // Get user's Ethereum public address const fromAddress = await signer.getAddress(); const originalMessage = { types: { EIP712Domain: [ { name: 'name', type: 'string', }, { name: 'version', type: 'string', }, { name: 'verifyingContract', type: 'address', }, ], Greeting: [ { name: 'contents', type: 'string', }, ], }, primaryType: 'Greeting', domain: { name: 'Magic', version: '1', verifyingContract: '0xE0cef4417a772512E6C95cEf366403839b0D6D6D', }, message: { contents: 'Hello, from Magic!', }, }; const params = [fromAddress, originalMessage]; const method = 'eth_signTypedData_v4'; const signedMessage = await signer.provider.send(method, params); ``` ## Get Balance ### Ethers.js ```javascript Ethers (v6) icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY') const provider = new ethers.BrowserProvider(magic.rpcProvider); // ⭐️ After user is successfully authenticated const signer = await provider.getSigner(); // Get user's Ethereum public address const address = await signer.getAddress(); // Get user's balance in ether const balance = ethers.formatEther( await provider.getBalance(address), // Balance is in wei ); ``` ```javascript Ethers (v5) icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.providers.Web3Provider(magic.rpcProvider); await provider.send('eth_requestAccounts', []); // ⭐️ After user is successfully authenticated const signer = provider.getSigner(); // Get user's Ethereum public address const address = await signer.getAddress(); // Get user's balance in ether const balance = ethers.utils.formatEther( await provider.getBalance(address), // Balance is in wei ); ``` ## Smart Contract In this example, we'll be demonstrating how to use Magic with ethers.js to interact with Solidity smart contracts. The simple Hello World contract allows anyone to read and write a message to it. ```solidity Solidity icon="ethereum" theme={null} pragma solidity ^0.5.10; contract HelloWorld { string public message; constructor(string memory initMessage) public { message = initMessage; } function update(string memory newMessage) public { message = newMessage; } } ``` ## Deploy Contract ### Ethers.js ```javascript Ethers (v6) icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.BrowserProvider(magic.rpcProvider); // ⭐️ After user is successfully authenticated const signer = await provider.getSigner(); const contractABI = '[{"constant":false,"inputs":[{"name":"newMessage","type":"string"}],"name":"update","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"message","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"initMessage","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]'; const contractByteCode = '0x608060405234801561001057600080fd5b5060405161047f38038061047f8339818101604052602081101561003357600080fd5b81019080805164010000000081111561004b57600080fd5b8281019050602081018481111561006157600080fd5b815185600182028301116401000000008211171561007e57600080fd5b5050929190505050806000908051906020019061009c9291906100a3565b5050610148565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100e457805160ff1916838001178555610112565b82800160010185558215610112579182015b828111156101115782518255916020019190600101906100f6565b5b50905061011f9190610123565b5090565b61014591905b80821115610141576000816000905550600101610129565b5090565b90565b610328806101576000396000f3fe608060405234801561001057600080fd5b5060043610610053576000357c0100000000000000000000000000000000000000000000000000000000900480633d7403a314610058578063e21f37ce14610113575b600080fd5b6101116004803603602081101561006e57600080fd5b810190808035906020019064010000000081111561008b57600080fd5b82018360208201111561009d57600080fd5b803590602001918460018302840111640100000000831117156100bf57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050610196565b005b61011b6101b0565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561015b578082015181840152602081019050610140565b50505050905090810190601f1680156101885780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b80600090805190602001906101ac92919061024e565b5050565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156102465780601f1061021b57610100808354040283529160200191610246565b820191906000526020600020905b81548152906001019060200180831161022957829003601f168201915b505050505081565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061028f57805160ff19168380011785556102bd565b828001600101855582156102bd579182015b828111156102bc5782518255916020019190600101906102a1565b5b5090506102ca91906102ce565b5090565b6102f091905b808211156102ec5760008160009055506001016102d4565b5090565b9056fea265627a7a7230582003ae1ef5a63bf058bfd2b31398bdee39d3cbfbb7fbf84235f4bc2ec352ee810f64736f6c634300050a0032'; const contractFactory = new ethers.ContractFactory(contractABI, contractByteCode, signer); // Deploy contract with "Hello World!" in the constructor const contract = await contractFactory.deploy('Hello World!'); // Wait for deployment to finish const receipt = contract.deploymentTransaction() ``` ```javascript Ethers (v5) icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.providers.Web3Provider(magic.rpcProvider); await provider.send('eth_requestAccounts', []); // ⭐️ After user is successfully authenticated const signer = provider.getSigner(); // ⭐️ After user is successfully authenticated const contractABI = '[{"constant":false,"inputs":[{"name":"newMessage","type":"string"}],"name":"update","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"message","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"initMessage","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]'; const contractByteCode = '0x608060405234801561001057600080fd5b5060405161047f38038061047f8339818101604052602081101561003357600080fd5b81019080805164010000000081111561004b57600080fd5b8281019050602081018481111561006157600080fd5b815185600182028301116401000000008211171561007e57600080fd5b5050929190505050806000908051906020019061009c9291906100a3565b5050610148565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100e457805160ff1916838001178555610112565b82800160010185558215610112579182015b828111156101115782518255916020019190600101906100f6565b5b50905061011f9190610123565b5090565b61014591905b80821115610141576000816000905550600101610129565b5090565b90565b610328806101576000396000f3fe608060405234801561001057600080fd5b5060043610610053576000357c0100000000000000000000000000000000000000000000000000000000900480633d7403a314610058578063e21f37ce14610113575b600080fd5b6101116004803603602081101561006e57600080fd5b810190808035906020019064010000000081111561008b57600080fd5b82018360208201111561009d57600080fd5b803590602001918460018302840111640100000000831117156100bf57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050610196565b005b61011b6101b0565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561015b578082015181840152602081019050610140565b50505050905090810190601f1680156101885780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b80600090805190602001906101ac92919061024e565b5050565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156102465780601f1061021b57610100808354040283529160200191610246565b820191906000526020600020905b81548152906001019060200180831161022957829003601f168201915b505050505081565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061028f57805160ff19168380011785556102bd565b828001600101855582156102bd579182015b828111156102bc5782518255916020019190600101906102a1565b5b5090506102ca91906102ce565b5090565b6102f091905b808211156102ec5760008160009055506001016102d4565b5090565b9056fea265627a7a7230582003ae1ef5a63bf058bfd2b31398bdee39d3cbfbb7fbf84235f4bc2ec352ee810f64736f6c634300050a0032'; const contractFactory = new ethers.ContractFactory(contractABI, contractByteCode, signer); // Deploy contract with "Hello World!" in the constructor const contract = await contractFactory.deploy('Hello World!'); // Wait for deployment to finish const receipt = await contract.deployed(); ``` ## Read From Contract ### Ethers.js ```javascript Ethers (v6) icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.BrowserProvider(magic.rpcProvider); // ⭐️ After user is successfully authenticated const signer = await provider.getSigner(); const contractABI = '[{"constant":false,"inputs":[{"name":"newMessage","type":"string"}],"name":"update","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"message","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"initMessage","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]'; const contractAddress = 'CONTRACT_ADDRESS'; const contract = new ethers.Contract(contractAddress, contractABI, signer); // Read message from smart contract const message = await contract.message(); ``` ```javascript Ethers (v5) icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.providers.Web3Provider(magic.rpcProvider); await provider.send('eth_requestAccounts', []); // ⭐️ After user is successfully authenticated const signer = provider.getSigner(); const contractABI = '[{"constant":false,"inputs":[{"name":"newMessage","type":"string"}],"name":"update","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"message","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"initMessage","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]'; const contractAddress = 'CONTRACT_ADDRESS'; const contract = new ethers.Contract(contractAddress, contractABI, signer); // Read message from smart contract const message = await contract.message(); ``` ## Write to Contract ### Ethers.js ```javascript Ethers (v6) icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.BrowserProvider(magic.rpcProvider); // ⭐️ After user is successfully authenticated const signer = await provider.getSigner(); const contractABI = '[{"constant":false,"inputs":[{"name":"newMessage","type":"string"}],"name":"update","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"message","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"initMessage","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]'; const contractAddress = 'CONTRACT_ADDRESS'; const contract = new ethers.Contract(contractAddress, contractABI, signer); // Send transaction to smart contract to update message const tx = await contract.update('NEW_MESSAGE'); // Wait for transaction to finish const receipt = await tx.wait(); ``` ```javascript Ethers (v5) icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.providers.Web3Provider(magic.rpcProvider); await provider.send('eth_requestAccounts', []); // ⭐️ After user is successfully authenticated const signer = provider.getSigner(); const contractABI = '[{"constant":false,"inputs":[{"name":"newMessage","type":"string"}],"name":"update","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"message","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"initMessage","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]'; const contractAddress = 'CONTRACT_ADDRESS'; const contract = new ethers.Contract(contractAddress, contractABI, signer); // Send transaction to smart contract to update message const tx = await contract.update('NEW_MESSAGE'); // Wait for transaction to finish const receipt = await tx.wait(); ``` ## Resources * [Magic JavaScript SDK](https://github.com/magiclabs/magic-js) * [Ethers.js JavaScript example](https://go.magic.link/example-ethers) # Arbitrum Source: https://docs.magic.link/embedded-wallets/blockchains/evm/arbitrum Arbitrum is especially beneficial for those looking to boost performance while maintaining the decentralized principles of Ethereum. ## Overview [Arbitrum](https://arbitrum.io/rollup) is a Layer 2 solution enhancing Ethereum by providing developers with faster and more cost-efficient transactions. It upholds Ethereum's security through its innovative Arbitrum Rollup protocol, facilitating trustless operations. For developers, its seamless compatibility with Ethereum's ecosystem and tools simplifies dApp development. **As Arbitrum is EVM compatible**, you can follow the [Ethereum](/embedded-wallets/blockchains/ethereum/javascript) documentation to send your first transaction and utilize all other wallet features. ## Configure Arbitrum Ensure you have installed the Magic SDK and have access to your API key, follow the [quickstart](/embedded-wallets/quickstart/overview) to get started. You can use Magic's network aliases to connect to either testnet or mainnet on Arbitrum. ```js Testnet icon="square-js" theme={null} // Setting network to point to testnet const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: { rpcUrl: 'https://sepolia-rollup.arbitrum.io/rpc', chainId: 421614, } }); ``` ```js Mainnet icon="square-js" theme={null} // Setting network to point to mainnet const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: { rpcUrl: 'https://rpc.ankr.com/arbitrum', chainId: 42161, } }); ``` ## Compatibility * All `Auth`, `User` and `Wallet` module methods * All EVM Provider functionality to respond to supported [RPC methods](/embedded-wallets/sdk/client-side/javascript#evm-rpc-methods) *Need a feature or see a problem?* File an issue on our [github repo](https://github.com/magiclabs/magic-js). ## Resources & Tools * Documentation: [https://docs.arbitrum.io/](https://docs.arbitrum.io/) * Block Explorer: * [https://arbiscan.io/](https://arbiscan.io/) (Mainnet) * [https://sepolia.arbiscan.io/](https://sepolia.arbiscan.io/) (Testnet) * Faucet: [https://faucet.quicknode.com/arbitrum](https://faucet.quicknode.com/arbitrum) # Base Source: https://docs.magic.link/embedded-wallets/blockchains/evm/base ## Overview [Base](https://base.org/), a Layer 2 blockchain compatible with the Ethereum Virtual Machine (EVM), offers developers a robust platform for building decentralized apps. It integrates seamlessly with Coinbase products and distribution, ensuring scalability and accessibility. **As Base is EVM compatible**, you can follow the [Ethereum](/embedded-wallets/blockchains/ethereum/javascript) documentation to send your first transaction and utilize all other wallet features. ## Configure Base Ensure you have installed the Magic SDK and have access to your API key, follow the [quickstart](/embedded-wallets/quickstart/overview) to get started. You can use Magic's network aliases to connect to either testnet or mainnet on Base. ```js Testnet icon="square-js" theme={null} // Setting network to point to Base testnet const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: { rpcUrl: 'https://sepolia.base.org', chainId: 84532, } }); ``` ```js Mainnet icon="square-js" theme={null} // Setting network to point to Base mainnet const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: { rpcUrl: 'https://mainnet.base.org', chainId: 8453, } }); ``` ## Compatibility * All `Auth`, `User` and `Wallet` module methods * All EVM Provider functionality to respond to supported [RPC methods](/embedded-wallets/sdk/client-side/javascript#evm-rpc-methods) *Need a feature or see a problem?* File an issue on our [github repo](https://github.com/magiclabs/magic-js). ## Resources & Tools * Documentation: [https://docs.base.org/](https://docs.base.org/) * Block Explorer: * [https://basescan.org/](https://basescan.org/) (Mainnet) * [https://base-sepolia.blockscout.com/](https://base-sepolia.blockscout.com/) (Testnet) * Faucets: [https://docs.base.org/tools/network-faucets/](https://docs.base.org/tools/network-faucets/) # Berachain Source: https://docs.magic.link/embedded-wallets/blockchains/evm/berachain ## Overview [Berachain](https://www.berachain.com/) is an EVM-compatible L1 blockchain built on-top of the Cosmos-SDK, which uses [Proof-of-Liquidity](https://docs.berachain.com/learn/#proof-of-liquidity-overview) consensus. **As Berachain is EVM compatible**, you can follow the [Ethereum](/embedded-wallets/blockchains/ethereum/javascript) documentation to send your first transaction and utilize all other wallet features. ## Configure Berachain Ensure you have installed the Magic SDK and have access to your API key, follow the [quickstart](/embedded-wallets/quickstart/overview) to get started. ```js Testnet icon="square-js" theme={null} // Setting network to point to Berachain Artio testnet const magic = new Magic("YOUR_PUBLISHABLE_API_KEY", { network: { rpcUrl: "https://artio.rpc.berachain.com/", chainId: 80085, }, }); ``` ## Compatibility * All `Auth`, `User` and `Wallet` module methods * All EVM Provider functionality to respond to supported [RPC methods](/embedded-wallets/sdk/client-side/javascript#evm-rpc-methods) *Need a feature or see a problem?* File an issue on our [github repo](https://github.com/magiclabs/magic-js). ## Resources & Tools * Documentation: [https://docs.berachain.com/](https://docs.berachain.com/) * Block Explorer: * [https://artio.beratrail.io/](https://artio.beratrail.io/) (Testnet) * Faucets: [https://docs.berachain.com/developers/testnet-faucet](https://docs.berachain.com/developers/testnet-faucet) # Binance Smart Chain Source: https://docs.magic.link/embedded-wallets/blockchains/evm/binance-smart-chain How to Build a Decentralized App on Binance Smart Chain with Magic ## Overview [Binance Smart Chain](https://www.bnbchain.org/en/bnb-smart-chain) is a Layer 1 blockchain network that's compatible with the Ethereum Virtual Machine (EVM), specifically designed to revolutionize the world of sports. Through the Magic SDK, users can easily create wallets and engage with Binance Smart Chain, enabling teams, fans, and developers to craft Web3 products and experiences that unite fans and brands like never before. **As Binance Smart Chain is EVM compatible**, you can follow the [Ethereum](/embedded-wallets/blockchains/ethereum/javascript) documentation to send your first transaction and utilize all other features. ## Configure Binance Smart Chain ### Smart Chain Mainnet Block Explorer URL: [https://bscscan.com](https://bscscan.com) ```javascript JavaScript icon="square-js" theme={null} const BSCOptions = { rpcUrl: "https://bsc-dataseed.binance.org/", // Smart Chain RPC URL chainId: 56, // Smart Chain chain id }; // Setting network to Smart Chain const magic = new Magic("YOUR_PUBLISHABLE_API_KEY", { network: BSCOptions }); ``` ### Smart Chain - Testnet Testnet Block Explorer URL: [https://testnet.bscscan.com](https://testnet.bscscan.com) ```javascript JavaScript icon="square-js" theme={null} const BSCOptions = { rpcUrl: "https://data-seed-prebsc-1-s1.binance.org:8545/", // Smart Chain - Testnet RPC URL chainId: 97, // Smart Chain - Testnet chain id }; // Setting network to Smart Chain - Testnet const magic = new Magic("YOUR_PUBLISHABLE_API_KEY", { network: BSCOptions }); ``` ## Compatibility * All `Auth`, `User` and most `Wallet` module methods\* * All EVM Provider functionality to respond to supported [RPC methods](/embedded-wallets/sdk/client-side/javascript#evm-rpc-methods) *\*Some features are not yet compatible such as NFT Viewer and Fiat On-ramps.* *Need a feature or see a problem?* File an issue on our [github repo](https://github.com/magiclabs/magic-js). ## Resources & Tools * Documentation: [https://docs.bnbchain.org/](https://docs.bnbchain.org/) * Block Explorer: * [https://bscscan.com/](https://bscscan.com/) (Mainnet) * [https://testnet.bscscan.com/](https://testnet.bscscan.com/) (Testnet) * Testnet Faucet: [https://www.bnbchain.org/en/testnet-faucet](https://www.bnbchain.org/en/testnet-faucet) # Celo Source: https://docs.magic.link/embedded-wallets/blockchains/evm/celo How to use Magic with the Celo blockchain
## Overview [Celo](https://celo.org/) is a Layer 2 solution enhancing Ethereum by providing developers with faster and more cost-efficient transactions. For developers, its seamless compatibility with Ethereum's ecosystem and tools simplifies dApp development. Celo is especially beneficial for those looking to boost performance while maintaining the decentralized principles of Ethereum. **As Celo is EVM compatible**, you can follow the [Ethereum](/embedded-wallets/blockchains/ethereum/javascript)documentation to send your first transaction and utilize all other wallet features. ## Installation To install Magic and Celo, follow the instructions below. ```bash NPM icon="npm" theme={null} npm install --save magic-sdk @celo/contractkit ``` ```bash Yarn icon="yarn" theme={null} yarn add magic-sdk @celo/contractkit ``` ## Send Transaction ### Getting Test CELO token Before you can send transaction on the Celo blockchain, you'll need to acquire some test CELO token (Celo's native cryptocurrency for test network). 1. Go to our [**Celo Example**](https://go.magic.link/example-celo) application 2. Login with your email address 3. Copy your Celo public address 4. Go to the [**Celo Faucet**](https://celo.org/developers/faucet) 5. Paste your copied Celo public address in the text input 6. Now you can use your test CELO token in our [**Celo Example**](https://go.magic.link/example-celo) ### Use Magic Rpc Provider ```js JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { newKitFromWeb3 } from '@celo/contractkit'; import Web3 from 'web3'; const magic = new Magic('YOUR_API_KEY', { network: { rpcUrl: 'https://alfajores-forno.celo-testnet.org', }, }); const web3 = new Web3(magic.rpcProvider); const kit = newKitFromWeb3(web3); const { publicAddress } = await magic.user.getMetadata(); kit.defaultAccount = publicAddress; const oneGold = kit.web3.utils.toWei('1', 'ether'); const tx = await kit.sendTransaction({ from: publicAddress, to: 'Destination Address', value: oneGold, gasPrice: 1000000000, }); const hash = await tx.getHash(); const receipt = await tx.waitReceipt(); console.log('transaction result: ', hash, receipt); ``` ## Smart Contract ### Getting Test CELO token Before you can send transaction on the Celo blockchain, you'll need to acquire some test CELO token (Celo's native cryptocurrency for test network). 1. Go to our [**Celo Example**](https://go.magic.link/example-celo) application 2. Login with your email address 3. Copy your Celo public address 4. Go to the [**Celo Faucet**](https://celo.org/developers/faucet) 5. Paste your copied Celo public address in the text input 6. Now you can use your test CELO token in our [**Celo Example**](https://go.magic.link/example-celo) ### Contract Send #### ES Modules/TypeScript ```js JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { newKitFromWeb3 } from '@celo/contractkit'; import Web3 from 'web3'; const magic = new Magic('YOUR_API_KEY', { network: { rpcUrl: 'https://alfajores-forno.celo-testnet.org', }, }); const contractAddress = '0xcf71aB733148F70647129F3006E92439d11946A9'; const abi = [ { constant: true, inputs: [], name: 'getName', outputs: [ { internalType: 'string', name: '', type: 'string', }, ], payable: false, stateMutability: 'view', type: 'function', }, { constant: false, inputs: [ { internalType: 'string', name: 'newName', type: 'string', }, ], name: 'setName', outputs: [], payable: false, stateMutability: 'nonpayable', type: 'function', }, ]; const { publicAddress } = await magic.user.getMetadata(); const web3 = new Web3(magic.rpcProvider); const kit = newKitFromWeb3(web3); let instance = new web3.eth.Contract(abi, contractAddress); const txObject = await instance.methods.setName('new name'); let tx = await kit.sendTransactionObject(txObject, { from: publicAddress, gasPrice: 1000000000 }); const hash = await tx.getHash(); let receipt = await tx.waitReceipt(); console.log('contract send result: ', hash, receipt); ``` ## Compatibility * All `Auth`, `User` and most `Wallet` module methods\* * All EVM Provider functionality to respond to supported [RPC methods](/embedded-wallets/sdk/client-side/javascript#evm-rpc-methods) *\*Some features are not yet compatible such as NFT Viewer and Fiat On-ramps.* *Need a feature or see a problem?* File an issue on our [github repo](https://github.com/magiclabs/magic-js). ## Resources & Tools * Documentation: [https://docs.celo.org/](https://docs.celo.org/) * Block Explorer: * [https://explorer.celo.org/mainnet/](https://explorer.celo.org/mainnet/) (Mainnet) * Faucet: [https://faucet.quicknode.com/celo](https://faucet.quicknode.com/celo) * [Demo](https://magic-celo.vercel.app/login) * [Example](https://go.magic.link/example-celo) # Chiliz Source: https://docs.magic.link/embedded-wallets/blockchains/evm/chiliz ## Overview [Chiliz](https://www.chiliz.com/) is a Layer 1 blockchain network that's compatible with the Ethereum Virtual Machine (EVM), specifically designed to revolutionize the world of sports. Through the Magic SDK, users can easily create wallets and engage with Chiliz Chain, enabling teams, fans, and developers to craft Web3 products and experiences that unite fans and brands like never before. **As Chiliz is EVM compatible**, you can follow the [Ethereum](/embedded-wallets/blockchains/ethereum/javascript) documentation to send your first transaction and utilize all other features. ## Configure Chiliz Ensure you have installed the Magic SDK and have access to your API key, follow the [quickstart](/embedded-wallets/quickstart/overview) to get started. You can use Magic's network aliases to connect to either testnet or mainnet on Chiliz. For more network information, see [here](https://docs.chiliz.com/chiliz-chain-mainnet). ```js Testnet icon="square-js" theme={null} // Setting network to point to Chiliz testnet const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: { rpcUrl: 'https://spicy-rpc.chiliz.com', chainId: 88882, } }); ``` ```js Mainnet icon="square-js" theme={null} // Setting network to point to Chiliz mainnet const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: { rpcUrl: 'https://rpc.ankr.com/chiliz', chainId: 88888, } }); ``` ## Compatibility * All `Auth`, `User` and most `Wallet` module methods\* * All EVM Provider functionality to respond to supported [RPC methods](/embedded-wallets/sdk/client-side/javascript#evm-rpc-methods) *\*Some Widget UI features are not yet compatible such as NFT Viewer and Fiat On-ramps.* *Need a feature or see a problem?* File an issue on our [github repo](https://github.com/magiclabs/magic-js). ## Resources & Tools * Documentation: [https://docs.chiliz.com/](https://docs.chiliz.com/) * Block Explorer: * [https://scan.chiliz.com/](https://scan.chiliz.com/) (Mainnet) * [https://spicy-explorer.chiliz.com/](https://spicy-explorer.chiliz.com/) (Testnet) * Testnet Faucet: [https://spicy-faucet.chiliz.com/](https://spicy-faucet.chiliz.com/) # Cronos Source: https://docs.magic.link/embedded-wallets/blockchains/evm/cronos Cronos is especially beneficial for those looking to boost performance while maintaining the decentralized principles of Ethereum. ## Overview [Cronos](https://cronos.org/) is a Layer 2 solution enhancing Ethereum by providing developers with faster and more cost-efficient transactions. **As Cronos is EVM compatible**, you can follow the [Ethereum](/embedded-wallets/blockchains/ethereum/javascript) documentation to send your first transaction and utilize all other wallet features. ## Configure Cronos Via Magic SDK, you can connect to [Cronos](https://cronos.org/). Cronos is EVM compatible so you can directly follow the [Ethereum installation](/embedded-wallets/blockchains/ethereum/javascript). ### Mainnet Mainnet Block Explorer: [https://cronoscan.com/](https://cronoscan.com/) ```javascript JavaScript icon="square-js" theme={null} const customNodeOptions = { rpcUrl: "https://evm.cronos.org/", // Cronos mainnet URL chainId: 25, // Your own node's chainId }; // Setting network to Cronos const magic = new Magic("YOUR_PUBLISHABLE_API_KEY", { network: customNodeOptions, }); ``` ### Testnet Testnet Block Explorer: [https://testnet.cronoscan.com/](https://testnet.cronoscan.com/) ```javascript JavaScript icon="square-js" theme={null} const customNodeOptions = { rpcUrl: 'https://evm-t3.cronos.org/', // Cronos testnet URL chainId: 338, // Cronos testnet chainId } // Setting network to Cronos const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: customNodeOptions }); ``` ## Compatibility * All `Auth`, `User` and most `Wallet` module methods\* * All EVM Provider functionality to respond to supported [RPC methods](/embedded-wallets/sdk/client-side/javascript#evm-rpc-methods) *\*Some Widget UI features are not yet compatible such as NFT Viewer and Fiat On-ramps.* *Need a feature or see a problem?* File an issue on our [github repo](https://github.com/magiclabs/magic-js). ## Resources & Tools * Documentation: [https://docs.cronos.org/getting-started/readme](https://docs.cronos.org/getting-started/readme) * Block Explorer: * [https://cronoscan.com/](https://cronoscan.com/) (Mainnet) * [https://explorer.cronos.org/testnet/](https://explorer.cronos.org/testnet/) (Testnet) * [GitHub](https://github.com/magiclabs/example-cronos) # Etherlink Source: https://docs.magic.link/embedded-wallets/blockchains/evm/etherlink ## Overview [Etherlink](https://www.etherlink.com/) is a Layer 2 EVM-compatible blockchain offering low transaction fees and native MEV protection. It is powered by Tezos Smart Rollup technology, uses decentralized sequencers, and is secured by the Tezos layer 1 blockchain. **As Etherlink is EVM compatible**, you can follow the [Ethereum](/embedded-wallets/blockchains/ethereum/javascript) documentation to send your first transaction and utilize all other wallet features. ## Configure Etherlink Ensure you have installed the Magic SDK and have access to your API key, follow the [quickstart](/embedded-wallets/quickstart/overview) to get started. ```js Testnet icon="square-js" theme={null} // Setting network to point to Etherlink testnet const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: { rpcUrl: 'https://node.ghostnet.etherlink.com', chainId: 128123, } }); ``` ```js Mainnet icon="square-js" theme={null} // Setting network to point to Etherlink mainnet const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: { rpcUrl: 'https://node.mainnet.etherlink.com', chainId: 42793, } }); ``` ## Compatibility * All `Auth`, `User` and `Wallet` module methods * All EVM Provider functionality to respond to supported [RPC methods](/embedded-wallets/sdk/client-side/javascript#evm-rpc-methods) *Need a feature or see a problem?* File an issue on our [github repo](https://github.com/magiclabs/magic-js). ## Resources & Tools * Documentation: [https://docs.etherlink.com/](https://docs.etherlink.com/) * Block Explorer: * [https://testnet-explorer.etherlink.com/](https://testnet-explorer.etherlink.com/) (Testnet) * [https://explorer.etherlink.com/](https://explorer.etherlink.com/) (Mainnet) * Faucets: [https://faucet.etherlink.com/](https://faucet.etherlink.com/) # Fantom Source: https://docs.magic.link/embedded-wallets/blockchains/evm/fantom ## Overview [Fantom](https://fantom.foundation/) is a smart-contract platform that aims to solve the scalability challenges of other blockchains with its Lachesis consensus mechanism. Fantom’s unique architecture allows developers to create highly scalable, fast, and inexpensive Web3 applications. **As Fantom is EVM compatible**, you can follow the [Ethereum](/embedded-wallets/blockchains/ethereum/javascript)documentation to send your first transaction and utilize all other wallet features. ## Configure Fantom ### Mainnet Mainnet Block Explorer URL: [https://ftmscan.com/](https://ftmscan.com/) ```javascript JavaScript icon="square-js" theme={null} const customNodeOptions = { rpcUrl: 'https://rpc.ftm.tools/', chainId: 250, } // Setting network to Fantom const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: customNodeOptions }); ``` ### Testnet Testnet Block Explorer URL: [https://testnet.ftmscan.com/](https://testnet.ftmscan.com/) ```javascript JavaScript icon="square-js" theme={null} const customNodeOptions = { rpcUrl: 'https://rpc.testnet.fantom.network', chainId: 4002, } // Setting network to Fantom - Testnet const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: customNodeOptions }); ``` ## Compatibility * All `Auth`, `User` and most `Wallet` module methods\* * All EVM Provider functionality to respond to supported [RPC methods](/embedded-wallets/sdk/client-side/javascript#evm-rpc-methods) * Widget UI for token balances and token transfers\* *\*Some features are not yet compatible such as NFT Viewer and Fiat On-ramps.* *Need a feature or see a problem?* File an issue on our [github repo](https://github.com/magiclabs/magic-js). ## Resources & Tools * Documentation: [https://docs.fantom.foundation/](https://docs.fantom.foundation/) * Block Explorer: * [https://ftmscan.com/](https://ftmscan.com/) (Mainnet) * [https://testnet.ftmscan.com/](https://testnet.ftmscan.com/) (Testnet) * [Demo](https://codesandbox.io/s/elastic-ishizaka-4tifc) # Flare Source: https://docs.magic.link/embedded-wallets/blockchains/evm/flare ## Overview [Flare](https://flare.network/) is a Layer 1 blockchain with native data acquisition protocols, providing developers with decentralized access to high-integrity data from other blockchains without relying on third party oracles. Users can effortlessly create wallets and interact with Flare using the Magic SDK. **As Flare is EVM compatible**, you can follow the [Ethereum](/embedded-wallets/blockchains/ethereum/javascript) documentation to send your first transaction and utilize all other features. ## Configure Flare ```js Testnet icon="square-js" theme={null} const FlareOptions = { rpcUrl: 'https://coston-api.flare.network/ext/bc/C/rpc', chainId: 16, }; // Setting network to point to Coston Testnet const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: FlareOptions }); ``` ```js Mainnet icon="square-js" theme={null} const FlareOptions = { rpcUrl: 'https://flare-api.flare.network/ext/bc/C/rpc', chainId: 14, }; // Setting network to point to Flare const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: FlareOptions }); ``` ## Compatibility * All `Auth`, `User` and most `Wallet` module methods\* * All EVM Provider functionality to respond to supported [RPC methods](/embedded-wallets/sdk/client-side/javascript#evm-rpc-methods) *\*Some features are not yet compatible such as NFT Viewer and Fiat On-ramps.* *Need a feature or see a problem?* File an issue on our [github repo](https://github.com/magiclabs/magic-js). ## Resources & Tools * Documentation: [https://docs.flare.network/](https://docs.flare.network/) * Block Explorer: * [https://mainnet.flarescan.com/](https://mainnet.flarescan.com/) (Mainnet) * [https://coston-explorer.flare.network/](https://coston-explorer.flare.network/) (Testnet) * Testnet Faucet: [https://faucet.flare.network/coston](https://faucet.flare.network/coston) # Harmony Source: https://docs.magic.link/embedded-wallets/blockchains/evm/harmony ## Overview [Harmony](https://www.harmony.one/) (ONE) blockchain is an L2 blockchain platform built on the Ethereum network, making it easier for developers to create decentralized apps. **As Harmony is EVM compatible**, you can follow the [Ethereum](/embedded-wallets/blockchains/ethereum/javascript)documentation to send your first transaction and utilize all other wallet features. ## Installation Magic interacts with the [Harmony](https://www.harmony.one/) blockchain via Magic's extension NPM package [`@magic-ext/harmony`](https://www.npmjs.com/package/@magic-ext/harmony). The Harmony extension also lets you interact with the blockchain using methods from [Harmony's JavaScript SDK](https://github.com/harmony-one/sdk). ```bash NPM icon="npm" theme={null} npm install --save @magic-ext/harmony ``` ```bash Yarn icon="yarn" theme={null} yarn add @magic-ext/harmony ``` ## Configure Harmony ```js JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { HarmonyExtension } from '@magic-ext/harmony'; const { Harmony: Index } = require('@harmony-js/core'); const { ChainID, ChainType } = require('@harmony-js/utils'); const magic = new Magic('YOUR_API_KEY', { extensions: [ new HarmonyExtension({ rpcUrl: 'https://api.s0.b.hmny.io', chainId: ChainID.HmyTestnet, }), ], }); ``` ## Send Transaction ### Getting Test ONE token Before you can send transaction on the Harmony blockchain, you'll need to acquire some test ONE token (Harmony's native cryptocurrency for test network). 1. Go to our [**Harmony Example**](https://go.magic.link/example-harmony) application 2. Login with your email address 3. Copy your Harmony public address 4. Go to the [**Harmony Faucet**](https://onefaucet.ibriz.ai/) 5. Paste your copied Harmony public address in the text input 6. Now you can use your test ONE token in our [**example app**](https://go.magic.link/example-harmony) ### Call Extension Method Note that the Magic Harmony extension follows the method names and conventions by [**Harmony's JavaScript SDK**](https://github.com/harmony-one/sdk). To send a standard Harmony blockchain transaction, you can call the `magic.harmony.sendTransaction` method. ```js JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { HarmonyExtension } from '@magic-ext/harmony'; const { Harmony: Index } = require('@harmony-js/core'); const { ChainID, ChainType } = require('@harmony-js/utils'); const magic = new Magic('YOUR_API_KEY', { extensions: [ new HarmonyExtension({ rpcUrl: 'https://api.s0.b.hmny.io', chainId: ChainID.HmyTestnet, }), ], }); const params = { // token send to to: 'one1jzxhswufkh7wgyq7s49u3rvp9vlts8wcwsq8y2', // amount to send value: '50000', // gas limit, you can use string gasLimit: '210000', // send token from shardID shardID: 0, // send token to toShardID toShardID: 0, // gas Price, you can use Unit class, and use Gwei, then remember to use toWei(), which will be transformed to BN gasPrice: 1000000000, }; const txHash = await magic.harmony.sendTransaction(params); console.log('transaction hash', txHash); ``` ## Smart Contract ### Deploy Contract #### Getting Test ONE token Before you can send transaction on the Harmony blockchain, you'll need to acquire some test ONE token (Harmony's native cryptocurrency for test network). 1. Go to our [**Harmony Example**](https://go.magic.link/example-harmony) application 2. Login with your email address 3. Copy your Harmony public address 4. Go to the [**Harmony Faucet**](https://faucet.pops.one/) 5. Paste your copied Harmony public address in the text input 6. Now you can use your test ONE token in our [**example app**](https://go.magic.link/example-harmony) #### Call Extension Method Note that the Magic Harmony extension follows the method names and conventions by [**Harmony's JavaScript SDK**](https://github.com/harmony-one/sdk). To deploy an Harmony smart contract, you can call the `magic.harmony.sendTransaction` method to send deploy contract transaction. ```js JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { HarmonyExtension } from '@magic-ext/harmony'; const { Harmony: Index } = require('@harmony-js/core'); const { ChainID, ChainType } = require('@harmony-js/utils'); const magic = new Magic('YOUR_API_KEY', { extensions: [ new HarmonyExtension({ rpcUrl: 'https://api.s0.b.hmny.io', chainId: ChainID.HmyTestnet, }), ], }); const bin = '608060405234801561001057600080fd5b5060c68061001f6000396000f3fe6080604052348015600f576000' + '80fd5b506004361060325760003560e01c80636057361d146037578063b05784b8146062575b600080fd5b6060600480' + '36036020811015604b57600080fd5b8101908080359060200190929190505050607e565b005b60686088565b60405180' + '82815260200191505060405180910390f35b8060008190555050565b6000805490509056fea265627a7a723158209e86' + '9bf97eba094ccf7533f0f92b4de32cf3cce7d7cff974769bca975e178b0164736f6c63430005110032'; const contractBytecode = { data: `0x${bin}`, gasLimit: '210000', // send token from shardID shardID: 0, // send token to toShardID toShardID: 0, // gas Price, you can use Unit class, and use Gwei, then remember to use toWei(), which will be transformed to BN gasPrice: 1000000000, arguments: [], }; const txHash = await magic.harmony.sendTransaction(contractBytecode); console.log('transaction hash', txHash); ``` ### Contract Send #### Getting Test ONE token Before you can send transaction on the Harmony blockchain, you'll need to acquire some test ONE token (Harmony's native cryptocurrency for test network). 1. Go to our [**Harmony Example**](https://go.magic.link/example-harmony) application 2. Login with your email address 3. Copy your Harmony public address 4. Go to the [**Harmony Faucet**](https://onefaucet.ibriz.ai/) 5. Paste your copied Harmony public address in the text input 6. Now you can use your test ONE token in our [**example app**](https://go.magic.link/example-harmony) #### Call Extension Method Note that the Magic Harmony extension follows the method names and conventions by [**Harmony's JavaScript SDK**](https://github.com/harmony-one/sdk). To call an Harmony smart contract function, you can call the `magic.harmony.sendTransaction` method to send contract transaction. ```js JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { HarmonyExtension } from '@magic-ext/harmony'; const { Harmony: Index } = require('@harmony-js/core'); const { ChainID, ChainType } = require('@harmony-js/utils'); const magic = new Magic('YOUR_API_KEY', { extensions: [ new HarmonyExtension({ rpcUrl: 'https://api.s0.b.hmny.io', chainId: ChainID.HmyTestnet, }), ], }); const harmony = new Index( // rpc url 'https://api.s0.b.hmny.io', { // chainType set to Index chainType: ChainType.Harmony, // chainType set to HmyLocal chainId: ChainID.HmyTestnet, }, ); let contractAddress = '0x67a3f8db0c98524e8e4513f95cd68f7fbbca7f06'; const contractAbi = [ { constant: false, inputs: [ { internalType: 'uint256', name: 'num', type: 'uint256', }, ], name: 'store', outputs: [], payable: false, stateMutability: 'nonpayable', type: 'function', }, { constant: true, inputs: [], name: 'retreive', outputs: [ { internalType: 'uint256', name: '', type: 'uint256', }, ], payable: false, stateMutability: 'view', type: 'function', }, ]; const deployedContract = harmony.contracts.createContract(contractAbi, contractAddress); const tx = await deployedContract.methods.store(900); let { txPayload } = tx.transaction; txPayload.from = (await magic.user.getMetadata()).publicAddress; txPayload.gasLimit = '210000'; txPayload.gasPrice = '1000000000'; const txHash = await magic.harmony.sendTransaction(txPayload); console.log('transaction hash', txHash); ``` ## Compatibility * All `Auth`, `User` and most `Wallet` module methods\* * All EVM Provider functionality to respond to supported [RPC methods](/embedded-wallets/sdk/client-side/javascript#evm-rpc-methods) * Widget UI for token balances and token transfers\* *\*Some features are not yet compatible such as NFT Viewer and Fiat On-ramps.* *Need a feature or see a problem?* File an issue on our [github repo](https://github.com/magiclabs/magic-js). ## Resources & Tools * Documentation: [https://docs.harmony.one/home/](https://docs.harmony.one/home/) * Block Explorer: [https://explorer.harmony.one/](https://explorer.harmony.one/) (Mainnet) * Faucet: [https://onefaucet.ibriz.ai/](https://onefaucet.ibriz.ai/) * [Example](https://go.magic.link/example-harmony) # Hedera Source: https://docs.magic.link/embedded-wallets/blockchains/evm/hedera ## Overview [Hedera](https://hedera.com/) is an EVM-compatible decentralized, open-source and proof-of-stake Layer1 blockchain. It utilizes the leaderless, asynchronous Byzantine Fault Tolerance (aBFT) hashgraph, offering fast transactions and low fees. **As Hedera is EVM compatible**, you can follow the [Ethereum](/embedded-wallets/blockchains/ethereum/javascript) documentation to send your first transaction and utilize all other wallet features. ## Installation Magic interacts with the [Hedera](https://hedera.com/) blockchain via Magic's extension NPM package [`@magic-ext/hedera`](https://www.npmjs.com/package/@magic-ext/hedera). The Hedera extension also lets you interact with the blockchain using methods from the [Hiero SDK](https://www.npmjs.com/package/@hiero-ledger/sdk) ([Hiero](https://hiero.org) is the Linux Decentralized Trust Foundation's project that hosts the Hedera source code). ```bash NPM icon="npm" theme={null} npm install --save @magic-ext/hedera ``` ```bash Yarn icon="yarn" theme={null} yarn add @magic-ext/hedera ``` ## Configure Hedera ```js JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { HederaExtension } from '@magic-ext/hedera'; const magic = new Magic("YOUR_API_KEY", { extensions: [new HederaExtension({ network: 'testnet' // 'mainnet' or 'testnet' })] }); ``` ## Send Transaction #### Call Extension Method Note that the Magic Hedera extension follows the method names and conventions by the [Hiero SDK](https://www.npmjs.com/package/@hiero-ledger/sdk). To send a standard Hedera blockchain transaction, you can call the inject the MagicWallet to hedera-sdk-js. More details please reference to [example-hedera](https://github.com/magiclabs/example-hedera) github repo. ```js JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { HederaExtension } from '@magic-ext/hedera'; const magic = new Magic("YOUR_API_KEY", { extensions: [new HederaExtension({ network: 'testnet' // 'mainnet' or 'testnet' })] }); const { publicKeyDer } = await magic.hedera.getPublicKey() const magicSign = message => magic.hedera.sign(message); const magicWallet = new MagicWallet(publicAddress, new MagicProvider('testnet'), publicKeyDer, magicSign); let transaction = await new TransferTransaction() .setNodeAccountIds([new AccountId(3)]) .addHbarTransfer(publicAddress, -1 * sendAmount) .addHbarTransfer(destinationAddress, sendAmount) .freezeWithSigner(magicWallet); transaction = await transaction.signWithSigner(magicWallet); const result = await transaction.executeWithSigner(magicWallet); const receipt = await result.getReceiptWithSigner(magicWallet); console.log(receipt.status.toString()); ``` ## Compatibility * All `Auth`, `User` module methods * All EVM Provider functionality to respond to supported [RPC methods](/embedded-wallets/sdk/client-side/javascript#evm-rpc-methods) *\*Some features are not yet compatible such as the Widget UI.* *Need a feature or see a problem?* File an issue on our [github repo](https://github.com/magiclabs/magic-js). ## Resources & Tools * Documentation: [https://docs.hedera.com/hedera/](https://docs.hedera.com/hedera/) * Block Explorers: [https://hedera.com/ecosystem/network-explorers/](https://hedera.com/ecosystem/network-explorers) * [Example](https://github.com/magiclabs/example-hedera) # Horizen EON Source: https://docs.magic.link/embedded-wallets/blockchains/evm/horizen-eon ## Overview [Horizen EON](https://docs.horizen.io/horizen_eon/) is a highly scalable EVM-compatible sidechain network and smart contract platform. Users can effortlessly create wallets and interact with EON using the Magic SDK. **As EON is EVM compatible**, you can follow the [Ethereum](/embedded-wallets/blockchains/ethereum/javascript) documentation to send your first transaction and utilize all other features. ## Configure EON ```js Testnet icon="square-js" theme={null} const EonOptions = { rpcUrl: 'https://gobi-rpc.horizenlabs.io/ethv1', chainId: 1663, }; // Setting network to point to EON Testnet const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: EonOptions }); ``` ```js Mainnet icon="square-js" theme={null} const EonOptions = { rpcUrl: 'https://eon-rpc.horizenlabs.io/ethv1', chainId: 7332, }; // Setting network to point to EON Mainnet const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: EonOptions }); ``` ## Compatibility * All `Auth`, `User` and most `Wallet` module methods\* * All EVM Provider functionality to respond to supported [RPC methods](/embedded-wallets/sdk/client-side/javascript#evm-rpc-methods) *\*Some features are not yet compatible such as NFT Viewer and Fiat On-ramps.* *Need a feature or see a problem?* File an issue on our [github repo](https://github.com/magiclabs/magic-js). ## Resources & Tools * Documentation: [https://docs.horizen.io/](https://docs.horizen.io/) * Block Explorer: * [https://eon-explorer.horizenlabs.io/](https://eon-explorer.horizenlabs.io/) (Mainnet) * [https://gobi-explorer.horizenlabs.io/](https://gobi-explorer.horizenlabs.io/) (Testnet) * Testnet Faucet: [https://faucet.horizen.io/](https://faucet.horizen.io/) # Loopring Source: https://docs.magic.link/embedded-wallets/blockchains/evm/loopring ## Overview [Loopring](https://loopring.org/#/) is a zkRollup layer 2 open protocol for building decentralized exchanges (DEXs) on the Ethereum blockchain. It allows for high-throughput, low-cost trading and payment on Ethereum. **As Loopring is EVM compatible**, you can follow the [Ethereum](/embedded-wallets/blockchains/ethereum/javascript)documentation to send your first transaction and utilize all other wallet features. ## Installation Magic interacts with the Loopring blockchain via Magic SDK and the `loopring-sdk` package. ```bash NPM icon="npm" theme={null} npm install --save magic-sdk @loopring-web/loopring-sdk ``` ```bash Yarn icon="yarn" theme={null} yarn add magic-sdk @loopring-web/loopring-sdk ``` ## Send Transaction ```js JavaScript icon="square-js" theme={null} import { Magic } from "magic-sdk"; import * as sdk from '@loopring-web/loopring-sdk'; import {LOOPRING_EXPORTED_ACCOUNT, LoopringAPI, signatureKeyPairMock, TOKEN_INFO, web3} from "./Loopring"; const customNodeOptions = { // Your own ethereum node URL rpcUrl: 'https://goerli.infura.io/v3/a06ed9c6b5424b61beafff27ecc3abf3', chainId: 5, // chainId }; const magic = new Magic("YOUR API KEY", {network: customNodeOptions}); const web3 = new Web3(magic.rpcProvider) // Step1. get account info payerAddress should be magic wallet public address const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({owner: payerAddress}); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo, web3); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const { apiKey } = await LoopringAPI.userAPI.getUserApiKey({ accountId: accInfo.accountId, }, eddsaKey.sk); console.log("apiKey:", apiKey); // Step 4. get storageId const storageId = await LoopringAPI.userAPI.getNextStorageId({ accountId: accInfo.accountId, sellTokenId: TOKEN_INFO.tokenMap["LRC"].tokenId }, apiKey); console.log("storageId:", storageId); const fee = await LoopringAPI.userAPI.getOffchainFeeAmt({ accountId: accInfo.accountId, requestType: sdk.OffchainFeeReqType.TRANSFER_AND_UPDATE_ACCOUNT }, apiKey); console.log("fee:", fee); const request = { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, payerAddr: accInfo.owner, payerId: accInfo.accountId, payeeAddr: payeeAddress, payeeId: payeeAccountId, storageId: storageId.offchainId, token: { tokenId: TOKEN_INFO.tokenMap.LRC.tokenId, volume: amount.toString() }, maxFee: { tokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, volume: fee.fees["LRC"].fee ?? "9400000000000000000" }, validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, payPayeeUpdateAccount: true }; const transactionResult = await LoopringAPI.userAPI.submitInternalTransfer({ request, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.Unknown, eddsaKey: eddsaKey.sk, apiKey: apiKey,}); console.log(transactionResult) ``` ## Compatibility * All `Auth`, `User` methods and * All EVM Provider functionality to respond to supported [RPC methods](/embedded-wallets/sdk/client-side/javascript#evm-rpc-methods) *\*Some features are not yet compatible such as the Widget UI.* *Need a feature or see a problem?* File an issue on our [github repo](https://github.com/magiclabs/magic-js). ## Resources & Tools * Block Explorer: [https://explorer.loopring.io/](https://explorer.loopring.io/) * [Example](https://github.com/magiclabs/example-loopring) # Moonbeam / Moonriver Source: https://docs.magic.link/embedded-wallets/blockchains/evm/moonbeam ## Overview [Moonbeam](https://moonbeam.network/) is a platform for cross-chain connected applications that unites assets and functionality from many blockchains. As a smart contract platform for building cross-chain connected applications, Moonbeam powers dApps that can access users, assets, and services on any chain. **As Moonbeam is EVM compatible**, you can follow the [Ethereum](/embedded-wallets/blockchains/ethereum/javascript)documentation to send your first transaction and utilize all other wallet features. ## Configure Moonbeam ### Moonbeam ```javascript JavaScript icon="square-js" theme={null} // Mainnet const customNodeOptions = { rpcUrl: 'https://rpc.api.moonbeam.network', chainId: 1284 } // Or connect to Moonbeam's Testnet const customNodeOptions = { rpcUrl: 'https://rpc.api.moonbase.moonbeam.network', chainId: 1287 } const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: customNodeOptions }); ``` ## Moonriver You can also connect to [Moonriver](https://docs.moonbeam.network/learn/platform/networks/moonriver/), which is currently live on [Kusama](https://kusama.network/), Polkadot's canary network. Moonriver is also EVM compatible so you can directly follow the [Ethereum installation](/embedded-wallets/blockchains/ethereum/javascript). ```javascript JavaScript icon="square-js" theme={null} const customNodeOptions = { rpcUrl: 'https://rpc.api.moonriver.moonbeam.network', chainId: 1285 } const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: customNodeOptions }); ``` ## Compatibility * All `Auth`, `User` and most `Wallet` module methods\* * All EVM Provider functionality to respond to supported [RPC methods](/embedded-wallets/sdk/client-side/javascript#evm-rpc-methods) *\*Some features are not yet compatible such as NFT Viewer and Fiat On-ramps.* *Need a feature or see a problem?* File an issue on our [github repo](https://github.com/magiclabs/magic-js). ## Resources & Tools * Documentation: [https://docs.moonbeam.network/](https://docs.moonbeam.network/) * Faucet: [https://faucet.moonbeam.network/](https://faucet.moonbeam.network/) * Block Explorer: [https://moonscan.io/](https://moonscan.io/) # Optimism Source: https://docs.magic.link/embedded-wallets/blockchains/evm/optimism ## Overview [OP Mainnet](https://www.optimism.io/) is a low-cost and lightning-fast Ethereum L2 blockchain powered by Optimism. Built as a minimal extension to existing Ethereum software, OP Mainnet's EVM-equivalent architecture scales your Ethereum apps without surprises. **As Optimism is EVM compatible**, you can follow the [Ethereum](/embedded-wallets/blockchains/ethereum/javascript)documentation to send your first transaction and utilize all other features. ## Configure Optimism #### Mainnet Mainnet Block Explorer: [https://optimistic.etherscan.io](https://optimistic.etherscan.io) ```javascript JavaScript icon="square-js" theme={null} const customNodeOptions = { rpcUrl: 'https://mainnet.optimism.io', chainId: 10, } // Setting network to Optimism const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: customNodeOptions }); ``` #### Testnet Testnet Block Explorer[:](https://kovan-optimistic.etherscan.io) [https://blockscout.com/optimism/goerli/](https://blockscout.com/optimism/goerli/) ```javascript JavaScript icon="square-js" theme={null} const customNodeOptions = { rpcUrl: 'https://goerli.optimism.io', chainId: 420 } // Setting network to Optimism const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: customNodeOptions }); ``` ## Compatibility * All `Auth`, `User` and most `Wallet` module methods for any wallet type * All EVM Provider functionality to respond to supported [RPC methods](/embedded-wallets/sdk/client-side/javascript#evm-rpc-methods) *Need a feature or see a problem?* File an issue on our [github repo](https://github.com/magiclabs/magic-js). ## Resources & Tools * Block Explorer: * [https://optimistic.etherscan.io/](https://optimistic.etherscan.io/) (Mainnet) * [https://sepolia-optimism.etherscan.io/](https://sepolia-optimism.etherscan.io/) (Sepolia Testnet) * [Ethers.js Demo](https://codesandbox.io/s/magic-optimism-demo-ethers-js-8gqhdx) # Plume Source: https://docs.magic.link/embedded-wallets/blockchains/evm/plume Build on Plume, the EVM compatible blockchain purpose-built for Real World Asset (RWA) tokenization and DeFi integration. ## Overview [Plume](https://www.plume.org) is a public, EVM compatible blockchain designed to facilitate the tokenization and management of Real World Assets (RWAs). Built specifically for RWA integration into DeFi, Plume enables developers to create applications where users can tokenize, stake, swap, lend, and borrow real-world assets alongside crypto-native assets. **What are Real World Assets (RWAs)?** RWAs are tangible or intangible assets from the traditional financial world that are tokenized on-chain. Examples include real estate, commodities, private credit, ETFs, and even esoteric assets like GPUs and mineral rights. ### Key Features Plume's architecture consists of three core components: * **Arc (Tokenization Engine)**: Simplifies the creation, onboarding, and management of tokenized RWAs * **Passport (Smart Wallets)**: Embeds custody and compliance directly within wallets, enhancing user experience * **Nexus (Data Highway)**: Integrates real-world data on-chain, enabling new use cases for prediction markets and DeFi applications As Plume is EVM compatible, you can follow the [Ethereum](/embedded-wallets/blockchains/ethereum/javascript) documentation to send your first transaction and utilize all other wallet features. ## Configure Plume Ensure you have installed the Magic SDK and have access to your API key. Follow the [quickstart](/embedded-wallets/quickstart/overview) to get started. Configure your Magic instance to connect to Plume's mainnet or testnet: ```js Mainnet icon="square-js" theme={null} const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: { rpcUrl: 'https://rpc.plume.org', chainId: 98866, } }); ``` ```js Testnet icon="square-js" theme={null} const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: { rpcUrl: 'https://testnet-rpc.plume.org', chainId: 98867, } }); ``` ## Resources & Tools * **Documentation**: [https://docs.plume.org](https://docs.plume.org) * **Block Explorer**: * [https://explorer.plume.org](https://explorer.plume.org) (Mainnet) * [https://testnet-explorer.plume.org](https://testnet-explorer.plume.org) (Testnet) * **Website**: [https://www.plume.org](https://www.plume.org) # Polygon Source: https://docs.magic.link/embedded-wallets/blockchains/evm/polygon ## Installation To interact with the Polygon network, you will need to use a web3 provider library, such as [`ethers.js`](https://docs.ethers.io/v5/getting-started/), with Magic. For a more in-depth guide on available functions provided, refer to the [EVM documentation](/embedded-wallets/blockchains/ethereum/javascript).⁠ ⁠To get started, install the following dependencies for your project: ### Ethers.js ```bash NPM icon="npm" theme={null} npm install ethers magic-sdk ``` ```bash Yarn icon="yarn" theme={null} yarn add ethers magic-sdk ``` ```javascript CDN icon="code" theme={null} ``` ## Initialization The Magic class is the entry-point to the Magic SDK. It must be instantiated with a Magic **publishable** key. **Important** Ethereum provider is only supported in **magic-sdk\@1.0.1 or later** versions. ### Mainnet Mainnet Block Explorer URL: [https://polygonscan.com/](https://polygonscan.com/) ```javascript JavaScript icon="square-js" theme={null} const customNodeOptions = { rpcUrl: 'https://polygon-rpc.com/', // Polygon RPC URL chainId: 137, // Polygon chain id } // Setting network to Polygon const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: customNodeOptions }); ``` ### Testnet Mumbai Block Explorer: [https://www.oklink.com/amoy](https://www.oklink.com/amoy) Mumbai ⁠Testnet Faucet: [https://faucet.polygon.technology/](https://faucet.polygon.technology/) ```javascript JavaScript icon="square-js" theme={null} const customNodeOptions = { rpcUrl: 'https://rpc-amoy.polygon.technology/', // Polygon RPC URL chainId: 80002, // Polygon chain id } // Setting network to Polygon - Testnet const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: customNodeOptions }); ``` ## Send Transaction ### Ethers.js ```javascript Ethers (v6) icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.BrowserProvider(magic.rpcProvider); // ⭐️ After user is successfully authenticated const signer = await provider.getSigner(); const destination = '0xE0cef4417a772512E6C95cEf366403839b0D6D6D'; const amount = ethers.parseEther('1.0'); // Convert 1 ether to wei // Submit transaction to the blockchain const tx = await signer.sendTransaction({ to: destination, value: amount, }); // Wait for transaction to be mined const receipt = await tx.wait(); ``` ```javascript Ethers (v5) icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.providers.Web3Provider(magic.rpcProvider); // ⭐️ After user is successfully authenticated const signer = provider.getSigner(); const destination = '0xE0cef4417a772512E6C95cEf366403839b0D6D6D'; const amount = ethers.utils.parseEther('1.0'); // Convert 1 ether to wei // Submit transaction to the blockchain const tx = await signer.sendTransaction({ to: destination, value: amount, }); // Wait for transaction to be mined const receipt = await tx.wait(); ``` ## Sign Message ### Ethers.js #### Personal Sign ```javascript Ethers (v6) icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.BrowserProvider(magic.rpcProvider); // ⭐️ After user is successfully authenticated const signer = await provider.getSigner(); const originalMessage = 'YOUR_MESSAGE'; const signedMessage = await signer.signMessage(originalMessage); ``` ```javascript Ethers (v5) icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.providers.Web3Provider(magic.rpcProvider); await provider.send('eth_requestAccounts', []); // ⭐️ After user is successfully authenticated const signer = provider.getSigner(); const originalMessage = 'YOUR_MESSAGE'; const signedMessage = await signer.signMessage(originalMessage); ``` #### Sign Typed Data v1 ```javascript Ethers (v6) icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.BrowserProvider(magic.rpcProvider); const signer = await provider.getSigner(); // ⭐️ After user is successfully authenticated // Get user's Ethereum public address const fromAddress = await signer.getAddress(); const originalMessage = [ { type: 'string', name: 'fullName', value: 'John Doe', }, { type: 'uint32', name: 'userId', value: '1234', }, ]; const params = [originalMessage, fromAddress]; const method = 'eth_signTypedData'; const signedMessage = await signer.provider.send(method, params); ``` ```javascript Ethers (v5) icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.providers.Web3Provider(magic?.rpcProvider); await provider.send('eth_requestAccounts', []); const signer = provider.getSigner(); // ⭐️ After user is successfully authenticated // Get user's Ethereum public address const fromAddress = await signer.getAddress(); const originalMessage = [ { type: 'string', name: 'fullName', value: 'John Doe', }, { type: 'uint32', name: 'userId', value: '1234', }, ]; const params = [originalMessage, fromAddress]; const method = 'eth_signTypedData'; const signedMessage = await signer.provider.send(method, params); ``` #### Sign Typed Data v3 ```javascript Ethers (v6) icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.BrowserProvider(magic.rpcProvider); const signer = await provider.getSigner(); // ⭐️ After user is successfully authenticated // Get user's Ethereum public address const fromAddress = await signer.getAddress(); const originalMessage = { types: { EIP712Domain: [ { name: 'name', type: 'string', }, { name: 'version', type: 'string', }, { name: 'verifyingContract', type: 'address', }, ], Greeting: [ { name: 'contents', type: 'string', }, ], }, primaryType: 'Greeting', domain: { name: 'Magic', version: '1', verifyingContract: '0xE0cef4417a772512E6C95cEf366403839b0D6D6D', }, message: { contents: 'Hello, from Magic!', }, }; const params = [fromAddress, originalMessage]; const method = 'eth_signTypedData_v3'; const signedMessage = await signer.provider.send(method, params); ``` ```javascript Ethers (v5) icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.providers.Web3Provider(magic?.rpcProvider); await provider.send('eth_requestAccounts', []); const signer = provider.getSigner(); // ⭐️ After user is successfully authenticated // Get user's Ethereum public address const fromAddress = await signer.getAddress(); const originalMessage = { types: { EIP712Domain: [ { name: 'name', type: 'string', }, { name: 'version', type: 'string', }, { name: 'verifyingContract', type: 'address', }, ], Greeting: [ { name: 'contents', type: 'string', }, ], }, primaryType: 'Greeting', domain: { name: 'Magic', version: '1', verifyingContract: '0xE0cef4417a772512E6C95cEf366403839b0D6D6D', }, message: { contents: 'Hello, from Magic!', }, }; const params = [fromAddress, originalMessage]; const method = 'eth_signTypedData_v3'; const signedMessage = await signer.provider.send(method, params); ``` #### Sign Typed Data v4 ```javascript Ethers (v6) icon="square-js" theme={null} /* Sign Typed Data v4 adds support for arrays and recursive data types. Otherwise, it works the same as Sign Typed Data v3. */ import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.BrowserProvider(magic.rpcProvider); const signer = await provider.getSigner(); // ⭐️ After user is successfully authenticated // Get user's Ethereum public address const fromAddress = await signer.getAddress(); const originalMessage = { types: { EIP712Domain: [ { name: 'name', type: 'string', }, { name: 'version', type: 'string', }, { name: 'verifyingContract', type: 'address', }, ], Greeting: [ { name: 'contents', type: 'string', }, ], }, primaryType: 'Greeting', domain: { name: 'Magic', version: '1', verifyingContract: '0xE0cef4417a772512E6C95cEf366403839b0D6D6D', }, message: { contents: 'Hello, from Magic!', }, }; const params = [fromAddress, originalMessage]; const method = 'eth_signTypedData_v4'; const signedMessage = await signer.provider.send(method, params); ``` ```javascript Ethers (v5) icon="square-js" theme={null} /* Sign Typed Data v4 adds support for arrays and recursive data types. Otherwise, it works the same as Sign Typed Data v3. */ import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.providers.Web3Provider(magic.rpcProvider); const signer = provider.getSigner(); // ⭐️ After user is successfully authenticated // Get user's Ethereum public address const fromAddress = await signer.getAddress(); const originalMessage = { types: { EIP712Domain: [ { name: 'name', type: 'string', }, { name: 'version', type: 'string', }, { name: 'verifyingContract', type: 'address', }, ], Greeting: [ { name: 'contents', type: 'string', }, ], }, primaryType: 'Greeting', domain: { name: 'Magic', version: '1', verifyingContract: '0xE0cef4417a772512E6C95cEf366403839b0D6D6D', }, message: { contents: 'Hello, from Magic!', }, }; const params = [fromAddress, originalMessage]; const method = 'eth_signTypedData_v4'; const signedMessage = await signer.provider.send(method, params); ``` ## Get Balance ### Ethers.js ```javascript Ethers (v6) icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY') const provider = new ethers.BrowserProvider(magic.rpcProvider); const signer = await provider.getSigner(); // Get user's Ethereum public address const address = await signer.getAddress(); // Get user's balance in ether const balance = ethers.formatEther( await provider.getBalance(address), // Balance is in wei ); ``` ```javascript Ethers (v5) icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { ethers } from 'ethers'; const magic = new Magic('YOUR_PUBLISHABLE_API_KEY'); const provider = new ethers.providers.Web3Provider(magic.rpcProvider); await provider.send('eth_requestAccounts', []); const signer = provider.getSigner(); // Get user's Ethereum public address const address = await signer.getAddress(); // Get user's balance in ether const balance = ethers.utils.formatEther( await provider.getBalance(address), // Balance is in wei ); ``` ## Resources * [Magic JavaScript SDK](https://github.com/magiclabs/magic-js) * [EVM Documentation](/embedded-wallets/blockchains/ethereum/javascript) # RARI Chain Source: https://docs.magic.link/embedded-wallets/blockchains/evm/rari-chain ## Overview [RARI Chain](https://rarichain.org/) is a Layer 3 blockchain powered by Arbitrum, offering high throughput and low transaction costs. It also introduces a new royalty system embedding them at the node level, guaranteeing royalty payments to creators. **As RARI Chain is EVM equivalent**, you can follow the [Ethereum](/embedded-wallets/blockchains/ethereum/javascript) documentation to send your first transaction and utilize all other wallet features. ## Configure RARI Chain Ensure you have installed the Magic SDK and have access to your API key, follow the [quickstart](/embedded-wallets/quickstart/overview) to get started. ```js Testnet icon="square-js" theme={null} // Setting network to point to RARI testnet const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: { rpcUrl: 'https://testnet.rpc.rarichain.org/http', chainId: 1918988905, } }); ``` ```js Mainnet icon="square-js" theme={null} // Setting network to point to RARI mainnet const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: { rpcUrl: 'https://mainnet.rpc.rarichain.org/http', chainId: 1380012617, } }); ``` ## Compatibility * All `Auth`, `User` and `Wallet` module methods * All EVM Provider functionality to respond to supported [RPC methods](/embedded-wallets/sdk/client-side/javascript#evm-rpc-methods) *Need a feature or see a problem?* File an issue on our [github repo](https://github.com/magiclabs/magic-js). ## Resources & Tools * Documentation: [https://rari.docs.caldera.dev/](https://rari.docs.caldera.dev/) * Block Explorer: * [https://mainnet.explorer.rarichain.org/](https://mainnet.explorer.rarichain.org/) (Mainnet) * [https://testnet.explorer.rarichain.org/](https://testnet.explorer.rarichain.org/) (Testnet) * Faucets: [https://rari.docs.caldera.dev/faucet](https://rari.docs.caldera.dev/faucet) # Sei Source: https://docs.magic.link/embedded-wallets/blockchains/evm/sei ## Overview [Sei](https://www.sei.io/) is the first parallelized EVM layer 1 blockchain network. It offers significant scalability, up to five thousand transactions per second, along with sub-one-second finality and reduced transaction fees compared to Ethereum. **As Sei is EVM compatible**, you can follow the [Ethereum](/embedded-wallets/blockchains/ethereum/javascript) documentation to send your first transaction and utilize all other features. ## Configure Sei ```js Devnet icon="square-js" theme={null} const SeiOptions = { rpcUrl: 'https://evm-rpc-arctic-1.sei-apis.com', // Sei devnet RPC URL chainId: 713715, // Sei chain id }; // Setting network to point to Sei const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: SeiOptions }); ``` ```js Mainnet icon="square-js" theme={null} const SeiOptions = { rpcUrl: 'https://evm-rpc.sei-apis.com', // Sei mainnet RPC URL chainId: 1329, // Sei chain id }; // Setting network to point to Sei const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: SeiOptions }); ``` ## Compatibility * All `Auth`, `User` and most `Wallet` module methods\* * All EVM Provider functionality to respond to supported [RPC methods](/embedded-wallets/sdk/client-side/javascript#evm-rpc-methods) *\*Some features are not yet compatible such as NFT Viewer and Fiat On-ramps.* *Need a feature or see a problem?* File an issue on our [github repo](https://github.com/magiclabs/magic-js). ## Resources & Tools * Documentation: [https://www.docs.sei.io/](https://www.docs.sei.io/) * Block Explorer: [https://www.seiscan.app/pacific-1](https://www.seiscan.app/pacific-1) * Testnet Faucet: [https://www.docs.sei.io/dev-ecosystem-providers/faucets](https://www.docs.sei.io/dev-ecosystem-providers/faucets) # Soneium Source: https://docs.magic.link/embedded-wallets/blockchains/evm/soneium ## Overview [Soneium](https://soneium.org/en/) is an Ethereum layer-2 blockchain developed by Sony Group Corporation and Startale, designed to create an accessible, developer-friendly platform that empowers creators and communities to explore blockchain's potential in entertainment, gaming, finance, and beyond. Users can effortlessly create wallets and interact with Soneium using the Magic SDK. **As Soneium is EVM compatible**, you can follow the [Ethereum](/embedded-wallets/blockchains/ethereum/javascript) documentation to send your first transaction and utilize all other features. ## Configure Soneium ```js Testnet icon="square-js" theme={null} // Get your Startale API key here - https://portal.scs.startale.com const SoneiumOptions = { rpcUrl: 'https://soneium-minato.rpc.scs.startale.com?apikey=your-api-key', // Soneium testnet RPC URL chainId: 1946, // Soneium chain id }; // Setting network to point to Soneium const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: SoneiumOptions }); ``` ```js Mainnet icon="square-js" theme={null} // Get your Startale API key here - https://portal.scs.startale.com const SoneiumOptions = { rpcUrl: 'https://soneium.rpc.scs.startale.com?apikey=your-api-key', // Soneium mainnet RPC URL chainId: 1868, // Soneium chain id }; // Setting network to point to Soneium const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: SoneiumOptions }); ``` * All `Auth`, `User` and most `Wallet` module methods\* * All EVM Provider functionality to respond to supported [RPC methods](/embedded-wallets/sdk/client-side/javascript#evm-rpc-methods) *\*Some features are not yet compatible such as NFT Viewer and Fiat On-ramps.* *Need a feature or see a problem?* File an issue on our [github repo](https://github.com/magiclabs/magic-js). ## Resources & Tools * Startale Cloud Services: [https://startale.com/en/scs](https://startale.com/en/scs) * Documentation: [https://docs.soneium.org/docs/](https://docs.soneium.org/docs/) * Block Explorer: [https://explorer-testnet.soneium.org/](https://explorer-testnet.soneium.org/) * Testnet Faucet: [https://docs.soneium.org/docs/builders/tools/faucets](https://docs.soneium.org/docs/builders/tools/faucets) # Stability Source: https://docs.magic.link/embedded-wallets/blockchains/evm/stability Stability's blockchain is a tokenless, open-source, decentralized, public, and highly scalable platform. It eliminates transaction fees by using API keys instead of tokens. ## Overview The open-source nature of Stability allows for transparency and security audits. Decentralization is achieved through a trusted network consensus, ensuring no single entity controls the network. Its public accessibility invites widespread participation, and it supports up to 10,000 transactions per second, addressing scalability challenges. For more details, you can visit the [Stability Protocol documentation](https://docs.stabilityprotocol.com/how_stability_works/intro_to_stability). **As Stability is EVM compatible**, you can follow the [Ethereum](/embedded-wallets/blockchains/ethereum/javascript) documentation to send your first transaction and utilize all other wallet features. ## Configure Stability Ensure you have installed the Magic SDK and have access to your API key, follow the [quickstart](/embedded-wallets/quickstart/overview) to get started. You can use Magic's network aliases to connect to either testnet or mainnet on Base. ```js Testnet icon="square-js" theme={null} // Setting network to point to Stability testnet const magic = new Magic('MAGIC_API_KEY', { network: { rpcUrl: 'https://free.testnet.stabilityprotocol.com/zgt/', chainId: 20180427, } }); ``` ```js Mainnet icon="square-js" theme={null} // Setting network to point to Stability mainnet const magic = new Magic('MAGIC_API_KEY', { network: { rpcUrl: 'https://gtn.stabilityprotocol.com/zgt/', chainId: 101010, } }); ``` ## Transactions In order to perform transactions on Stability, developers need to sign up for an API key using [Stability's Account Portal](https://account.stabilityprotocol.com/). For a quickstart tutorial on Stability, view their tutorial on their documentation at [https://docs.stble.io/developers/getting\_started](https://docs.stble.io/developers/getting_started). The testnet public RPC will allow a few transactions to go through, but most clients will need an API key. The mainnet public RPC will not allow any transactions to go through unless an API key is concatenated to it. ## Compatibility * All `Auth`, `User` and `Wallet` module methods * All EVM Provider functionality to respond to supported [RPC methods](/embedded-wallets/sdk/client-side/javascript#evm-rpc-methods) *Need a feature or see a problem?* File an issue on our [github repo](https://github.com/magiclabs/magic-js). ## Resources & Tools * Documentation: [https://docs.stabilityprotocol.com/](https://docs.stabilityprotocol.com/) * Block Explorer: * [https://stability.blockscout.com/](https://stability.blockscout.com/) (Mainnet) * [https://stability-testnet.blockscout.com/](https://stability-testnet.blockscout.com/) (Testnet) # XDC Network Source: https://docs.magic.link/embedded-wallets/blockchains/evm/xdc-network Users can effortlessly create wallets and interact with XDC using the Magic SDK. ## Overview [XDC Network](https://xdc.community/) is an open-source, decentralized blockchain platform based on the XDPoS consensus algorithm. It features high scalability, low transaction fees, and a two-second block time. **As XDC Network is EVM compatible**, you can follow the [Ethereum](/embedded-wallets/blockchains/ethereum/javascript) documentation to send your first transaction and utilize all other features. ## Configure XDC Network ```js Testnet icon="square-js" theme={null} const XdcNetworkOptions = { rpcUrl: 'https://erpc.apothem.network', // XDC Apothem testnet RPC URL chainId: 51, // XDC Apothem chain id }; // Setting network to point to XDC const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: XdcNetworkOptions }); ``` ```js Mainnet icon="square-js" theme={null} const XdcNetworkOptions = { rpcUrl: 'https://erpc.xinfin.network', // XDC mainnet RPC URL chainId: 50, // XDC chain id }; // Setting network to point to XDC const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: XdcNetworkOptions }); ``` ## Compatibility * All `Auth`, `User` and most `Wallet` module methods\* * All EVM Provider functionality to respond to supported [RPC methods](/embedded-wallets/sdk/client-side/javascript#evm-rpc-methods) *\*Some features are not yet compatible such as NFT Viewer and Fiat On-ramps.* *Need a feature or see a problem?* File an issue on our [github repo](https://github.com/magiclabs/magic-js). ## Resources & Tools * Documentation: [https://docs.xdc.community/](https://docs.xdc.community/) * Block Explorers: * [https://apothem.xdcscan.io/](https://apothem.xdcscan.io/) (testnet) * [https://xdcscan.io/](https://xdcscan.io/) (mainnet) * Testnet Faucet: [https://faucet.blocksscan.io/](https://faucet.blocksscan.io/) # ZetaChain Source: https://docs.magic.link/embedded-wallets/blockchains/evm/zetachain ## Overview [ZetaChain](https://www.zetachain.com/) is a blockchain network based on the Cosmos SDK and compatible with the Ethereum Virtual Machine (EVM). Users can effortlessly create wallets and interact with ZetaChain using the Magic SDK. **As Zetachain is EVM compatible**, you can follow the [Ethereum](/embedded-wallets/blockchains/ethereum/javascript) documentation to send your first transaction and utilize all other features. ## Configure Zetachain ```js Testnet icon="square-js" theme={null} const ZetaChainOptions = { rpcUrl: 'https://rpc.ankr.com/zetachain_evm_athens_testnet', // Zetachain testnet RPC URL chainId: 7001, // ZetaChain chain id }; // Setting network to point to ZetaChain const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: ZetaChainOptions }); ``` ```js Mainnet icon="square-js" theme={null} const ZetaChainOptions = { rpcUrl: 'https://zetachain-evm.blockpi.network/v1/rpc/public', // Zetachain mainnet RPC URL chainId: 7000, // ZetaChain chain id }; // Setting network to point to ZetaChain const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: ZetaChainOptions }); ``` ## Compatibility * All `Auth`, `User` and most `Wallet` module methods\* * All EVM Provider functionality to respond to supported [RPC methods](/embedded-wallets/sdk/client-side/javascript#evm-rpc-methods) *\*Some features are not yet compatible such as NFT Viewer and Fiat On-ramps.* *Need a feature or see a problem?* File an issue on our [github repo](https://github.com/magiclabs/magic-js). ## Resources & Tools * Documentation: [https://www.zetachain.com/docs/](https://www.zetachain.com/docs/) * Block Explorer: [https://explorer.zetachain.com/](https://explorer.zetachain.com/) * Testnet Faucet: [https://www.zetachain.com/docs/reference/get-testnet-zeta/](https://www.zetachain.com/docs/reference/get-testnet-zeta/) # zkSync Source: https://docs.magic.link/embedded-wallets/blockchains/evm/zksync ## Overview [zkSync](https://zksync.io/) is a Layer 2 scaling solution for Ethereum, leveraging zk-rollup technology to offer fast and low-cost transactions while maintaining a high level of security. **As zkSync is EVM compatible**, you can follow the [Ethereum](/embedded-wallets/blockchains/ethereum/javascript) documentation to send your first transaction and utilize all other wallet features. ## Configure zkSync Ensure you have installed the Magic SDK and have access to your API key, follow the [quickstart](/embedded-wallets/quickstart/overview) to get started. ```js Testnet icon="square-js" theme={null} // Setting network to point to zkSync testnet const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: { rpcUrl: 'https://sepolia.era.zksync.dev', chainId: 300, }, }); ``` ```js Mainnet icon="square-js" theme={null} // Setting network to point to zkSync mainnet const magic = new Magic('YOUR_PUBLISHABLE_API_KEY', { network: { rpcUrl: 'https://mainnet.era.zksync.io', chainId: 324, }, }); ``` ## Compatibility * All `Auth`, `User` and `Wallet` module methods * All EVM Provider functionality to respond to supported [RPC methods](/embedded-wallets/sdk/client-side/javascript#evm-rpc-methods) *Need a feature or see a problem?* File an issue on our [github repo](https://github.com/magiclabs/magic-js). ## Resources & Tools * Documentation: [https://docs.zksync.io/](https://docs.zksync.io/) * Block Explorer: * [https://explorer.zksync.io/](https://explorer.zksync.io/) (Mainnet) * [https://sepolia.explorer.zksync.io/](https://sepolia.explorer.zksync.io/) (Testnet) * Faucets: [https://faucet.chainstack.com/zksync-testnet-faucet](https://faucet.chainstack.com/zksync-testnet-faucet) # Algorand Source: https://docs.magic.link/embedded-wallets/blockchains/non-evm/algorand ## Installation Magic interacts with the [Algorand](https://www.algorand.com/) blockchain via Magic's extension NPM package [`@magic-ext/algorand`](https://www.npmjs.com/package/@magic-ext/algorand). The Algorand extension also lets you interact with the blockchain using methods from [Algorand's JavaScript SDK](https://github.com/algorand/js-algorand-sdk). **NOTE** You can skip straight to our kitchen sink example directly: [**Algorand Example**](https://codesandbox.io/s/github/magiclabs/example-algorand) ```bash NPM icon="npm" theme={null} npm install --save @magic-ext/algorand ``` ```bash Yarn icon="yarn" theme={null} yarn add @magic-ext/algorand ``` ## Initialization ```js JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { AlgorandExtension } from '@magic-ext/algorand'; const magic = new Magic('YOUR_API_KEY', { extensions: { algorand: new AlgorandExtension({ rpcUrl: '', // should remain empty }), }, }); ``` ## Common Methods **Get Test Algorand** Before you can send transaction on the Algorand blockchain, you'll need to acquire some test Algorand. 1. Go to our [**Algorand Example**](https://codesandbox.io/s/github/magiclabs/example-algorand) application 2. Login with your email address 3. Copy your Algorand public address 4. Go to the[**Algorand Faucet**](https://bank.testnet.algorand.network/) 5. Paste your copied Algorand public address in the text input 6. Now you can use your test Algorand in our [**example app**](https://codesandbox.io/s/github/magiclabs/example-algorand) ### Get Wallet Use the `getWallet` function to get the Algorand public address for the current user. ```js JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { AlgorandExtension } from '@magic-ext/algorand'; const magic = new Magic('YOUR_API_KEY', { extensions: { algorand: new AlgorandExtension({ rpcUrl: '', }), }, }); // Get user's Algorand public address const publicAddress = await magic.algorand.getWallet(); console.log('algorand public address', publicAddress); ``` ### Sign Transaction Note that the Magic Algorand extension follows the method names and conventions by [**Algorand's JavaScript SDK**](https://www.npmjs.com/package/@magic-ext/algorand). To sign a standard Algorand blockchain transaction, you can call the `magic.algorand.signTransaction()` method. ```js JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { AlgorandExtension } from '@magic-ext/algorand'; const magic = new Magic('YOUR_API_KEY', { extensions: { algorand: new AlgorandExtension({ rpcUrl: '', }), }, }); const sendPaymentTransaction = async () => { // Construct Payload let params = await algodClient.getTransactionParams().do(); let note = new TextEncoder().encode("Hello World"); let txn = algosdk.makePaymentTxnWithSuggestedParams( from, to, amount, closeRemainderTo, note, params ); console.log("txn", txn); // Sign Payload let encodedTxn = algosdk.encodeObj(txn.get_obj_for_encoding()); const signedTxn = await magic.algorand.signTransaction(encodedTxn); // Broadcast Tx const hash = await algodClient.sendRawTransaction(signedTxn.blob).do(); console.log("hash", hash); // Wait for confirmation const receipt = await waitForConfirmation(algodClient, hash.txId, 4); console.log("receipt", receipt); }; const sendAssetConfigTransaction = async () => { // Construct Payload let params = await algodClient.getTransactionParams().do(); let txn = algosdk.makeAssetCreateTxnWithSuggestedParams( from, note, totalSupply, decimals, defaultFrozen, manager, reserve, freeze, clawback, unitName, assetName, assetURL, assetMetadataHash params ); console.log("txn", txn); // Sign Payload let encodedTxn = algosdk.encodeObj(txn.get_obj_for_encoding()); const signedTxn = await magic.algorand.signTransaction(encodedTxn); // Broadcast Tx const hash = await algodClient.sendRawTransaction(signedTxn.blob).do(); console.log("hash", hash); // Wait for confirmation const receipt = await waitForConfirmation(algodClient, hash.txId, 4); console.log("receipt", receipt); }; const sendAssetTransferTransaction = async () => { // Construct Payload let params = await algosdk.getTransactionParams().do(); let txn = algosdk.makeAssetTransferTxnWithSuggestedParams( from, to, closeRemainderTo, revocationTarget, amount, note, assetIndex, // can get from from acfg tx receipt params ); // Sign Payload let encodedTxn = algosdk.encodeObj(txn.get_obj_for_encoding()); const signedTxn = await magic.algorand.signTransaction(encodedTxn); // Broadcast Tx const hash = await algosdk.sendRawTransaction(signedTxn.blob).do(); console.log("hash", hash); // Wait for confirmation const receipt = await waitForConfirmation(algosdk, hash.txId, 4); console.log("receipt", receipt); }; ``` ### Sign Bid Note that the Magic Algorand extension follows the method names and conventions by [**Algorand's JavaScript SDK**](https://www.npmjs.com/package/@magic-ext/algorand). To sign a standard Algorand blockchain Bid, you can call the `magic.algorand.signBid()` method. ```js JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { AlgorandExtension } from '@magic-ext/algorand'; const magic = new Magic('YOUR_API_KEY', { extensions: { algorand: new AlgorandExtension({ rpcUrl: '', }), }, }); const bid = { bidderKey: 'IB3NJALXLDX5JLYCD4TMTMLVCKDRZNS4JONHMIWD6XM7DSKYR7MWHI6I7U', auctionKey: '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q', bidAmount: 1000, maxPrice: 10, bidID: 2, auctionID: 56, }; const tx = await magic.algorand.signBid(bid); console.log('signed bid', tx); ``` ### Sign Group Transaction To sign a standard Algorand blockchain Group transaction with magic user, you can call the `magic.algorand.signGroupTransaction()` method. ```js JavaScript icon="square-js" theme={null} import { Magic } from "magic-sdk"; import { AlgorandExtension } from "@magic-ext/algorand"; const algosdk = require('algosdk'); const magic = new Magic('YOUR_API_KEY', { extensions: { algorand: new AlgorandExtension({ rpcUrl: '' }) } }); let client = null; async function setupClient() { if( client == null){ const token = { "x-api-key": "x api key" }; const server = "algorand rpc url"; const port = ''; let algodClient = new algosdk.Algodv2(token, server, port); client = algodClient; } else { return client; } return client; } let algodClient = await setupClient(); let params = await algodClient.getTransactionParams().do(); const txns = [{ from: 'magic user public address', to: 'OFHW3Z3T2RML7J2S6KYGHPAMO6IQH76PE2HSCAIN5U5NBGXAIPBOY7DCHI', amount: 1000000, closeRemainderTo: undefined, note: undefined, suggestedParams: params, }, { from: 'magic user public address', to: 'XRKQBEV7FINQ66SYAFY33UYHOC4GRAICWI3V6V2TXLCQMPJBGGRHLG2E74', amount: 1000000, closeRemainderTo: undefined, note: undefined, suggestedParams: params, } ] const signedTX = await magic.algorand.signGroupTransaction(txns); console.log("signedTX", signedTX); ``` ### Sign Group Transaction V2 To sign a standard Algorand blockchain Group transaction with magic user, you can call the `magic.algorand.signGroupTransactionV2()` method. ```js JavaScript icon="square-js" theme={null} import { Magic } from "magic-sdk"; import { AlgorandExtension } from "@magic-ext/algorand"; const algosdk = require('algosdk'); const magic = new Magic('YOUR_API_KEY', { extensions: { algorand: new AlgorandExtension({ rpcUrl: '' }) } }); let client = null; async function setupClient() { if( client == null){ const token = { "x-api-key": "x api key" }; const server = "algorand rpc url"; const port = ''; let algodClient = new algosdk.Algodv2(token, server, port); client = algodClient; } else { return client; } return client; } let algodClient = await setupClient(); let params = await algodClient.getTransactionParams().do(); const txns = [{ from: 'magic user public address', to: 'OFHW3Z3T2RML7J2S6KYGHPAMO6IQH76PE2HSCAIN5U5NBGXAIPBOY7DCHI', amount: 1000000, closeRemainderTo: undefined, note: undefined, suggestedParams: params, }, { from: 'magic user public address', to: 'XRKQBEV7FINQ66SYAFY33UYHOC4GRAICWI3V6V2TXLCQMPJBGGRHLG2E74', amount: 1000000, closeRemainderTo: undefined, note: undefined, suggestedParams: params, } ] const signedTX = await magic.algorand.signGroupTransaction(txns); console.log("signedTX", signedTX); ``` ## Resources * [Algorand Developer Portal](https://developer.algorand.org) # Aptos Source: https://docs.magic.link/embedded-wallets/blockchains/non-evm/aptos ## Installation Magic interacts with the [Aptos](https://aptoslabs.com/) blockchain via Magic's extension NPM package [`@magic-ext/aptos`](https://www.npmjs.com/package/@magic-ext/aptos) (beta version 0.5.0). Additionally, the Aptos extension lets you interact with the blockchain using methods from [Aptos' TypeScript SDK](https://aptos.dev/sdks/ts-sdk/index) and/or [@aptos-labs/wallet-adapter-core](https://github.com/aptos-labs/aptos-wallet-adapter). **NOTE** This extension requires @magic-ext/aptos version ^0.5.0 , and magic-sdk version ^17.1.6 (latest preferred) To get started, install the following dependencies for your project: ```bash NPM icon="npm" theme={null} npm install @aptos-labs/wallet-adapter-core @magic-ext/aptos magic-sdk ``` ```bash Yarn icon="yarn" theme={null} yarn add @aptos-labs/wallet-adapter-core @magic-ext/aptos magic-sdk ``` ## Initialization The Magic class is the entry-point to the Magic SDK. It must be instantiated with a Magic **publishable** key. You can then use the SDK object to initialize an AptosWallet object that meets the [Aptos Wallet standard](https://aptos.dev/standards/wallets/). You can use any [supported login method](/embedded-wallets/authentication/overview) supported by Magic SDK in the "connect" function. ```js JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { AptosExtension } from '@magic-ext/aptos'; const magic = new Magic('YOUR_API_KEY', { extensions: [ new AptosExtension({ nodeUrl: 'https://fullnode.testnet.aptoslabs.com' }), ], }); const magicAptosWallet = new MagicAptosWallet(magic, { connect: async () => { await magic.auth.loginWithMagicLink({ email }); const accountInfo = await magic.aptos.getAccountInfo(); return accountInfo; } }); ``` **WARNING** The **nodeUrl** you specify will determine whether you are using the **mainnet** or **testnet** for Aptos. ## Common Methods ### Send Transaction #### Getting the Aptos Account The first step will be getting the logged in user's Aptos account address. Follow any of the login guides on the [Authentication tab](/embedded-wallets/authentication/overview) to get started. You can check if a user is logged in with the following code: ```javascript JavaScript icon="square-js" theme={null} const isLoggedIn = await magic.user.isLoggedIn(); ``` Once the user is logged in, you can get their Aptos account address by running: ```javascript JavaScript icon="square-js" theme={null} const { address } = await magic.aptos.getAccountInfo(); ``` Note: this does not return an actual AptosAccount object, only the address. See [the note below](/embedded-wallets/blockchains/non-evm/aptos#sign-and-send-transaction) for more detail. Alternatively, you can use MagicAptosWallet's `account()` method: ```javascript JavaScript icon="square-js" theme={null} const { address } = await magicAptosAccount.account(); ``` #### Funding an Account with the Faucet Next up for our testing, you'll need to fund the account using the Faucet. This code snippet is taken from the [Aptos Tutorial](https://aptos.dev/tutorials/your-first-transaction#step-43-creating-blockchain-accounts) ```javascript JavaScript icon="square-js" theme={null} const DEVNET_NODE_URL = 'https://fullnode.testnet.aptoslabs.com'; const DEVNET_FAUCET_URL = 'https://faucet.testnet.aptoslabs.com'; const faucetClient = new FaucetClient(DEVNET_NODE_URL, DEVNET_FAUCET_URL); ⁠await faucetClient.fundAccount(address, 100_000_000); ``` ### Sign and Send Transaction Note that the Magic Aptos extension follows the method names and conventions of [Aptos' TypeScript SDK](https://aptos.dev/sdks/ts-sdk/index). If you are using MagicAptosWallet, you can skip the rest of this section and build your application using the [Aptos Wallet Standard](https://aptos.dev/standards/wallets/). You can also find example apps by Aptos [here](https://github.com/aptos-labs/aptos-wallet-adapter/tree/main/apps/nextjs-example) (MagicAptosWallet is not included in this useWallet, however, due to the flexibility Magic SDK provides - and the complexity of setting it up does not neatly fit into this example's React hook). **WARNING** The Beta version of the Magic Aptos Extension only supports Raw transactions. Future versions will include full support for the following: - CoinClient - TokenClient⁠ - AptosAccount In the meantime, you can find the equivalent RawTransactions in the Aptos source code for any of the object methods above. See the bottom of this page for a detailed example. ```javascript JavaScript icon="square-js" theme={null} // Set the input variables const MAGIC_WALLET_ADDRESS = '0x906fd65afe31b7237cd4d7c4073d8bf76c61b6a24ec64dd26f0c16de5c2444d5' const SAMPLE_RAW_TRANSACTION = { function: "0x1::coin::transfer", type_arguments: ["0x1::aptos_coin::AptosCoin"], arguments: [MAGIC_WALLET_ADDRESS, 1000] } ⁠ // Initialize the AptosClient and transaction const client = new AptosClient(DEVNET_NODE_URL); const rawTransaction = await client.generateTransaction(address, SAMPLE_RAW_TRANSACTION) // Sign the transaction using Magic SDK, then send const signedTransaction = await magic.aptos.signTransaction(rawTransaction) ⁠const transaction = await client.submitTransaction(signedTransaction) const result = await client.waitForTransactionWithResult(transaction.hash, { checkSuccess: true }) ``` ### Get Balance ```javascript JavaScript icon="square-js" theme={null} const address = await magic.aptos.getAccountInfo(); const client = new AptosClient(DEVNET_NODE_URL); const coinClient = new CoinClient(client); const balance = await coinClient.checkBalance(address); console.log(balance) ``` ## Resources * [Aptos Wallet Adapter](https://github.com/magiclabs/aptos-wallet-adapter) * [Magic Aptos Example](https://github.com/magiclabs/example-aptos) # Avalanche Source: https://docs.magic.link/embedded-wallets/blockchains/non-evm/avalanche ## Installation Magic interacts with the [Avalanche](https://www.avalabs.org/) blockchain via Magic's extension NPM package [`@magic-ext/avalanche`](https://www.npmjs.com/package/@magic-ext/avalanche). The Avalanche extension also lets you interact with the blockchain using methods from [Avalanche's JavaScript SDK](https://github.com/ava-labs/avalanche.js). **NOTE** You can skip straight to our github example directly: [**Avalanche Example**](https://go.magic.link/example-avalanche) ```bash NPM icon="npm" theme={null} npm install --save @magic-ext/avalanche ``` ```bash Yarn icon="yarn" theme={null} yarn add @magic-ext/avalanche ``` ## Initialization *Note: This is for X-Chain implementation.* ```js JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { AvalancheExtension } from '@magic-ext/avalanche'; const magic = new Magic('YOUR API KEY', { extensions: { xchain: new AvalancheExtension({ rpcUrl: 'Avalanche node url', chainId: 'Avalanche chain id', networkId: 4, // Avalanche networkId }), }, }); ``` ## Common Methods **Get Test AVAX** Before you can send transaction on the Avalanche blockchain, you'll need to acquire some test AVAX (Avalanche's native cryptocurrency for test network). 1. Go to our [**Avalanche Example**](https://go.magic.link/example-avalanche) 2. Login with your email address 3. Copy your Avalanche public address 4. Go to the [**Avalanche Faucet**](https://faucet.avax.network/) 5. Paste your copied Avalanche public address in the text input 6. You can receive 10000000 nAVAX 7. Now you can use your test AVAX in our [**example app**](https://go.magic.link/example-avalanche) ### Send Transaction Note that the Magic Avalanche extension follows the method names and conventions by [**Avalanche's JavaScript SDK**](https://www.npmjs.com/package/@magic-ext/avalanche). To send a standard Avalanche blockchain transaction, you can call the `magic.xchain.signTransaction()` method. ```js JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { AvalancheExtension } from '@magic-ext/avalanche'; import { Avalanche, BinTools, Buffer, BN } from 'avalanche'; const magic = new Magic('YOUR API KEY', { extensions: { xchain: new AvalancheExtension({ rpcUrl: 'Avalanche node url', chainId: 'Avalanche chain id', networkId: 4, // Avalanche networkId }), }, }); const metadata = await magic.user.getMetadata(); let myNetworkID = 4; //default is 3, we want to override that for our local network let myBlockchainID = 'X'; // The XChain blockchainID on this network let ava = new Avalanche('testapi.avax.network', 443, 'https', myNetworkID, myBlockchainID); let xchain = ava.XChain(); let assetId = 'nznftJBicce1PfWQeNEVBmDyweZZ6zcM3p78z9Hy9Hhdhfaxm'; let fromAddresses = [metadata.publicAddress]; let toAddresses = ['X-everest1zr334udmau3xruusmwnyng3hug38errx83h6xq']; let sendAmount = 1000000; const signedTx = await magic.xchain.signTransaction(sendAmount, assetId, toAddresses, fromAddresses, toAddresses); console.log('signedTX', signedTx); let txid = await xchain.issueTx(signedTx); console.log('send transaction', txid); ``` ## Resources * [Avalanche Developer Academy](https://academy.avax.network/?utm_source=avalanche\&utm_medium=website\&utm_content=navbar) # Bitcoin Source: https://docs.magic.link/embedded-wallets/blockchains/non-evm/bitcoin ## Installation Magic interacts with the Bitcoin blockchain via Magic's extension NPM package [`@magic-ext/bitcoin`](https://www.npmjs.com/package/@magic-ext/bitcoin). The Bitcoin extension also lets you interact with the blockchain using methods from [Bitcoinjs-lib](https://github.com/bitcoinjs/bitcoinjs-lib). **NOTE** You can skip straight to our kitchen sink example directly: [**Bitcoin Example**](https://go.magic.link/example-bitcoin) ```bash NPM icon="npm" theme={null} npm install --save @magic-ext/bitcoin ``` ```bash Yarn icon="yarn" theme={null} yarn add @magic-ext/bitcoin ``` ## Initialization ```js JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { BitcoinExtension } from "@magic-ext/bitcoin"; const magic = new Magic('YOUR_API_KEY', { extensions: [ new BitcoinExtension({ rpcUrl: 'BTC_RPC_NODE_URL', network: 'testnet' // testnet or mainnet }), ], }); ``` ## Common Methods ### Sign Transaction To send a standard Bitcoin blockchain transaction, you can call the `magic.bitcoin.signTransaction` method to get the signature and raw transaction then send to blockchain by yourself. ```javascript JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { BitcoinExtension } from "@magic-ext/bitcoin"; import * as bitcoin from 'bitcoinjs-lib' const magic = new Magic('YOUR_API_KEY', { extensions: [ new BitcoinExtension({ rpcUrl: 'BTC_RPC_NODE_URL', network: 'testnet' // testnet or mainnet }), ], }); const TESTNET = bitcoin.networks.testnet; const tx = new bitcoin.TransactionBuilder(TESTNET); tx.addInput('fde789dad13b52e33229baed29b11d3e6f6dd306eb159865957dce13219bf85c', 0); tx.addOutput('mfkv2a593E1TfDVFmf1szjAkyihLowyBaT', 80000); const txHex = tx.buildIncomplete().toHex(); const signedTransactionHex = await magic.bitcoin.signTransaction(txHex, 0); console.log("signed transaction", signedTransactionHex); ``` ## Resources * [Bitcoin Developer Site](https://developer.bitcoin.org) # Cosmos Source: https://docs.magic.link/embedded-wallets/blockchains/non-evm/cosmos ## Installation Magic interacts with the [Cosmos](https://cosmos.network/) blockchain via Magic's extension NPM package [`@magic-ext/cosmos`](https://www.npmjs.com/package/@magic-ext/cosmos). The Cosmos extension also lets you interact with the blockchain using methods from cosmjs. **NOTE** You can skip straight to our kitchen sink example directly: [**Cosmos Example**](https://go.magic.link/example-cosmos) ```bash NPM icon="npm" theme={null} npm install --save @magic-ext/cosmos ``` ```bash Yarn icon="yarn" theme={null} yarn add @magic-ext/cosmos ``` ## Initialization ```js JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { CosmosExtension } from '@magic-ext/cosmos'; const magic = new Magic('YOUR_API_KEY', { extensions: [ new CosmosExtension({ rpcUrl: 'cosmos rpc url', }), ], }); ``` ## Common Methods ### Sign and Send Transaction To send or sign a standard Cosmos blockchain transaction, you can call the `magic.cosmos.signAndBroadcast` method or `magic.cosmos.sign` method. ```js JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { CosmosExtension } from '@magic-ext/cosmos'; import { coins } from '@cosmjs/launchpad'; const magic = new Magic('YOUR_API_KEY', { extensions: [ new CosmosExtension({ rpcUrl: 'cosmos rpc url', }), ], }); const handlerSendTransaction = async () => { const metadata = await magic.user.getMetadata(); const message = [ { typeUrl: '/cosmos.bank.v1beta1.MsgSend', value: { fromAddress: metadata.publicAddress, toAddress: destinationAddress, amount: [ { amount: String(sendAmount), denom: 'atom', }, ], }, }, ]; const fee = { amount: [{ denom: 'uatom', amount: '500' }], gas: '200000', }; const sendTransactionResult = await magic.cosmos.signAndBroadcast(message, fee); //or const signTransactionResult = await magic.cosmos.sign(message, fee); }; ``` ### Send Tokens Using `magic.cosmos.sendTokens` function to native tokens on Cosmos blockchain. ```js JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { CosmosExtension } from '@magic-ext/cosmos'; const magic = new Magic('YOUR_API_KEY', { extensions: [ new CosmosExtension({ rpcUrl: 'cosmos rpc url', }), ], }); const result = await magic.cosmos.sendTokens('recipientAddress', 'transferAmount', 'denom', 'memo'); ``` ### Change Address Using `magic.cosmos.changeAddress` function to change the address prefix. ```js JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { CosmosExtension } from '@magic-ext/cosmos'; const magic = new Magic('YOUR_API_KEY', { extensions: [ new CosmosExtension({ rpcUrl: 'cosmos rpc url', }), ], }); const result = await magic.cosmos.changeAddress('address prefix'); ``` ## Resources * [Cosmos Developer Portal](https://tutorials.cosmos.network) # Flow Source: https://docs.magic.link/embedded-wallets/blockchains/non-evm/flow ## Overview [Flow](https://flow.com/) is an L1 blockchain featuring a distinctive multi-role architecture designed to increase throughput and efficiency without resorting to sharding. It adopts Cadence as its smart contract language and [FCL (Flow Client Library)](https://developers.flow.com/tools/clients/fcl-js) as the primary protocol for dapp, wallet, and user interactions with the chain. ## Magic Extension This section will cover how to use our previous Flow integration via a custom extension that can be used alongside the core Magic SDK. You can use this extension to access Magic functionality alongside your FCL integration, however you do not need to use the authorization function shown here. ## Installation Magic interacts with the [Flow](https://www.onflow.org/) blockchain via Magic's extension NPM package [`@magic-ext/flow`](https://www.npmjs.com/package/@magic-ext/flow). The Flow extension also lets you interact with the blockchain using methods from [Flow's JavaScript SDK](https://github.com/onflow/flow-js-sdk/tree/master/packages/fcl). To get started, install the following dependencies for your project: ```bash NPM icon="npm" theme={null} npm install @magic-ext/flow magic-sdk ``` ```bash Yarn icon="yarn" theme={null} yarn add @magic-ext/flow magic-sdk ``` ## Initialization The Magic class is the entry-point to the Magic SDK. It must be instantiated with a Magic **publishable** key. **NOTE** If this is your first time using Magic with Flow, you may need to wait up to 30 seconds after logging in before your login completes because Magic has to wait for the Flow blockchain to confirm a transaction that creates your account. ```js JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { FlowExtension } from '@magic-ext/flow'; const magic = new Magic('YOUR_API_KEY', { extensions: [ new FlowExtension({ // testnet or mainnet to connect different network rpcUrl: 'https://rest-testnet.onflow.org', network: 'testnet' }), ], }); ``` ## Login You can use `magic.flow.getAccount()` method to let users login. ```js JavaScript icon="square-js" theme={null} import * as fcl from '@onflow/fcl'; import { Magic } from 'magic-sdk'; import { FlowExtension } from '@magic-ext/flow'; const magic = new Magic('YOUR_API_KEY', { extensions: [ new FlowExtension({ rpcUrl: 'https://rest-testnet.onflow.org', network: 'testnet' // testnet or mainnet to connect different network }), ], }); const login = async () => { const account = await magic.flow.getAccount(); console.log(account) } login() ``` ## Common Methods ### Send Transaction #### Getting Test Flow Before you can send transaction on the Flow blockchain, you'll need to acquire some test Flow (Flow's native cryptocurrency for test network). 1. Go to our [**Flow Example**](https://go.magic.link/example-flow) application 2. Login with your email address 3. Copy your Flow public address 4. Go to the [**Flow Faucet**](https://testnet-faucet.onflow.org/fund-account) 5. Fill in the form and paste your copied Flow public address in the Address field 6. You can receive **1000 test Flow** 7. Now you can use your test Flow in our [**example app**](https://go.magic.link/example-flow) #### Call Extension Method Note that the Magic Flow extension follows the method names and conventions of [**Flow's JavaScript SDK**](https://github.com/onflow/flow-js-sdk/tree/master/packages/fcl). You can use the `magic.flow.authorization()` method to replace the `fcl.authenticate()`. ```js JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { FlowExtension } from '@magic-ext/flow'; import * as fcl from '@onflow/fcl'; const magic = new Magic('YOUR_API_KEY', { extensions: [ new FlowExtension({ // testnet or mainnet to connect different network rpcUrl: 'https://rest-testnet.onflow.org', network: 'testnet' }), ], }); // CONFIGURE ACCESS NODE fcl.config().put('accessNode.api', 'https://rest-testnet.onflow.org'); // CONFIGURE WALLET // replace with your own wallets configuration // Below is the local environment configuration for the dev-wallet fcl.config().put('challenge.handshake', 'http://access-001.devnet9.nodes.onflow.org:8000'); const AUTHORIZATION_FUNCTION = magic.flow.authorization; const verify = async () => { try { const getReferenceBlock = async () => { const response = await fcl.send([fcl.getBlock()]); const data = await fcl.decode(response); return data.id; }; console.log('SENDING TRANSACTION'); var response = await fcl.send([ fcl.transaction` transaction { var acct: AuthAccount prepare(acct: AuthAccount) { self.acct = acct } execute { log(self.acct.address) } } `, fcl.ref(await getReferenceBlock()), fcl.proposer(AUTHORIZATION_FUNCTION), fcl.authorizations([AUTHORIZATION_FUNCTION]), fcl.payer(AUTHORIZATION_FUNCTION), ]); console.log('TRANSACTION SENT'); console.log('TRANSACTION RESPONSE', response); console.log('WAITING FOR TRANSACTION TO BE SEALED'); var data = await fcl.tx(response).onceSealed(); console.log('TRANSACTION SEALED', data); if (data.status === 4 && data.statusCode === 0) { console.log('Congrats!!! I Think It Works'); } else { console.log(`Oh No: ${data.errorMessage}`); } } catch (error) { console.error('FAILED TRANSACTION', error); } }; ``` ## Resources * [Flow Developer Portal](https://developers.flow.com/) * [Flow Faucet](https://testnet-faucet.onflow.org/fund-account) # ICON Source: https://docs.magic.link/embedded-wallets/blockchains/non-evm/icon ## Installation Magic interacts with the [ICON](https://icon.foundation/) blockchain via Magic's extension NPM package [`@magic-ext/icon`](https://www.npmjs.com/package/@magic-ext/icon). The ICON extension also lets you interact with the blockchain using methods from [ICON's JavaScript SDK](https://www.icondev.io/icon-sdks/javascript). **NOTE** You can skip straight to our kitchen sink example directly: [**ICON Example**](https://go.magic.link/example-icon) ```bash NPM icon="npm" theme={null} npm install --save @magic-ext/icon ``` ```bash Yarn icon="yarn" theme={null} yarn add @magic-ext/icon ``` ## Initialization ```js JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { IconExtension } from '@magic-ext/icon'; const magic = new Magic('YOUR_API_KEY', { extensions: [ new IconExtension({ rpcUrl: 'ICON_RPC_NODE_URL', }), ], }); ``` ## Common Methods **Get Test ICON** Before you can send transaction on the ICON blockchain, you'll need to acquire some test ICON (ICON's native cryptocurrency for test network). 1. Go to our [**ICON Example**](https://go.magic.link/example-icon) application 2. Login with your email address 3. Copy your ICON public address 4. Go to the [**ICON Faucet**](https://icon-faucet.ibriz.ai/) 5. Paste your copied ICON public address in the text input 6. You can receive up to **100 test ICON per day** 7. Now you can use your test ICON in our [**example app**](https://go.magic.link/example-icon) ### Get User Info Using `getAccount` function to get ICON public address for current user. ```js JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { IconExtension } from '@magic-ext/icon'; const magic = new Magic('YOUR_API_KEY', { extensions: [ new IconExtension({ rpcUrl: 'ICON_RPC_NODE_URL', }), ], }); // Get user's ICON public address const publicAddress = await magic.icon.getAccount(); console.log('ICON Public Address: ', publicAddress); ``` ### Send Transaction Note that the Magic ICON extension follows the method names and conventions by [**ICON's JavaScript SDK**](https://www.icondev.io/icon-sdks/javascript). To send a standard ICON blockchain transaction, you can call the `magic.icon.sendTransaction` method. ```js JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { IconExtension } from '@magic-ext/icon'; import IconService from 'icon-sdk-js'; const { IconBuilder, IconAmount, IconConverter } = IconService; const magic = new Magic('YOUR_API_KEY', { extensions: [ new IconExtension({ rpcUrl: 'ICON_RPC_NODE_URL', }), ], }); const metadata = await magic.user.getMetadata(); const destinationAddress = 'hx19f4fc31c6e51d5facccb52e3ccbe6b7d61f409e'; const sendICXAmount = '10'; // Build a transaction const txObj = new IconBuilder.IcxTransactionBuilder() .from(metadata.publicAddress) .to(destinationAddress) .value(IconAmount.of(sendICXAmount, IconAmount.Unit.ICX).toLoop()) .stepLimit(IconConverter.toBigNumber(100000)) .nid(IconConverter.toBigNumber(3)) .nonce(IconConverter.toBigNumber(1)) .version(IconConverter.toBigNumber(3)) .timestamp(new Date().getTime() * 1000) .build(); // Send a transaction const txhash = await magic.icon.sendTransaction(txObj); console.log(`Transaction Hash: ${txhash}`); ``` ### Sign Transaction To send a standard ICON blockchain transaction, you can call the `magic.icon.signTransaction` method to get the signature and raw transaction then send to blockchain by yourself. ```js JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { IconExtension } from '@magic-ext/icon'; import IconService from 'icon-sdk-js'; const { IconBuilder, IconAmount, IconConverter } = IconService; const magic = new Magic('YOUR_API_KEY', { extensions: [ new IconExtension({ rpcUrl: 'ICON_RPC_NODE_URL', }), ], }); const metadata = await magic.user.getMetadata(); const destinationAddress = 'hx19f4fc31c6e51d5facccb52e3ccbe6b7d61f409e'; const sendICXAmount = '10'; // Build a transaction const txObj = new IconBuilder.IcxTransactionBuilder() .from(metadata.publicAddress) .to(destinationAddress) .value(IconAmount.of(sendICXAmount, IconAmount.Unit.ICX).toLoop()) .stepLimit(IconConverter.toBigNumber(100000)) .nid(IconConverter.toBigNumber(3)) .nonce(IconConverter.toBigNumber(1)) .version(IconConverter.toBigNumber(3)) .timestamp(new Date().getTime() * 1000) .build(); // Send a transaction const { signature, rawTransaction } = await magic.icon.signTransaction(txObj); console.log(`result:`, signature, rawTransaction); ``` ### Deploy Smart Contract Note that the Magic ICON extension follows the method names and conventions by [**ICON's JavaScript SDK**](https://www.icondev.io/icon-sdks/javascript). Please follow [**ICON contract deploy documentation**](https://www.icondev.io/btp-gitbook/deploy-smart-contracts-icon) to create and compile the smart contract. To deploy an ICON smart contract, you can call the `magic.icon.sendTransaction` method to send deploy contract transaction. ```js JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { IconExtension } from '@magic-ext/icon'; import IconService from 'icon-sdk-js'; const { DeployTransactionBuilder, IconConverter } = IconService; const magic = new Magic('YOUR_API_KEY', { extensions: [ new IconExtension({ rpcUrl: 'ICON_RPC_NODE_URL', }), ], }); const metadata = await magic.user.getMetadata(); // Build a transaction const txObj = new DeployTransactionBuilder() .from(metadata.publicAddress) .to('cx0000000000000000000000000000000000000000') .stepLimit(IconConverter.toBigNumber(2100000000).toString()) .nid(IconConverter.toBigNumber(3).toString()) .nonce(IconConverter.toBigNumber(1).toString()) .version(IconConverter.toBigNumber(3).toString()) .timestamp(new Date().getTime() * 1000) .contentType('application/zip') .content(`0x${compiledContractContent}`) .params({ initialSupply: IconConverter.toHex('100000000000'), decimals: IconConverter.toHex(18), name: 'StandardToken', symbol: 'ST', }) .build(); // Send transaction to deploy contract const txhash = await magic.icon.sendTransaction(txObj); console.log(`Transaction Hash: ${txhash}`); ``` ## Resources * [Icon Developer Tutorials](https://icon.community/tutorials/) # Kadena Source: https://docs.magic.link/embedded-wallets/blockchains/non-evm/kadena Integrate with Kadena blockchain using Magic's Kadena extension ## Overview Kadena is a secure and scalable proof-of-work network designed to power global financial systems. It is unique in its braided multi-chain architecture, currently consisting of 20 different chains per network. Magic interacts with the [Kadena](https://www.kadena.io/) blockchain via Magic's extension NPM package [`@magic-ext/kadena`](https://www.npmjs.com/package/@magic-ext/kadena). The Kadena extension also lets you interact with the blockchain using methods from [Kadena's Javascript SDK](https://github.com/kadena-community/kadena.js). You can skip straight to our kitchen sink example directly: [Kadena Example](https://example-kadena.vercel.app/) ## Installation ```bash NPM icon="npm" theme={null} npm install --save @magic-ext/kadena ``` ```bash Yarn icon="yarn" theme={null} yarn add @magic-ext/kadena ``` ## Initialization Developers can initialize the Kadena extension by providing the following config: * `rpcUrl` - endpoint for the Kadena node * `chainId` - chain ID for the Kadena network (0 - 19) * `networkId` - network ID for the Kadena network * `createAccountsOnChain` - this field specifies whether or not Magic will submit a `create-account` transaction as part of the new user sign up flow. Note that due to the on-chain transaction, enabling this will result in signups that take up to 30 seconds. ```js Testnet icon="square-js" theme={null} import { Magic } from "magic-sdk"; import { KadenaExtension } from "@magic-ext/kadena"; export const chainId = "0"; export const networkId = "testnet04"; export const rpcUrl = `https://api.testnet.chainweb.com/chainweb/0.0/${networkId}/chain/${chainId}/pact`; const magic = new Magic('YOUR_API_KEY', { extensions: [ new KadenaExtension({ rpcUrl, chainId, networkId, createAccountsOnChain: true, }), ], }); ``` ```js Mainnet icon="square-js" theme={null} import { Magic } from "magic-sdk"; import { KadenaExtension } from "@magic-ext/kadena"; export const chainId = "0"; export const networkId = "mainnet01"; export const rpcUrl = `https://api.chainweb.com/chainweb/0.0/${networkId}/chain/${chainId}/pact`; const magic = new Magic('YOUR_API_KEY', { extensions: [ new KadenaExtension({ rpcUrl, chainId, networkId, createAccountsOnChain: true, }), ], }); ``` ## Common Methods ### SpireKey Login Magic supports Kadena's SpireKey library, allowing users to login with just a passkey. Visit the [Kadena docs](https://www.kadena.io/spirekey) to learn more about SpireKey. ```js JavaScript icon="square-js" theme={null} const account = magic.kadena.loginWithSpireKey(); ``` ### Get User Info Returns information about the authenticated user, such as `accountName`, `publicKey`, `loginType` ('email\_otp', 'sms', 'spirekey', etc), `isMfaEnabled`, `email`, `phoneNumber`, and `spireKeyInfo`. ```js JavaScript icon="square-js" theme={null} const userInfo = magic.kadena.getUserInfo(); ``` ### Get Test KDA Tokens Before you can send a transaction on the Kadena blockchain, you'll need to acquire some test KDA. 1. Go to our [Kadena Example](https://codesandbox.io/p/sandbox/github/magiclabs/example-kadena) application 2. Login with your email address 3. Copy your Kadena account 4. Go to the [Kadena Faucet](https://tools.kadena.io/faucet/existing) 5. Paste your copied Kadena public address in the text input 6. Now you can use your test KDA in our [example app](https://example-kadena.vercel.app/) ### Sign Transaction (with SpireKey user) To sign a Kadena transaction for a SpireKey user, you can call the `magic.kadena.signTransactionWithSpireKey()` method. ```js JavaScript icon="square-js" theme={null} import { addSignatures, createClient, Pact } from "@kadena/client"; import { PactNumber } from "@kadena/pactjs"; export const chainId = "0"; export const networkId = "testnet04"; export const rpcUrl = `https://api.testnet.chainweb.com/chainweb/0.0/${networkId}/chain/${chainId}/pact`; const handleSendTransaction = async () => { const userInfo = await magic.kadena.getInfo(); const kadenaClient = createClient(rpcUrl); const fromAccount = userInfo.accountName; const senderPublicKey = userInfo.publicKey; const toAccount = "k:..."; const receiverPublicKey = toAccount.substring(2); const amount = new PactNumber(sendAmount).toPactDecimal(); const transaction = Pact.builder .execution( (Pact.modules as any).coin.transfer(fromAccount, toAccount, amount) ) .addSigner( { pubKey: senderPublicKey, scheme: "WebAuthn", }, (withCapability: any) => [ withCapability("coin.GAS"), withCapability("coin.TRANSFER", fromAccount, toAccount, amount), ] ) .addData("receiverKeyset", { keys: [receiverPubKey], pred: "keys-all", }) .setMeta({ chainId, senderAccount: fromAccount }) .setNetworkId(networkId) .createTransaction(); try { const { transactions } = await magic.kadena.signTransactionWithSpireKey( transaction ); const signedTx = transactions[0]; const transactionDescriptor = await kadenaClient.submit(signedTx); const response = await kadenaClient.listen(transactionDescriptor); if (response.result.status === "failure") { console.error(response.result.error); } else { console.log(response.result); } } catch (error) { console.error("Failed to send transaction", error); } }; ``` ### Sign Transaction (with non-SpireKey user) To sign a standard Kadena transaction, you can call the `magic.kadena.signTransaction()` method. ```js JavaScript icon="square-js" theme={null} import { addSignatures, createClient, Pact } from "@kadena/client"; import { PactNumber } from "@kadena/pactjs"; export const chainId = "0"; export const networkId = "testnet04"; export const rpcUrl = `https://api.testnet.chainweb.com/chainweb/0.0/${networkId}/chain/${chainId}/pact`; const handleSendTransaction = async () => { const userInfo = await magic.kadena.getInfo(); const kadenaClient = createClient(rpcUrl); const fromAccount = userInfo.accountName; const senderPublicKey = userInfo.publicKey; const toAccount = "k:..."; const receiverPublicKey = toAccount.substring(2); const amount = new PactNumber(sendAmount).toPactDecimal(); const transaction = Pact.builder .execution( (Pact.modules as any).coin.transfer(fromAccount, toAccount, amount) ) .addSigner(senderPubKey, (withCapability: any) => [ withCapability("coin.GAS"), withCapability("coin.TRANSFER", fromAccount, toAccount, amount), ]) .addData("receiverKeyset", { keys: [receiverPubKey], pred: "keys-all", }) .setMeta({ chainId, senderAccount: fromAccount }) .setNetworkId(networkId) .createTransaction(); try { const signature = await magic.kadena.signTransaction(transaction.hash); const signedTx = addSignatures(transaction, signature); const transactionDescriptor = await kadenaClient.submit(signedTx); const response = await kadenaClient.listen(transactionDescriptor); if (response.result.status === "failure") { console.error(response.result.error); } else { console.log(response.result); } } catch (error) { console.error("Failed to send transaction", error); } }; ``` ## Resources * [Kadena Docs](https://docs.kadena.io/) * [Kadena Block Explorer](https://explorer.chainweb.com/testnet) * [Kadena Testnet Faucet](https://tools.kadena.io/faucet/new) # Near Source: https://docs.magic.link/embedded-wallets/blockchains/non-evm/near ## Installation Magic interacts with the Near blockchain via Magic's extension NPM package [`@magic-ext/near`](https://www.npmjs.com/package/@magic-ext/near). The Near extension also lets you interact with the blockchain using methods from [near-api-js](https://www.npmjs.com/package/near-api-js). **NOTE** You can skip straight to our Near Guide directly: [**Near Guide**](https://magic.link/posts/near) ```bash NPM icon="npm" theme={null} npm install --save @magic-ext/near ``` ```bash Yarn icon="yarn" theme={null} yarn add @magic-ext/near ``` ## Initialization ```js JavaScript icon="square-js" theme={null} import { Magic } from "magic-sdk"; import { NearExtension } from "@magic-ext/near"; const magic = new Magic('YOUR_API_KEY', { extensions: [ new NearExtension({ rpcUrl: '', }), ]}); ``` ## Common Methods ### Sign Transaction To sign a standard Near blockchain transaction, you can call the `magic.near.signTransaction` method to get the signature and raw transaction then send to blockchain by yourself. ```javascript JavaScript icon="square-js" theme={null} import { Magic } from "magic-sdk"; import { NearExtension } from "@magic-ext/near"; import { transactions, utils } from 'near-api-js' const publicKeyString = await magic.near.getPublicKey(); const publicKey = utils.PublicKey.fromString(publicKeyString); const actions = [ transactions.transfer(sendAmount) ]; const transaction = transactions.createTransaction(publicAddress, publicKey, destinationAddress, 0, actions, '9av2U6cova7LZPA9NPij6CTUrpBbgPG6'); const rawTransaction = transaction.encode(); const result = await magic.near.signTransaction({rawTransaction, networkID: 'testnet'}); const signedTransaction = transactions.SignedTransaction.decode(Buffer.from(result.encodedSignedTransaction)); console.log('signedTransaction', signedTransaction) ``` ## Resources * [Near Developer Guide](https://near.org/blog/the-beginners-guide-to-the-near-blockchain) # Polkadot Source: https://docs.magic.link/embedded-wallets/blockchains/non-evm/polkadot ## Installation Magic interacts with the [Polkadot](https://polkadot.network/) blockchain via Magic's extension NPM package [`@magic-ext/polkadot`](https://www.npmjs.com/package/@magic-ext/polkadot). The Polkadot extension also lets you interact with the blockchain using methods from polkadot-js. **NOTE** You can skip straight to our kitchen sink example directly: [**Polkadot Example**](https://go.magic.link/example-polkadot) ```bash NPM icon="npm" theme={null} npm install --save @magic-ext/polkadot ``` ```bash Yarn icon="yarn" theme={null} yarn add @magic-ext/polkadot ``` ## Initialization ```js JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { PolkadotExtension } from '@magic-ext/polkadot'; const magic = new Magic('YOUR_API_KEY', { extensions: [ new PolkadotExtension({ rpcUrl: 'polkadot rpc url', }), ], }); ``` ## Common Methods ### Get User Info Using `getAccount` function to get Polkadot public address for current user. ```js JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { PolkadotExtension } from '@magic-ext/polkadot'; const magic = new Magic('YOUR_API_KEY', { extensions: [ new PolkadotExtension({ rpcUrl: 'polkadot rpc url', }), ], }); // Get user's Polkadot public address const publicAddress = await magic.polkadot.getAccount(); console.log('Polkadot Public Address: ', publicAddress); ``` ### Send Transaction To send a standard Polkadot blockchain transaction, you can call the `magic.polkadot.sendTransaction` method. ```js JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { PolkadotExtension } from '@magic-ext/polkadot'; const magic = new Magic('YOUR_API_KEY', { extensions: [ new PolkadotExtension({ rpcUrl: 'polkadot rpc url', }), ], }); const tx = await magic.polkadot.sendTransaction( // to address '5H3pELHbg9skXE2HfLqP23UPgrgu2Juj55CH6sdDGWc2HKNs', 1000000000000000, // amount ); console.log('transaction hash', tx); ``` ### Smart Contract To call a Polkadot blockchain contract as transaction, you can call the `magic.polkadot.contractCall` method. ```js JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { PolkadotExtension } from '@magic-ext/polkadot'; const magic = new Magic('YOUR_API_KEY', { extensions: [ new PolkadotExtension({ rpcUrl: 'polkadot rpc url', }), ], }); const api = await ApiPromise.create({ provider: new WsProvider('polkadot rpc url'), }); await api.isReady; const abi = new Abi(api.registry, contractABI); const data = abi.messages.flip(); const tx = await magic.polkadot.contractCall( // contract address '5C52CfgkwANdFuN3VgPSprQwNWKfkLWMHJbMRzp12h4YarCa', 0, // value 1000000, // max gas data, // contract data ); console.log('transaction hash', tx); ``` #### Deploy ink contract on substrate Please follow [substrate documention](https://substrate.dev/substrate-contracts-workshop/#/0/introduction) to create and deploy contract on substrate. ## Resources * [Polkadot Block Explorer](https://explorer.polkascan.io/) * [Polkadot Blockchain Academy](https://polkadot.network/development/blockchain-academy/) # Solana Source: https://docs.magic.link/embedded-wallets/blockchains/non-evm/solana ## Installation To get started, install the following dependencies for your project: ```bash NPM icon="npm" theme={null} npm install @magic-ext/solana @solana/web3.js magic-sdk ``` ```bash Yarn icon="yarn" theme={null} yarn add @magic-ext/solana @solana/web3.js magic-sdk ``` ## Initialization The Magic class is the entry-point to the Magic SDK. It must be instantiated with a Magic **publishable** key. **ES Modules/TypeScript** ```javascript JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { SolanaExtension } from '@magic-ext/solana'; const magic = new Magic('YOUR_API_KEY', { extensions: [ new SolanaExtension({ rpcUrl: 'SOLANA_RPC_NODE_URL', }), ], }); ``` **CDN** ```javascript JavaScript icon="square-js" theme={null} ``` ## Common Methods Note that the Magic Solana extension follows the method names and conventions by [**Solana's JavaScript SDK**](https://solana.com/docs/clients/javascript). ### Sign and Send Transaction **ES Modules/TypeScript** To sign a standard Solana blockchain transaction, call the `magic.solana.signTransaction` method. Note that you must sign with the Magic SDK method but send the transaction using the `@solana/web3.js` method `connection.sendRawTransaction`. ```javascript JavaScript icon="square-js" theme={null} import * as web3 from "@solana/web3.js"; // Ensure you have Magic initialized with the Solana extension // Ensure that user is already authenticated const connection = new web3.Connection(clusterApiUrl("devnet")) const metadata = await magic.user.getMetadata(); const userPublicKey = new web3.PublicKey(metadata.publicAddress); const recipientPubkey = new web3.Keypair.generate().publicKey; const blockhash = await connection?.getLatestBlockhash(); if (!blockhash) return; const transaction = new web3.Transaction({ ...blockhash, feePayer: userPublicKey, }).add( web3.SystemProgram.transfer({ fromPubkey: userPublicKey, toPubkey: recipientPubkey, lamports: web3.LAMPORTS_PER_SOL * 0.01, }) ); const signedTransaction = await magic?.solana.signTransaction( transaction, { requireAllSignatures: false, verifySignatures: true, } ); const signature = await connection?.sendRawTransaction( Buffer.from(signedTransaction?.rawTransaction as string, "base64") ); console.log(signature); ``` ### Sign Message **ES Modules/TypeScript** ```javascript JavaScript icon="square-js" theme={null} // Ensure you have Magic initialized with the Solana extension // Ensure that user is already authenticated const signedMessage = await magic?.solana.signMessage("Hello World") console.log(signedMessage) ``` ## Resources * [Magic Solana SDK](https://solana.com/docs/clients/javascript) * [Solana JavaScript Web3 example](https://codesandbox.io/s/github/MagicLabs/example-solana) * [Solana Faucet](https://solfaucet.com/) * [How to use email to create wallets on Solana](https://magic.link/posts/email-otp-with-solana-guide) # Tezos Source: https://docs.magic.link/embedded-wallets/blockchains/non-evm/tezos The Tezos extension also lets you interact with the blockchain using methods from Tezos's [Taquito](https://tezostaquito.io/) SDK. ## Installation Magic interacts with the [Tezos](https://tezos.com/) blockchain via Magic's extension NPM package [`@magic-ext/taquito`](https://www.npmjs.com/package/@magic-ext/taquito). **NOTE** You can skip straight to our kitchen sink example directly: [**Tezos Taquito Example**](https://codesandbox.io/s/github/magiclabs/example-taquito) ```bash NPM icon="npm" theme={null} npm install --save @magic-ext/taquito ``` ```bash Yarn icon="yarn" theme={null} yarn add @magic-ext/taquito ``` ## Initialization ```javascript JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { TaquitoExtension } from '@magic-ext/taquito'; const magic = new Magic('YOUR_API_KEY', { extensions: [ new TaquitoExtension({ rpcUrl: 'TEZOS_RPC_NODE_URL', }), ], }); ``` ## Common Methods ### Send Transaction Note that the Magic Taquito extension follows the method names and conventions by [**Taquito**](https://tezostaquito.io/). To send a standard Tezos blockchain transaction, you can call the `magic.taquito.createMagicSigner` method to create a signer to inject to Tezos client. ```javascript JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { TaquitoExtension } from '@magic-ext/taquito'; import { TezosToolkit } from '@taquito/taquito'; const magic = new Magic('YOUR_API_KEY', { extensions: [ new TaquitoExtension({ rpcUrl: 'https://rpc.oxfordnet.teztnets.com', }), ], }); const Tezos = new TezosToolkit('https://rpc.oxfordnet.teztnets.com'); const magicSigner = await magic.taquito.createMagicSigner(); Tezos.setProvider({ signer: magicSigner }); const op = await Tezos.wallet.transfer({ to: destinationAddress, amount: sendXTZAmount }); const result = await op.confirmation(); console.log('result', result) ``` ### Resources * [Tezos Block Explorer](https://tzstats.com/) # Zilliqa Source: https://docs.magic.link/embedded-wallets/blockchains/non-evm/zilliqa ## Installation Magic interacts with the [Zilliqa](https://www.zilliqa.com/) blockchain via Magic's extension NPM package [`@magic-ext/zilliqa`](https://www.npmjs.com/package/@magic-ext/zilliqa). The Zilliqa extension also lets you interact with the blockchain using methods from [Zilliqa's JavaScript SDK](https://github.com/Zilliqa/Zilliqa-JavaScript-Library). **NOTE** You can skip straight to our kitchen sink example directly: [**Zilliqa Example**](https://codesandbox.io/s/github/MagicLabs/example-zilliqa) ```bash NPM icon="npm" theme={null} npm install --save @magic-ext/zilliqa ``` ```bash Yarn icon="yarn" theme={null} yarn add @magic-ext/zilliqa ``` ## Initialization ```js JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { ZilliqaExtension } from '@magic-ext/zilliqa'; const magic = new Magic('YOUR_API_KEY', { extensions: [ new ZilliqaExtension({ rpcUrl: 'Zilliqa_RPC_NODE_URL', }), ], }); ``` ### CDN ```javascript JavaScript icon="square-js" theme={null} ``` ## Common Methods **Getting Test ZIL** Before you can send transaction on the Zilliqa blockchain, you'll need to acquire some test ZIL (Zilliqa's native cryptocurrency for test network). 1. Go to our [**Zilliqa Example**](https://codesandbox.io/s/github/MagicLabs/example-zilliqa) application 2. Login with your email address 3. Copy your **Zilliqa** public address 4. Go to the [**ZIL Faucet**](https://dev-wallet.zilliqa.com/faucet) 5. Paste your copied Zilliqa public address in the text input 6. You can receive **300 test ZIL** 7. Now you can use your test ZIL in our [**example app**](https://codesandbox.io/s/github/MagicLabs/example-zilliqa) ### Get User Wallet Using `getWallet` function to get a Zilliqa wallet for the current user. ```js JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { ZilliqaExtension } from '@magic-ext/zilliqa'; const magic = new Magic('YOUR_API_KEY', { extensions: [ new ZilliqaExtension({ rpcUrl: 'Zilliqa_RPC_NODE_URL', }), ], }); // Get user's Zilliqa wallet info const wallet = await magic.zilliqa.getWallet(); console.log('Zilliqa wallet: ', wallet); ``` ### Send Transaction To send a standard Zilliqa blockchain transaction, you can call the `magic.Zilliqa.sendTransaction` method. ```js JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { ZilliqaExtension } from '@magic-ext/zilliqa'; const { BN, Long, bytes, units } = require('@zilliqa-js/util'); const magic = new Magic('YOUR_API_KEY', { extensions: [ new ZilliqaExtension({ rpcUrl: 'Zilliqa_RPC_NODE_URL', }), ], }); const chainId = 333; // chainId of the developer testnet const msgVersion = 1; // current msgVersion const VERSION = bytes.pack(chainId, msgVersion); const myGasPrice = units.toQa('1000', units.Units.Li); const params = { version: VERSION, toAddr: 'zil14vut0rh7q78ydc0g7yt7e5zkfyrmmps00lk6r7', amount: new BN(units.toQa('0.5', units.Units.Zil)), gasPrice: myGasPrice, gasLimit: Long.fromNumber(1), }; const tx = await magic.zil.sendTransaction(params, false); // Send a transaction console.log('send transaction', tx); ``` ## Smart Contract ### Deploy Contract To deploy a smart contract, you can call the `magic.zilliqa.deployContract` method. ```js JavaScript icon="square-js" theme={null} import { Magic } from 'magic-sdk'; import { ZilliqaExtension } from '@magic-ext/zilliqa'; const { BN, Long, bytes, units } = require('@zilliqa-js/util'); const magic = new Magic('YOUR_API_KEY', { extensions: [ new ZilliqaExtension({ rpcUrl: 'Zilliqa_RPC_NODE_URL', }), ], }); const wallet = await magic.zilliqa.getWallet(); const address = wallet.address; const code = `scilla_version 0 (* HelloWorld contract *) import ListUtils (***************************************************) (* Associated library *) (***************************************************) library HelloWorld let not_owner_code = Int32 1 let set_hello_code = Int32 2 (***************************************************) (* The contract definition *) (***************************************************) contract HelloWorld (owner: ByStr20) field welcome_msg : String = "" transition setHello (msg : String) is_owner = builtin eq owner _sender; match is_owner with | False => e = {_eventname : "setHello()"; code : not_owner_code}; event e | True => welcome_msg := msg; e = {_eventname : "setHello()"; code : set_hello_code}; event e end end transition getHello () r <- welcome_msg; e = {_eventname: "getHello()"; msg: r}; event e end`; const init = [ // this parameter is mandatory for all init arrays { vname: '_scilla_version', type: 'Uint32', value: '0', }, { vname: 'owner', type: 'ByStr20', value: `${address}`, }, ]; const chainId = 333; // chainId of the developer testnet const msgVersion = 1; // current msgVersion const VERSION = bytes.pack(chainId, msgVersion); const myGasPrice = units.toQa('1000', units.Units.Li); const params = { version: VERSION, gasPrice: myGasPrice, gasLimit: Long.fromNumber(10000), }; const result = await magic.zil.deployContract(init, code, params, 33, 1000, false); console.log('deploy contract', result); ``` ## Resources * [Zilliqa Block Explorer](https://viewblock.io/zilliqa) # Blockchains Source: https://docs.magic.link/embedded-wallets/blockchains/overview The Magic SDK offers a streamlined way to effortlessly integrate 30+ blockchain networks into your application. Magic aims to keep up to date with wallet standards on these chains and ensure compatibility with their respective web3 wallet libraries. ## Ethereum ## EVM Chains } href="/embedded-wallets/blockchains/evm/arbitrum" /> } href="/embedded-wallets/blockchains/evm/base" /> } href="/embedded-wallets/blockchains/evm/berachain" /> } href="/embedded-wallets/blockchains/evm/binance-smart-chain" /> } href="/embedded-wallets/blockchains/evm/celo" /> } href="/embedded-wallets/blockchains/evm/chiliz" /> } href="/embedded-wallets/blockchains/evm/cronos" /> } href="/embedded-wallets/blockchains/evm/etherlink" /> } href="/embedded-wallets/blockchains/evm/fantom" /> } href="/embedded-wallets/blockchains/evm/flare" /> } href="/embedded-wallets/blockchains/evm/harmony" /> } href="/embedded-wallets/blockchains/evm/hedera" /> } href="/embedded-wallets/blockchains/evm/horizen-eon" /> } href="/embedded-wallets/blockchains/evm/loopring" /> } href="/embedded-wallets/blockchains/evm/moonbeam" /> } href="/embedded-wallets/blockchains/evm/optimism" /> } href="/embedded-wallets/blockchains/evm/plume" /> } href="/embedded-wallets/blockchains/evm/polygon" /> } href="/embedded-wallets/blockchains/evm/rari-chain" /> } href="/embedded-wallets/blockchains/evm/sei" /> } href="/embedded-wallets/blockchains/evm/soneium" /> } href="/embedded-wallets/blockchains/evm/stability" /> } href="/embedded-wallets/blockchains/evm/xdc-network" /> } href="/embedded-wallets/blockchains/evm/zetachain" /> } href="/embedded-wallets/blockchains/evm/zksync" /> ## Non-EVM Chains } href="/embedded-wallets/blockchains/non-evm/algorand" /> } href="/embedded-wallets/blockchains/non-evm/aptos" /> } href="/embedded-wallets/blockchains/non-evm/avalanche" /> } href="/embedded-wallets/blockchains/non-evm/bitcoin" /> } href="/embedded-wallets/blockchains/non-evm/cosmos" /> } href="/embedded-wallets/blockchains/non-evm/flow" /> } href="/embedded-wallets/blockchains/non-evm/icon" /> } href="/embedded-wallets/blockchains/non-evm/kadena" /> } href="/embedded-wallets/blockchains/non-evm/near" /> } href="/embedded-wallets/blockchains/non-evm/polkadot" /> } href="/embedded-wallets/blockchains/non-evm/solana" /> } href="/embedded-wallets/blockchains/non-evm/tezos" /> } href="/embedded-wallets/blockchains/non-evm/zilliqa" /> # Introduction Source: https://docs.magic.link/embedded-wallets/introduction Magic's Embedded Wallets provide seamless Web3 onboarding with non-custodial security, passwordless authentication, and multi-chain support. ## What are Embedded Wallets? Magic's Embedded Wallets are non-custodial wallets that are seamlessly integrated directly into your application. When users authenticate through any of Magic's passwordless login methods, they're automatically provisioned with a secure wallet that enables seamless Web3 onboarding experiences. ### Key Benefits Users maintain full control over their private keys through Magic's patent pending TEE Key Management System (TKMS) No passwords to manage - users authenticate via email OTP, SMS, social logins, and more Works with 30+ blockchain networks including Ethereum, Polygon, Solana, Bitcoin, and more Users get a wallet immediately upon authentication, eliminating complex setup processes ## How It Works User logs in using any supported method (email, SMS, social, etc.) Magic instantly creates a non-custodial wallet for the user Private keys are secured using Magic's patent pending TKMS technology User can immediately interact with dApps, sign transactions, and manage assets Magic's Embedded Wallets work across web, mobile (iOS/Android), Flutter, and React Native applications. Check out our [SDK documentation](/embedded-wallets/sdk/overview) for platform-specific integration guides. ## Core Features ### Authentication Options Email OTP, SMS, and more Google, Facebook, Twitter, Apple, Discord, GitHub, LinkedIn, and more Advanced MFA options including device registration Authenticate users with their Farcaster account ### Wallet Capabilities Built-in UI for secure transaction signing with gas fee estimation Sign arbitrary messages and data for authentication Integrated fiat-to-crypto conversion Export private keys for backup or migration ## Resources # Embedded Wallet CLI Source: https://docs.magic.link/embedded-wallets/quickstart/cli The fastest way to get started with Embedded Wallet is to bootstrap your project using `make-magic`, an easy-to-use CLI tool that generates a fully working Next.js application with Magic built in. ## Run `make-magic` To get started, use the following command in your preferred shell: ```bash Bash theme={null} npx make-magic ``` The tool will guide you through setting up your project with a series of interactive prompts, starting with your project's name. Alternatively, you can use the command from your [Magic Dashboard](https://dashboard.magic.link) after you create a new project. This will include pre-configured flags to skip most of the prompts. ## Configure Your Project After entering a name, you’ll be asked to choose between two starting configurations: 1. Quickstart 2. Custom Setup Select `Quickstart` for the simplest setup. This option bootstraps a Next.js project that connects to the Polygon Amoy test network. The Embedded Wallet is a white-label wallet you can use to tailor the wallet experience to your app and users. It [supports over 30 blockchains](/embedded-wallets/blockchains/overview), has a suite of enterprise features, and gives you broad control over the user authentication, onboarding, and wallet experience. If you prefer to use another wallet, network, or authentication method, you can select `Custom Setup`. The CLI tool currently supports major chains such as Ethereum, Solana, and Flow. For additional blockchain support, you can get started with our [Integration Quickstart](/embedded-wallets/quickstart/overview) and add the relevant [blockchain extension](/embedded-wallets/blockchains/overview). ## Add Your API Key After finishing your initial configuration, you'll be prompted to enter your Magic **Publishable** API Key. You can find this in your [Magic Dashboard](https://dashboard.magic.link). You can also leave this empty for now and add it to the project's `.env` file later. ## Run Your App At this point, `make-magic` will create a new directory named after your project, download the code, install dependencies, and start running automatically on port 3000 (if it's not already in use). ⁠ ⁠Next.js has built-in support for hot reloading, so most front-end code changes are applied instantly during development without the need for a server restart. You can update the UI, swap out the RPC URL in the `.env` file, and more, all without a restart. If you do need to restart the server, you can terminate the console process and execute `npm run dev`. If you provided your Publishable API Key during the interactive prompts, you'll be able to log in and send a transaction right away! Otherwise, your project will show a landing page directing you to find and add your API Key as an environment variable. The login options available depend on your project's configuration. The `Quickstart` option defaults to Email OTP. See the Embedded Wallet's documentation for the [full list of available authentication options](/embedded-wallets/authentication/overview). ## Usage Once a user is logged in, the Quickstart app shows a dashboard with 4 cards: * `UserInfoCard.tsx` - Shows the network you're connected to, your address, and your token balance. * `SendTransactionCard.tsx` - Lets you send native tokens to another address * `WalletMethodsCard.tsx` - Shows and lets you test the various user methods available to you * `SmartContract.tsx` - Input element with a button that is connected to a function that triggers a smart contract interaction. It allows you to update the stored integer in a basic storage contract. **Only available on EVM testnet templates**. ⁠⁠If you're on a test network, the Send Transaction card will have a button to get test tokens. Once you have test tokens, you can add a receiving address, an amount of tokens to send, and click Send Transaction to send tokens with your Magic wallet. Congratulations! You've successfully bootstrapped a full application and sent your first transaction. ### Customize Your App To customize the app, you can modify any of the code and restructure the project according to your needs. Magic-related components are organized in the updated file structure for better clarity and maintenance. Here are some key directories and files you might want to work with: * `src/components/magic/`: Contains all Magic-related logic, including authentication providers, wallet methods, login/logout functionality, and action cards such as `SendTransaction`. This directory centralizes components that interact with Magic, making it easier to manage authentication and wallet operations. * `src/components/ui/`: Houses reusable UI components like cards and dividers. This directory ensures that UI elements are consistently styled and easily maintainable across the application. * `src/hooks/`: Provides custom hooks for managing Magic and Web3 instances, such as `useMagic` and `useWeb3`. These hooks make Magic and Web3 instances globally available, facilitating their use throughout your components. * `src/utils/`: Contains utility functions used throughout the app, including network configuration, smart contract interactions, type definitions, and more. **Note**: The configuration for the Solana and Flow templates may vary from the EVM examples. You can swap out the RPC URL in the `.env` file under the `NEXT_PUBLIC_BLOCKCHAIN_NETWORK` variable. Replace the value of that variable with one of the following: * `polygon-amoy` * `polygon` * `ethereum-sepolia` * `ethereum` * `etherlink-testnet` * `zksync` * `zksync-sepolia` To update any of the styling go to `src/styles/globals.css`. This application uses our Embedded Wallet. The Embedded Wallet meets the widest variety of needs while still being incredibly simple to implement. In addition to the baked in [Login UI](/embedded-wallets/authentication/customization/login-ui), it has plenty of customization options, supports [social login](/embedded-wallets/authentication/login/oauth/oauth-implementation) through providers like GitHub and Discord, and more. ### Wallet Information Once you have successfully logged in, the first card on your dashboard will display your Magic wallet details. This card provides a comprehensive overview of your connected wallet, including the network you are connected to, your wallet address, and your token balance. Additionally, there is a disconnect button that allows you to disconnect your wallet from the app. ### Send Your First Transaction ⁠⁠If you're on a test network, the Send Transaction card will have a button to get test tokens. Once you have test tokens, you can add a receiving address, an amount of tokens to send, and click Send Transaction to send tokens with your Magic wallet. Congratulations! You've successfully sent your first transaction using Magic. ### Wallet Methods This card offers various user methods that provide actions for managing user authentication and wallet operations, including: * **Update Email**: Allows users to update their registered email address. This feature is particularly useful for users who logged in using the EMAIL method. * **Get ID Token**: Retrieves the user's ID token. ID tokens are essential for verifying the user's identity and can be used for authentication purposes. * **Get Metadata**: Fetches and displays metadata related to the user's Magic wallet. Metadata can include details like the public address, email (if available), and other relevant user information. * **Disconnect**: Provides an option for users to disconnect their wallet from the application. This is useful for logging out securely or switching to a different account. ### Smart contracts If you generate an app connected to EVM testnets, a component will be displayed that allows users to update the value in a basic storage smart contract. This serves as an illustration of how to connect Magic to a smart contract. The functionality is available as long as the user is connected to either Ethereum Sepolia, Polygon Amoy, or zkSync Sepolia. ## Next Steps We have a suite of resources to help developers and companies with a wide variety of use cases. Below are some ideas to get you started, but feel free to browse our documentation or reach out with specific questions. * Integrate Magic into your existing app by following the [Integration Quickstart](/embedded-wallets/quickstart/integration) * Add support for OAuth social providers like [Google](/embedded-wallets/authentication/login/oauth/social-providers/google), [GitHub](/embedded-wallets/authentication/login/oauth/social-providers/github), and [Discord](/embedded-wallets/authentication/login/oauth/social-providers/discord) * Add support for one or more of the [30+ blockchains accessible through Magic](/embedded-wallets/blockchains/overview) * Use Magic across a [variety of platforms](/embedded-wallets/sdk/overview), including Web, iOS, Android, and more * Learn more about [Magic's security framework](/home/security/product-security) and how it can make your applications more secure * Read Magic's [Whitepaper](https://magic-whitepaper-key-based-authentication-system.s3.us-west-2.amazonaws.com/Magic+Whitepaper.pdf) * View [Magic Guides](https://magic.link/guides) for comprehensive articles covering a wide range of use cases # Embedded Wallet Integration Source: https://docs.magic.link/embedded-wallets/quickstart/integration Add Embedded Wallet to a new or existing Next.js app Integrating Magic into a new or existing app is as simple as installing the SDK, initializing Magic with your Magic Publishable API Key and chosen blockchain, authenticating your users with `magic.wallet.connectWithUI()` and other common smart contract interactions like sending a transaction and checking a user's balance. The sections below go through each of these steps one at a time. If you want to jump straight into the code, check out this [GitHub Repository](https://github.com/magiclabs/example-next-quickstart). ## Install the SDK Navigate to your project directory and install the Magic SDK as a project dependency. ```bash NPM icon="npm" theme={null} npm install magic-sdk ``` ```bash Yarn icon="yarn" theme={null} yarn add magic-sdk ``` ## Get your Magic Publishable API Key Grab your Magic **Publishable** API Key from your [Magic Dashboard](https://dashboard.magic.link). If you haven't already, you'll need to sign up for a free developer account. dashboard-api-key ## Initialize Magic To get started, simply initialize an instance of Magic with your Publishable API Key and your choice of blockchain. Then initialize your chosen blockchain library, like Ethers.js, with the RPC provider in a separate file. Here's how you would initialize the Magic instance. ```typescript TypeScript icon="square-js" theme={null} const magic = new Magic(process.env.NEXT_PUBLIC_MAGIC_API_KEY, { network: { rpcUrl: "https://rpc2.sepolia.org/", chainId: 11155111, }, }) ``` The suggested approach for the Magic instance is to create a hook so Magic can be made available and used across the whole application like the one below. ```typescript TypeScript icon="square-js" theme={null} import { Magic as MagicBase } from 'magic-sdk'; import { ReactNode, createContext, useContext, useEffect, useMemo, useState } from 'react'; export type Magic = MagicBase; type MagicContextType = { magic: Magic | null; }; const MagicContext = createContext({ magic: null, }); export const useMagic = () => useContext(MagicContext); const MagicProvider = ({ children }: { children: ReactNode }) => { const [magic, setMagic] = useState(null); useEffect(() => { if (process.env.NEXT_PUBLIC_MAGIC_API_KEY) { const magic = new MagicBase(process.env.NEXT_PUBLIC_MAGIC_API_KEY as string, { network: { rpcUrl: "https://rpc2.sepolia.org/", chainId: 11155111, }, }); setMagic(magic); } }, []); const value = useMemo(() => { return { magic, }; }, [magic]); return {children}; }; export default MagicProvider; ``` When you want to use the Magic instance, import the hook and destructure the required properties from it, which in this case is the Magic instance itself. ```typescript TypeScript icon="square-js" theme={null} const { magic } = useMagic(); ``` In a separate file, create a hook to initialize your web3 instance. For this quickstart we will be using the Web3.js library but you can use other web3 blockchain libraries such as Ethers.js. ```typescript TypeScript icon="square-js" theme={null} import { Web3 } from 'web3'; import { useEffect, useState } from 'react'; import { useMagic } from './MagicProvider'; const useWeb3 = () => { const { magic } = useMagic(); const [web3, setWeb3] = useState(null); useEffect(() => { if (magic) { setWeb3(new Web3((magic as any).rpcProvider)); } else { console.log('Magic is not initialized'); } }, [magic]); return web3; }; export default useWeb3; ``` Now whenever you need to use the web3 instance, import the hook into the file you need it in and call it within your component to get the web3 instance. ```typescript TypeScript icon="square-js" theme={null} const web3 = useWeb3(); ``` The above code snippets initializes Magic and web3 with a public Sepolia Testnet URL. You can point the instance to a different chain by modifying the URL and Chain ID. Magic seamlessly supports over 30+ different blockchains. ## Authenticate your users Authenticating your users is as easy as calling `magic.wallet.connectWithUI()`. This will display Magic's Login UI. Magic will handle authentication using Email OTP with no additional code needed from your app. You log your users out by calling `magic.user.logout()`. In addition to the flow provided by the Login UI, you can customize a Dedicated Wallet to use a [variety of authentication options](/embedded-wallets/authentication/overview) like SMS login, OAuth through popular social login providers, and more. ## Display the authenticated user's wallet Display the authenticated user's wallet with `magic.wallet.showUI()`. This will show the user's wallet using Magic's Widget UI. ## Get user address One thing you may want to do is retrieve the address and balance of a logged in user. To do this, call the getInfo function and set it as a variable. Then on that variable call the publicAddress property to get the user's address. ```typescript TypeScript icon="square-js" theme={null} const metadata = await magic?.user.getInfo(); const publicAddress = metadata.publicAddress; ``` ## Get user balance To get the token balance of a user, import the web3 instance and then inside an asynchronous function call the getBalance function and pass the Magic user's public address to it. Given that the Magic instance is connected to the Sepolia network, calling getBalance will return the amount of Sepolia tokens the user has. In this we will be using the web3 instance mentioned earlier. ```typescript TypeScript icon="square-js" theme={null} const web3 = useWeb3(); const balance = await web3.eth.getBalance(publicAddress); ``` ## Interact with the network Magic integrates with all popular blockchain libraries so that you don't have to change anything about how you interact with the blockchain. For example, if you're using Ethereum or other EVM chains, you can get the user's wallet address or sign and send transactions the same way you normally would using Web3.js or Ethers.js. Here is an example of sending a transaction: ```typescript TypeScript icon="square-js" theme={null} async function sendEth(amount: number, recipientAddress: string) { const metadata = await magic?.user.getInfo(); const senderAddress = metadata.publicAddress; const txnParams = { from: senderAddress, to: recipientAddress, value: web3.utils.toWei(amount, "ether"), gas: 21000, } web3.eth .sendTransaction(txnParams as any) .on("transactionHash", (txHash: string) => { console.log("Transaction hash:", txHash) }) .then((receipt: any) => { console.log("Transaction receipt:", receipt) }) } ``` ## Customize Your App This application uses our Embedded Wallet. The Embedded Wallet meets the widest variety of needs while still being incredibly simple to implement. In addition to the baked-in [Login UI](/embedded-wallets/authentication/customization/login-ui), it has plenty of customization options, supports [social login](/embedded-wallets/authentication/login/oauth/oauth-implementation) through providers like GitHub and Discord, and more. ## Next Steps We have a suite of resources to help developers and companies with a wide variety of use cases. Below are some ideas to get you started, but feel free to browse our documentation or reach out with specific questions. * Take a look at this demo's [GitHub Repository](https://github.com/magiclabs/embedded-wallet-quickstart) or [CodeSandbox](https://codesandbox.io/s/magic-embedded-wallet-quickstart-7h8wj) * Add support for OAuth social providers like [Google](/embedded-wallets/authentication/login/oauth/social-providers/google), [GitHub](/embedded-wallets/authentication/login/oauth/social-providers/github), and [Discord](/embedded-wallets/authentication/login/oauth/social-providers/discord) * Add support for one or more of the [30+ blockchains accessible through Magic](/embedded-wallets/blockchains/overview) * Use Magic across a [variety of platforms](/embedded-wallets/sdk/overview), including Web, iOS, Android, and more * Learn more about [Magic's security framework](/home/security/product-security) and how it can make your applications more secure * Read Magic's [Whitepaper](https://magic-whitepaper-key-based-authentication-system.s3.us-west-2.amazonaws.com/Magic+Whitepaper.pdf) * View [Magic Guides](https://magic.link/guides) for comprehensive articles covering a wide range of use cases # Embedded Wallet Quickstart Source: https://docs.magic.link/embedded-wallets/quickstart/overview Magic makes it easy to authenticate users and integrate them into your Web3 dApps quickly. The quickstart options below show how you can use Embedded Wallet in a Next.js app to connect to an EVM-based testnet, but you can follow a similar flow with [Magic’s mobile SDKs](/embedded-wallets/sdk/overview) or integrate with any of the [30+ blockchains supported by Magic](/embedded-wallets/blockchains/overview).