# Dynamic

## Docs

- [Create a new entry for an allowlist](https://docs.dynamic.xyz/api-reference/allowlists/create-a-new-entry-for-an-allowlist)
- [Create new allowlist for a environment](https://docs.dynamic.xyz/api-reference/allowlists/create-new-allowlist-for-a-environment)
- [Delete an allowlist](https://docs.dynamic.xyz/api-reference/allowlists/delete-an-allowlist)
- [Delete an allowlist entry](https://docs.dynamic.xyz/api-reference/allowlists/delete-an-allowlist-entry)
- [Disable the allow list](https://docs.dynamic.xyz/api-reference/allowlists/disable-the-allow-list)
- [Enable the allowlist](https://docs.dynamic.xyz/api-reference/allowlists/enable-the-allowlist)
- [Get all allowlists for a environment](https://docs.dynamic.xyz/api-reference/allowlists/get-all-allowlists-for-a-environment)
- [Get all entries for an allowlist](https://docs.dynamic.xyz/api-reference/allowlists/get-all-entries-for-an-allowlist)
- [Get allowlist by id](https://docs.dynamic.xyz/api-reference/allowlists/get-allowlist-by-id)
- [Update the outcome, scope, or name of an allowlist entry by ID](https://docs.dynamic.xyz/api-reference/allowlists/update-the-outcome-scope-or-name-of-an-allowlist-entry-by-id)
- [Get environments analyticsvisits](https://docs.dynamic.xyz/api-reference/analytics/get-environments-analyticsvisits): Fetch visit analytics
- [Get environments analyticswallets](https://docs.dynamic.xyz/api-reference/analytics/get-environments-analyticswallets): Fetch wallets breakdown
- [Disables the Sanctions API](https://docs.dynamic.xyz/api-reference/chainalysis/disables-the-sanctions-api)
- [Enable the Sanctions API](https://docs.dynamic.xyz/api-reference/chainalysis/enable-the-sanctions-api)
- [Find the Chainalysis configuration for an environment.](https://docs.dynamic.xyz/api-reference/chainalysis/find-the-chainalysis-configuration-for-an-environment)
- [Get tokens for passed chainName.](https://docs.dynamic.xyz/api-reference/chains/get-tokens-for-passed-chainname)
- [Create a new custom field for an environment](https://docs.dynamic.xyz/api-reference/custom-fields/create-a-new-custom-field-for-an-environment)
- [Delete a specific custom field by its ID](https://docs.dynamic.xyz/api-reference/custom-fields/delete-a-specific-custom-field-by-its-id)
- [Get the custom fields for an environment](https://docs.dynamic.xyz/api-reference/custom-fields/get-the-custom-fields-for-an-environment)
- [Retrieve a specific custom field by its ID](https://docs.dynamic.xyz/api-reference/custom-fields/retrieve-a-specific-custom-field-by-its-id)
- [Update an existing custom field by its ID](https://docs.dynamic.xyz/api-reference/custom-fields/update-an-existing-custom-field-by-its-id)
- [Create a new custom hostname for this environment](https://docs.dynamic.xyz/api-reference/customhostnames/create-a-new-custom-hostname-for-this-environment)
- [Delete the custom hostname for this environment](https://docs.dynamic.xyz/api-reference/customhostnames/delete-the-custom-hostname-for-this-environment)
- [Get the custom hostnames for this environemnt](https://docs.dynamic.xyz/api-reference/customhostnames/get-the-custom-hostnames-for-this-environemnt)
- [Delete a mobile deeplink URL by id](https://docs.dynamic.xyz/api-reference/deeplinkurls/delete-a-mobile-deeplink-url-by-id)
- [Get all the whitelisted mobile deeplink URLs for a project environment](https://docs.dynamic.xyz/api-reference/deeplinkurls/get-all-the-whitelisted-mobile-deeplink-urls-for-a-project-environment)
- [Whitelists a mobile deeplink URL for this project environment](https://docs.dynamic.xyz/api-reference/deeplinkurls/whitelists-a-mobile-deeplink-url-for-this-project-environment)
- [Find an environment by ID](https://docs.dynamic.xyz/api-reference/environments/find-an-environment-by-id)
- [Get keys for an environment given environmentId](https://docs.dynamic.xyz/api-reference/environments/get-keys-for-an-environment-given-environmentid)
- [Get Live and Sandbox environments by projectId](https://docs.dynamic.xyz/api-reference/environments/get-live-and-sandbox-environments-by-projectid)
- [Get the unique number of visitors for an environment by environment ID](https://docs.dynamic.xyz/api-reference/environments/get-the-unique-number-of-visitors-for-an-environment-by-environment-id)
- [Updates the environment settings](https://docs.dynamic.xyz/api-reference/environments/updates-the-environment-settings)
- [Get environment events](https://docs.dynamic.xyz/api-reference/events/get-environment-events)
- [Get event types](https://docs.dynamic.xyz/api-reference/events/get-event-types)
- [Create a new export request for the project environment](https://docs.dynamic.xyz/api-reference/exports/create-a-new-export-request-for-the-project-environment)
- [Download an export by ID](https://docs.dynamic.xyz/api-reference/exports/download-an-export-by-id)
- [Get an export using the ID](https://docs.dynamic.xyz/api-reference/exports/get-an-export-using-the-id)
- [Get the exports for an environment](https://docs.dynamic.xyz/api-reference/exports/get-the-exports-for-an-environment)
- [Check if the external provided JWT is valid for the specified environment](https://docs.dynamic.xyz/api-reference/externaljwt/check-if-the-external-provided-jwt-is-valid-for-the-specified-environment)
- [Creates a new gate for the project environment](https://docs.dynamic.xyz/api-reference/gates/creates-a-new-gate-for-the-project-environment)
- [Delete a gate](https://docs.dynamic.xyz/api-reference/gates/delete-a-gate)
- [Disable the gate for the environment](https://docs.dynamic.xyz/api-reference/gates/disable-the-gate-for-the-environment)
- [Enable the gate for the environment](https://docs.dynamic.xyz/api-reference/gates/enable-the-gate-for-the-environment)
- [Get the gates for an environment](https://docs.dynamic.xyz/api-reference/gates/get-the-gates-for-an-environment)
- [Gets a gate](https://docs.dynamic.xyz/api-reference/gates/gets-a-gate)
- [Updates a gate](https://docs.dynamic.xyz/api-reference/gates/updates-a-gate)
- [Creates an invite to the organization](https://docs.dynamic.xyz/api-reference/invites/creates-an-invite-to-the-organization)
- [Delete invite for user](https://docs.dynamic.xyz/api-reference/invites/delete-invite-for-user)
- [Fetches all the Invites the belong to the organization](https://docs.dynamic.xyz/api-reference/invites/fetches-all-the-invites-the-belong-to-the-organization)
- [Get all the user invites](https://docs.dynamic.xyz/api-reference/invites/get-all-the-user-invites)
- [Update invite for user (accept/reject)](https://docs.dynamic.xyz/api-reference/invites/update-invite-for-user-acceptreject)
- [Delete user membership from being an admin of an organization](https://docs.dynamic.xyz/api-reference/members/delete-user-membership-from-being-an-admin-of-an-organization)
- [Get all users that are admins for an organization](https://docs.dynamic.xyz/api-reference/members/get-all-users-that-are-admins-for-an-organization)
- [Get Membership Environment IDs](https://docs.dynamic.xyz/api-reference/members/get-membership-environment-ids): Retrieve the list of project environments that the user is a member of.
- [Update a given members role](https://docs.dynamic.xyz/api-reference/members/update-a-given-members-role)
- [Delete all MFA Devices for the user](https://docs.dynamic.xyz/api-reference/mfa/delete-all-mfa-devices-for-the-user)
- [Disable the given mfa device for the organization](https://docs.dynamic.xyz/api-reference/mfasettings/disable-the-given-mfa-device-for-the-organization)
- [Enable the given mfa device for the organization](https://docs.dynamic.xyz/api-reference/mfasettings/enable-the-given-mfa-device-for-the-organization)
- [Get all mfa settings for an organization](https://docs.dynamic.xyz/api-reference/mfasettings/get-all-mfa-settings-for-an-organization)
- [Create a new name service for the environment and chain](https://docs.dynamic.xyz/api-reference/nameservices/create-a-new-name-service-for-the-environment-and-chain)
- [delete the name service for the environment and chain](https://docs.dynamic.xyz/api-reference/nameservices/delete-the-name-service-for-the-environment-and-chain)
- [Disabled name service for this environment and chain](https://docs.dynamic.xyz/api-reference/nameservices/disabled-name-service-for-this-environment-and-chain)
- [Enable name service for this environment and chain](https://docs.dynamic.xyz/api-reference/nameservices/enable-name-service-for-this-environment-and-chain)
- [Get the name service for the environment and chain](https://docs.dynamic.xyz/api-reference/nameservices/get-the-name-service-for-the-environment-and-chain)
- [Get the unsigned chain-specific message needed to be signed by the wallet owner using the wallet private key](https://docs.dynamic.xyz/api-reference/nameservices/get-the-unsigned-chain-specific-message-needed-to-be-signed-by-the-wallet-owner-using-the-wallet-private-key)
- [Add coupon to subscription](https://docs.dynamic.xyz/api-reference/organizations/add-coupon-to-subscription)
- [Creates organization](https://docs.dynamic.xyz/api-reference/organizations/creates-organization)
- [Fetches all the active organizations that the user has access to](https://docs.dynamic.xyz/api-reference/organizations/fetches-all-the-active-organizations-that-the-user-has-access-to)
- [Find an organization by ID](https://docs.dynamic.xyz/api-reference/organizations/find-an-organization-by-id)
- [Find the subscription of an organization using its ID](https://docs.dynamic.xyz/api-reference/organizations/find-the-subscription-of-an-organization-using-its-id)
- [Update an organization by ID](https://docs.dynamic.xyz/api-reference/organizations/update-an-organization-by-id)
- [Upgrade organziation to advanced plan](https://docs.dynamic.xyz/api-reference/organizations/upgrade-organziation-to-advanced-plan)
- [Adds an allowed origin for this project environment](https://docs.dynamic.xyz/api-reference/origins/adds-an-allowed-origin-for-this-project-environment)
- [Delete a origin by id](https://docs.dynamic.xyz/api-reference/origins/delete-a-origin-by-id)
- [Get all the allowed origins for a project environment](https://docs.dynamic.xyz/api-reference/origins/get-all-the-allowed-origins-for-a-project-environment)
- [Introduction](https://docs.dynamic.xyz/api-reference/overview)
- [Creates a new project](https://docs.dynamic.xyz/api-reference/projects/creates-a-new-project)
- [Deletes a project by ID](https://docs.dynamic.xyz/api-reference/projects/deletes-a-project-by-id)
- [Fetches all the active projects the belong to the organization](https://docs.dynamic.xyz/api-reference/projects/fetches-all-the-active-projects-the-belong-to-the-organization)
- [Find an project by ID](https://docs.dynamic.xyz/api-reference/projects/find-an-project-by-id)
- [Update a project](https://docs.dynamic.xyz/api-reference/projects/update-a-project)
- [Get a sdk view given a type and environment](https://docs.dynamic.xyz/api-reference/sdk-views/get-a-sdk-view-given-a-type-and-environment)
- [Get the sdk views for an environment](https://docs.dynamic.xyz/api-reference/sdk-views/get-the-sdk-views-for-an-environment)
- [Updates the configs for the sdk view and project environment](https://docs.dynamic.xyz/api-reference/sdk-views/updates-the-configs-for-the-sdk-view-and-project-environment)
- [Add a wallet account to a user's existing Turnkey HD wallet](https://docs.dynamic.xyz/api-reference/sdk/add-a-wallet-account-to-a-users-existing-turnkey-hd-wallet): Add a wallet account to a user's existing Turnkey HD wallet
- [Add fees to a Solana transaction](https://docs.dynamic.xyz/api-reference/sdk/add-fees-to-a-solana-transaction)
- [After successful verification allow to merge two owned accounts](https://docs.dynamic.xyz/api-reference/sdk/after-successful-verification-allow-to-merge-two-owned-accounts): Used to merge two owned accounts connected by email address
- [Auth Passkey MFA device](https://docs.dynamic.xyz/api-reference/sdk/auth-passkey-mfa-device)
- [Check that session is valid](https://docs.dynamic.xyz/api-reference/sdk/check-that-session-is-valid)
- [Claim a pregenerated embedded wallet](https://docs.dynamic.xyz/api-reference/sdk/claim-a-pregenerated-embedded-wallet)
- [Completes the passkey recovery process for a user's passkey embedded wallet](https://docs.dynamic.xyz/api-reference/sdk/completes-the-passkey-recovery-process-for-a-users-passkey-embedded-wallet): Completes the passkey recovery process for a user's passkey embedded wallet
- [Configuration for enabled onramps](https://docs.dynamic.xyz/api-reference/sdk/configuration-for-enabled-onramps): Find the configuration for the enabled onramps associated to an environment
- [Create a visit](https://docs.dynamic.xyz/api-reference/sdk/create-a-visit): Endpoint to send minimal wallet information for a connect-only or connect-first visitor.
- [Create nonce for authentication flow](https://docs.dynamic.xyz/api-reference/sdk/create-nonce-for-authentication-flow)
- [Create one or more new embedded wallets for a user](https://docs.dynamic.xyz/api-reference/sdk/create-one-or-more-new-embedded-wallets-for-a-user): Create one or more embedded wallets for a user
- [Delete a device (if not default device)](https://docs.dynamic.xyz/api-reference/sdk/delete-a-device-if-not-default-device)
- [Deletes the embedded wallets for a user](https://docs.dynamic.xyz/api-reference/sdk/deletes-the-embedded-wallets-for-a-user): Deletes the embedded wallets for a user
- [Endpoint to refresh the JWT auth token using the current valid JWT auth token in the authorization header](https://docs.dynamic.xyz/api-reference/sdk/endpoint-to-refresh-the-jwt-auth-token-using-the-current-valid-jwt-auth-token-in-the-authorization-header)
- [Endpoint to sign-in using a token issued by an external auth provider](https://docs.dynamic.xyz/api-reference/sdk/endpoint-to-sign-in-using-a-token-issued-by-an-external-auth-provider)
- [Endpoint to verify using a token issued by an external auth provider](https://docs.dynamic.xyz/api-reference/sdk/endpoint-to-verify-using-a-token-issued-by-an-external-auth-provider)
- [Farcaster provider SignIn endpoint](https://docs.dynamic.xyz/api-reference/sdk/farcaster-provider-signin-endpoint): Farcaster SignIn endpoint to exchange SIWF data
- [Farcaster provider Verify endpoint](https://docs.dynamic.xyz/api-reference/sdk/farcaster-provider-verify-endpoint): Farcaster Verify endpoint to exchange SIWF data
- [Fetch user profile for the current authenticated user](https://docs.dynamic.xyz/api-reference/sdk/fetch-user-profile-for-the-current-authenticated-user)
- [Find jwks for public key](https://docs.dynamic.xyz/api-reference/sdk/find-jwks-for-public-key)
- [Find the configuration for the enabled networks associated to an environment](https://docs.dynamic.xyz/api-reference/sdk/find-the-configuration-for-the-enabled-networks-associated-to-an-environment)
- [Generate new recovery codes for user](https://docs.dynamic.xyz/api-reference/sdk/generate-new-recovery-codes-for-user)
- [Generates the turnkey request body for a user's create wallet account request](https://docs.dynamic.xyz/api-reference/sdk/generates-the-turnkey-request-body-for-a-users-create-wallet-account-request): Generates the turnkey request body for a user's create wallet account request
- [Generates the turnkey request body for a user's delete wallets request](https://docs.dynamic.xyz/api-reference/sdk/generates-the-turnkey-request-body-for-a-users-delete-wallets-request): Generates the turnkey request body for a user's delete wallets request
- [Get auth token for given embedded wallet type](https://docs.dynamic.xyz/api-reference/sdk/get-auth-token-for-given-embedded-wallet-type)
- [Get exchangerates](https://docs.dynamic.xyz/api-reference/sdk/get-exchangerates): Exchange rates for the given currency
- [Get HTML with rendered Telegram login widget](https://docs.dynamic.xyz/api-reference/sdk/get-html-with-rendered-telegram-login-widget)
- [Get recovery codes](https://docs.dynamic.xyz/api-reference/sdk/get-recovery-codes)
- [Get redirect URL for Oauth provider login](https://docs.dynamic.xyz/api-reference/sdk/get-redirect-url-for-oauth-provider-login)
- [Get redirect URL for Oauth provider login. This will return a string that the SDK can manipulate before redirecting the browser to.](https://docs.dynamic.xyz/api-reference/sdk/get-redirect-url-for-oauth-provider-login-this-will-return-a-string-that-the-sdk-can-manipulate-before-redirecting-the-browser-to)
- [Get sdk healthcheck](https://docs.dynamic.xyz/api-reference/sdk/get-sdk-healthcheck): Health check endpoint to check for uptime of API.
- [Get signin email provider](https://docs.dynamic.xyz/api-reference/sdk/get-signin-email-provider): Return the email provider to be used for signing in with a given email. If an external provider such as magicLink is disabled, this endpoint would return emailOnly. If an external provider such as magicLink is enabled, it will return emailOnly when the email already exists in Dynamic and it is associated with a wallet, otherwise it will return magicLink.
- [Get the current user's MFA by device id](https://docs.dynamic.xyz/api-reference/sdk/get-the-current-users-mfa-by-device-id)
- [Get the current user's MFA Devices](https://docs.dynamic.xyz/api-reference/sdk/get-the-current-users-mfa-devices)
- [Get the environment's settings](https://docs.dynamic.xyz/api-reference/sdk/get-the-environments-settings)
- [Get the token balances for an account](https://docs.dynamic.xyz/api-reference/sdk/get-the-token-balances-for-an-account)
- [Gets passkey data associated with a user](https://docs.dynamic.xyz/api-reference/sdk/gets-passkey-data-associated-with-a-user)
- [Handle Telegram authentication](https://docs.dynamic.xyz/api-reference/sdk/handle-telegram-authentication)
- [Initialize email verification process](https://docs.dynamic.xyz/api-reference/sdk/initialize-email-verification-process)
- [Initialize oauth auth sequence](https://docs.dynamic.xyz/api-reference/sdk/initialize-oauth-auth-sequence): This endpoint initializes a secure oauth authentication sequence by providing the state and current url path to the API.
- [Initialize sms verification process](https://docs.dynamic.xyz/api-reference/sdk/initialize-sms-verification-process)
- [Initialize the email authentication process for a user's embedded wallet](https://docs.dynamic.xyz/api-reference/sdk/initialize-the-email-authentication-process-for-a-users-embedded-wallet): Initialize the email authentication process for a user's embedded wallet
- [Initialize the passkey recovery process for a user's passkey embedded wallet](https://docs.dynamic.xyz/api-reference/sdk/initialize-the-passkey-recovery-process-for-a-users-passkey-embedded-wallet): Initialize the passkey recovery process for a user's passkey embedded wallet
- [Link wallet to user](https://docs.dynamic.xyz/api-reference/sdk/link-wallet-to-user): Link a wallet to a valid environment user, and return an updated JWT
- [Merge users with conflict resolutions](https://docs.dynamic.xyz/api-reference/sdk/merge-users-with-conflict-resolutions): Used to merge two users with user-chosen resolutions to conflicting data between the two users to be merged
- [Oauth provider authorization endpoint](https://docs.dynamic.xyz/api-reference/sdk/oauth-provider-authorization-endpoint): Endpoint where an oauth provider would return authorization HTML used for mobile-friendly login, such as Apple ID with Touch ID on enabled devices.
- [Oauth provider redirect endpoint](https://docs.dynamic.xyz/api-reference/sdk/oauth-provider-redirect-endpoint): Endpoint where an oauth provider would redirect after a successful user authorizing the oauth application.
- [Oauth provider redirect endpoint for apple ID](https://docs.dynamic.xyz/api-reference/sdk/oauth-provider-redirect-endpoint-for-apple-id): Endpoint where an oauth provider would redirect after a successful user authorizing the oauth application.
- [Oauth provider SignIn endpoint](https://docs.dynamic.xyz/api-reference/sdk/oauth-provider-signin-endpoint): Standard OAuth SignIn callback to exchange temproary code for oauth access and return a valid Dynamic JWT and user
- [Oauth provider verify endpoint](https://docs.dynamic.xyz/api-reference/sdk/oauth-provider-verify-endpoint): Standard OAuth verify callback to exchange temproary code for oauth access
- [prefetch information for wallet address](https://docs.dynamic.xyz/api-reference/sdk/prefetch-information-for-wallet-address): Endpoint to send minimal wallet information to the API to prefetch name service information for an address.
- [Publish events for SDK](https://docs.dynamic.xyz/api-reference/sdk/publish-events-for-sdk)
- [Register a new Passkey MFA device](https://docs.dynamic.xyz/api-reference/sdk/register-a-new-passkey-mfa-device)
- [Register a new TOTP MFA device and get Totp Secret](https://docs.dynamic.xyz/api-reference/sdk/register-a-new-totp-mfa-device-and-get-totp-secret)
- [Registers a session key for an embedded wallet](https://docs.dynamic.xyz/api-reference/sdk/registers-a-session-key-for-an-embedded-wallet): Registers a session key for an embedded wallet
- [Requests an export of an embedded wallet](https://docs.dynamic.xyz/api-reference/sdk/requests-an-export-of-an-embedded-wallet): Requests an export of an embedded wallet
- [Return the backup key for the embedded wallet of the authenticated user](https://docs.dynamic.xyz/api-reference/sdk/return-the-backup-key-for-the-embedded-wallet-of-the-authenticated-user)
- [Return the passcode for the pregenerated embedded wallet of the authenticated user](https://docs.dynamic.xyz/api-reference/sdk/return-the-passcode-for-the-pregenerated-embedded-wallet-of-the-authenticated-user)
- [Returns the authorization code or error retuned by oauth provider](https://docs.dynamic.xyz/api-reference/sdk/returns-the-authorization-code-or-error-retuned-by-oauth-provider)
- [Revoke a session](https://docs.dynamic.xyz/api-reference/sdk/revoke-a-session)
- [Scan a URL for potential malicious activity](https://docs.dynamic.xyz/api-reference/sdk/scan-a-url-for-potential-malicious-activity)
- [Send new code for email verification](https://docs.dynamic.xyz/api-reference/sdk/send-new-code-for-email-verification)
- [Send new code for sms verification](https://docs.dynamic.xyz/api-reference/sdk/send-new-code-for-sms-verification)
- [Simulate a Solana transaction](https://docs.dynamic.xyz/api-reference/sdk/simulate-a-solana-transaction)
- [Simulate an EVM transaction](https://docs.dynamic.xyz/api-reference/sdk/simulate-an-evm-transaction)
- [Telegram provider check auth](https://docs.dynamic.xyz/api-reference/sdk/telegram-provider-check-auth)
- [Telegram provider SignIn endpoint](https://docs.dynamic.xyz/api-reference/sdk/telegram-provider-signin-endpoint)
- [Telegram provider Verify endpoint](https://docs.dynamic.xyz/api-reference/sdk/telegram-provider-verify-endpoint)
- [unlink wallet from user](https://docs.dynamic.xyz/api-reference/sdk/unlink-wallet-from-user): Remove a link from to a valid environment user, and return an updated JWT
- [Update a device](https://docs.dynamic.xyz/api-reference/sdk/update-a-device)
- [Update a passkey's info](https://docs.dynamic.xyz/api-reference/sdk/update-a-passkeys-info)
- [Update a user's fields](https://docs.dynamic.xyz/api-reference/sdk/update-a-users-fields)
- [Update a user's Turnkey recovery email](https://docs.dynamic.xyz/api-reference/sdk/update-a-users-turnkey-recovery-email)
- [Update SDK settings for a specific environment](https://docs.dynamic.xyz/api-reference/sdk/update-sdk-settings-for-a-specific-environment)
- [Update wallet in a multi-wallet selection](https://docs.dynamic.xyz/api-reference/sdk/update-wallet-in-a-multi-wallet-selection): When a user selects a wallet to be the primary wallet in a multi-wallet enabled environment, this endpoint is called to record this on the backend.
- [Upgrade JWT scope with valid MFA session](https://docs.dynamic.xyz/api-reference/sdk/upgrade-jwt-scope-with-valid-mfa-session)
- [Upgrade JWT scope with valid MFA session](https://docs.dynamic.xyz/api-reference/sdk/upgrade-jwt-scope-with-valid-mfa-session-1)
- [Upgrade JWT scope with valid MFA session](https://docs.dynamic.xyz/api-reference/sdk/upgrade-jwt-scope-with-valid-mfa-session-2)
- [Verify an email and sign in user](https://docs.dynamic.xyz/api-reference/sdk/verify-an-email-and-sign-in-user)
- [Verify an sms and sign in user](https://docs.dynamic.xyz/api-reference/sdk/verify-an-sms-and-sign-in-user)
- [Verify email verification request](https://docs.dynamic.xyz/api-reference/sdk/verify-email-verification-request)
- [Verify Passkey MFA device](https://docs.dynamic.xyz/api-reference/sdk/verify-passkey-mfa-device)
- [Verify payload and return JWT](https://docs.dynamic.xyz/api-reference/sdk/verify-payload-and-return-jwt)
- [Verify sms verification request](https://docs.dynamic.xyz/api-reference/sdk/verify-sms-verification-request)
- [Verify TOTP MFA device](https://docs.dynamic.xyz/api-reference/sdk/verify-totp-mfa-device)
- [Verify wallet transfer](https://docs.dynamic.xyz/api-reference/sdk/verify-wallet-transfer): Used to link a wallet after user has confirmed transfer to the new account
- [Revoke a session](https://docs.dynamic.xyz/api-reference/sessions/revoke-a-session)
- [Creates a new provider for the project environment](https://docs.dynamic.xyz/api-reference/settings/creates-a-new-provider-for-the-project-environment)
- [Delete a provider by providerId](https://docs.dynamic.xyz/api-reference/settings/delete-a-provider-by-providerid)
- [Disable the provider for the environment](https://docs.dynamic.xyz/api-reference/settings/disable-the-provider-for-the-environment)
- [Enable the provider for the environment](https://docs.dynamic.xyz/api-reference/settings/enable-the-provider-for-the-environment)
- [Get the providers for an environment](https://docs.dynamic.xyz/api-reference/settings/get-the-providers-for-an-environment)
- [Get the URLs for the environment providers](https://docs.dynamic.xyz/api-reference/settings/get-the-urls-for-the-environment-providers)
- [Gets a provider](https://docs.dynamic.xyz/api-reference/settings/gets-a-provider)
- [Updates a provider](https://docs.dynamic.xyz/api-reference/settings/updates-a-provider)
- [Create a new test account for this environment](https://docs.dynamic.xyz/api-reference/testaccount/create-a-new-test-account-for-this-environment)
- [Delete the test account for this environment](https://docs.dynamic.xyz/api-reference/testaccount/delete-the-test-account-for-this-environment)
- [Get the test account for this environemnt](https://docs.dynamic.xyz/api-reference/testaccount/get-the-test-account-for-this-environemnt)
- [Create a new API Token](https://docs.dynamic.xyz/api-reference/tokens/create-a-new-api-token)
- [Delete a token by token id](https://docs.dynamic.xyz/api-reference/tokens/delete-a-token-by-token-id)
- [Get all the tokens for a project environment (does not include the raw token)](https://docs.dynamic.xyz/api-reference/tokens/get-all-the-tokens-for-a-project-environment-does-not-include-the-raw-token)
- [Creates a new user](https://docs.dynamic.xyz/api-reference/users/creates-a-new-user)
- [Creates many new users](https://docs.dynamic.xyz/api-reference/users/creates-many-new-users)
- [Delete user](https://docs.dynamic.xyz/api-reference/users/delete-user)
- [Get a user by Id](https://docs.dynamic.xyz/api-reference/users/get-a-user-by-id)
- [Get all users for an environment](https://docs.dynamic.xyz/api-reference/users/get-all-users-for-an-environment)
- [Get the access token for a user OAuth account](https://docs.dynamic.xyz/api-reference/users/get-the-access-token-for-a-user-oauth-account)
- [Revoke sessions by user ID](https://docs.dynamic.xyz/api-reference/users/revoke-sessions-by-user-id)
- [Update a user](https://docs.dynamic.xyz/api-reference/users/update-a-user)
- [Get all visitors for an environment](https://docs.dynamic.xyz/api-reference/visits/get-all-visitors-for-an-environment)
- [Creates a new embedded wallet for a user given an identifier](https://docs.dynamic.xyz/api-reference/wallets/creates-a-new-embedded-wallet-for-a-user-given-an-identifier): Creates a new embedded wallet for a user given an email or userId. If an email is provided and it is not associated with an existing user this call will also create a new user.
- [Creates a new embedded wallet. This API is meant to be called from a frame server.](https://docs.dynamic.xyz/api-reference/wallets/creates-a-new-embedded-wallet-this-api-is-meant-to-be-called-from-a-frame-server): Creates a new embedded wallet. This API is meant to be called from a frame server.
- [Creates a new wallet for the user. Note that if the user already has an embedded wallet linked, this call will fail.](https://docs.dynamic.xyz/api-reference/wallets/creates-a-new-wallet-for-the-user-note-that-if-the-user-already-has-an-embedded-wallet-linked-this-call-will-fail): Creates a new wallet for the user. Note that this API is not meant for creating embedded wallets. To create embedded wallets use the /embeddedWallets endpoint.
- [Delete wallet](https://docs.dynamic.xyz/api-reference/wallets/delete-wallet)
- [Get a wallet using the ID](https://docs.dynamic.xyz/api-reference/wallets/get-a-wallet-using-the-id)
- [Get wallets by user](https://docs.dynamic.xyz/api-reference/wallets/get-wallets-by-user)
- [Creates a new Webhooks for the project environment](https://docs.dynamic.xyz/api-reference/webhooks/creates-a-new-webhooks-for-the-project-environment)
- [Delete the Webhook for an environment](https://docs.dynamic.xyz/api-reference/webhooks/delete-the-webhook-for-an-environment)
- [Get the Message deliveries for a webhook](https://docs.dynamic.xyz/api-reference/webhooks/get-the-message-deliveries-for-a-webhook)
- [Get the Messages for an webhook](https://docs.dynamic.xyz/api-reference/webhooks/get-the-messages-for-an-webhook)
- [Get the Webhook for an environment](https://docs.dynamic.xyz/api-reference/webhooks/get-the-webhook-for-an-environment)
- [Get the Webhooks for an environment](https://docs.dynamic.xyz/api-reference/webhooks/get-the-webhooks-for-an-environment)
- [Redeliver message for an webhook](https://docs.dynamic.xyz/api-reference/webhooks/redeliver-message-for-an-webhook)
- [Update the Webhook for an environment](https://docs.dynamic.xyz/api-reference/webhooks/update-the-webhook-for-an-environment)
- [JWTs/Authentication Tokens](https://docs.dynamic.xyz/authentication-methods/auth-tokens)
- [Sign Up with Branded Wallets](https://docs.dynamic.xyz/authentication-methods/branded-wallets)
- [Adding Captcha Protection](https://docs.dynamic.xyz/authentication-methods/captcha)
- [Cookie Based Authentication](https://docs.dynamic.xyz/authentication-methods/cookie-authentication)
- [Sign Up with Email/Social/SMS](https://docs.dynamic.xyz/authentication-methods/email-social-sms)
- [Server-side verification](https://docs.dynamic.xyz/authentication-methods/how-to-validate-users-on-the-backend)
- [Multi-Factor Authentication (MFA)](https://docs.dynamic.xyz/authentication-methods/mfa)
- [Third-party Authentication](https://docs.dynamic.xyz/authentication-methods/third-party-auth)
- [Bridge Widget](https://docs.dynamic.xyz/building-bridges/dynamic-bridge-widget): The `DynamicBridgeWidget` component allows you to handle multi chain bridging
- [Custom Cosmos network](https://docs.dynamic.xyz/chains/customCosmosNetwork)
- [Embedded Wallet Chains](https://docs.dynamic.xyz/chains/embedded-wallets-chains)
- [Enabling Chains](https://docs.dynamic.xyz/chains/enabling-chains)
- [Custom EVM networks](https://docs.dynamic.xyz/chains/evmNetwork)
- [Switching Networks](https://docs.dynamic.xyz/chains/network-switching)
- [Using RPC Providers](https://docs.dynamic.xyz/chains/rpc-urls)
- [Smart Wallet Chains](https://docs.dynamic.xyz/chains/smart-wallet-chains)
- [Custom Information Capture](https://docs.dynamic.xyz/custom-fields/overview)
- [CSS Variables](https://docs.dynamic.xyz/design-customizations/css/css-variables)
- [Custom CSS](https://docs.dynamic.xyz/design-customizations/css/custom-css)
- [Themes & Settings](https://docs.dynamic.xyz/design-customizations/css/themes)
- [Adapt Copy With Translations](https://docs.dynamic.xyz/design-customizations/customizing-copy-translations)
- [UI Customization Overview](https://docs.dynamic.xyz/design-customizations/customizing-ui)
- [Customizing Signup/Login UI](https://docs.dynamic.xyz/design-customizations/signup-login)
- [Inject Extra Content in Widget](https://docs.dynamic.xyz/design-customizations/tutorials/custom-widget-content)
- [Design Techniques Tutorial](https://docs.dynamic.xyz/design-customizations/tutorials/design-tutorial)
- [Login Views Tutorial](https://docs.dynamic.xyz/design-customizations/tutorials/login-views-guide)
- [Wallet List Views Tutorial](https://docs.dynamic.xyz/design-customizations/tutorials/wallet-list-views-guide)
- [Adapt UI With Views](https://docs.dynamic.xyz/design-customizations/views)
- [Best Practices](https://docs.dynamic.xyz/developer-best-practices)
- [Analytics](https://docs.dynamic.xyz/developer-dashboard/analytics)
- [Users](https://docs.dynamic.xyz/developer-dashboard/authenticated-users)
- [Webhook Event types](https://docs.dynamic.xyz/developer-dashboard/eventTypes)
- [General Settings](https://docs.dynamic.xyz/developer-dashboard/general): One of the first things you'll want to do as you create a new project is update some of the basic metadata associated with the SDK such as your app name, logo url, and support url.
- [Invite Members](https://docs.dynamic.xyz/developer-dashboard/invite-members)
- [Privacy Settings](https://docs.dynamic.xyz/developer-dashboard/privacy)
- [Projects vs Organizations](https://docs.dynamic.xyz/developer-dashboard/projects-vs-orgs)
- [Rate Limits](https://docs.dynamic.xyz/developer-dashboard/rate-limits): Dynamic rate limit policies
- [Sandbox vs Live](https://docs.dynamic.xyz/developer-dashboard/sandbox-vs-live): Every project you create in Dynamic comes with a Sandbox and a Live environment. Both these environments behave similarly but we've customized the Sandbox environment to make it ideal for testing and exploring features.
- [Security Settings](https://docs.dynamic.xyz/developer-dashboard/security): We take security seriously at Dynamic and most of the work we do is behind the scenes so you don't have to worry about it. We will surface some security features that can be configured. 
- [Test Accounts](https://docs.dynamic.xyz/developer-dashboard/test-accounts): Test Accounts allow you to log in with a static OTP
- [SDK and API Keys](https://docs.dynamic.xyz/developer-dashboard/tokens-api-keys): This page hosts the keys that you'll need to setup the SDK, validate the JWT, and generate tokens for API usage.
- [Visitors](https://docs.dynamic.xyz/developer-dashboard/visitors)
- [General](https://docs.dynamic.xyz/developer-dashboard/webhooks)
- [Overview](https://docs.dynamic.xyz/ecosystem-wallets/overview)
- [QR Scanning (Global Connectivity)](https://docs.dynamic.xyz/ecosystem-wallets/qr-scanning)
- [Web3 Wallet Libraries](https://docs.dynamic.xyz/ecosystem-wallets/web3-libraries)
- [Example apps](https://docs.dynamic.xyz/example-apps)
- [Banxa](https://docs.dynamic.xyz/fiat-onboarding/banxa)
- [Bring Your Own Onramp](https://docs.dynamic.xyz/fiat-onboarding/bring-your-own)
- [Dynamic Package](https://docs.dynamic.xyz/flutter/client)
- [Deeplink URLs](https://docs.dynamic.xyz/flutter/deeplink-urls)
- [Client Reference](https://docs.dynamic.xyz/flutter/package-references/client)
- [Web3dart Reference](https://docs.dynamic.xyz/flutter/package-references/web3dart)
- [Social Authentication](https://docs.dynamic.xyz/flutter/social)
- [Web3Dart Package](https://docs.dynamic.xyz/flutter/web3dart)
- [Access Lists](https://docs.dynamic.xyz/gating/access-lists)
- [NFT/Token Gating](https://docs.dynamic.xyz/gating/nft-token-gating)
- [Go Live Checklist](https://docs.dynamic.xyz/go-live-checklist)
- [Phantom Redirect](https://docs.dynamic.xyz/guides/features/phantom-redirect)
- [NextAuth & Dynamic](https://docs.dynamic.xyz/guides/frameworks/next-auth)
- [Dynamic in PWA](https://docs.dynamic.xyz/guides/frameworks/react-pwa)
- [Create-web3-dapp & Dynamic](https://docs.dynamic.xyz/guides/frameworks/web3-dapp)
- [Enhanced analytics with Bello](https://docs.dynamic.xyz/guides/integrations/analytics/bello)
- [Generate an multi-chain embedded wallet from a Farcaster frame](https://docs.dynamic.xyz/guides/integrations/farcaster/embedded-wallet-frame)
- [Farcaster Overview](https://docs.dynamic.xyz/guides/integrations/farcaster/farcaster-overview)
- [Enable Write Access to Farcaster](https://docs.dynamic.xyz/guides/integrations/farcaster/farcaster-write-access)
- [Sign in with Farcaster](https://docs.dynamic.xyz/guides/integrations/farcaster/sign-in-with-farcaster)
- [How to Use Dynamic in a Safe App](https://docs.dynamic.xyz/guides/integrations/safe-app)
- [Shield3 & Dynamic Integration](https://docs.dynamic.xyz/guides/integrations/shield3): Public Beta
- [Telegram Auto-Wallets](https://docs.dynamic.xyz/guides/integrations/telegram/telegram-auto-wallets)
- [Telegram Mini App](https://docs.dynamic.xyz/guides/integrations/telegram/telegram-mini-app)
- [XMTP & Dynamic Integration](https://docs.dynamic.xyz/guides/integrations/xmtp)
- [Add new users to your CRM](https://docs.dynamic.xyz/guides/webhooks-crm)
- [Webhooks using a serverless function](https://docs.dynamic.xyz/guides/webhooks-serverless)
- [Verifying webhook message signatures](https://docs.dynamic.xyz/guides/webhooks-signature-validation)
- [Slack notifications of new signups](https://docs.dynamic.xyz/guides/webhooks-slack): Post in a slack channel when you have a new signup
- [Send new users an XMTP message](https://docs.dynamic.xyz/guides/webhooks-xmtp)
- [ETHGlobal Singapore](https://docs.dynamic.xyz/hackathons/ethglobal-singapore)
- [Headless Email Signup](https://docs.dynamic.xyz/headless/headless-email)
- [Headless Embedded Wallets With OTP](https://docs.dynamic.xyz/headless/headless-embedded-otp)
- [Headless embedded wallets using Passkeys](https://docs.dynamic.xyz/headless/headless-embedded-passkey)
- [Headless Embedded Wallet Export](https://docs.dynamic.xyz/headless/headless-embedded-reveal)
- [Headless MFA](https://docs.dynamic.xyz/headless/headless-mfa)
- [Headless Overview](https://docs.dynamic.xyz/headless/headless-overview)
- [Headless SMS signup](https://docs.dynamic.xyz/headless/headless-sms)
- [Headless Social Login](https://docs.dynamic.xyz/headless/headless-social)
- [Headless User Profile](https://docs.dynamic.xyz/headless/headless-user-profile)
- [Headless Wallet Management](https://docs.dynamic.xyz/headless/headless-wallet-management)
- [Welcome](https://docs.dynamic.xyz/introduction/welcome): Welcome to Dynamic! We're so excited that you're here!
- [Migrating to Dynamic](https://docs.dynamic.xyz/migrations/migration-guide)
- [Migrating Users via CSV](https://docs.dynamic.xyz/migrations/tutorials/importing)
- [Migration from RainbowKit](https://docs.dynamic.xyz/migrations/tutorials/rainbowkit)
- [Quickstart](https://docs.dynamic.xyz/quickstart): Let's get you up and running with a full signup/login UI! Just [sign up](https://app.dynamic.xyz) and follow the guide below!
- [Account Abstraction with ZeroDev](https://docs.dynamic.xyz/react-native/account-abstraction)
- [Setting up the Dynamic Client](https://docs.dynamic.xyz/react-native/client)
- [Deeplink URLs](https://docs.dynamic.xyz/react-native/deeplink-urls)
- [Using Dynamic's UI](https://docs.dynamic.xyz/react-native/dynamic-ui)
- [Email and SMS verification](https://docs.dynamic.xyz/react-native/email-and-sms-verification)
- [Embedded wallets](https://docs.dynamic.xyz/react-native/embedded-wallets)
- [Listening to events](https://docs.dynamic.xyz/react-native/events)
- [Signing in with Third-party Authentication](https://docs.dynamic.xyz/react-native/external-auth)
- [Introduction to the React Native SDK](https://docs.dynamic.xyz/react-native/introduction)
- [Managing your User's Wallets](https://docs.dynamic.xyz/react-native/managing-users-wallets)
- [client](https://docs.dynamic.xyz/react-native/package-references/client)
- [react-hooks](https://docs.dynamic.xyz/react-native/package-references/react-hooks)
- [react-native-extension](https://docs.dynamic.xyz/react-native/package-references/react-native-extension)
- [solana-extension](https://docs.dynamic.xyz/react-native/package-references/solana-extension)
- [viem-extension](https://docs.dynamic.xyz/react-native/package-references/viem-extension)
- [zero-dev-extension](https://docs.dynamic.xyz/react-native/package-references/zero-dev-extension)
- [Rendering client state to React](https://docs.dynamic.xyz/react-native/react-hooks)
- [Adding React Native support](https://docs.dynamic.xyz/react-native/react-native-extension)
- [Install & Setup Passkey](https://docs.dynamic.xyz/react-native/setup-passkey)
- [Social authentication](https://docs.dynamic.xyz/react-native/social)
- [Solana integration](https://docs.dynamic.xyz/react-native/solana)
- [Troubleshooting Common Issues](https://docs.dynamic.xyz/react-native/troubleshooting)
- [Viem integration](https://docs.dynamic.xyz/react-native/viem)
- [Components Introduction](https://docs.dynamic.xyz/react-sdk/components/components-introduction)
- [DynamicBridgeWidget](https://docs.dynamic.xyz/react-sdk/components/dynamicbridgewidget): The `DynamicBridgeWidget` component allows you to handle multi chain bridging
- [DynamicConnectButton](https://docs.dynamic.xyz/react-sdk/components/dynamicconnectbutton)
- [DynamicEmbeddedWidget](https://docs.dynamic.xyz/react-sdk/components/dynamicembeddedwidget)
- [DynamicMultiWalletPromptsWidget](https://docs.dynamic.xyz/react-sdk/components/dynamicmultiwalletpromptswidget)
- [DynamicUserProfile](https://docs.dynamic.xyz/react-sdk/components/dynamicuserprofile)
- [DynamicWidget](https://docs.dynamic.xyz/react-sdk/components/dynamicwidget)
- [DynamicNav](https://docs.dynamic.xyz/react-sdk/components/dynamicwidgetnav)
- [Dynamic Events](https://docs.dynamic.xyz/react-sdk/events/dynamic-events)
- [Events Introduction](https://docs.dynamic.xyz/react-sdk/events/events-introduction)
- [onAuthFailure](https://docs.dynamic.xyz/react-sdk/events/onauthfailure)
- [onAuthFlowCancel](https://docs.dynamic.xyz/react-sdk/events/onauthflowcancel)
- [onAuthFlowClose](https://docs.dynamic.xyz/react-sdk/events/onauthflowclose)
- [onAuthFlowOpen](https://docs.dynamic.xyz/react-sdk/events/onauthflowopen)
- [onAuthInit](https://docs.dynamic.xyz/react-sdk/events/onauthinit)
- [onAuthSuccess](https://docs.dynamic.xyz/react-sdk/events/onauthsuccess)
- [onEmbeddedWalletCreated](https://docs.dynamic.xyz/react-sdk/events/onembeddedwalletcreated)
- [onLogout](https://docs.dynamic.xyz/react-sdk/events/onlogout)
- [onOtpVerificationResult](https://docs.dynamic.xyz/react-sdk/events/onotpverificationresult)
- [onSignedMessage](https://docs.dynamic.xyz/react-sdk/events/onsignedmessage)
- [onUserProfileUpdate](https://docs.dynamic.xyz/react-sdk/events/onuserprofileupdate)
- [onWalletAdded](https://docs.dynamic.xyz/react-sdk/events/onwalletadded)
- [onWalletRemoved](https://docs.dynamic.xyz/react-sdk/events/onwalletremoved)
- [Examples](https://docs.dynamic.xyz/react-sdk/examples)
- [handleAuthenticatedUser](https://docs.dynamic.xyz/react-sdk/handlers/handleauthenticateduser)
- [handleConnectedWallet](https://docs.dynamic.xyz/react-sdk/handlers/handleconnectedwallet)
- [Handlers Introduction](https://docs.dynamic.xyz/react-sdk/handlers/handlers-introduction)
- [handleUserOnboarding](https://docs.dynamic.xyz/react-sdk/handlers/handleuseronboarding)
- [Hooks Introduction](https://docs.dynamic.xyz/react-sdk/hooks/hooks-introduction)
- [useAuthenticateConnectedUser](https://docs.dynamic.xyz/react-sdk/hooks/useauthenticatedconnecteduser)
- [useConnectWithOtp](https://docs.dynamic.xyz/react-sdk/hooks/useconnectwithotp): Allow for headless email or sms OTP authentication
- [useDynamicContext](https://docs.dynamic.xyz/react-sdk/hooks/usedynamiccontext): Dynamic's React Context which is set via DynamicContextProvider
- [useDynamicEvents](https://docs.dynamic.xyz/react-sdk/hooks/usedynamicevents)
- [useDynamicModals](https://docs.dynamic.xyz/react-sdk/hooks/usedynamicmodals)
- [useDynamicScopes](https://docs.dynamic.xyz/react-sdk/hooks/usedynamicscopes)
- [useEmbeddedReveal](https://docs.dynamic.xyz/react-sdk/hooks/useembeddedreveal)
- [useEmbeddedWallet](https://docs.dynamic.xyz/react-sdk/hooks/useembeddedwallet)
- [useExternalAuth](https://docs.dynamic.xyz/react-sdk/hooks/useexternalauth)
- [useFunding](https://docs.dynamic.xyz/react-sdk/hooks/usefunding)
- [useIsLoggedIn](https://docs.dynamic.xyz/react-sdk/hooks/useisloggedin)
- [useOtpVerificationRequest](https://docs.dynamic.xyz/react-sdk/hooks/useotpverificationrequest)
- [usePasskeyRecovery](https://docs.dynamic.xyz/react-sdk/hooks/usepasskeyrecovery)
- [useRefreshUser](https://docs.dynamic.xyz/react-sdk/hooks/userefreshuser)
- [useReinitialize](https://docs.dynamic.xyz/react-sdk/hooks/usereinitialize)
- [useRpcProviders](https://docs.dynamic.xyz/react-sdk/hooks/userpcproviders)
- [useSendBalance](https://docs.dynamic.xyz/react-sdk/hooks/usesendbalance)
- [useSmartWallets](https://docs.dynamic.xyz/react-sdk/hooks/usesmartwallets)
- [useSocialAccounts](https://docs.dynamic.xyz/react-sdk/hooks/usesocialaccounts)
- [useSwitchNetwork](https://docs.dynamic.xyz/react-sdk/hooks/useswitchnetwork)
- [useSwitchWallet](https://docs.dynamic.xyz/react-sdk/hooks/useswitchwallet)
- [useTokenBalances](https://docs.dynamic.xyz/react-sdk/hooks/usetokenbalances)
- [useUserUpdateRequest](https://docs.dynamic.xyz/react-sdk/hooks/useuserupdaterequest): Programmatically update the user profile
- [useUserWallets](https://docs.dynamic.xyz/react-sdk/hooks/useuserwallets): Get access to the current user/session wallets
- [useWalletConnectorEvent](https://docs.dynamic.xyz/react-sdk/hooks/usewalletconnectorevent)
- [useWalletItemActions](https://docs.dynamic.xyz/react-sdk/hooks/usewalletitemactions)
- [useWalletOptions](https://docs.dynamic.xyz/react-sdk/hooks/usewalletoptions)
- [SDK/User Loading States](https://docs.dynamic.xyz/react-sdk/loading-login-states)
- [EvmRpcProvider](https://docs.dynamic.xyz/react-sdk/objects/EvmRpcProvider)
- [EvmRpcProviderMethods](https://docs.dynamic.xyz/react-sdk/objects/EvmRpcProviderMethods)
- [GenericNetwork](https://docs.dynamic.xyz/react-sdk/objects/GenericNetwork)
- [NameService](https://docs.dynamic.xyz/react-sdk/objects/NameService)
- [NativeCurrency](https://docs.dynamic.xyz/react-sdk/objects/NativeCurrency)
- [SolanaRpcProvider](https://docs.dynamic.xyz/react-sdk/objects/SolanaRpcProvider)
- [SolanaRpcProviderMethods](https://docs.dynamic.xyz/react-sdk/objects/SolanaRpcProviderMethods)
- [AccessDeniedCustomButton](https://docs.dynamic.xyz/react-sdk/objects/access-denied-custom-button)
- [EvmNetwork](https://docs.dynamic.xyz/react-sdk/objects/evmNetwork)
- [LocaleResource](https://docs.dynamic.xyz/react-sdk/objects/locale)
- [PhoneData](https://docs.dynamic.xyz/react-sdk/objects/phone-data)
- [React Objects Introduction](https://docs.dynamic.xyz/react-sdk/objects/references-introduction)
- [SocialProvider](https://docs.dynamic.xyz/react-sdk/objects/social-provider)
- [JWT Payload](https://docs.dynamic.xyz/react-sdk/objects/user-payload)
- [UserProfile](https://docs.dynamic.xyz/react-sdk/objects/userprofile): An object containing attributes about the user such as id, email, first name, etc.
- [JwtVerifiedCredential](https://docs.dynamic.xyz/react-sdk/objects/verified-credential)
- [SdkViews](https://docs.dynamic.xyz/react-sdk/objects/views)
- [Wallet](https://docs.dynamic.xyz/react-sdk/objects/wallet): An object containing the attributes about the user wallets.
- [WalletOption](https://docs.dynamic.xyz/react-sdk/objects/wallet-option)
- [WalletConnectConnector](https://docs.dynamic.xyz/react-sdk/objects/walletconnect-connector): This is Dynamic's WalletConnect connector over the basic wallet interface. This can be used to interact with the users WalletConnect wallets. It has all methods available in the [WalletConnector](https://docs.dynamic.xyz/react-sdk/objects/walletconnector) and some additional method specific to WalletConnect wallets.
- [WalletConnector](https://docs.dynamic.xyz/react-sdk/objects/walletconnector): This is Dynamic's abstraction over the wallet interface. This can be used to interact with the users wallet.
- [WalletsByChain](https://docs.dynamic.xyz/react-sdk/objects/wallets-by-chain)
- [Introduction to the SDK](https://docs.dynamic.xyz/react-sdk/overview): The SDK allows you to build authentication and authorization into your application in a few lines of code.
- [DynamicContextProvider](https://docs.dynamic.xyz/react-sdk/providers/dynamiccontextprovider)
- [DynamicWagmiConnector](https://docs.dynamic.xyz/react-sdk/providers/dynamicwagmiconnector): The `DynamicWagmiConnector` component integrates your Dynamic project settings with Wagmi
- [Providers and Connectors Introduction](https://docs.dynamic.xyz/react-sdk/providers/providers-introduction)
- [Upgrade from V0 to V1](https://docs.dynamic.xyz/react-sdk/upgrade/v1)
- [Upgrade from V1 to V2](https://docs.dynamic.xyz/react-sdk/upgrade/v2)
- [Upgrade from V2 to V3](https://docs.dynamic.xyz/react-sdk/upgrade/v3)
- [Upgrade from V3 to v4](https://docs.dynamic.xyz/react-sdk/upgrade/v4)
- [Using Ethers](https://docs.dynamic.xyz/react-sdk/using-ethers)
- [Using Wagmi](https://docs.dynamic.xyz/react-sdk/using-wagmi)
- [getAuthToken](https://docs.dynamic.xyz/react-sdk/utilities/getauthtoken): This utility can be used to get a JWT token generated by Dynamic which is stored inside of localStorage.
- [getNetwork](https://docs.dynamic.xyz/react-sdk/utilities/getnetwork)
- [getOrMapViemChain](https://docs.dynamic.xyz/react-sdk/utilities/getormapviemchain)
- [isZeroDevConnector](https://docs.dynamic.xyz/react-sdk/utilities/is-zero-dev-connector)
- [isAuthenticatedWithAWallet](https://docs.dynamic.xyz/react-sdk/utilities/isauthenticatedwithawallet): This utility can be used check if the user has authenticated with a wallet.
- [isLedgerSolanaWallet](https://docs.dynamic.xyz/react-sdk/utilities/isledgersolanawallet): Determine if a given wallet is connected as a Ledger Solana wallet.
- [mergeNetworks](https://docs.dynamic.xyz/react-sdk/utilities/mergenetworks)
- [Utilities Introduction](https://docs.dynamic.xyz/react-sdk/utilities/utilities-introduction)
- [Smart Wallets](https://docs.dynamic.xyz/smart-wallets/add-smart-wallets)
- [Alchemy](https://docs.dynamic.xyz/smart-wallets/smart-wallet-providers/alchemy)
- [Biconomy](https://docs.dynamic.xyz/smart-wallets/smart-wallet-providers/biconomy)
- [Pimlico](https://docs.dynamic.xyz/smart-wallets/smart-wallet-providers/pimlico)
- [Safe](https://docs.dynamic.xyz/smart-wallets/smart-wallet-providers/safe)
- [ZeroDev](https://docs.dynamic.xyz/smart-wallets/smart-wallet-providers/zerodev)
- [Apple](https://docs.dynamic.xyz/social-providers/apple)
- [Discord](https://docs.dynamic.xyz/social-providers/discord)
- [Epic Games](https://docs.dynamic.xyz/social-providers/epicgames)
- [Facebook](https://docs.dynamic.xyz/social-providers/facebook)
- [Farcaster](https://docs.dynamic.xyz/social-providers/farcaster)
- [Github](https://docs.dynamic.xyz/social-providers/github)
- [Google](https://docs.dynamic.xyz/social-providers/google)
- [LINE](https://docs.dynamic.xyz/social-providers/line)
- [Social Providers Overview](https://docs.dynamic.xyz/social-providers/overview)
- [Spotify](https://docs.dynamic.xyz/social-providers/spotify)
- [Telegram](https://docs.dynamic.xyz/social-providers/telegram)
- [TikTok](https://docs.dynamic.xyz/social-providers/tiktok)
- [Twitch](https://docs.dynamic.xyz/social-providers/twitch)
- [Twitter/X](https://docs.dynamic.xyz/social-providers/twitter)
- [Dynamic Doctor](https://docs.dynamic.xyz/troubleshooting/dynamic-doctor)
- [Module not found Error (Framer-motion/browser.js)](https://docs.dynamic.xyz/troubleshooting/module-not-found-error)
- [Hydration failed because initial UI does not match what was rendered on the server](https://docs.dynamic.xyz/troubleshooting/next/hydration-failed)
- [FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed](https://docs.dynamic.xyz/troubleshooting/next/javascript-heap-out-of-memory)
- [Module not found: Can't resolve 'encoding'](https://docs.dynamic.xyz/troubleshooting/next/module-not-found)
- [React Static Flag Error](https://docs.dynamic.xyz/troubleshooting/react-static-flag-error)
- [Cannot convert a BigInt value to a number](https://docs.dynamic.xyz/troubleshooting/react/cannot-convert-a-bigInt-value-to-a-number)
- [Cannot resolve rpc-websockets](https://docs.dynamic.xyz/troubleshooting/react/cannot-resolve-rpc-websockets)
- [Failed to parse source map warnings](https://docs.dynamic.xyz/troubleshooting/react/failed-to-parse-source-map-warnings)
- [Jest Error: SyntaxError: Cannot use import statement outside a module](https://docs.dynamic.xyz/troubleshooting/react/jest-error-syntaxerror-unexpected-token-export)
- [NextJS with React 17 With Dynamic React SDK >=0.14.0](https://docs.dynamic.xyz/troubleshooting/react/nextjs-with-react-17-with-dynamic-react-sdk-0140)
- [Modal pop up isn't visible](https://docs.dynamic.xyz/troubleshooting/react/pop-up-isnt-visible-on-some-devices)
- [Vite.js polyfills necessary for Dynamic SDK](https://docs.dynamic.xyz/troubleshooting/react/vitejs-polyfills-necessary-for-dynamic-sdk)
- [TypeScript Compilation Errors](https://docs.dynamic.xyz/troubleshooting/typescript-compilation-error)
- [Wallet is redirecting to the App Store](https://docs.dynamic.xyz/troubleshooting/wallet-is-redirecting-to-the-app-store)
- [Working with environments that have both v1 and v2 embedded wallets](https://docs.dynamic.xyz/troubleshooting/wallet/environments-with-v1-and-v2-embedded-wallets)
- [WalletConnect Unsupported Chain](https://docs.dynamic.xyz/troubleshooting/walletconnect/walletconnect-unsupported-chain)
- [Accessing Users](https://docs.dynamic.xyz/users/accessing-users)
- [Email and Phone Verification](https://docs.dynamic.xyz/users/email-verification)
- [Get the user's profile](https://docs.dynamic.xyz/users/examples/get-the-users-profile): Here is an example where you can use the user profile to display in a HTML table.
- [Collecting Extra Information (Information Capture)](https://docs.dynamic.xyz/users/information-capture)
- [Social Linking](https://docs.dynamic.xyz/users/social-linking)
- [Verified Credentials](https://docs.dynamic.xyz/users/verified-credential)
- [Integrate your Chain](https://docs.dynamic.xyz/wallets-and-chains/chains)
- [Integrate your Wallet](https://docs.dynamic.xyz/wallets-and-chains/wallets)
- [Branded Wallets Overview](https://docs.dynamic.xyz/wallets/advanced-wallets/branded-wallets-overview)
- [Block Addresses With Chainalysis](https://docs.dynamic.xyz/wallets/advanced-wallets/chainalysis)
- [Coinbase Smart Wallet](https://docs.dynamic.xyz/wallets/advanced-wallets/coinbase-smart-wallet)
- [Configure Connect Only Mode](https://docs.dynamic.xyz/wallets/advanced-wallets/connect)
- [Configure Authenticate Mode](https://docs.dynamic.xyz/wallets/advanced-wallets/connect-and-sign)
- [Connected vs Authenticated](https://docs.dynamic.xyz/wallets/advanced-wallets/connected-vs-authenticated)
- [Custom Sign Message](https://docs.dynamic.xyz/wallets/advanced-wallets/custom-sign-message)
- [Multiple Wallet Connections (Multi-Wallet)](https://docs.dynamic.xyz/wallets/advanced-wallets/multi-wallet)
- [Recommend Wallets at Signup](https://docs.dynamic.xyz/wallets/advanced-wallets/recommend-wallets)
- [Sort and Filter Wallets](https://docs.dynamic.xyz/wallets/advanced-wallets/sort-and-filter-wallets)
- [TOS & Privacy Policy](https://docs.dynamic.xyz/wallets/advanced-wallets/tos-privacy-policy)
- [Architecture & Security](https://docs.dynamic.xyz/wallets/embedded-wallets/architecture-security)
- [Creating Embedded Wallets](https://docs.dynamic.xyz/wallets/embedded-wallets/create-wallets/overview)
- [Creating Transactions](https://docs.dynamic.xyz/wallets/embedded-wallets/creating-transactions)
- [Embedded Wallets Overview](https://docs.dynamic.xyz/wallets/embedded-wallets/dynamic-embedded-wallets)
- [Customizing Transaction UI](https://docs.dynamic.xyz/wallets/embedded-wallets/embedded-transaction-ui)
- [Wallet Reveal & Unlinking](https://docs.dynamic.xyz/wallets/embedded-wallets/reveal-export)
- [Transactional MFA](https://docs.dynamic.xyz/wallets/embedded-wallets/transactional-mfa/overview)
- [Multi Asset UI](https://docs.dynamic.xyz/wallets/multi-asset)
- [Multi Wallet](https://docs.dynamic.xyz/wallets/multi-wallet)
- [Send Assets](https://docs.dynamic.xyz/wallets/send-balance-ui)
- [Accessing Wallets](https://docs.dynamic.xyz/wallets/using-wallets/accessing-wallets): Learn about primaryWallet, useUserWallets, onEmbeddedWalletCreated, and handleConnectedWallet
- [Bitcoin Wallets](https://docs.dynamic.xyz/wallets/using-wallets/bitcoin/bitcoin-wallets)
- [Send Bitcoin](https://docs.dynamic.xyz/wallets/using-wallets/bitcoin/send-a-transaction)
- [Send a raw transaction](https://docs.dynamic.xyz/wallets/using-wallets/bitcoin/send-raw-transaction)
- [Sign a message](https://docs.dynamic.xyz/wallets/using-wallets/bitcoin/sign-a-message)
- [Sign a Partially Signed Bitcoin Transaction (PSBT)](https://docs.dynamic.xyz/wallets/using-wallets/bitcoin/sign-a-psbt)
- [EVM Wallets](https://docs.dynamic.xyz/wallets/using-wallets/evm/evm-wallets)
- [Get balance for all wallets](https://docs.dynamic.xyz/wallets/using-wallets/evm/get-balance-for-all-wallets): In this example, we will get the balance for each connected wallet.
- [Send a transaction on Ethereum/EVM](https://docs.dynamic.xyz/wallets/using-wallets/evm/send-a-transaction)
- [Mint an NFT on EVM with Wagmi](https://docs.dynamic.xyz/wallets/using-wallets/evm/send-a-transaction-wagmi)
- [Sign a message in Ethereum/EVM](https://docs.dynamic.xyz/wallets/using-wallets/evm/sign-a-message): In this example, we are creating a button to sign a message and log the signature to the console.
- [Fetch all token balances for a wallet](https://docs.dynamic.xyz/wallets/using-wallets/general/get-token-balances)
- [Switch user's primary wallet](https://docs.dynamic.xyz/wallets/using-wallets/general/switch-primary-wallet)
- [Interacting with wallets](https://docs.dynamic.xyz/wallets/using-wallets/interacting-with-wallets)
- [Send Solana Legacy Transaction](https://docs.dynamic.xyz/wallets/using-wallets/solana/send-legacy-solana-transaction)
- [Send a Versioned Solana Transaction](https://docs.dynamic.xyz/wallets/using-wallets/solana/send-versioned-solana-transaction)
- [Tips for transactions with Solana embedded wallets](https://docs.dynamic.xyz/wallets/using-wallets/solana/solana-transaction-tips)
- [Solana Wallets](https://docs.dynamic.xyz/wallets/using-wallets/solana/solana-wallets)


## Optional

- [Live Support](https://www.dynamic.xyz/slack)
- [System Status](https://status.dynamic.xyz/)
- [Changelog](https://www.dynamic.xyz/dynamic-dispatch)


# Create a new entry for an allowlist

post /allowlists/{allowlistId}/entries



# Create new allowlist for a environment

post /environments/{environmentId}/allowlists



# Delete an allowlist

delete /allowlists/{allowlistId}



# Delete an allowlist entry

delete /allowlistEntries/{allowlistEntryId}



# Disable the allow list

put /allowlists/{allowlistId}/disable



# Enable the allowlist

put /allowlists/{allowlistId}/enable



# Get all allowlists for a environment

get /environments/{environmentId}/allowlists



# Get all entries for an allowlist

get /allowlists/{allowlistId}/entries



# Get allowlist by id

get /allowlists/{allowlistId}



# Update the outcome, scope, or name of an allowlist entry by ID

put /allowlists/{allowlistId}



# Get environments analyticsvisits

get /environments/{environmentId}/analytics/visits
Fetch visit analytics



# Get environments analyticswallets

get /environments/{environmentId}/analytics/wallets
Fetch wallets breakdown



# Disables the Sanctions API

put /environments/{environmentId}/integrations/chainalysis/sanctions/disable



# Enable the Sanctions API

put /environments/{environmentId}/integrations/chainalysis/sanctions/enable



# Find the Chainalysis configuration for an environment.

get /environments/{environmentId}/integrations/chainalysis



# Get tokens for passed chainName.

get /chains/{chainName}/tokens



# Create a new custom field for an environment

post /environments/{environmentId}/custom/fields



# Delete a specific custom field by its ID

delete /custom/fields/{customFieldId}



# Get the custom fields for an environment

get /environments/{environmentId}/custom/fields



# Retrieve a specific custom field by its ID

get /custom/fields/{customFieldId}



# Update an existing custom field by its ID

put /custom/fields/{customFieldId}



# Create a new custom hostname for this environment

post /environments/{environmentId}/custom/hostnames



# Delete the custom hostname for this environment

delete /environments/{environmentId}/custom/hostnames



# Get the custom hostnames for this environemnt

get /environments/{environmentId}/custom/hostnames



# Delete a mobile deeplink URL by id

delete /deeplinkUrls/{deeplinkUrlId}



# Get all the whitelisted mobile deeplink URLs for a project environment

get /environments/{environmentId}/deeplinkUrls



# Whitelists a mobile deeplink URL for this project environment

post /environments/{environmentId}/deeplinkUrls



# Find an environment by ID

get /environments/{environmentId}



# Get keys for an environment given environmentId

get /environments/{environmentId}/keys



# Get Live and Sandbox environments by projectId

get /projects/{projectId}/environments



# Get the unique number of visitors for an environment by environment ID

get /environments/{environmentId}/statistics/visitors



# Updates the environment settings

put /environments/{environmentId}



# Get environment events

get /environments/{environmentId}/events



# Get event types

get /eventTypes



# Create a new export request for the project environment

post /environments/{environmentId}/exports



# Download an export by ID

get /exports/{exportId}/download



# Get an export using the ID

get /exports/{exportId}



# Get the exports for an environment

get /environments/{environmentId}/exports



# Check if the external provided JWT is valid for the specified environment

post /environments/{environmentId}/externalJwt/check



# Creates a new gate for the project environment

post /environments/{environmentId}/gates



# Delete a gate

delete /gates/{gateId}



# Disable the gate for the environment

put /gates/{gateId}/disable



# Enable the gate for the environment

put /gates/{gateId}/enable



# Get the gates for an environment

get /environments/{environmentId}/gates



# Gets a gate

get /gates/{gateId}



# Updates a gate

put /gates/{gateId}



# Creates an invite to the organization

post /organizations/{organizationId}/invites



# Delete invite for user

delete /invites/{inviteId}



# Fetches all the Invites the belong to the organization

get /organizations/{organizationId}/invites



# Get all the user invites

get /invites



# Update invite for user (accept/reject)

put /invites/{inviteId}



# Delete user membership from being an admin of an organization

delete /members/{memberId}



# Get all users that are admins for an organization

get /organizations/{organizationId}/members



# Get Membership Environment IDs

get /membershipEnvironmentIds
Retrieve the list of project environments that the user is a member of.



# Update a given members role

put /members/{memberId}



# Delete all MFA Devices for the user

delete /users/{userId}/mfa



# Disable the given mfa device for the organization

put /organizations/{organizationId}/mfaSettings/{mfaDeviceType}/disable



# Enable the given mfa device for the organization

put /organizations/{organizationId}/mfaSettings/{mfaDeviceType}/enable



# Get all mfa settings for an organization

get /organizations/{organizationId}/mfaSettings



# Create a new name service for the environment and chain

post /environments/{environmentId}/nameService/{chainName}



# delete the name service for the environment and chain

delete /environments/{environmentId}/nameService/{chainName}



# Disabled name service for this environment and chain

put /environments/{environmentId}/nameService/{chainName}/disable



# Enable name service for this environment and chain

put /environments/{environmentId}/nameService/{chainName}/enable



# Get the name service for the environment and chain

get /environments/{environmentId}/nameService/{chainName}



# Get the unsigned chain-specific message needed to be signed by the wallet owner using the wallet private key

post /environments/{environmentId}/nameService/{chainName}/signingMessage



# Add coupon to subscription

put /organizations/{organizationId}/billing/subscription/coupon



# Creates organization

post /organizations



# Fetches all the active organizations that the user has access to

get /organizations



# Find an organization by ID

get /organizations/{organizationId}



# Find the subscription of an organization using its ID

get /organizations/{organizationId}/billing/subscription



# Update an organization by ID

put /organizations/{organizationId}



# Upgrade organziation to advanced plan

put /organizations/{organizationId}/billing/upgrade



# Adds an allowed origin for this project environment

post /environments/{environmentId}/origins



# Delete a origin by id

delete /origins/{originId}



# Get all the allowed origins for a project environment

get /environments/{environmentId}/origins



# Introduction



## Introduction

Dynamic provides a robust API that allows developers to securely access their
dashboard environment's data and programmatically update settings relevant to
their Dynamic-powered application.

## Authentication

All APIs in this section require a bearer token used to authenticate requests
and make sure the caller is authorized to access the resources in the requests.

Please follow these steps:

#### Get an API token from Dashboard

1.  Go to the your environment's dashboard
    [Developer Tab](https://app.dynamic.xyz/dashboard/developer/api)
2.  In the "API Token" section, click on "Create Token"
3.  Provide a name for the token. The best practice here would be to name the
    token after the system you intend to use this token with. A few examples:
    "Mycompany Admin", "Ben's personal token", or "background-job service".
4.  **Make sure to copy the API token** before closing the modal. This is the
    last time you will have access to the plaintext API token! Dynamic DOES NOT
    have access to the plaintext API token anywhere. If you lose this API token,
    you will need to create a new one.
5.  The token should start with the prefix `dyn_` followed by 56 alphanumeric
    characters.

#### Using the API token

Use the token generated through dashboard to programmatically access Dynamic's
API by adding it to the `Authentication` header of your HTTP request.

Example:

```bash curl
curl \
--location 'https://app.dynamic.xyz/api/v0/environments/<<sandboxEnvironmentId>>' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer ADD_YOUR_DYN_TOKEN_HERE'
```

## Standard errors

*   `400` - Bad Request. The form of the request is invalid. Please check that
    the path parameters, query parameters, or the request's body contains the
    correct and expected information defined in our docs.
*   `401`- Unauthorized. The endpoint that is being accessed requires an
    `Authorization` header.
*   `403` - Forbidden. The token authorized for the HTTP call does not have
    access to the resource defined by the endpoint (eg. the specific environment,
    allowlist, user, etc.)
*   `404` - Not found. The path of the requested resource could not be found.
    Please check that the URL path is correct or that the ID provided is correct.

## Rate Limits

Dynamic's API endpoints are subject to rate limits. Refer to [Rate Limits](/developer-dashboard/rate-limits) for more information.


# Creates a new project

post /organizations/{organizationId}/projects



# Deletes a project by ID

delete /projects/{projectId}



# Fetches all the active projects the belong to the organization

get /organizations/{organizationId}/projects



# Find an project by ID

get /projects/{projectId}



# Update a project

put /projects/{projectId}



# Get a sdk view given a type and environment

get /environments/{environmentId}/sdkViews/{sdkViewType}



# Get the sdk views for an environment

get /environments/{environmentId}/sdkViews



# Updates the configs for the sdk view and project environment

put /environments/{environmentId}/sdkViews/{sdkViewType}



# Add a wallet account to a user's existing Turnkey HD wallet

post /sdk/{environmentId}/users/embeddedWallets/walletAccounts
Add a wallet account to a user's existing Turnkey HD wallet



# Add fees to a Solana transaction

post /sdk/{environmentId}/solana/optimizeTransaction



# After successful verification allow to merge two owned accounts

post /sdk/{environmentId}/users/merge
Used to merge two owned accounts connected by email address



# Auth Passkey MFA device

get /sdk/{environmentId}/users/mfa/auth/passkey



# Check that session is valid

get /sdk/{environmentId}/session



# Claim a pregenerated embedded wallet

put /sdk/{environmentId}/users/embeddedWallets/passcode/claim



# Completes the passkey recovery process for a user's passkey embedded wallet

put /sdk/{environmentId}/users/embeddedWallets/passkeyRecovery
Completes the passkey recovery process for a user's passkey embedded wallet



# Configuration for enabled onramps

get /sdk/{environmentId}/onramps
Find the configuration for the enabled onramps associated to an environment



# Create a visit

post /sdk/{environmentId}/connect
Endpoint to send minimal wallet information for a connect-only or connect-first visitor.



# Create nonce for authentication flow

get /sdk/{environmentId}/nonce



# Create one or more new embedded wallets for a user

post /sdk/{environmentId}/users/embeddedWallets
Create one or more embedded wallets for a user



# Delete a device (if not default device)

delete /sdk/{environmentId}/users/mfa/{mfaDeviceId}



# Deletes the embedded wallets for a user

delete /sdk/{environmentId}/users/embeddedWallets
Deletes the embedded wallets for a user



# Endpoint to refresh the JWT auth token using the current valid JWT auth token in the authorization header

post /sdk/{environmentId}/refresh



# Endpoint to sign-in using a token issued by an external auth provider

post /sdk/{environmentId}/externalAuth/signin



# Endpoint to verify using a token issued by an external auth provider

post /sdk/{environmentId}/externalAuth/verify



# Farcaster provider SignIn endpoint

post /sdk/{environmentId}/farcaster/signin
Farcaster SignIn endpoint to exchange SIWF data



# Farcaster provider Verify endpoint

post /sdk/{environmentId}/farcaster/verify
Farcaster Verify endpoint to exchange SIWF data



# Fetch user profile for the current authenticated user

get /sdk/{environmentId}/users



# Find jwks for public key

get /sdk/{environmentId}/.well-known/jwks



# Find the configuration for the enabled networks associated to an environment

get /sdk/{environmentId}/networks



# Generate new recovery codes for user

post /sdk/{environmentId}/users/mfa/recovery



# Generates the turnkey request body for a user's create wallet account request

get /sdk/{environmentId}/users/embeddedWallets/walletAccounts/createRequest
Generates the turnkey request body for a user's create wallet account request



# Generates the turnkey request body for a user's delete wallets request

get /sdk/{environmentId}/users/embeddedWallets/deleteRequest
Generates the turnkey request body for a user's delete wallets request



# Get auth token for given embedded wallet type

get /sdk/{environmentId}/users/embeddedWallets/authToken



# Get exchangerates

get /exchangeRates
Exchange rates for the given currency



# Get HTML with rendered Telegram login widget

get /sdk/{environmentId}/telegram/auth



# Get recovery codes

get /sdk/{environmentId}/users/mfa/recovery



# Get redirect URL for Oauth provider login

get /sdk/{environmentId}/providers/{providerType}/login



# Get redirect URL for Oauth provider login. This will return a string that the SDK can manipulate before redirecting the browser to.

get /sdk/{environmentId}/providers/{providerType}/loginUrl



# Get sdk healthcheck

get /sdk/{environmentId}/healthcheck
Health check endpoint to check for uptime of API.



# Get signin email provider

get /sdk/{environmentId}/emailProvider
Return the email provider to be used for signing in with a given email. If an external provider such as magicLink is disabled, this endpoint would return emailOnly. If an external provider such as magicLink is enabled, it will return emailOnly when the email already exists in Dynamic and it is associated with a wallet, otherwise it will return magicLink.



# Get the current user's MFA by device id

get /sdk/{environmentId}/users/mfa/{mfaDeviceId}



# Get the current user's MFA Devices

get /sdk/{environmentId}/users/mfa



# Get the environment's settings

get /sdk/{environmentId}/settings



# Get the token balances for an account

get /sdk/{environmentId}/chains/{chainName}/balances



# Gets passkey data associated with a user

get /sdk/{environmentId}/users/passkeys



# Handle Telegram authentication

post /sdk/{environmentId}/telegram/auth



# Initialize email verification process

post /sdk/{environmentId}/emailVerifications/create



# Initialize oauth auth sequence

post /sdk/{environmentId}/providers/{providerType}/initAuth
This endpoint initializes a secure oauth authentication sequence by providing the state and current url path to the API.



# Initialize sms verification process

post /sdk/{environmentId}/smsVerifications/create



# Initialize the email authentication process for a user's embedded wallet

post /sdk/{environmentId}/users/embeddedWallets/emailAuth
Initialize the email authentication process for a user's embedded wallet



# Initialize the passkey recovery process for a user's passkey embedded wallet

post /sdk/{environmentId}/users/embeddedWallets/passkeyRecovery
Initialize the passkey recovery process for a user's passkey embedded wallet



# Link wallet to user

post /sdk/{environmentId}/verify/link
Link a wallet to a valid environment user, and return an updated JWT



# Merge users with conflict resolutions

post /sdk/{environmentId}/verify/merge
Used to merge two users with user-chosen resolutions to conflicting data between the two users to be merged



# Oauth provider authorization endpoint

get /sdk/{environmentId}/providers/{providerType}/authorizeHtml
Endpoint where an oauth provider would return authorization HTML used for mobile-friendly login, such as Apple ID with Touch ID on enabled devices.



# Oauth provider redirect endpoint

get /sdk/{environmentId}/providers/{providerType}/redirect
Endpoint where an oauth provider would redirect after a successful user authorizing the oauth application.



# Oauth provider redirect endpoint for apple ID

post /sdk/{environmentId}/providers/apple/redirect
Endpoint where an oauth provider would redirect after a successful user authorizing the oauth application.



# Oauth provider SignIn endpoint

post /sdk/{environmentId}/providers/{providerType}/signIn
Standard OAuth SignIn callback to exchange temproary code for oauth access and return a valid Dynamic JWT and user



# Oauth provider verify endpoint

post /sdk/{environmentId}/providers/{providerType}/verify
Standard OAuth verify callback to exchange temproary code for oauth access



# prefetch information for wallet address

post /sdk/{environmentId}/prefetch
Endpoint to send minimal wallet information to the API to prefetch name service information for an address.



# Publish events for SDK

post /sdk/{environmentId}/events



# Register a new Passkey MFA device

get /sdk/{environmentId}/users/mfa/register/passkey



# Register a new TOTP MFA device and get Totp Secret

get /sdk/{environmentId}/users/mfa/register/totp



# Registers a session key for an embedded wallet

post /sdk/{environmentId}/users/embeddedWallets/sessionKey
Registers a session key for an embedded wallet



# Requests an export of an embedded wallet

get /sdk/{environmentId}/users/embeddedWallets/{walletId}/activities/{activityId}/export
Requests an export of an embedded wallet



# Return the backup key for the embedded wallet of the authenticated user

get /sdk/{environmentId}/users/embeddedWallets/backup



# Return the passcode for the pregenerated embedded wallet of the authenticated user

get /sdk/{environmentId}/users/embeddedWallets/passcode



# Returns the authorization code or error retuned by oauth provider

post /sdk/{environmentId}/providers/{providerType}/oauthResult



# Revoke a session

post /sdk/{environmentId}/revoke



# Scan a URL for potential malicious activity

post /sdk/{environmentId}/scan/websiteUrl



# Send new code for email verification

post /sdk/{environmentId}/emailVerifications/retry



# Send new code for sms verification

post /sdk/{environmentId}/smsVerifications/retry



# Simulate a Solana transaction

post /sdk/{environmentId}/solana/simulateTransaction



# Simulate an EVM transaction

post /sdk/{environmentId}/evm/simulateTransaction



# Telegram provider check auth

post /sdk/{environmentId}/checkTelegramAuth



# Telegram provider SignIn endpoint

post /sdk/{environmentId}/telegram/signin



# Telegram provider Verify endpoint

post /sdk/{environmentId}/telegram/verify



# unlink wallet from user

post /sdk/{environmentId}/verify/unlink
Remove a link from to a valid environment user, and return an updated JWT



# Update a device

put /sdk/{environmentId}/users/mfa/{mfaDeviceId}



# Update a passkey's info

put /sdk/{environmentId}/users/passkeys



# Update a user's fields

put /sdk/{environmentId}/users



# Update a user's Turnkey recovery email

put /sdk/{environmentId}/users/embeddedWallets/recoveryEmail



# Update SDK settings for a specific environment

post /sdk/{environmentId}/sdkSettings



# Update wallet in a multi-wallet selection

put /sdk/{environmentId}/users/wallets/selection
When a user selects a wallet to be the primary wallet in a multi-wallet enabled environment, this endpoint is called to record this on the backend.



# Upgrade JWT scope with valid MFA session

post /sdk/{environmentId}/users/mfa/auth/totp



# Upgrade JWT scope with valid MFA session

post /sdk/{environmentId}/users/mfa/auth/passkey



# Upgrade JWT scope with valid MFA session

post /sdk/{environmentId}/users/mfa/auth/recovery



# Verify an email and sign in user

post /sdk/{environmentId}/emailVerifications/signin



# Verify an sms and sign in user

post /sdk/{environmentId}/smsVerifications/signin



# Verify email verification request

post /sdk/{environmentId}/emailVerifications/verify



# Verify Passkey MFA device

post /sdk/{environmentId}/users/mfa/register/passkey



# Verify payload and return JWT

post /sdk/{environmentId}/verify



# Verify sms verification request

post /sdk/{environmentId}/smsVerifications/verify



# Verify TOTP MFA device

post /sdk/{environmentId}/users/mfa/register/totp



# Verify wallet transfer

post /sdk/{environmentId}/verify/transfer
Used to link a wallet after user has confirmed transfer to the new account



# Revoke a session

put /sessions/{sessionId}/revoke



# Creates a new provider for the project environment

post /environments/{environmentId}/settings/providers



# Delete a provider by providerId

delete /settings/providers/{providerId}



# Disable the provider for the environment

put /settings/providers/{providerId}/disable



# Enable the provider for the environment

put /settings/providers/{providerId}/enable



# Get the providers for an environment

get /environments/{environmentId}/settings/providers



# Get the URLs for the environment providers

get /environments/{environmentId}/settings/providers/urls



# Gets a provider

get /settings/providers/{providerId}



# Updates a provider

put /settings/providers/{providerId}



# Create a new test account for this environment

post /environments/{environmentId}/test/account



# Delete the test account for this environment

delete /environments/{environmentId}/test/account



# Get the test account for this environemnt

get /environments/{environmentId}/test/account



# Create a new API Token

post /environments/{environmentId}/tokens



# Delete a token by token id

delete /tokens/{tokenId}



# Get all the tokens for a project environment (does not include the raw token)

get /environments/{environmentId}/tokens



# Creates a new user

post /environments/{environmentId}/users



# Creates many new users

post /environments/{environmentId}/users/bulk



# Delete user

delete /users/{userId}



# Get a user by Id

get /users/{userId}



# Get all users for an environment

get /environments/{environmentId}/users



# Get the access token for a user OAuth account

get /oauthAccounts/{oauthAccountId}/accessToken



# Revoke sessions by user ID

post /users/{userId}/sessions/revoke



# Update a user

put /users/{userId}



# Get all visitors for an environment

get /environments/{environmentId}/visitors



# Creates a new embedded wallet for a user given an identifier

post /environments/{environmentId}/embeddedWallets
Creates a new embedded wallet for a user given an email or userId. If an email is provided and it is not associated with an existing user this call will also create a new user.



# Creates a new embedded wallet. This API is meant to be called from a frame server.

post /environments/{environmentId}/embeddedWallets/farcaster
Creates a new embedded wallet. This API is meant to be called from a frame server.



# Creates a new wallet for the user. Note that if the user already has an embedded wallet linked, this call will fail.

post /users/{userId}/wallets
Creates a new wallet for the user. Note that this API is not meant for creating embedded wallets. To create embedded wallets use the /embeddedWallets endpoint.



# Delete wallet

delete /wallets/{walletId}



# Get a wallet using the ID

get /wallets/{walletId}



# Get wallets by user

get /users/{userId}/wallets



# Creates a new Webhooks for the project environment

post /environments/{environmentId}/webhooks



# Delete the Webhook for an environment

delete /environments/{environmentId}/webhooks/{webhookId}



# Get the Message deliveries for a webhook

get /environments/{environmentId}/webhooks/{webhookId}/messages/{messageId}



# Get the Messages for an webhook

get /environments/{environmentId}/webhooks/{webhookId}/messages



# Get the Webhook for an environment

get /environments/{environmentId}/webhooks/{webhookId}



# Get the Webhooks for an environment

get /environments/{environmentId}/webhooks



# Redeliver message for an webhook

post /environments/{environmentId}/webhooks/{webhookId}/messages/{messageId}/redeliver



# Update the Webhook for an environment

put /environments/{environmentId}/webhooks/{webhookId}



# JWTs/Authentication Tokens



## Introduction

When an end user connects their wallet, you, the developer, get a [JSON Web Token (JWT)](https://jwt.io/introduction) that can be used to verify some claims about the end user, notably a proof of ownership over a wallet public address.

We use the JWT information in the SDK to create the user object and their Verified Credentials so that you can simply interact with the user object to get the information you need, so if you don't need to access the JWT directly, you can skip this page and visit the [Users section](/users) instead.

## Storing JWTs

### Localstorage

Once the user has logged in, the JWT gets stored in localstorage under the `dynamic_authentication_token` key. We also store a minified version under the `dynamic_min_authentication_token` key.

### Cookies

If you prefer to store the JWT in a HTTPOnly cookie, you might want to use the [Cookie auth feature](/authentication-methods/cookie-authentication). Bear in mind that with Cookie auth turned on, as long as you're still in Sandbox mode, the JWT will still be stored in localstorage but once you move to live mode it won't be, and you will no longer be able to access it using javascript.

## Accessing JWTs

### Fetch JWT from SDK

The [getAuthToken](/react-sdk/utilities/getauthtoken) utility function will return the JWT token to you from local storage as long as the user is logged in. It will return undefined otherwise.

### Fetch JWT from Local Storage

You can also access the JWT directly from local storage using the following code (note that this will not work if Cookie auth is enabled):

```javascript
const token = localStorage.getItem('dynamic_authentication_token');

if (token) {
  // do something with the token
}
```

## Using JWTs

### JWT Vs User Object

Normally, you do not need to use the JWT directly, as all of the information/claims are stored on the user object, which you can learn about in the [Users section](/users).

If however you are doing server-side verification, you can use the JWT to verify the user's identity. See the [Server-side verification](/authentication-methods/how-to-validate-users-on-the-backend) page for more information.

### JWT Payload

##### Standard JWT claims:

See: [https://www.rfc-editor.org/rfc/rfc7519#section-4.1](https://www.rfc-editor.org/rfc/rfc7519#section-4.1)

| Field | Description                                                                                   |
| ----- | --------------------------------------------------------------------------------------------- |
| aud   | Audience for the JWT token. This claim shows what domain of the indended audience of the JWT. |
| iss   | Issuer of the JWT token. This claim shows app.dynamic.xyz generated and issued the JWT.       |
| sub   | Subject of the JWT token. userId in the deprecated info claim.                                |
| iat   | Timestamp when the JWT token was issued.                                                      |
| exp   | Timestamp when the JWT token will expire.                                                     |

##### Dynamic-specific claims:

These fields are **optional** and you depends on whether you want to collect this information during onboarding. For more information about collecting this information, see [here](/users/information-capture).

| alias                 | Alias field from customer information capture.                                                                                                                                     |
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| email                 | Email field from customer information capture.                                                                                                                                     |
| environment\_id       | Unique ID of the project environment for the SDK, from [https://app.dynamic.xyz/dashboard/api](https://app.dynamic.xyz/dashboard/api). environmentId in the deprecated info claim. |
| given\_name           | First name field from customer information capture. firstName in the deprecated info claim.                                                                                        |
| family\_name          | Last name field from customer information capture. lastName in the deprecated info claim.                                                                                          |
| lists                 | Names of access lists enabled for this user.                                                                                                                                       |
| verified\_credentials | List of all [verified credentials](/react-sdk/objects/verified-credential) connected to this user.                                                                                 |
| verified\_account     | If present, this was the most recently signed and verified account.                                                                                                                |

#### Example

```JSON{
  "alias": "john",
  "aud": "https://dashboard.hello.xyz",
  "verified_credentials": [
    {
      "address": "0x000123abc",
      "chain": "eip155",
      "id": "af615228-99e5-48ee-905d-4575f0a6bfc9",
      "wallet_name": "metamask"
    }
  ],
  "email": "[[email protected]](/cdn-cgi/l/email-protection)",
  "environment_id": "fb6dd9d1-09f5-43c3-8a8c-eab6e44c37f9",
  "family_name": "bot",
  "given_name": "jon",
  "iss": "app.dynamic.xyz/fb6dd9d1-09f5-43c3-8a8c-eab6e44c37f9",
  "lists": [ "Community dashboard acess list" ],
  "sub": "d261ee91-8ea0-4949-b8bb-b6ab4f712a49",
  "verified_account": {
    "address": "0x000123abc",
    "chain": "eip155",
    "id": "af615228-99e5-48ee-905d-4575f0a6bfc9",
    "wallet_name": "metamask"
  },
  "iat": 1660677597,
  "exp": 1660684797
}
```

## Using your own JWTs

If you have your own authentication system, you can use your own JWTs to authenticate users in Dynamic. This can be done by exchanging your token for a Dynamic JWT. This will return Dynamic’s standard sign-in artifacts (i.e., a minified JWT and user).

Checkout the [Third-party Authentication](/authentication-methods/third-party-auth) page for more information on how to set up and use this feature.


# Sign Up with Branded Wallets



## Enable Branded Wallet Signup/Login

<Steps>
  <Step title="Enable Chains & Add Connectors">
    The first step is to enable the appropriate chains that you'd like to support and add the appropriate connectors to your app. The following [chains and networks configuration guide](/chains/enabling-chains) will show you how to do both.
  </Step>

  <Step title="Configure RPC">
    You can use the default RPC URLs that we provide for each chain/network, but if you'd like to configure your own, please follow [this RPC guide](/chains/rpc-urls).
  </Step>

  <Step title="Enable Web3 Wallet Signup">
    In [the Log in & User Profile section of the dashboard](https://app.dynamic.xyz/dashboard/log-in-user-profile), toggle on Wallet Log in under "Branded Wallets" and you're good to go!
  </Step>
</Steps>

## Basic Configuration

In the same section of the dashboard where you enabled Branded Wallets, you can also configure the following:

*   Multi-Wallet: This option, when toggled on, allows your end users to have more than one connected wallet to their account and change between them. In this way users don't have to sign out and back in again if they want to use a different wallet, they simply switch between them. You can learn more about Multi-Wallet on [the overview page](/wallets/advanced-wallets/multi-wallet).
*   Hide Network: This is used in conjunction with the UI components, specifically [the DynamicUserProfile](/react-sdk/components/dynamicuserprofile) which is available on its own but also bundled as part of [DynamicWidget](/react-sdk/components/dynamicwidget). By default, the user profile shows the network the user is currently connected to and allows them to switch to another. If you want to hide this, simply toggle this option on.
*   WalletConnect: Configuration for [https://walletconnect.com/](https://walletconnect.com/)

## Further Configuration

When you enabled Branded Wallets, by default you will be in what's called "connect-and-sign" mode. It's worth reading about the implications of this in [the overview of authentication modes](/wallets/advanced-wallets/connected-vs-authenticated) to decide what's right for your use case.

There's a bunch of further customizations you can do for the Branded Wallet experience including things like [sorting and filtering wallets](/wallets/advanced-wallets/sort-and-filter-wallets), so it's worth reviewing [the advanced wallets section of the docs](/wallets/advanced-wallets) in depth when you're ready.


# Adding Captcha Protection



## Summary

Setting up a captcha can help protect your Dynamic-enabled site from bots. We currently support [hCaptcha](https://www.hcaptcha.com/) as our captcha provider.

Here is an example of how it looks like when enabled in Dynamic's SDK.

## Setting up hCaptcha

To get started, you must first create an hCaptcha account.

1.  **Sign up for a new account on [hCaptcha](https://www.hcaptcha.com/)** - We recommend considering upgrading to the "Pro" plan, which allows for "passive" and "friction-free" modes.
2.  **Add a new site** - Make sure the hostname corresponds to the domain you are using the Dynamic SDK on. For example, if you plan to set up the Dynamic SDK in `app.myawesomesite.com`, then you should add this value to the Hostname sections in hCaptcha.
    ![](https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/hcaptcha-general.png)
3.  **Get your site key** - This is used by the Dynamic SDK to communicate with hCaptcha and display a captcha challenge before the verification step in the wallet sign-in flow. Copy this value, which we will need to set up in Dynamic.
    ![](https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/hcaptcha-site-key.png)
4.  **Get your secret key** - This is used by the Dynamic server-side backend to verify the captcha result from the SDK, and is never leaked to the SDK. To get your secret key, click on your account profile on the upper right corner and select "Settings". You should see your plaintext secret key, which you should also copy to set up in Dynamic.
    ![](https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/hcaptcha-settings-button.png)

## Setting up Dynamic captcha

Now that we have hCaptcha set up, we can move on to final configuration within Dynamic.

1.  **Log in to Dynamic Dashboard.** On the right navigation bar, click on "Configurations" then select "hCaptcha" under "Onboarding Fields"

2.  **Add your site key and secret key from hCaptcha** - See previous steps on how to set up your hCaptcha credentials.

3.  **Enable** - When you are ready to enable the captcha, please make sure to switch the "enable" toggle.


# Cookie Based Authentication



#### Introduction

Dynamic can now be configured to set a secure, HttpOnly cookie that can be used for authenticating with Dynamic’s backend. This will contain a minified version of our JWT token.

This feature would also allow your site's end users to sign in on one subdomain and go to another subdomain without that end user needing to log in again using the same Dynamic environment ID.

#### Approach

Dynamic will require the setup of a custom hostname. This is a subdomain that you own, but pointed by DNS CNAME to Dynamic's API. This will allow Dynamic's backend to set secure, HttpOnly cookies on your domain.

For example, if your Dynamic-powered site is [https://app.example.io](https://app.example.io), the custom hostname you could use is [https://auth.example.io](https://auth.example.io).

This would allow your users to sign in with Dynamic on [https://app.example.io](https://app.example.io), receive an HttpOnly secure cookie for `.example.io`. This cookie can then be used on any subdomain ending with `.example.io`, such as [https://marketplace.example.io](https://marketplace.example.io) and [https://shop.example.io](https://shop.example.io).

#### Steps

The following steps would be required to properly set up cookie-based authentication.

1.  Go to the dashbaord security page and configure a custom domain.
2.  Provide us with a subdomain you intend to use. We need this so we can set a first party cookie on your behalf on our backend. Suggestion here is to prefix with auth. For instance for `example.io`, you can potentially use `auth.example.io`.
3.  Follow the instructions to set up DNS. There should be three DNS records: 2 TXT records for site and certificate verification, and 1 CNAME record to proxy the custom subdomain of your choice.
4.  Note: In `sandbox`, we will also attempt to set the cookie from the SDK frontend. This will ensure continued support for local development and other preview environments.
5.  Once DNS is validated, update the `apiBaseUrl` prop in `DynamicContextProvider` settings. For example:

```TypeScript
<DynamicContextProvider
    settings={{
        apiBaseUrl: 'https://auth.example.io/api/v0', // this should start with your new custom base domain
        environmentId: DYNAMIC_ENVIRONMENT_ID,
        walletConnectors: [EthereumWalletConnectors],
    }}
>
    ...
</DynamicContextProvider>
```

7.  Enable the cookies toggle. When this is enabled in `live`, Dynamic **WILL NO LONGER** return a JWT to store in local storage. The auth token will only be set on a cookie.


# Sign Up with Email/Social/SMS



### Introduction

When it comes to onboarding users, having a flexible sign-up flow that can accommodate different preferences is key. Whether users prefer to sign up with their email, phone number, social media accounts, or directly with a wallet, providing the options that fit your user's needs can make the onboarding process smoother, more user-friendly, and increase conversion.

That is why we are offering the flexibility to design the sign up screen to suit your needs. You can now choose whether you want users to sign up with their email, phone number, social accounts, or with wallets. You can configure this as needed.

<Tip>
  If you then want to generate a wallet for your users when they sign up using
  email, social or sms, please refer to [the embedded wallets
  section](/wallets/embedded-wallets/dynamic-embedded-wallets) after configuring email/social/sms below.
</Tip>

#### Sign up with Email

Simply toggle on "Email" in [the Log in & User Profile section of the dashboard](https://app.dynamic.xyz/dashboard/log-in-user-profile).

#### Sign up with Social

Sign up/sign in with Apple, Discord, Facebook, Farcaster, Github, Google, Telegram, Twitch or Twitter!

Similar to email, you can toggle and configure each social provider in the [social providers section of the dashboard](https://app.dynamic.xyz/dashboard/log-in-user-profile).

Configuration guides for individual social signup options can be found in [the social providers section of the docs](/social-providers/overview).

<Tip>
  Note that when configuring any social provider, you can adjust [the `social` prop](/react-sdk/providers/dynamiccontextprovider#social) in the `DynamicContextProvider` component to customize the user experience i.e. whether you use a redirect or popup.
</Tip>

#### Sign up with SMS

Toggle on "Phone Number" in [the Log in & User Profile section of the dashboard](https://app.dynamic.xyz/dashboard/log-in-user-profile).

<Accordion title="SMS Configuration">
  <Frame>
    <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/sms-configuration.png" />
  </Frame>

  By default, you can use Dynamics credentials to send SMS messages to your users. However, if you want to send beyond US & Canada, you will need to set up your own Twilio account. In order to do this, you toggle off "Use Dynamic's credentials" and a section will open up for you, where you can enter your own credentials.

  {/* 
  ## SMS & Embedded Wallets

  When you enable SMS sign-up, you can also enable embedded wallets for your users. This means that when a user signs up with their phone number, they will also receive a wallet that they can use to interact with your application.

  In order to ensure your end users are adequately protected against attacks like sim swaps, we also require a second factor before the wallet is generated. This can be [a passkey](/wallets/embedded-wallets/passkeys), or [a one time code](/wallets/embedded-wallets/one-time-codes). _We highly recommend using passkeys, which shortens the onboarding flow._

  If however, the user loses or deletes their passkey, they will be locked out of their wallet. To prevent this, we recommend prompting users to add an email address to their account. This way, they can recover their wallet if they lose their passkey. When using one time codes, the user will automatically prompted to add their email. */}
</Accordion>


# Server-side verification



When an end user connects their wallet, you, the developer, get a [JSON Web Token (JWT)](https://jwt.io/introduction) that can be used to verify some claims about the end user, notably a proof of ownership over a wallet public address.

Upon authentication, we generate a JWT signed with a private key (using RS256 algorithm) that is unique to you. In turn, you can use the associated public key (found in the [API tab](https://app.dynamic.xyz/dashboard/api) of your developer dashboard) to ensure that the token is authentic and hasn't been tampered with. In other words, if a JWT issued by Dynamic can be successfully verified with your public key, the information it contains can be trusted.

You can do this in multiple ways.

#### Option 1: Leverage [NextAuth](https://next-auth.js.org/)

If you are using Next.js, you can easily [integrate the NextAuth library with Dynamic](/guides/frameworks/next-auth) to perform server-side verification and then use a session client-side.

#### Option 2: Leverage [Passport.js](https://www.passportjs.org)

We offer an official [passport-dynamic](https://github.com/dynamic-labs/passport-dynamic) extension.

#### Option 3: Do-It-Yourself Verification

1.  Get the JWT through the Dynamic SDK with an [authToken](/react-sdk/utilities/getauthtoken).
2.  Send the authToken to the server as a Bearer token

```JavaScript
import { useEffect, useState } from "react";

export const useFetch = (authToken: string | null) => {
  const [data, setData] = useState({});

  useEffect(() => {
    const fetchApi = async () => {
      await fetch("http://localhost:9000/api", {
        headers: {
          Authorization: `Bearer ${authToken}`,
        },
      }).then(response => response.json()).then(setData);
    }

    fetchApi()
  }, [authToken]);

  return { data };
};
```

3.  Install the [node-jsonwebtoken](https://github.com/auth0/node-jsonwebtoken) and [jwks-rsa](https://www.npmjs.com/package/jwks-rsa) packages
4.  Validate the JWT on your server by fetching the public key from the [JWKS endpoint](/api-reference/endpoints/sdk/getJwksByEnvironmentId.mdx) API endpoint and verifying the encoded JWT against the public key:

```TypeScript
import jwt, { JwtPayload } from 'jsonwebtoken';
import { JwksClient } from 'jwks-rsa';

// can be found in https://app.dynamic.xyz/dashboard/developer/api
const jwksUrl = `https://app.dynamic.xyz/api/v0/sdk/${YOUR_DYNAMIC_ENV_ID}/.well-known/jwks`

// The clinet should be initalized as
const client = new JwksClient({
  jwksUri: jwksUrl,
  rateLimit: true,
  cache: true,
  cacheMaxEntries: 5,  // Maximum number of cached keys
  cacheMaxAge: 600000 // Cache duration in milliseconds (10 minutes in this case))}
 });

const signingKey = await client.getSigningKey();
const publicKey = signingKey.getPublicKey();

const decodedToken: JwtPayload = jwt.verify(encodedJwt, publicKey, {
  ignoreExpiration: false,
}) as JwtPayload;

if (decodedToken.scopes.includes('requiresAdditionalAuth')) {
  // Either reject or handle the scopes appropriately.
  // `requiresAdditionalAuth` is the scope used to indicate that JWT requires additional verification such as MFA.
  throw new Error('Additional verification required');
}

console.log(decodedToken) // { iss: 'xxxx', exp: nnnn, ... }
```

This uses the following libraries:

*   [jwks-rsa](https://www.npmjs.com/package/jwks-rsa): Provides client to interact and parse JWKS key signing data for a JWT.
*   [jsonwebtoken](https://www.npmjs.com/package/jsonwebtoken): Provides library to encode/decode and validate a JWT token.


# Multi-Factor Authentication (MFA)



## Introduction

This guide will show you how to enable Dynamic's Account level multi-factor authentication in your app.

<Note>
  Dynamic offers two different types of multi-factor authentication: account level and transaction level. At the account level, users must complete 2FA on login, while at the transaction level, they must complete 2FA when creating a transaction.

  You can learn more about transaction-level multi-factor authentication here.

  (And yes, we know it might be a bit confusing. Just remember: one MFA is for logging in, the other is for doing stuff after logging in. Think of it like locking your front door and then locking the safe inside!)
</Note>

## Supported methods

Dynamic currently supports these Account level methods:

*   Authenticator app (e.g. Google Authenticator or Authy)
*   Passkeys (coming soon)
*   SMS (coming soon)

## Setup

1.  Make sure you are on the latest Dynamic packages (V3)
2.  Go to the [Security page](https://app.dynamic.xyz/dashboard/security) in your developer Dashboard.
3.  In the Account MFA section, enable the Authenticator Apps toggle, then click Save Changes.
4.  Optionally, you can require users to MFA on signup by clicking on the settings gear to the right of the Authenticar Apps toggle, then toggle on "Require at onboarding"

That's it! Make sure you are using the same environment id from the [SDK & API Keys page](https://app.dynamic.xyz/dashboard/developer/api) in your app. When you sign in to your app, you will be prompted to MFA if you toggled on "Require at onboarding", otherwise you will be able to optionally add MFA from the user profile section of the Dynamic widget.

## Supporting users who lose access to their Authenticator App

<Note>
  Please ensure you only delete MFA devices after confirming the identity of your end users.
</Note>

In the event that one of your users contacts you that they lost access to their authenticator device, you can delete their device by going to the User Management table.

1.  Go to the User Management table.
2.  Find the user by searching based on email, username, or other verified credentials.
3.  Open the details panel and click the button to delete the authenticator devices.
4.  If MFA is required, then on the next login the user will be required to register a new device. Otherwise, the user can optionally add a device after logging in.


# Third-party Authentication



<Note>
  ### This is an enterprise-only feature. Please contact us [in slack](https://dynamic.xyz/slack) or via email ([hello@dynamic.xyz](mailto:hello@dynamic.xyz)) to enable.
</Note>

#### Introduction

If you have built your own authentication system or use other auth providers outside Dynamic, you may want to keep using those services while taking advantage of Dynamic’s product to enable web3-based functionality and interactions.

Third-party authentication allows you to do this by exchanging your token from a JWT issuer to "sign-in" or "link" to Dynamic.

#### Approach

Dynamic provides a new API endpoint and utility hook `useExternalAuth`, to allow sign-in or linking with an external 3rd party JWT by exchanging it for a Dynamic JWT. This will return Dynamic’s standard sign-in artifacts (ie, minified JWT and user).

At a high level, this will do the following steps:

1.  Verify the authenticity of the JWT by getting the public key from a JWKS endpoint provided by you, and using this public key to verify the JWT.
2.  Require you to provide exact value for either the `iss` (issuer) JWT claim. If the value of this field deviates from that is provided in the project environment’s configuration, we will reject the JWT.
3.  Dynamic will also require the `sub` (subject, or user ID) field be provided. These will correspond to the your user ID. These will be mapped to the similar user model in Dynamic.
4.  Dynamic will also require `exp` (expires at) JWT claim from external JWT. Dynamic will ensure that this `exp` claim is respected with timeouts on the SDK.
5.  Once signed in using this external JWT, we will create an **new verified credential** of type `externalAuth`, letting us know that the user has an external authentication mechanism they used to verify their account. This should have `externalUserId` with the value of the JWT’s `sub`.
6.  Optional verified credential data, such as e-mail sign in can be provided as part of the JWT. This would allow Dynamic to create an email verified credential tied to the Dynamic user on our side.

#### Workflow

![](https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/swimlanes-e22f25180d42b5d9494a3d58f2f1cbb5.png)

#### Dashboard Configuration

To access this page, navigate to the [Third-Party Auth](https://app.dynamic.xyz/dashboard/developer/third-party-auth) page in your dashboard.

1.  Provide values for the following fields:
    *   `iss` (required): Standard JWT claim for the "issuer" of the JWT. This should be the entity that issued the token. This is typically a URL, but can be a valid constant string.
    *   `jwksUrl` (required): This is a publicly-accessible URL that returns the JWT's signer public key in the standard [JWKS format](https://datatracker.ietf.org/doc/html/rfc7517). This is used to verify the signatures of your JWTs.
    *   `aud` (optional): Standard JWT claim for the "audience" of the JWT. This should be the intended recipient of the token. This is typically a URL, but can be a valid constant string.
    *   `cookieName` (optional): For clients that use cookie-based authentication for their 3rd party auth and who have no acccess to the raw JWT on the frontend, we provide a way for clients to specify the cookie name to expect the JWT to be stored.
2.  When you are ready, enable the feature using the toggle.
3.  Additionally, we provide a way for you to check a JWT against your saved settings, and we will return the errors, if any.

#### Frontend Implementation

After properly configuring the settings for this feature, you should be able to "sign-in" with a non-Dynamic JWT token by calling the `signInWithExternalJwt` method from the `useExternalAuth` hook.

```TypeScript
const { signInWithExternalJwt } = useExternalAuth();

try {
  // `externalUserId`: User ID in the external auth system
  // `externalJwt`: Raw encoded JWT issued by external auth system
  const userProfile = await signInWithExternalJwt({
    externalUserId,
    externalJwt
  });

  if (userProfile) {
    // You should be logged in at this point
  }
} catch (e: any) {
  console.error('Dynamic login failed:', e);
}
```

Similarly, you can verify the user using the verifyWithExternalJwt method:

```TypeScript
const { verifyWithExternalJwt } = useExternalAuth();

try {
  const verifiedProfile = await verifyWithExternalJwt({
    externalUserId,
    externalJwt
  });

  if (verifiedProfile) {
    // User verification successful
  }
} catch (e: any) {
  console.error('Dynamic verification failed:', e);
}
```


# Bridge Widget

The `DynamicBridgeWidget` component allows you to handle multi chain bridging

<Info>
  See it in action on [https://starkgate.starknet.io/](https://starkgate.starknet.io/)
</Info>

## Background

This widget gives you a full UI to handle the Bridge use case (multi chain bridging). It will allow you to connect multiple wallets from different chains and bridge assets between them.

The video above shows our Bridge Widget in action on our demo site. Here you can see how to connect two wallets from different chains and then you can see that you can easily toggle the wallets so different wallets can sign.

For this multi-chain bridge, this flow is custom built for this scenario. It is different from our multiwallet widget, since only 1 wallet of each chain is desired. Additionally, this bridge widget supports the following:

1.  It is a Connect-only multiwallet widget where all relevant information about the connected wallets are stored in Local Storage instead of the DB
2.  It directs users to add a wallet from 1 chain, and then a second chain
3.  It is flexible in that:
    *   A user can easily add or remove wallets
    *   It can toggle between the wallets so defining the Depositing wallet vs the Withdrawl wallet is simple.
    *   It supports network switching.
    *   It supports unlinking through our widget or via a hook
    *   The event listeners work on both wallets, so your dapp is always up to date with the proper account and network.

## Usage

To get the bridge component setup, you’ll want to have [your desired chains configured](/chains/enabling-chains.mdx), for example Ethereum and Starknet. Then, in your `index.tsx` (or wherever you render `DynamicContextProvider`, specify the `initialAuthenticationMode ` and [`bridgeChains`](/react-sdk/objects/wallets-by-chain) prop like so:

```typescript
import { DynamicContextProvider } from "@dynamic-labs/sdk-react-core";
import { EthereumWalletConnectors } from "@dynamic-labs/ethereum ";
import { StarknetWalletConnectors } from "@dynamic-labs/starknet";

<DynamicContextProvider
  settings={{
    environmentId: "ENV_ID",
    initialAuthenticationMode: "connect-only",
    walletConnectors: [EthereumWalletConnectors, StarknetWalletConnectors]
    bridgeChains: [
      {
        chain: "EVM",
      },
      {
        chain: "STARK",
      },
    ],
  }}
>
  <App />
</DynamicContextProvider>
```

Then, in `app.tsx`:

```typescript
import { DynamicBridgeWidget } from "@dynamic-labs/sdk-react-core";

export default function App() {
  return (
    <div className="App">
      <DynamicBridgeWidget />
    </div>
  );
}
```

And that's it! Now you're rendering our bridge widget.

## bridgeChains

Primarily used for [bridging](/building-bridges/dynamic-bridge-widget) purposes, this object is passed as a prop to the `settings` in the [`DynamicContextProvider` component](/react-sdk/providers/dynamiccontextprovider). It creates a situation where the `isFullyConnected` prop on `useDynamicContext` hook is true only if the user has connected at least one wallet per chain, from each chain in the bridgeChains array.

### Description

An Array of objects, in which each object contains the chain name to be connected.

| Field         | Description |
| ------------- | ----------- |
| chain: string | Chain name  |

### Example

```jsx
[
  {
    chain: "EVM",
  },
  {
    chain: "STARK",
  },
];
```


# Custom Cosmos network



You can enable any Cosmos network that we do not currently support in our dashboard by passing an array of `GenericNetwork`
to the `DynamicContextProvider`'s `overrides.cosmosNetworks` settings.

This can be done in two different ways:

1.  By passing an array of `GenericNetwork`, it completely overrides whatever networks were received from your dashboard configurations and uses that array instead.

2.  By passing a method with signature `(dashboardNetworks: GenericNetwork[]) => GenericNetwork[]`, you can use this callback to first
    receive the array of networks that was sent from your dashboard configurations, and then return the array of networks you want the app
    to use.

<Note>
  The second approach is best for making adjustments to the networks you get from our dashboard (like changing rpc urls),
  as well as when you want to hide some specific networks.

  If you're just trying to merge new networks with the ones from dashboard, we have a helper function that will make that easier:

  ```TypeScript
  import { mergeNetworks } from '@dynamic-labs/sdk-react-core';

  const DynamicSettings = {
    overrides: {
      cosmosNetworks: (networks) => mergeNetworks(myCosmosNetworks, networks),
    }
  };
  ```

  > Note that the order of the params for `mergeNetworks` matters: the first param takes precedence in case of a conflict.
</Note>

## Example

The following example sets Sei for the application. Remember to enable the cosmos blockchain in the dashboard

```TypeScript
// we are setting the chainId and networkID to a random ID that will not conflict with other active chains in Dynamic.

const cosmosNetworks= [
  {
      blockExplorerUrls: ["https://www.seiscan.app/pacific-1"],
      chain: 'Sei',
      chainId: '404',
      iconUrls: ['https://app.dynamic.xyz/assets/networks/sei.svg'],
      lcdUrl: 'https://rest.wallet.pacific-1.sei.io',
      name: 'pacific-1',
      nativeCurrency: {
        decimals: 18,
        denom: 'usei',
        name: 'Sei',
        symbol: 'Sei',
      },
      networkId: '404',
      rpcUrls: ['https://rpc.wallet.pacific-1.sei.io'],
      shortName: 'Sei',
      vanityName: 'Sei',
    }
];

const App = () => (
  <DynamicContextProvider
    settings={{
      environmentId: 'REPLACE_WITH_YOUR_ENV_ID',
      overrides: { cosmosNetworks },
    }}
  >
    <Home />
  </DynamicContextProvider>
);

export default App;
```

## Type Reference

### Definition

| Attribute              | Value            | Required/Optional |
| ---------------------- | ---------------- | ----------------- |
| blockExplorerUrls      | `string[]`       | Required          |
| chainId                | `number`         | Required          |
| name                   | `string`         | Required          |
| iconUrls               | `string[]`       | Required          |
| nativeCurrency         | `NativeCurrency` | Required          |
| networkId              | `number`         | Required          |
| privateCustomerRpcUrls | `string[]`       | Optional          |
| rpcUrls                | `string[]`       | Required          |
| vanityName             | `string`         | Optional          |

#### NativeCurrency

| Attribute | Value    | Required/Optional |
| --------- | -------- | ----------------- |
| decimals  | `number` | Required          |
| name      | `string` | Required          |
| symbol    | `string` | Required          |
| denom     | `string` | Optional          |


# Embedded Wallet Chains



This guide expands on the [integrating chains section](/chains/enabling-chains), and shows you what SDK setup you need to have to enable embedded wallets for your users.

## Prerequisites

You have enabled embedded wallets in the dashboard already, for more information on that, check out the [embedded wallets guide](/wallets/embedded-wallets/dynamic-embedded-wallets).

## Initial setup

You can use embedded wallets with Ethereum, Solana or both! Therefore, whichever one you choose, you need to make sure you have the correct walletConnectors are installed and added to the walletConnectors array in the SDK settings.

<Tabs>
  <Tab title="Ethereum">
    For Ethereum, you need to install the `@dynamic-labs/ethereum` package:

    <CodeGroup>
      ```bash npm
      npm i @dynamic-labs/ethereum
      ```

      ```bash yarn
      npm i @dynamic-labs/ethereum

      ```
    </CodeGroup>

    Then, you need to add the `EthereumWalletConnectors` to the `walletConnectors` array in the SDK settings:

    ```jsx
    import { DynamicContextProvider } from '@dynamic-labs/sdk-react-core';

    import { EthereumWalletConnectors } from '@dynamic-labs/ethereum';

    const App = () => (
      <DynamicContextProvider
        settings={{
          environmentId: 'XXXXX',
          walletConnectors: [ EthereumWalletConnectors ],
          ...
        }}
      >
      </DynamicContextProvider>
    );
    ```
  </Tab>

  <Tab title="Solana">
    For Solana, you need to install the `@dynamic-labs/solana` package:

    <CodeGroup>
      ```bash npm
      npm i @dynamic-labs/solana
      ```

      ```bash yarn
      npm i @dynamic-labs/solana
      ```
    </CodeGroup>

    Then, you need to add the `SolanaWalletConnectors` to the `walletConnectors` array in the SDK settings:

    ```jsx
    import { DynamicContextProvider } from '@dynamic-labs/sdk-react-core';

    import { SolanaWalletConnectors } from '@dynamic-labs/solana';

    const App = () => (
      <DynamicContextProvider
        settings={{
          environmentId: 'XXXXX',
          walletConnectors: [ SolanaWalletConnectors ],
          ...
        }}
      >
      </DynamicContextProvider>
    );
    ```
  </Tab>
</Tabs>

That's it! You've set up your chains correctly in order to create embedded wallets for your users.

<Card title="What next?" href="/chains/smart-wallet-chains" icon="link" color="#4779FE">
  Click here to learn how to enable chains for Smart Wallets!
</Card>


# Enabling Chains



## Supported Chains/Networks

We support EVM, Solana, Eclipse, Bitcoin, Flow, StarkNet, Cosmos and Algorand out of the box. If you don't see the chain/network you need and it's not EVM compatible, just [let us know](https://dynamic.xyz/slack)!

<Tip>
  We also support all EVM networks. You can currently turn on many via the
  dashboard (simply go to the [EVM
  tab](https://app.dynamic.xyz/dashboard/chains-and-networks#evm) in your
  dashboard and flip the toggle). For any not shown there, you can easily
  add them manually using [our evmNetworks prop](/chains/evmNetwork).
</Tip>

## Enabling a Chain/Network

To integrate a specific chain/network, you should first [enable it in the dashboard](https://app.dynamic.xyz/dashboard/chains-and-networks). You'll also see that you can add custom RPC URLs for each network. Each provider will use the RPC configured in the Dashboard if present, otherwise they fall back to public RPCs urls. By default, all EVM and Solana networks have public default providers as shown in this table:

| Network         | Public RPC Url                                                                                                                           |
| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| Ethereum        | [https://cloudflare-eth.com](https://cloudflare-eth.com)                                                                                 |
| Optimism        | [https://mainnet.optimism.io](https://mainnet.optimism.io)                                                                               |
| Gnosis Chain    | [https://rpc.gnosischain.com](https://rpc.gnosischain.com)                                                                               |
| Aurora          | [https://mainnet.aurora.dev](https://mainnet.aurora.dev)                                                                                 |
| Polygon         | [https://polygon-rpc.com](https://polygon-rpc.com)                                                                                       |
| Palm            | [https://palm-mainnet.infura.io/v3/3a961d6501e54add9a41aa53f15de99b](https://palm-mainnet.infura.io/v3/3a961d6501e54add9a41aa53f15de99b) |
| BNB Smart Chain | [https://arb1.arbitrum.io/rpc](https://arb1.arbitrum.io/rpc)                                                                             |
| Solana          | [https://api.mainnet-beta.solana.com](https://api.mainnet-beta.solana.com)                                                               |
| Eclipse         | [https://mainnetbeta-rpc.eclipse.xyz](https://mainnetbeta-rpc.eclipse.xyz)                                                               |

Once your chains are enabled and you're happy with your RPC providers, you're going to want to add the appropriate
Wallet Connector to the Dynamic Context Provider that you integrated when [getting started](/quickstart).

Each chain has it's own wallet connector, and there are also some add on wallet connectors used to enable
things like smart wallets. Follow the steps below and you'll be up and running in no time!

<Steps>
  <Step title="Find the right packages">
    Below is a list of all the available wallet connectors and their corresponding packages.

    | Package Name           | Chain   | WalletConnector to include                                       |
    | :--------------------- | :------ | ---------------------------------------------------------------- |
    | @dynamic-labs/ethereum | EVM     | `EthereumWalletConnectors`                                       |
    | @dynamic-labs/algorand | ALGO    | `AlgorandWalletConnectors`                                       |
    | @dynamic-labs/solana   | SOL     | `SolanaWalletConnectors` or `SolanaWalletConnectorsWithConfig`   |
    | @dynamic-labs/eclipse  | ECLIPSE | `EclipseWalletConnectors` or `EclipseWalletConnectorsWithConfig` |
    | @dynamic-labs/flow     | FLOW    | `FlowWalletConnectors`                                           |
    | @dynamic-labs/starknet | STARK   | `StarknetWalletConnectors`                                       |
    | @dynamic-labs/cosmos   | COSMOS  | `CosmosWalletConnectors`                                         |
    | @dynamic-labs/bitcoin  | BTC     | `BitcoinWalletConnectors`                                        |

    ##### EVM Addon Wallets

    | Package Name              | Which Wallets | WalletConnector to include     |
    | :------------------------ | :------------ | :----------------------------- |
    | @dynamic-labs/magic       | *magic*       | `MagicWalletConnectors`        |
    | @dynamic-labs/blocto-evm  | *blocto*      | `BloctoEvmWalletConnectors`    |
    | @dynamic-labs/starknet    | STARK         | `StarknetWalletConnectors`     |
    | @dynamic-labs/ethereum-aa | ZeroDev       | `ZeroDevSmartWalletConnectors` |

    <Tip>
      EthereumWalletConnectors (@dynamic-labs/ethereum) also includes all EVM
      compatible chains including layer 2's i.e. Base as well as [Dynamic Embedded
      Wallets](/wallets/embedded-wallets/dynamic-embedded-wallets).
    </Tip>
  </Step>

  <Step title="Install the connectors">
    Install 1 or more wallet connectors from the packages listed above. Here is an example for Ethereum and Solana:

    <CodeGroup>
      ```bash npm
      npm i @dynamic-labs/ethereum @dynamic-labs/solana
      ```

      ```bash yarn
      yarn add @dynamic-labs/ethereum @dynamic-labs/solana
      ```
    </CodeGroup>
  </Step>

  <Step title="Add the connectors to DynamicContextProvider">
    Add to an array in your settings under `walletConnectors`. Here is an example for Ethereum and Solana:

    ```jsx
    import { DynamicContextProvider, DynamicWidget} from '@dynamic-labs/sdk-react-core';
    import { EthereumWalletConnectors } from '@dynamic-labs/ethereum';
    import { SolanaWalletConnectors } from '@dynamic-labs/solana';

    const App = () => (
    <DynamicContextProvider
        settings={{
        ...
        walletConnectors: [ EthereumWalletConnectors, SolanaWalletConnectors ],
            ...
        }}>
    </DynamicContextProvider>
    );

    export default App;
    ```
  </Step>
</Steps>

## Configurable Wallet Connectors

Some wallet connectors come with a configurable alternative.
This is useful if you want to customize the wallet connector to your needs.

> For example, you can pass a `ConnectionConfig` to set the commitement level or define HTTP request headers for Solana and Eclipse.

See an example of how to use the configurable wallet connector for Solana:

```jsx
import { DynamicContextProvider } from '@dynamic-labs/sdk-react-core';
import { SolanaWalletConnectorsWithConfig } from '@dynamic-labs/solana';

<DynamicContextProvider
  settings={{
    walletConnectors: [
      SolanaWalletConnectorsWithConfig({
        commitment: 'confirmed',
        httpHeaders: {
          'X-Requested-With': 'XMLHttpRequest',
        },
      }),
    ],
  }}
>
  {...}
</DynamicContextProvider>
```

Here are our available configurable wallet connectors:

| Chain   | Wallet Connector with Config        | Type of Wallet Connector with Config                      |
| :------ | :---------------------------------- | :-------------------------------------------------------- |
| Solana  | `SolanaWalletConnectorsWithConfig`  | `(connectionConfig: ConnectionConfig) => WalletConnector` |
| Eclipse | `EclipseWalletConnectorsWithConfig` | `(connectionConfig: ConnectionConfig) => WalletConnector` |

> `ConnectionConfig` is the object type that can be passed as second argument to` @solana/web3.js`'s `Connection` constructor.
> You can find more information about it [here](https://solana.com/docs/clients/javascript-reference#connection).

<Card title="What next?" href="/chains/embedded-wallets-chains" icon="link" color="#4779FE">
  Click here to learn how to enable chains for embedded wallets!
</Card>


# Custom EVM networks



<Note>
  For adding a custom EVM networks with Wagmi see: [WAGMI - adding custom networks](https://docs.dynamic.xyz/react-sdk/using-wagmi#adding-custom-networks)
</Note>

You can enable any EVM network that we do not currently support out of the box by passing an array of `EvmNetwork`
to the `DynamicContextProvider`'s `overrides.evmNetworks` settings.

This can be done in two different ways:

1.  By passing an array of `EvmNetwork`, it completely overrides whatever networks were received from your dashboard configurations and uses that array instead.

2.  By passing a method with signature `(dashboardNetworks: EvmNetwork[]) => EvmNetwork[]`, you can use this callback to first
    receive the array of networks that was sent from your dashboard configurations, and then return the array of networks you want the app
    to use.

<Note>
  The second approach is best for making adjustments to the networks you get from our dashboard (like changing rpc urls),
  as well as when you want to hide some specific networks.

  If you're just trying to merge new networks with the ones from dashboard, we have a helper function that will make that easier:

  ```TypeScript
  import { mergeNetworks } from '@dynamic-labs/sdk-react-core';

  const DynamicSettings = {
    overrides: {
      evmNetworks: (networks) => mergeNetworks(myEvmNetworks, networks),
    }
  };
  ```

  > Note that the order of the params for `mergeNetworks` matters: the first param takes precedence in case of a conflict.
</Note>

## Example

The following example sets the Ethereum mainnet and Polygon as supported networks for the application.
A comprehensive list of networks can be found at [chainlist.org](https://chainlist.org)

```TypeScript
// Setting up list of evmNetworks
const evmNetworks = [
  {
    blockExplorerUrls: ['https://etherscan.io/'],
    chainId: 1,
    chainName: 'Ethereum Mainnet',
    iconUrls: ['https://app.dynamic.xyz/assets/networks/eth.svg'],
    name: 'Ethereum',
    nativeCurrency: {
      decimals: 18,
      name: 'Ether',
      symbol: 'ETH',
      iconUrl: 'https://app.dynamic.xyz/assets/networks/eth.svg',
    },
    networkId: 1,

    rpcUrls: ['https://mainnet.infura.io/v3/'],
    vanityName: 'ETH Mainnet',
  },
{
    blockExplorerUrls: ['https://etherscan.io/'],
    chainId: 5,
    chainName: 'Ethereum Goerli',
    iconUrls: ['https://app.dynamic.xyz/assets/networks/eth.svg'],
    name: 'Ethereum',
    nativeCurrency: {
      decimals: 18,
      name: 'Ether',
      symbol: 'ETH',
      iconUrl: 'https://app.dynamic.xyz/assets/networks/eth.svg',
    },
    networkId: 5,
    rpcUrls: ['https://goerli.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161'],

    vanityName: 'Goerli',
  },
  {
    blockExplorerUrls: ['https://polygonscan.com/'],
    chainId: 137,
    chainName: 'Matic Mainnet',
    iconUrls: ["https://app.dynamic.xyz/assets/networks/polygon.svg"],
    name: 'Polygon',
    nativeCurrency: {
      decimals: 18,
      name: 'MATIC',
      symbol: 'MATIC',
      iconUrl: 'https://app.dynamic.xyz/assets/networks/polygon.svg',
    },
    networkId: 137,
    rpcUrls: ['https://polygon-rpc.com'],
    vanityName: 'Polygon',
  },
];

const App = () => (
  <DynamicContextProvider
    settings={{
      environmentId: 'REPLACE_WITH_YOUR_ENV_ID',
      overrides: { evmNetworks },
    }}
  >
    <Home />
  </DynamicContextProvider>
);

export default App;
```

## Type Reference

### Definition

| Attribute              | Value            | Required/Optional |
| ---------------------- | ---------------- | ----------------- |
| blockExplorerUrls      | `string[]`       | Required          |
| chainId                | `number`         | Required          |
| name                   | `string`         | Required          |
| iconUrls               | `string[]`       | Required          |
| nativeCurrency         | `NativeCurrency` | Required          |
| networkId              | `number`         | Required          |
| privateCustomerRpcUrls | `string[]`       | Optional          |
| rpcUrls                | `string[]`       | Required          |
| vanityName             | `string`         | Optional          |

#### NativeCurrency

| Attribute | Value    | Required/Optional |
| --------- | -------- | ----------------- |
| decimals  | `number` | Required          |
| iconUrl   | `string` | Optional          |
| name      | `string` | Required          |
| symbol    | `string` | Required          |
| denom     | `string` | Optional          |


# Switching Networks



### Usage

Using the primaryWallet provided by [useDynamicContext](/react-sdk/hooks/usedynamiccontext),
you have two useful methods for network switching:

<ParamField path="supportsNetworkSwitching" type="() => boolean">
  Available on the connector object for the wallet.Whether the connector supports network switching.
</ParamField>

<ParamField path="switchNetwork" type="(networkChainId?: number | string) => Promise<void>">
  Available directly on the wallet object. Switch to another network by providing either the network name or chain id.
</ParamField>

When calling `switchNetwork` with a connector supporting network switching, the SDK will either request the user to confirm the network switch or add the network if it was not previously set.

### Example

```TypeScript
const { primaryWallet } = useDynamicContext();

if (primaryWallet?.connector.supportsNetworkSwitching()) {
  await primaryWallet.switchNetwork(137);
  console.log("Success! Network switched");
}
```

<Frame>
  ![1440](https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/network-switching.png "Metamask network switching prompt")
</Frame>


# Using RPC Providers



## Summary

We provide the [`useRpcProviders` hook](/react-sdk/hooks/userpcproviders)
that allows direct access to RPC providers for EVM & Solana.
Rpc providers can be used to make RPC calls to the blockchain while also providing convenience
methods without going through a wallet.

Each provider will use the RPC configured in the Dashboard if present,
otherwise they fall back to public RPCs urls. By default, all EVM and Solana
networks have public default providers as shown in this table:

| Network         | Public RPC Url                                                                                                                           |
| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| Ethereum        | [https://cloudflare-eth.com](https://cloudflare-eth.com)                                                                                 |
| Solana          | [https://api.mainnet-beta.solana.com](https://api.mainnet-beta.solana.com)                                                               |
| Optimism        | [https://mainnet.optimism.io](https://mainnet.optimism.io)                                                                               |
| Gnosis Chain    | [https://rpc.gnosischain.com](https://rpc.gnosischain.com)                                                                               |
| Aurora          | [https://mainnet.aurora.dev](https://mainnet.aurora.dev)                                                                                 |
| Polygon         | [https://polygon-rpc.com](https://polygon-rpc.com)                                                                                       |
| Palm            | [https://palm-mainnet.infura.io/v3/3a961d6501e54add9a41aa53f15de99b](https://palm-mainnet.infura.io/v3/3a961d6501e54add9a41aa53f15de99b) |
| BNB Smart Chain | [https://arb1.arbitrum.io/rpc](https://arb1.arbitrum.io/rpc)                                                                             |

## Dashboard Configuration

To enter your provider url for a given network:

1.  Go to the [Chains & Networks](https://app.dynamic.xyz/dashboard/chains-and-networks) page in your Dashboard.
2.  Click on the chain to open the details tab
3.  Click the down down arrow to expand a network
4.  Enter your Provider Url
5.  Click the test button to check url

## Usage

You are able to use the [`useRpcProviders` hook](/react-sdk/hooks/userpcproviders)
to obtain an object with rpc providers, and this hook expects a selector parameter
that you must use to select either EVM or Solana rpc providers.

```typescript
import { useRpcProviders } from '@dynamic-labs/sdk-react-core'
import { evmProvidersSelector } from '@dynamic-labs/ethereum-core'
import { solanaProvidersSelector } from '@dynamic-labs/solana-core'

const App = () => {
  const evmProviders = useRpcProviders(evmProvidersSelector)
  const solanaProviders = useRpcProviders(solanaProvidersSelector)
}
```

The hook returns either [EvmRpcProviderMethods](/react-sdk/objects/EvmRpcProviderMethods)
or [SolanaRpcProviderMethods](/react-sdk/objects/SolanaRpcProviderMethods), with the following fields:

<ParamField path="defaultProvider" type="EvmRpcProvider | SolanaRpcProvider | undefined">
  The provider for EVM or Solana Mainnet, if mainnet is enabled
</ParamField>

<ParamField path="providers" type="EvmRpcProvider[] | SolanaRpcProvider[] | undefined">
  A full list of all EVM or Solana providers that have been configured
</ParamField>

<ParamField path="getProviderByChainId" type="(chainId: number | string) => EvmRpcProvider | SolanaRpcProvider | undefined">
  A convenience method that lets you retrieve a provider for a specific Chain ID
</ParamField>

<Info>
  Check out the reference for
  [EvmRpcProvider](/react-sdk/objects/EvmRpcProvider) and
  [SolanaRpcProvider](/react-sdk/objects/SolanaRpcProvider)
</Info>

### Example

Below is a simple example using EVM providers to fetch an arbitrary ENS mapping:

```TypeScript
import { useRpcProviders } from '@dynamic-labs/sdk-react-core'
import { evmProvidersSelector } from '@dynamic-labs/ethereum-core'

const useLogEnsMapping = () => {
  const { defaultProvider } = useRpcProviders(evmProvidersSelector)

  const mainnetProvider = defaultProvider?.provider;

  const ensAddress = mainnetProvider.resolveName('myname.eth');

  console.log('address for myname.eth', ensAddress);
}
```


# Smart Wallet Chains



If you are using our Zerodev integration to enable Smart Wallets, you'll need to already have the Ethereum chain enabled. If you don't, please refer to [this guide](/chains/enabling-chains).

You will also need to install a new package, even if you already have `@dynamic-labs/ethereum` installed, it's called `ethereum-aa`.

<CodeGroup>
  ```bash npm
  npm i @dynamic-labs/ethereum-aa
  ```

  ```bash yarn
  npm i @dynamic-labs/ethereum-aa

  ```
</CodeGroup>

Then, you need to add the `ZeroDevSmartWalletConnectors` to the `walletConnectors` array in the SDK settings:

```jsx
import { DynamicContextProvider } from '@dynamic-labs/sdk-react-core';
import { EthereumWalletConnectors } from '@dynamic-labs/ethereum';
import { ZeroDevSmartWalletConnectors } from '@dynamic-labs/ethereum-aa';

const App = () => (
  <DynamicContextProvider
    settings={{
      environmentId: 'XXXXX',
      walletConnectors: [ EthereumWalletConnectors, ZeroDevSmartWalletConnectors ],
      ...
    }}
  >
  </DynamicContextProvider>
);
```


# Custom Information Capture



## Overview

In order to capture information immediately after a user logs in that is not already available via our standard info capture (such as their shoe size, etc.), you can create custom fields to be prompted during onboarding and stored in the `metadata` object of the `user`. There are three different field types:

### Input

*   **Unique**: Can force uniqueness in the environment, ex: only one user can have the nickname `Bob`.
*   **Regex**: Can follow a regex, ex: text must be an Ethereum address: `^0x`.

### Dropdown

*   A list of options that a user can select in a dropdown, ex: `Pizza`, `Ice Cream`.

### Checkbox

*   Provide the text that goes along with a checkbox, ex: `Confirm that you are using this app at your own risk`.

All of these fields can be configured to be optional, meaning they can be skipped during onboarding. Additionally, all fields can be edited by the user in the Dynamic Widget.

## Configuration

In the Dynamic Dashboard, go to [the Login and User Profile Settings page](https://app.dynamic.xyz/dashboard/log-in-user-profile). Here, under "Additional User Information" you will see a list of your current available fields and at the bottom you will see a button to "Create New Field".


# CSS Variables



The Dynamic SDK allows you to customize the theme via [CSS variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties). We recommend setting these CSS variables in your host site by targetting the `dynamic-shadow-dom` class.

Here is an example of a custom theme done via CSS variables and the code used below:

<iframe frameBorder="0" width="100%" height="600px" src="https://readme-embeds.dynamic.xyz/css-variables" />

```css
.dynamic-shadow-dom {
  --dynamic-font-family-primary: "Poppins", sans-serif;
  --dynamic-font-family-numbers: "Roboto Mono", monospace, sans-serif;
  --dynamic-base-1: #fff;
  --dynamic-base-2: #fff;
  --dynamic-base-3: #fff;
  --dynamic-base-4: #fff;
  --dynamic-base-5: #fff;
  --dynamic-brand-hover-color: linear-gradient(
      0deg,
      rgba(0, 0, 0, 0.08),
      rgba(0, 0, 0, 0.08)
    ), #4779ff;
  --dynamic-brand-primary-color: #4779ff;
  --dynamic-brand-secondary-color: rgba(71, 121, 255, 0.15);
  --dynamic-connection-green: #41cc99;
  --dynamic-border-radius: 12px;
  --dynamic-hover: rgba(22, 37, 77, 0.03);
  --dynamic-error-1: #ff4646;
  --dynamic-error-2: rgba(255, 70, 70, 0.1);
  --dynamic-footer-background-color: #fff;
  --dynamic-footer-text-color: #000;
  --dynamic-footer-icon-color: #4779ff;
  --dynamic-loading-animation-gradient: linear-gradient(
    90deg,
    rgba(255, 255, 255, 0) 0,
    rgba(255, 255, 255, 0.2) 20%,
    rgba(255, 255, 255, 0.5) 60%,
    rgba(255, 255, 255, 0)
  );
  --dynamic-overlay: #fff;
  --dynamic-shadow-down-1: 0px 3px 0px #fff;
  --dynamic-shadow-down-2: 0px 3px 0px #000000;
  --dynamic-shadow-down-3: 0px 6px 0px #000000;
  --dynamic-shadow-up-1: 0px -8px 48px -8px rgba(109, 121, 165, 0.16);
  --dynamic-text-link: #0047ff;
  --dynamic-text-primary: #000;
  --dynamic-text-secondary: black;
  --dynamic-text-size-body-mini: 11px;
  --dynamic-text-size-body-normal: 15px;
  --dynamic-text-size-body-small: 12px;
  --dynamic-text-size-button-primary: 14px;
  --dynamic-text-size-button-secondary: 12px;
  --dynamic-text-size-numbers-big: 14px;
  --dynamic-text-size-numbers-medium: 12px;
  --dynamic-text-size-numbers-small: 10px;
  --dynamic-text-size-title: 18px;
  --dynamic-text-tertiary: rgba(0, 0, 0, 0.65);
  --dynamic-badge-background: #000;
  --dynamic-badge-color: #fff;
  --dynamic-badge-dot-background: #fff;
  --dynamic-badge-primary-background: #4779FF;
  --dynamic-badge-primary-color: #fff;
  --dynamic-search-bar-background: ;
  --dynamic-search-bar-background-hover: ;
  --dynamic-search-bar-background-focus: #fff;
  --dynamic-search-bar-border: 2px solid #000;
  --dynamic-search-bar-border-hover: 2px solid #000;
  --dynamic-search-bar-border-focus: 2px solid #000;
  --dynamic-modal-border: 2px solid #000;
  --dynamic-modal-width: 22.5rem;
  --dynamic-modal-padding: 1.5rem;
  --dynamic-footer-padding: 1.25rem 1.25rem 1.25rem 1.5rem;
  --dynamic-footer-border: 2px solid #000;
  --dynamic-wallet-list-tile-padding: 0.75rem;
  --dynamic-wallet-list-tile-gap: 0.375rem;
  --dynamic-wallet-list-max-height: 16.25rem;
  --dynamic-wallet-list-tile-background: #fff;
  --dynamic-wallet-list-tile-border: 2px solid #000000;
  --dynamic-wallet-list-tile-shadow: 0px 3px 0px #fff;
  --dynamic-wallet-list-tile-background-hover: #fff;
  --dynamic-wallet-list-tile-border-hover: 2px solid #000000;
  --dynamic-wallet-list-tile-shadow-hover: 0px 3px 0px #000000;
  --dynamic-connect-button-background: #fff;
  --dynamic-connect-button-color: #000;
  --dynamic-connect-button-border: 2px solid #000;
  --dynamic-connect-button-shadow: 0px 3px 0px #fff;
  --dynamic-connect-button-background-hover: rgba(22, 37, 77, 0.03);
  --dynamic-connect-button-color-hover: #000;
  --dynamic-connect-button-border-hover: 2px solid #000;
  --dynamic-connect-button-shadow-hover: 0px 3px 0px #000000;
  --dynamic-tooltip-color: #000;
  --dynamic-tooltip-text-color: #fff;
}
```

### Theme variables

| Variable                               | Description                  |
| -------------------------------------- | ---------------------------- |
| `--dynamic-base-1`                     | Base 1 color                 |
| `--dynamic-base-2`                     | Base 2 color                 |
| `--dynamic-base-3`                     | Base 3 color                 |
| `--dynamic-base-4`                     | Base 4 color                 |
| `--dynamic-base-5`                     | Base 5 color                 |
| `--dynamic-brand-primary-color`        | Brand primary color          |
| `--dynamic-border-radius`              | Border radius (default 24px) |
| `--dynamic-connection-green`           | Connected status dot         |
| `--dynamic-hover`                      | Hover background color       |
| `--dynamic-error-1`                    | Error text color             |
| `--dynamic-error-2`                    | Error background color       |
| `--dynamic-loading-animation-gradient` | Loading animation gradient   |
| `--dynamic-shadow-down-1`              | Shadow down 1                |
| `--dynamic-shadow-down-2`              | Shadow down 2                |
| `--dynamic-shadow-down-3`              | Shadow down 3                |
| `--dynamic-shadow-up-1`                | Shadow up 1                  |
| `--dynamic-success-1`                  | Success text color           |
| `--dynamic-success-2`                  | Success background color     |

### Text variables

| Variable                               | Description                   |
| -------------------------------------- | ----------------------------- |
| `--dynamic-font-family-primary`        | Font used for general text    |
| `--dynamic-font-family-numbers`        | Font used for number elements |
| `--dynamic-text-primary`               | Primary text color            |
| `--dynamic-text-secondary`             | Secondary text color          |
| `--dynamic-text-tertiary`              | Tertiary text color           |
| `--dynamic-text-link`                  | Link text color               |
| `--dynamic-text-size-body-mini`        | Text size body mini           |
| `--dynamic-text-size-body-small`       | Text size body small          |
| `--dynamic-text-size-body-normal`      | Text size body normal         |
| `--dynamic-text-size-button-primary`   | Text size primary button      |
| `--dynamic-text-size-button-secondary` | Text size secondary button    |
| `--dynamic-text-size-numbers-small`    | Text size small numbers       |
| `--dynamic-text-size-numbers-medium`   | Text size medium numbers      |
| `--dynamic-text-size-numbers-big`      | Text size big numbers         |
| `--dynamic-text-size-title`            | Text size title               |

### Modal variables

| Variable                              | Description               |
| ------------------------------------- | ------------------------- |
| `--dynamic-modal-border`              | Modal border              |
| `--dynamic-modal-width`               | Modal width               |
| `--dynamic-modal-padding`             | Modal padding             |
| `--dynamic-modal-backdrop-background` | Modal backdrop background |
| `--dynamic-modal-backdrop-filter`     | Modal backdrop filter     |

### Footer variables

| Variable                            | Description           |
| ----------------------------------- | --------------------- |
| `--dynamic-footer-background-color` | Background color      |
| `--dynamic-footer-text-color`       | Text color            |
| `--dynamic-footer-icon-color`       | Icon color            |
| `--dynamic-footer-arrow-color`      | Arrow color           |
| `--dynamic-footer-border`           | Top and bottom border |
| `--dynamic-footer-padding`          | Footer padding        |

### Wallet list variables

| Variable                                        | Description                                                    |
| ----------------------------------------------- | -------------------------------------------------------------- |
| `--dynamic-wallet-list-max-height`              | Maximum wallet list height                                     |
| `--dynamic-wallet-list-tile-gap`                | Gap between wallet tiles                                       |
| `--dynamic-wallet-list-tile-padding`            | Wallet tile padding                                            |
| `--dynamic-wallet-list-tile-background`         | Wallet tile background                                         |
| `--dynamic-wallet-list-tile-border`             | Wallet tile border                                             |
| `--dynamic-wallet-list-tile-shadow`             | Wallet tile shadow                                             |
| `--dynamic-wallet-list-tile-background-hover`   | Wallet tile background on hover                                |
| `--dynamic-wallet-list-tile-border-hover`       | Wallet tile border on hover                                    |
| `--dynamic-wallet-list-tile-shadow-hover`       | Wallet tile shadow on hover                                    |
| `--dynamic-wallet-list-tile-animation-duration` | Wallet time animation duration between regular and hover state |
| `--dynamic-badge-background`                    | Wallet tile badge background                                   |
| `--dynamic-badge-color`                         | Wallet tile badge text color                                   |
| `--dynamic-badge-dot-background`                | Wallet tile badge dot background                               |
| `--dynamic-recommended-badge-background`        | Wallet tile recommended badge background                       |
| `--dynamic-recommended-badge-color`             | Wallet tile recommended badge text color                       |
| `--dynamic-search-bar-background`               | Wallet search bar background                                   |
| `--dynamic-search-bar-border`                   | Wallet search bar border                                       |
| `--dynamic-search-bar-background-hover`         | Wallet search bar background on hover                          |
| `--dynamic-search-bar-border-hover`             | Wallet search bar border on hover                              |
| `--dynamic-search-bar-background-focus`         | Wallet search bar background on focus                          |
| `--dynamic-search-bar-border-focus`             | Wallet search bar border on focus                              |

### Connect button variables

| Variable                                    | Description                |
| ------------------------------------------- | -------------------------- |
| `--dynamic-connect-button-background`       | Button background          |
| `--dynamic-connect-button-color`            | Button text color          |
| `--dynamic-connect-button-border`           | Button border              |
| `--dynamic-connect-button-shadow`           | Button shadow              |
| `--dynamic-connect-button-background-hover` | Button background on hover |
| `--dynamic-connect-button-color-hover`      | Button text color on hover |
| `--dynamic-connect-button-border-hover`     | Button border on hover     |
| `--dynamic-connect-button-shadow-hover`     | Button shadow on hover     |
| `--dynamic-connect-button-radius`           | Button border radius       |

### Other variables

| Variable                       | Description                     |
| ------------------------------ | ------------------------------- |
| `--dynamic-overlay`            | Widget connection modal overlay |
| `--dynamic-tooltip-color`      | Tooltip background color        |
| `--dynamic-tooltip-text-color` | Tooltip text color              |

If you wish to customize the UI even more, we recommend using [CSS overrides](/design-customizations/css/custom-css).


# Custom CSS



### Summary

The Dynamic SDK uses [shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web%5FComponents/Using%5Fshadow%5FDOM). Shadow DOM allows the SDK to look as intended wherever it is hosted and it plays nicely with your existing styling system. We completely rewrote our styling system to support encapsulation while still allowing for intentional styling overrides.

### How to overrides SDK styles

We introduced a new cssOverrides prop in the settings allowing you to pass either a string or JSX element for styling overrides. These overrides will be placed inside the shadow DOM and will only affect SDK elements. Overrides should be used when you want to completely customize the style and behaviour of the SDK. General theme customization can be done via CSS variables instead.

#### Using a string

You can pass string overrides directly through settings. The content will be wrapped by a style tag inside the shadow DOM. Here's an example that adds a one time rotating animation to the wallet logo in the wallet list.

```TypeScript
 const cssOverrides = `
  .wallet-list-item__tile:hover > img {
    animation: rotate 1s forwards;
  }

  @keyframes rotate {
    0% {
      transform: rotate(0deg);
    }
    100% {
      transform: rotate(360deg);
    }
  }
`
```

<DynamicContextProvider
  settings={{
   // ...
   cssOverrides,
   // ...
  }}
>
  <HomePage />
</DynamicContextProvider>

\`\`

#### Using JSX element

You can link a stylesheets to be added the shadow DOM. Since the stylesheet is loaded, there might be a small delay before the styles get applied. If this is a problem, we recommend using the string method.

```TypeScript
<DynamicContextProvider
  settings={{
    // ...
    cssOverrides: <link rel='stylesheet' href='/external-styles.css' />, // pass JSX element to be included in the shadow DOM
    // ...
  }}
  >
  <HomePage />
</DynamicContextProvider>
```

#### Disabling shadow DOM

Shadow DOM is enabled by default on the SDK version v0.15.0 rc and higher. If you wish to disable it, you can use the shadowDOMEnabled in the dynamic context settings.

```TypeScript
<DynamicContextProvider
  settings={{
    // ...
    shadowDOMEnabled: false,
    // ...
  }}
  >
  <Home />
</DynamicContextProvider>
```


# Themes & Settings



Dynamic offers beautiful themes in both light, dark, or auto themes with the ability to customize the colors and styles to your liking, all through your developer dashboard.

Simply go to the [design page](https://app.dynamic.xyz/dashboard/design) in your dashboard and toggle from the current list of settings (more customizations will be coming soon).

You can toggle between:

1.  Themes
2.  Light and dark theme
3.  Your brand color
4.  Subtle/Bold

## Light & Dark Modes

You can manage light and dark mode in three different ways depending on your use case:

### Using the dashboard

You can set your theme in our [Dashboard](https://app.dynamic.xyz/dashboard/design), under the "Style" heading, where you see the toggle for "Light" or "Dark".

### Using DynamicContextProvider

While you can set your theme in our [Dashboard](https://app.dynamic.xyz/dashboard/design), a lot of site support users switching between light and dark theme and it's important that our SDK updates to match the theme of the site.

You can update whether you want to show the Light or Dark theme to customers by passing a prop to DynamicContextProvider with `theme`:

```TypeScript
<DynamicContextProvider
  theme='dark', <-- you can pass either 'dark' or 'light'
  settings={{
    environmentId: "<<sandboxEnvironmentId>>",
    appName: "<<projectName>>",
    appLogoUrl: "...",
  }}>
  <HomePage />
</DynamicContextProvider>
```

### Using CSS directly

The root element of your application (perhaps `body`) will have a data-dynamic-theme variable set. You can override this at any time to adjust the theme to light or dark mode. There is also an "auto" value which will adapt to whatever is set in \[]`prefers-color-scheme`]\([https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme)).


# Adapt Copy With Translations



The SDK allows you to customize almost all of the text it displays, as well as add extra translations. You do so using the "locale" prop on the DynamicContextProvider.

In this short guide we will update some text in the default language (English), add an Italian translation, learn how to let the user change their language and along the way, learn all about the locale object you'll be working with!

Let's imagine we want to change the "Select your wallet" text in the image below to "Find your favorite" and then do the same with an Italian translation.

<Frame>![Select your wallet](https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/select-your-wallet.png)</Frame>

We simply need to inspect that element in the browser, and find the key that is being used to display that text by checking the `copykey` attribute on that element. In this case, you'll see that it is "dyn\_login.title.all\_wallet\_list".

Now we'll create our locale object, and add a new key with that value. It always starts with a key of the language you're editing, in this case "en" for English. Then we add the key that we found in the browser, and then the value we want to replace it with.

```tsx
const locale = {
  en: {
    dyn_login: {
      title: {
        all_wallet_list: "Find your favorite",
      },
    },
  },
  it: {
    dyn_login: {
      title: {
        all_wallet_list: "Trova il tuo preferito",
      },
    },
  },
};
```

<Accordion title="Using the translations file">
  You probably noticed the translation object has a very specific shape. You can actually check it yourself by looking in "@dynamic-labs/sdk-react-core/src/lib/locale/en/translation.js" which you can reach by going to the definition of LocaleResource in your IDE once imported (`import { LocaleResource } from '@dynamic-labs/sdk-react-core'`)

  Here is an example excerpt from that file:

  ```jsx
      dyn_account_exists: {
          connect: 'Connect with {{socialOauth}}',
          description: 'It looks like an account already exists using',
          title: 'Account already exists',
          trail_message_email: '. Please log in with your email.',
          trail_message_social: 'through {{socialOauth}}',
      },
  ```
</Accordion>

We're now ready to pass our locale object to the DynamicContextProvider like so:

```tsx
<DynamicContextProvider settings={settings_go_here} locale={locale}>
  <App />
</DynamicContextProvider>
```

The result should then immediately show up in the SDK UI like so:

<Frame>
  <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/find-your-favourite.png" />
</Frame>

Next we'll need to let the user change between languages, and it's as simple as the following:

```tsx
const {locale} = useDynamicContext();

 const handleOnClick = (localeProvided) => {
    if (localeProvided === 'it') {
      locale.changeLanguage('it');
    } else {
      locale.changeLanguage('en');
    }
 };

return (
  <button onClick={() => handleOnClick(locale.language)}>
    {locale.language === 'it' ? 'English' : 'Italiano'}
  </button>
);
```

That's it! You now know how to customize the text in the SDK to your heart's content.

## Further Reading

Make sure to check out the full reference for the locale object [here](/react-sdk/objects/locale).


# UI Customization Overview



<Tip>
  All the guides in this section pertain to UI customizations for [the Dynamic UI components](/react-sdk/components) of the SDK. If you are implementing Dynamic headlessly i.e. without any of our UI components, check out the [headless section](/headless) instead.
</Tip>

There are multiple ways to customize the UI of the Dynamic SDK. You can customize the views, translations, and CSS design among other things. We outline the main methods below and link to respective guides.

## Views

Views are used to customize the kind of UI that shows up at any point in time in your application, specifically giving you complete control over the signup/login options you show your user. [Learn more about views.](/design-customizations/views)

## Translations

Every piece of text displayed in a Dynamic UI component has a corresponding key in our translation file. You can override these keys to customize the text displayed in the UI. [Learn more about translations.](/design-customizations/customizing-copy-translations)

## CSS Design

You have three primary options for completely adapting the look and feel of the UI components on a CSS level. These are:

<CardGroup cols={3}>
  <Card title="Themes">
    You can use a theme to override the default styles of components cohesively. [Learn more.](/design-customizations/css/themes)
  </Card>

  <Card title="CSS Variables">
    You can use CSS variables to override individual styles of the UI components.

    [Learn more.](/design-customizations/css/css-variables)
  </Card>

  <Card title="Custom CSS">
    You can add custom CSS in order to ignore all Dynamic styles and create your own.

    [Learn more.](/design-customizations/css/custom-css)
  </Card>
</CardGroup>

<Tip>
  You'll get to interact with all three methods if you take the [Design Tutorial](/design-customizations/tutorials/design-tutorial)!
</Tip>


# Customizing Signup/Login UI



As you can see [on the demo](https://demo.dynamic.xyz/), you can configure the login page in many different ways. This page walks you through where to find those configuration options.

For the most part you will find them in [the "Log in & User Profile" section of the Dynamic Dashboard](https://app.dynamic.xyz/dashboard/log-in-user-profile).

## Choose login methods to display and in what order

As far as the login methods to display, this is determined firstly by whatever you have enabled at the time, but you can override it using Login Views and also handle custom ordering there - learn more in [the tutorial](/design-customizations/tutorials/login-views-guide).

## Embedded Widget or Button

By default, if you use our DynamicWidget, it will show up as a button which opens the login screen in a modal. You can change this to an embedded widget so that the whole signup/login modal shows up directly on the page.

You can see both these options in action on [the demo](https://demo.dynamic.xyz/) in the "Widget integration" section under "Design". Learn more about the standard widget [here](/react-sdk/components/dynamicwidget) and the embedded widget [here](/react-sdk/components/dynamicembeddedwidget).

## Email Continue button

By default, we split email login into two components, an input and a continue button directly below it. You can merge them into one single component using the "Display email submit button inside the input field" button in "Design Settings".

## Expanded vs Collapsed wallet list

The login screen normally lists wallets one by one, which can clutter the screen if branded wallet login is not the most important thing to your users. You can collapse this list down into a single "Continue with a wallet" button (which will in turn open this list) but using the "Collapse wallet list" button in "Design Settings".

## Splitting Social & Email

The social and email sections of the login screen are bundled together by default i.e. without an OR seperator like with branded wallets, but you can change this using the "Split email and social into separate sections" button in "Design Settings".

## Social above email

The default UI shows email login above social if both are enabled. You can switch the default ordering using the "Place social above email section" button in "Design Settings".

## Changing the text

You can change any part of the text which is shown in the login UI using our translations feature - learn more about that in [the tutorial](/design-customizations/customizing-copy-translations).

## Adjusting Signup/Login button

If you want to change the text/css, please refer to the Changing CSS section below. To change the text itself, we will use the innerButtonComponent prop on the DynamicWidget. This prop allows us to pass in a JSX element that will be rendered as the button.

```jsx
<DynamicWidget
  innerButtonComponent={<button>My Custom Connect Wallet Button</button>}
>
  {/* ... rest of your app ... */}
</DynamicWidget>
```

If you want to use your own custom button we can use `setShowAuthFlow` available though [the useDynamicContext hook](/react-sdk/hooks/usedynamiccontext).

We access it like this:

```jsx
const { setShowAuthFlow } = useDynamicContext();
```

We can then use this function to trigger the Dynamic Connect Wallet flow when our custom button is clicked.

```jsx
<button onClick={() => setShowAuthFlow(true)}>
  My Custom Connect Wallet Button
</button>
```

## Adding your own sections

You can inject your own custom content into the login UI using [our Custom Widget Content tutorial](/design-customizations/tutorials/custom-widget-content).

## Changing the CSS

We offer multiple ways of changing/overriding the CSS in the login view as well as every other view:

<CardGroup cols={3}>
  <Card title="Themes">
    You can use a theme to override the default styles of components cohesively. [Learn more.](/design-customizations/css/themes)
  </Card>

  <Card title="CSS Variables">
    You can use CSS variables to override individual styles of the UI components.

    [Learn more.](/design-customizations/css/css-variables)
  </Card>

  <Card title="Custom CSS">
    You can add custom CSS in order to ignore all Dynamic styles and create your own.

    [Learn more.](/design-customizations/css/custom-css)
  </Card>
</CardGroup>


# Inject Extra Content in Widget



Should you want to add content to the widget (or indeed any of our UI components), since we use [a Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM) to ensure encapsulation of styling, you should first access the `shadowRoot` property of the host element - from there you can access the elements and manipulate them or append new ones.

For example, if you want to target the Dynamic Widget, you'd look for an element with `data-testid="dynamic-modal-shadow"`:

```javascript
   const shadowHosts = document.querySelectorAll(
        '[data-testid="dynamic-modal-shadow"]'
    );
```

From there you'll need to pick the element that is the shadowRoot:

```javascript
  shadowHosts.forEach((shadowHost) => {
    if (shadowHost && shadowHost.shadowRoot) {
      // Logic here
    }
  })
```

As a best practice, you can encapsulate your manipulation logic in a function, then call that function immediately but also add it to a mutation observer.

The observer is important for multiple reasons:

1.  The widget content may change dynamically (e.g., different views or states).
2.  The widget might be destroyed and recreated in the DOM.
3.  Your custom content might be overwritten by internal widget updates.

```javascript
    const injectButton = () => {
      // Logic to inject a new button into the widget
    }

    injectButton();

    const observer = new MutationObserver((mutations) => {
      injectButton();
    });

    observer.observe(shadowHost.shadowRoot, {
      childList: true,
      subtree: true,
    });
```

Now you have access to the modal, you can target whatever element you want to adjust, or append new elements at will.


# Design Techniques Tutorial



Let's become familiar with the design customization techniques for your Dynamic implementation.

We achieve these three use cases along the way:

1.  Redesign and implement a Connect Wallet button.
2.  Change the whole design theme.
3.  Customize the logged in widget to suit your own style needs.
4.  Implement a custom welcome screen for our users when they click Connect Wallet.

During the guide we will utilize all three of the main design techniques, which are as follows:

*   [Configurations available via the Dashboard](/design-customizations/css/themes)
*   [CSS Variable configurations](/design-customizations/css/css-variables)
*   [CSS Overrides](/design-customizations/css/custom-css)

Let's go!

## Pre-requisites

Make sure you have the SDK set up in a simple application, you can find the guide to do that [here](/quickstart).

## Implement a custom Connect Wallet button

We want to achieve three things here:

1.  Change the default button colors
2.  Change the default button text
3.  Use our own custom button

Before we start you should know that the Connect button is a part of the [DynamicWidget](/react-sdk/components/dynamicwidget), but can also be used seperately through the [Dynamic Connect Button](/react-sdk/components/dynamicconnectbutton) component. It doesn't matter which one you use here, the techniques will affect both!

### Button colors

The easiest way to achieve this is to utilize the default CSS variables.

We'll focus on the background color, main color and the text color here but there are more variables available such as the border, shadow etc. You can find the full reference [here](/design-customizations/css/css-variables#connect-button-variables).

First we'll need to make sure in our CSS we use the following class name:

```css
.dynamic-shadow-dom {
}
```

Now we add add the following variables to that class:

```css
.dynamic-shadow-dom {
  --dynamic-connect-button-background: #000;
  --dynamic-connect-button-color: #fff;
  --dynamic-text-primary: #fff;
}
```

Run your application and you'll see the color changes. You can also use the inspector to see exactly which elements are being affected.

### Button text & font

We can also change the text of the button and the font used. For the font, the approach is exactly the same as for the colors, we just need to use the correct variables.

```css
.dynamic-shadow-dom {
  --dynamic-text-size-button-primary: 16px;
  --dynamic-font-family-primary: "Roboto", sans-serif;
}
```

For the text itself, we will use the innerButtonComponent prop on the DynamicWidget. This prop allows us to pass in a JSX element that will be rendered as the button.

```jsx
<DynamicWidget
  innerButtonComponent={<button>My Custom Connect Wallet Button</button>}
>
  {/* ... rest of your app ... */}
</DynamicWidget>
```

### Use our own button

For this we'll want to utilize one of the handy functions available though the useDynamicContext hook: "setShowAuthFlow".

We access it like this:

```jsx
const { setShowAuthFlow } = useDynamicContext();
```

We can then use this function to trigger the Dynamic Connect Wallet flow when our custom button is clicked.

```jsx
<button onClick={() => setShowAuthFlow(true)}>
  My Custom Connect Wallet Button
</button>
```

## Change the whole design theme

This is as simple as a visit to [the design section of your Dynamic dashboard](https://app.dynamic.xyz/dashboard/design).

Under the "Themes" section, choose any of the available themes, save when prompted, and you'll be live immediately with your new design!

## Customising the logged in widget

The logged in widget is the widget is the part of the DynamicWidget that renders the [Dynamic User Profile component](/react-sdk/components/dynamicuserprofile). As with the Connect button, you can use the Widget or the Widget Content component, this guide will work for both.

Let's say in this example we want to do two things:

1.  Remove the network picker from the logged in widget.
2.  Rotate the wallet icon using animations in the logged in widget.

### Remove the network picker

This is an option found under the "Branded Wallets" heading in [the Log in & User Profile section of the dashboard](https://app.dynamic.xyz/dashboard/log-in-user-profile),

Toggle on "Hide Network", save, and you're done!

### Change the border radius

There is no CSS variable to do this, so it means we will use a CSS override.

Once you have found the class names you want to override, and know what css you wish to apply instead, we need to declare the css as a string literal, ideally stored under a variable called cssOverrides in whichever component you're initialising DynamicContextProvider.

```jsx
const cssOverrides = `
    .dynamic-widget-inline-controls__account-control > img {
      animation: rotation 2s infinite linear
    }

    @keyframes rotation {
      from {
        transform: rotate(0deg);
      }
      to {
        transform: rotate(359deg);
      }
    };
  `;
```

Then, you can use this variable as a setting for DynamicContextProvider:

```jsx
<DynamicContextProvider
  settings={{
    cssOverrides,
  }}
>
  {/* ... rest of your app ... */}
</DynamicContextProvider>
```

Note that you can also pass a JSX element rather than a string, but more on that [here](/design-customizations/css/custom-css)

That's it! Run your app. Once logged in, you should no longer see your network picker and the wallet icon should be happily spinning!

<Tip>
  Note that is you need to add extra content to the widget, not just style the
  existing content differently, we recommend following the [custom widget
  content guide](/design-customizations/tutorials/custom-widget-content).
</Tip>

## Implement a custom welcome screen

Here we see how to reach beyond the confines of the components themselves, and extending the flow into your own application using callbacks and functions.

You cannot implement a welcome screen inside the Dynamic modal, but you can of course create your own in your application. This raises the question about how to trigger it at the right time.

The answer is to use the [onAuthSuccess](/react-sdk/events/onauthsuccess) callback. This callback is triggered when the user has successfully authenticated with Dynamic.

```jsx
<DynamicContextProvider
  settings={{
    events: {
      onAuthSuccess: (args) => {
        console.log("onAuthSuccess was called", args);
      },
    },
  }}
>
  {/* ... rest of your app ... */}
</DynamicContextProvider>
```

The second part of the puzzle is to check if this is a new user or not, since we might want a different experience for new users. We can do this by checking the "newUser" property on the object supplied by the callback above.

This is because the callback supplies you with the `user` object, and this is a representation of the [UserProfile](/react-sdk/objects/userprofile) which contains the `newUser` boolean.

```jsx
  onAuthSuccess: (args) => {
        if(args.user.newUser) {
          console.log("This is a new user")
        }
      },
```

If this is indeed a new user, you can replace the console log above with your own logic to call your welcome screen component, and voila!

## Conclusion

You just learned how to customize the design of your Dynamic implementation using the three main techniques:

1.  [Configurations available via the Dashboard](/design-customizations/css/themes)
2.  [CSS Variable configurations](/design-customizations/css/css-variables)
3.  [CSS Overrides](/design-customizations/css/custom-css)

For more information on the design customization options available, check out the [design customization section](/design-customizations).

If you have any questions at all about these techniques or how to achieve a certain design use case, don't hesitate to ask [in the community slack](https://www.dynamic.xyz/slack).


# Login Views Tutorial



In this guide we will set up a simple example of programmatically changing views based on where the user is coming from.

<Tip>
  We're going to be referring a lot to the views type, found [here](/design-customizations/views).
</Tip>

### Determine where the user is coming from

In our example, we will show a different signup/login experience to users coming from a specific URL. We will use the `useLocation` hook from `react-router-dom` to get the current URL.

```jsx
import { useLocation } from "react-router-dom";

const location = useLocation();
```

If the user is coming from /landing-page/facebook, we will show only a Facebook button which gives you an embedded wallet. If you are coming from /landing-page/web3, we will show the default signup/login experience.

In the below code we are using the `useState` hook to create a state variable called `source` and a setter function called `setSource`. We are then using the `useEffect` hook to set the source to the current location.

```jsx
const [source, setSource] = useState("");

const location = useLocation();

const isFacebookCampaign = location.pathname === "/landing-page/facebook";
const isWeb3Campaign = location.pathname === "/landing-page/web3";

const views = isFacebook ? "facebook" : isWeb3Campaign ? "web3" : "";

useEffect(() => {
  setSource(views);
}, [views]);
```

### Declare the views

Views are passed in via the overrides key on the settings prop of [DynamicContextProvider](/react-sdk/providers/dynamiccontextprovider)

The view object itself is of a specific type and you can review the type on [the reference](/react-sdk/objects/views).

In our case, we want two different views, one for Facebook and one for Web3. We will use the `facebook` and `web3` views.

```jsx
const FACEBOOK_VIEW = {
  type: SdkViewType.Login,
  sections: [
    {
      type: SdkViewSectionType.Social,
      defaultItem: "facebook",
    },
  ],
};
```

```jsx
const WEB3_VIEW = {
  type: SdkViewType.Login,
  sections: [
    { type: SdkViewSectionType.Wallet },
    {
      type: SdkViewSectionType.Separator,
      label: "Or",
    },
    {
      type: SdkViewSectionType.Email,
    },
    {
      type: SdkViewSectionType.Separator,
      label: "Or",
    },
    {
      type: SdkViewSectionType.Social,
      defaultItem: "twitter",
    },
  ],
};
```

### Setting types to state

We want somewhere to store our view overrides. We will use the `useState` hook to create a state variable called `viewOverrides` and a setter function called `setViewOverrides`.

```jsx
const [viewOverrides, setViewOverrides] = useState([]);
```

### Setting the view overrides

We will use the `useEffect` hook to set the view overrides to the correct view based on the source.

```jsx
useEffect(() => {
  if (source === "facebook") {
    setViewOverrides([FACEBOOK_VIEW]);
  } else if (source === "web3") {
    setViewOverrides([WEB3_VIEW]);
  } else {
    setViewOverrides([]);
  }
}, [source]);
```

### Adding to the DynamicContextProvider

We can now pass the viewOverrides to the `DynamicContextProvider`.

<DynamicContextProvider
  settings={{
   environmentId: "fba5127c-21c0-430e-bb03-7dc8f6b11397",
   overrides: { views: viewOverrides },
  }}
>
  <DynamicWidget />
</DynamicContextProvider>

### Putting it all together

```jsx
import React, { useEffect, useState } from "react";
import { useLocation } from "react-router-dom";
import {
  DynamicContextProvider,
  DynamicWidget,
} from "@dynamic-labs/sdk-react-core";

import { SdkViewSectionType, SdkViewType } from "@dynamic-labs/sdk-api";

const FACEBOOK_VIEW = {
  type: SdkViewType.Login,
  sections: [
    {
      type: SdkViewSectionType.Social,
      defaultItem: "facebook",
    },
  ],
};

const WEB3_VIEW = {
  type: SdkViewType.Login,
  sections: [
    { type: SdkViewSectionType.Wallet },
    {
      type: SdkViewSectionType.Separator,
      label: "Or",
    },
    {
      type: SdkViewSectionType.Email,
    },
    {
      type: SdkViewSectionType.Separator,
      label: "Or",
    },
    {
      type: SdkViewSectionType.Social,
      defaultItem: "twitter",
    },
  ],
};

const App = () => {
  const [source, setSource] = useState("");
  const [viewOverrides, setViewOverrides] = useState([]);

  const location = useLocation();

  const isFacebookCampaign = location.pathname === "/landing-page/facebook";
  const isWeb3Campaign = location.pathname === "/landing-page/web3";

  useEffect(() => {
    setSource(isFacebookCampaign ? "facebook" : isWeb3Campaign ? "web3" : "");
  }, [isFacebookCampaign, isWeb3Campaign]);

  useEffect(() => {
    if (source === "facebook") {
      setViewOverrides([FACEBOOK_VIEW]);
    } else if (source === "web3") {
      setViewOverrides([WEB3_VIEW]);
    } else {
      setViewOverrides([]);
    }
  }, [source]);

  return (
    <DynamicContextProvider
      settings={{
        environmentId: "fba5127c-21c0-430e-bb03-7dc8f6b11397",
        overrides: { views: viewOverrides },
      }}
    >
      <DynamicWidget />
    </DynamicContextProvider>
  );
};

export default App;
```

### Adding embedded wallets

That's it! To make sure our facebook group also get an embedded wallet,follow [this guide](/wallets/embedded-wallets/dynamic-embedded-wallets)


# Wallet List Views Tutorial



Below is an example showcasing the setup for tabs that categorize wallets by blockchain network, utilizing both custom and predefined filter functions:

```tsx
import {
  DynamicContextProvider,
  FilterChain,
} from "@dynamic-labs/sdk-react-core";
import {
  BitcoinIcon,
  EthereumIcon,
  FlowIcon,
  SolanaIcon,
} from "@dynamic-labs/iconic";

const App = () => {
  return (
    <DynamicContextProvider
      settings={{
        environmentId: "env-id",
        // Additional settings...

        overrides: {
          views: [
            {
              type: "wallet-list",
              tabs: {
                items: [
                  {
                    label: { text: "All chains" },
                  },
                  {
                    label: { icon: <EthereumIcon /> },
                    walletsFilter: FilterChain("EVM"),
                    recommendedWallets: [
                      {
                        walletKey: "phantomevm",
                      },
                    ],
                  },
                  {
                    label: { icon: <SolanaIcon /> },
                    walletsFilter: FilterChain("SOL"),
                  },
                  {
                    label: { icon: <BitcoinIcon /> },
                    walletsFilter: FilterChain("BTC"),
                  },
                  {
                    label: { icon: <FlowIcon /> },
                    walletsFilter: FilterChain("FLOW"),
                  },
                ],
              },
            },
          ],
        },
      }}
    ></DynamicContextProvider>
  );
};
```

This is the wallet list view with the tabs

| All chains tab selected                                                                                                | Ethereum selected                                                                                                    |
| ---------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/wallet-list-tabs-all-chains-selected.png" /> | <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/wallet-list-tabs-ethereum-selected.png" /> |

Let's create a button that, when clicked, will automatically open the Ethereum tab.

```tsx
import React from "react";
import { useDynamicContext } from "@dynamic-labs/sdk-react-core";

/**
 * Component for a button that opens the Ethereum tab by default.
 */
const ConnectWithEthereum: React.FC = () => {
  const { setShowAuthFlow, setSelectedTabIndex } = useDynamicContext();

  /**
   * Handles the button click event by setting the default tab to Ethereum and showing the authentication flow.
   */
  const onClickHandler = (): void => {
    setSelectedTabIndex(1); // Set the selected tab index to 1, which corresponds to the Ethereum tab
    setShowAuthFlow(true);
  };

  return <button onClick={onClickHandler}>Connect with Ethereum wallet</button>;
};

export default ConnectWithEthereum;
```


# Adapt UI With Views



## What is it?

Views are used to customize the kind of UI that shows up at any point in time in your application, specifically giving you complete control over the signup/login options you show your user.

## How does it work?

Views are used primarily in the overrides prop of the [DynamicContextProvider](/react-sdk/providers/dynamiccontextprovider). You pass in an array of configurations for each view you want to customize, each view has its own set of options.

## Types of Views

### Authentication Method Options (Login)

The `SdkViewType.Login` is used to adjust the login/signup UI options programmatically.

When using the login view, you add an object to the views array. This object should have `type: SdkViewType.Login` and `sections` which is an array of SdkViewSection objects.

### SdkViewSection structure

<ParamField path="type" type="SdkViewSectionType" required="true">
  An identifier of the kind of view section to be provided, possible values outlined in section below.
</ParamField>

<ParamField path="alignment" type="SdkViewSectionAlignment">
  The text alignment used for Text sections.
</ParamField>

<ParamField path="defaultItem" type="string">
  The option to be displayed as the main one.
  The default item will be displayed in a more prominent way than the rest of the items in the section.

  *   For `Social` section, represents the social provider to be displayed by default.
  *   For `EmailAndPhone` section, represents whether email or phone options are displayed by default (`"email"` vs `"phone"`).
</ParamField>

<ParamField path="label" type="string">
  The label for the section.
  This will be displayed above the section or as part of the separator component if it is a `Separator` section.
</ParamField>

<ParamField path="numOfItemsToDisplay" type="number">
  The default number of items to display in the section.
  User has to click a button to view more options if any are left out.

  *   For `Wallet` section, represents the number of wallet items to be displayed by default.
  *   For `Social` section, represents the number of social providers to be displayed by default.
</ParamField>

### SdkViewSectionType

The possible values are:

*   `SdkViewSectionType.Email`
*   `SdkViewSectionType.EmailAndPhone`
*   `SdkViewSectionType.Phone`
*   `SdkViewSectionType.Social`
*   `SdkViewSectionType.Separator`
*   `SdkViewSectionType.Text`
*   `SdkViewSectionType.Wallet`

<Note>
  The `EmailAndPhone` section is what you get by default when you enable both
  email and phone in our dashboard without using login view overrides. It's a
  section where the user has the option to toggle between email and phone number
  input. Read about `defaultItem` [here](/design-customizations/views#sdkviewsection-structure)
  to learn how to set whether to show email or phone by default.
</Note>

### SdkViewSectionAlignment

The possible values are:

*   `SdkViewSectionAlignment.Center`
*   `SdkViewSectionAlignment.Left`
*   `SdkViewSectionAlignment.Right`

#### Example snippets

Here's an example where you are overriding the login view to show only the email login option:

```jsx
import { SdkViewSectionType, SdkViewType } from "@dynamic-labs/sdk-api";

<DynamicContextProvider
  settings={{
    overrides: {
      views: [
        {
          type: SdkViewType.Login,
          sections: [
            {
              type: SdkViewSectionType.Email,
            },
          ],
        },
      ]
    }
  }}
>
  <App />
</DynamicContextProvider>;
```

Here is an example where you are overriding the login view to show email or social login options:

```jsx
import { SdkViewSectionType, SdkViewType } from "@dynamic-labs/sdk-api";

<DynamicContextProvider
  settings={{
    overrides: {
      views: [
        {
          type: SdkViewType.Login,
          sections: [
            {
              type: SdkViewSectionType.Email,
            },
            {
              type: SdkViewSectionType.Separator,
              label: "Or",
            },
            {
              type: SdkViewSectionType.Social,
              defaultItem: "google",
            },
          ],
        },
      ]
    }
  }}
>
  <App />
</DynamicContextProvider>;
```

#### Complete example

There is a more complete example also found [here](/design-customizations/tutorials/login-views-guide).

### Branded Wallet Signup/Login (Wallet List)

The `wallet-list` configuration enables you to define tabs with predetermined labels, icons, filters, and recommended wallets, enhancing your application's wallet selection interface. This feature is particularly useful for grouping wallets.

#### Configuring Wallet List Tabs

In the `DynamicContextProvider` setup, the `overrides` field is used to configure each tab in the wallet list. The configuration options available for each tab allow for detailed customization:

*   **Label and Icon**: Customize the tab's appearance with a `label` for text and an `icon` for visual representation. The `icon` can be one of the following options:
    *   A icon from the dynamic iconic package
        <Accordion title="Example using Dynamic Iconic icons">
          ```tsx
          import { BitcoinIcon } from '@dynamic-labs/iconic';

          <DynamicContextProvider
            settings={{
              overrides: {
                views: [
                  {
                    type: 'wallet-list',
                    tabs: {
                      items: [
                        {
                          text: 'Ethereum',
                          icon: <BitcoinIcon />
                        }
                      ]
                    }
                  }
                ]
              }
            }}
          />
          ```
        </Accordion>
    *   A image URL
        <Accordion title="Example custom image URL">
          ```tsx
          <DynamicContextProvider
            settings={{
              overrides: {
                views: [
                  {
                    type: 'wallet-list',
                    tabs: {
                      items: [
                        {
                          text: 'My own tab',
                          icon: 'https://example.org/my-image.png'
                        }
                      ]
                    }
                  }
                ]
              }
            }}
          />
          ```
        </Accordion>
    *   Or you can bring your own React icon
        <Accordion title="Example using custom react icon">
          ```tsx
          <DynamicContextProvider
            settings={{
              overrides: {
                views: [
                  {
                    type: 'wallet-list',
                    tabs: {
                      items: [
                        {
                          text: 'My own tab',
                          icon: <i />
                        }
                      ]
                    }
                  }
                ]
              }
            }}
          />
          ```
        </Accordion>

*   **Wallets Filter**: This option enables to dynamic display of wallets based on the selected tab. Clients have the flexibility to write custom filter functions or utilize predefined ones, for more information read the [sort and filter wallets](/wallets/advanced-wallets/sort-and-filter-wallets) doc

*   **Recommended Wallets**: Specify recommended wallets for each tab by providing [wallet option](/react-sdk/objects/wallet-option) keys and optional labels. This feature is designed to highlight preferred wallets, steering users towards secure and suitable options for their specific needs.

*   **Style**: An optional field that determines how the tabs are displayed within the wallet list. Currently, the only supported style is `"grid"`.

#### Complete example

There is a more complete example also found [here](/design-customizations/tutorials/wallet-list-views-guide).


# Best Practices



Dynamic is SOC 2 Type II compliant and regularly completes penetration testing and external security audits from Cure53. Dynamic also has an ongoing bug bounty program with HackerOne. All data with Dynamic is transmitted with encryption using HTTPS and similar protocols. Furthermore, all data is securely stored with encryption-at-rest using AES-256 or higher standards.

Dynamic-powered embedded wallets are non-custodial, meaning they are always end-user owned and controlled. Only the end-user has ownership and access to their wallet private keys. For a more detailed description of Dynamic-powered embedded wallets, you can review the architecture and security handling [here](/wallets/embedded-wallets/architecture-security).

### Protect Developer Credentials

*   Limit and manage access to the Dynamic Dashboard and API tokens.
*   Use Dynamic’s role-based permissions to restrict employee actions. Learn more [here](/developer-dashboard/invite-members).
*   Require employees to use a time-based one-time password (e.g., Google Authenticator) for accessing the Dynamic dashboard and features. Contact us for access to this feature.

### Secure Storage of Dynamic API keys

*   Store Dynamic’s API tokens securely and minimize access to these credentials.
*   They should only be used on the backend and never shared on the client side.
*   Institute an internal policy within your organization to rotate Dynamic API keys regularly.

### JWT Length and Storage

*   When the JWT expires a user’s session ends (user is logged out) so they will have to re-authenticate once it expires. The JWT token has a maximum lifetime of 30 days. Configure this to the shortest acceptable time to balance security and user experience. More details [here](/developer-dashboard/security).
*   Never save or log user JWTs.
    Note: When using Dynamic-powered embedded wallets without transactional MFA, it’s important to limit the shelf life of the JWT since the wallet is primarily gated by the JWT and the method used by the user to log in.

## Implement a Code Review Process

*   Ensure another employee approves new code before deployment.
*   Establish policies to control who can push code to production.

## Mitigate XSS Attacks

*   Use content security policies on the frontend.
*   Implement TLS and HTTPS for all requests.
*   Limit permissible JavaScript, set context headers properly, and avoid open redirects.
*   If you enable the frame-src CSP, then you need to perform this whitelisting (learn how [here](https://docs.dynamic.xyz/wallets/embedded-wallets/create-wallets/overview#content-security-policy-csp)).
*   Enforce a strict Content Security Policy (CSP). Refer to [this guide](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) for more information.

## Enhance CORS Security

*   Add specific origins for CORS to protect your environment from unauthorized websites using your public environment key.
*   Avoid using wildcards in favor of explicit domains.
*   Be especially strict with your live environment (e.g don’t have localhost etc)

## Ease of Seed Phrase Export (using Dynamic-Powered embedded wallets)

*   Make it easy and readily available for users to export their seed phrase and consider Including it as a step in your sign-up flow.
*   Educate Users on security and how you intend to communicate with them
*   Clearly communicate to users that neither Dynamic nor any associated parties will ask for private keys or encourage sharing this information.
*   Specify the forms of communication and types of interactions users can expect.

## Addressing Potential Risks - internal employee account accessed

If an employee account is compromised and best practices are not followed, there are several risks:

*   Malicious code could be deployed to your application that could attempt to drain user wallets on their next login.
*   Unauthorized activities could be conducted using acquired JWT and session key.

## Rate Limits

Familiarize yourself with Dynamic’s rate limits for IP addresses, project environments, and endpoints you are using. Refer to [Rate Limits](/developer-dashboard/rate-limits) for more information.


# Analytics



![](https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/analytics-overview.png)

#### Summary

The developer dashboard provides access to daily user count and wallet
distribution data for both connected users and visitors. We'll be adding
additional insights in the coming months.

<Note>
  ### 📘 Have charts you wish to see here?

  Let us know if you have specific insights through any of our
  [channels](/introduction/welcome#support-and-feedback).
</Note>

#### Usage

1.  Log in to the developer dashboard.
2.  Navigate to the [Analytics](https://app.dynamic.xyz/dashboard/analytics) tab.
3.  View the charts for daily user counts and wallet distribution.
4.  The user counts and wallet distribution data is separated by connected users
    and visitors.
5.  Use the data to gain insights on the usage and financial performance of your
    product or service.


# Users



#### Summary

In your developer dashboard, you have full access to view your users, those that have connected and signed.

From here, you can see which wallets connected and when, session information, any information you've collected during onboarding, and more.

To get here, simply navigate to the [Users](https://app.dynamic.xyz/dashboard/users) tab in your dashboard and you'll see your list of authenticated users.

#### User Management Table

We've enabled a simply search box for you. Here you can type in any data point, whether it be the wallet address, user id, email, alias, etc. By default, it will search across **All**  columns. If you want to search by a specific column, simply click the dropdown on the right of the search bar and select the column that you'll want to search by.

You can also sort by any column by simply click on the column header, or you filter by chains if you want to narrow your search criteria.

Lastly, if you've collected any custom information during onboarding, you can click on the Columns button in the upper right and select which columns you would like to view.

#### User Details

If you click on a user, then a slide out of the user details will appear. Here you have up to 3 tabs (for now): Account, Sessions, Chainalysis.

The Account tab will show any collected information about your user.

The sessions tab will show you all the sessions associated with that user, chronologically ordered from top to bottom. If you've collected IP addresses, then that information will appear here.

The Chainalysis tab will appear if you selected to turn it on in our [integrations](https://app.dynamic.xyz/dashboard/integrations) page. Here, we will show the results of every Chainlaysis check for a given wallet, including details if a user did not pass.


# Webhook Event types



This is the list of all the types of events we send as part of webhook payloads. We may add more at any time, so when developing and maintaining your code, you should not assume that only these types exist. The current list of event types can be fetched from the [event types endpoint](/api-reference/endpoints/events/getEventTypes).

The events follow the pattern: `resource.event`. Our goal is to design a consistent system that makes it easy to anticipate and understand.

<Note>
  Events that occur on subresources like user.session will include the parent id
  but do not trigger the parent's update event.
</Note>

## User

<ParamField body="user.created" type="object">
  Occurs whenever a user is created. Is a [user.](/api-reference/schemas/User)
</ParamField>

<ParamField body="user.updated" type="object">
  Occurs whenever a user is updated. Is a [user.](/api-reference/schemas/User)
</ParamField>

<ParamField body="user.deleted" type="object">
  Occurs whenever a user is deleted. Is a [user.](/api-reference/schemas/User)
</ParamField>

<ParamField body="user.passkeyRecovery.started" type="object">
  Occurs whenever a user started the passkey recovery process. Is a
  [wallet.](/api-reference/schemas/Wallet)
</ParamField>

<ParamField body="user.passkeyRecovery.completed" type="object">
  Occurs whenever a user completed the passkey recovery process. Is a
  [wallet.](/api-reference/schemas/Wallet)
</ParamField>

<ParamField body="user.session.created" type="object">
  Occurs whenever a user session is created. Is a
  [session.](/api-reference/schemas/Session)
</ParamField>

<ParamField body="user.session.revoked" type="object">
  Occurs whenever a user session is revoked. Is a
  [session.](/api-reference/schemas/Session)
</ParamField>

<ParamField body="user.social.linked" type="object">
  Occurs whenever a user links a social account. Is a
  [provider.](/api-reference/schemas/SocialSignInProvider)
</ParamField>

<ParamField body="user.social.unlinked" type="object">
  Occurs whenever a user unlinks a social account. Is a
  [provider.](/api-reference/schemas/SocialSignInProvider)
</ParamField>

## Wallet

<ParamField body="wallet.created" type="object">
  Occurs whenever a Dynamic-powered embedded wallet is created. Is a
  [wallet.](/api-reference/schemas/Wallet)
</ParamField>

<ParamField body="wallet.linked" type="object">
  Occurs whenever a wallet is linked to a user. Is a
  [wallet.](/api-reference/schemas/Wallet)
</ParamField>

<ParamField body="wallet.unlinked" type="object">
  Occurs whenever a wallet is unlinked from a user. Is a
  [wallet.](/api-reference/schemas/Wallet)
</ParamField>

<ParamField body="wallet.exported" type="object">
  Occurs whenever a Dynamic-powered embedded wallet is exported. Is a
  [wallet.](/api-reference/schemas/Wallet)
</ParamField>

<ParamField body="wallet.transferred" type="object">
  Occurs whenever a wallet is transferred between user accounts. Is a
  [wallet.](/api-reference/schemas/Wallet)
</ParamField>

## Visit

<ParamField body="visit.created" type="object">
  Occurs whenever a visit is created. Is a
  [visit.](/api-reference/schemas/Visitor)
</ParamField>


# General Settings

One of the first things you'll want to do as you create a new project is update some of the basic metadata associated with the SDK such as your app name, logo url, and support url.

To update this information, go to the
[General page under Settings](https://app.dynamic.xyz/dashboard/settings/general)
in your developer dashboard. The available options for you are:

| Setting                                        | Description                                                                                                                                                                        |
| ---------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Display Name                                   | This is the name that will appear in the SDK whenever we reference your site. By default, the Display Name will be pre-filled with the Project name.                               |
| App Logo Url                                   | Whenever we display an app logo url, then we'll use this url.                                                                                                                      |
| Image when user is not found in an Access list | If you use an access list, and want a friendly image in the SDK (think a success image), then you can add an image url that we'll display                                          |
| Image when user is on an Access list           | If you use an access list, and want a friendly image in the SDK (think a friendly image to show that a user doesn't have access), then you can add an image url that we'll display |
| Support URL                                    | Whenever a user may be stuck, we'll show your support url to customers so they can reach out.                                                                                      |


# Invite Members



## Inviting a user to your organization

You can invite members of your company to your Dynamic organization by navigating to the Admin screen and clicking on the Members tab.

You can then add a wallet address/email address and a name which will add this wallet/email based user to your
organization. You will also need to assign a role to each new user you invite. The four possible roles are:

| Role      | Description                                                                                                                                                                                                                                         |
| --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Owner     | Owners have unrestricted visibility and control over the entire dashboard, including sensitive sections such as billing and user management.                                                                                                        |
| Admin     | Admins have unrestricted visibility and control over the entire dashboard, including sensitive sections such as billing and user management. The only thing an admin cannot do is remove an Owner from an Organization.                             |
| Developer | Users assigned the Developer role have comprehensive access to all areas of the system, with the exception of the admin-specific functionalities. This exclusion includes areas like billing and member invitations.                                |
| Member    | This is a read-only role. Users are restricted from making any changes to settings or configurations. Their access is limited to viewing user management and analytics data, ensuring they can monitor and review information without modifying it. |

## Accepting an invite to an organization

If the invited user already has a Dynamic account associated with the invited wallet/email then the next time they log in, they will see a notification that they have been invited to join your organization. They can click on the notification to accept the invitation, or choose to ignore it.

If the invited user does not have a Dynamic account associated with the invited wallet/email, they will need to create an account and then accept the invitation. They can do this by following these steps:

1.  Connect their wallet/email
2.  Complete the onboarding flow
3.  Enter their Alias and Email

After this point, they will be asked if they want to join your organization as long as the wallet/email matches that which was invited. They can then accept, and then they will be logged into the organization's dashboard.


# Privacy Settings



#### Configure IP collection

Keeping privacy in mind is central to how we design and build our systems. We realize that some companies prefer to collect as little data as possible about their users while others would prefer to collect a little bit more.

That's why, in our effort to minimize information collected about users where possible have the option to collect or not to collect IP addresses. By default, IP logging is off for logged in users. If you need to enable IP collection for compliance or other reasons, you can toggle this option on in the [Privacy page](https://app.dynamic.xyz/dashboard/settings/privacy) of your dashboard.

As we build out more features, additional configurations will be placed here so you have control about what is or is not collected.


# Projects vs Organizations



If you visit your dashboard, on the top of the page, you will see two tabs: **Projects** and **Organizations**. There are related but different concepts, and this short section will help you understand when to utalize each.

## Projects

You can think of a project as a scoped configuration for a single implementation. Each project comes with both a sandbox and live mode, which each have their own environment ID and configuration.

Importantly, if a user is invited as a member of an organization, they will have access to all projects within that organization.

### Creating a new project

In the navigation bar of the [dashboard](https://app.dynamic.xyz), click on the Project dropdown menu (second from the left):

<Frame>
  <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/dashboard-top-nav-project.png" />
</Frame>

1.  Click "Create new project"
2.  Enter Project Name.

## Organizations

Organizations are a way to group projects together. Organizations are billed distinctly, and each organization can have multiple projects.

### Creating a new organization

In the navigation bar, click on the Organization dropdown menu (first on the left):

<Frame>
  <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/dashboard-top-nav-org.png" />
</Frame>

1.  Click "Create organization" at the bottom of the dropdown
2.  Enter Organization Name and Website URL

And you're done. You now have a new organization that you can use. You will need
to invite members of your team to this organization if you would like to work
with them there.


# Rate Limits

Dynamic rate limit policies

Dynamic enforces rate limits based upon IP address, project environment, and certain endpoints to protect against attacks and to prevent abuse of Dynamic's platform. These limits are subject to change.

## Errors

Requests returning a `429` status code have been rate limited.

```json
{
  "message": "Rate limit exceeded",
  "code": 429
}
```

## Rate limit categories

*   IP-based rate limits
*   Project environment-based rate limits
*   Endpoint-based rate limits

## SDK rate limits

All endpoints used by the SDK prefixed by `/sdk` are subject to the following limits.

*   100 requests per minute per IP
*   10000 requests per minute per project environment

### Endpoints

| Endpoint         | Paths                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              | Limited by | Rate Limit               |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | ------------------------ |
| Nonce            | [/:environmentId/nonce](/api-reference/endpoints/sdk/getNonce)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | IP Address | 10 requests per 1 minute |
| Signin           | [/:environmentId/verify](/api-reference/endpoints/sdk/verify)<br />[/:environmentId/emailVerifications/signin](/api-reference/endpoints/sdk/signInWithEmailVerification)<br />/:environmentId/smsVerifications/signin<br />[/:environmentId/providers/:providerType/signin](/api-reference/endpoints/sdk/oauthSignIn)<br />/:environmentId/telegram/signin<br />[/:environmentId/farcaster/signin](/api-reference/endpoints/sdk/farcasterSignIn)<br />[/:environmentId/externalAuth/signin](/api-reference/endpoints/sdk/externalAuthSignin)       | IP Address | 10 requests per 1 minute |
| OTP              | [/:environmentId/emailVerifications/create](/api-reference/endpoints/sdk/createEmailVerification)<br />[/:environmentId/emailVerifications/retry](/api-reference/endpoints/sdk/retryEmailVerification)<br />[/:environmentId/smsVerifications/create](/api-reference/endpoints/sdk/createSmsVerification)<br />[/:environmentId/smsVerifications/retry](/api-reference/endpoints/sdk/retrySmsVerification)                                                                                                                                         | IP Address | 10 requests per 1 minute |
| MFA              | [/:environmentId/users/mfa/register/totp](/api-reference/endpoints/sdk/registerTotpMfaDevice)<br />[/:environmentId/users/mfa/auth/totp](/api-reference/endpoints/sdk/authMfaTotpDevice)<br />[/:environmentId/users/mfa/register/passkey](/api-reference/endpoints/sdk/)<br />[/:environmentId/users/mfa/auth/passkey](/api-reference/endpoints/sdk/)<br />[/:environmentId/users/mfa/recovery](/api-reference/endpoints/sdk/createNewRecoveryCodes)<br />[/:environmentId/users/mfa/auth/recovery](/api-reference/endpoints/sdk/authMfaRecovery) | IP Address | 5 requests per 1 minute  |
| Token balances   | [/:environmentId/chains/:chainName/balances](/api-reference/endpoints/sdk/getAccountBalances)                                                                                                                                                                                                                                                                                                                                                                                                                                                      | IP Address | 20 requests per 1 minute |
| Embedded wallets | [/:environmentId/users/embeddedWallets](/api-reference/endpoints/sdk/createEmbeddedWallets)<br />[/:environmentId/users/embeddedWallets/walletAccounts](/api-reference/endpoints/sdk/createEmbeddedWallets)<br />[/:environmentId/users/embeddedWallets/sessionKey](/api-reference/endpoints/sdk/registerSessionKey)                                                                                                                                                                                                                               | IP Address | 20 requests per 1 minute |
| Update user      | [/:environmentId/users](/api-reference/endpoints/sdk/updateSelf)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   | IP Address | 5 requests per 1 minute  |

## Developer rate limits

All endpoints used by Developers are subject to the following rate limits.

*   1500 requests per minute per IP
*   3000 requests per minute per project environment

### Endpoints

| Endpoint               | Path                                                                                                                                                                                                                                                | Limited by | Rate Limit                |
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | ------------------------- |
| Bulk create user       | [/environments/:environmentId/users/bulk](/api-reference/endpoints/users/bulkCreateUser)                                                                                                                                                            | IP Address | 150 requests per 1 minute |
| Create embedded wallet | [/environments/:environmentId/users/embeddedWallets](/api-reference/endpoints/wallets/createEmbeddedWallet)<br />[/environments/:environmentId/users/embeddedWallets/farcaster](/api-reference/endpoints/wallets/createEmbeddedWalletFromFarcaster) | IP Address | 300 requests per 1 minute |
| Organization invites   | [/organizations/:organizationId/invites](/api-reference/endpoints/invites/createInvite)                                                                                                                                                             | IP Address | 20 requests per 1 minute  |

## Webhooks

See [webhooks limits](/developer-dashboard/webhooks#limits) for more information.


# Sandbox vs Live

Every project you create in Dynamic comes with a Sandbox and a Live environment. Both these environments behave similarly but we've customized the Sandbox environment to make it ideal for testing and exploring features.

### Two Key Differences

1.  **Everything is free**: all Dynamic features on Sandbox are free. Whether you
    choose to upgrade to our Advanced tier or not, all the features that we offer
    will be available for you to test and explore.
2.  **Sandbox is limited to 100 users**: your Sandbox environment is intended to
    help you test configurations and features and is not intended to be live on
    your site. We therefore have limited Sandbox to 100 users. After 100 users,
    you will not be able to authenticate new wallets unless you delete users.

<Frame>
  <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/delete-user.png" />
</Frame>


# Security Settings

We take security seriously at Dynamic and most of the work we do is behind the scenes so you don't have to worry about it. We will surface some security features that can be configured. 

Today, we have 2 features that you can configure

1.  CORS origin urls
2.  JWT expiration time

### Allowed CORS Origin

Adding origins for
[CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) to your project
environment (sandbox and live) protects your environment from unauthorized
websites using your public environment key.

Any origins added to an environment will allow only those domains to make API
requests via our SDK.

<Info>
  If you don't add an origin, all domains will be allowed to make API requests
</Info>

#### Adding an Origin

To add an origin, navigate to [the Security Settings in the dashboard](https://app.dynamic.xyz/dashboard/settings/security).

Click **Create Origin** and add your origin. (You can add multiple origins to
any environment)

Be sure to format your origin according to the RFC 6545 format (exception of the
wildcard `*`). An origin is a URL without the path.

#### Using wildcards

One or more `*` wildcard characters in your origin will represent 0 or more
characters (a-z, 0-9, -, .) when matching origins.

#### Acceptable Example Values

*   [http://domain.com](http://domain.com)
*   [https://domain.com](https://domain.com)
*   [https://sub.domain.com](https://sub.domain.com)
*   [http://127.0.0.1:4200](http://127.0.0.1:4200)
*   [http://localhost:3200](http://localhost:3200)
*   [https://my-app-\*.vercel.app](https://my-app-%2A.vercel.app)

#### Unacceptable Example Values

*   domain.com
*   //domain.com
*   [https://domain.com/home](https://domain.com/home)

### JWT Expiration Time

In the security settings page, you can update the expiration date of the JWT
token. The expiration time is the amount of time before one of your customers
will need to sign to log in.

To update this expiration time, navigate to **Settings > Security** in the
dashboard. Enter the amount of time in Day, Weeks, Months for the expiration
time.

![](https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/jwt-expiration.png "jwt-expiration.png")

The default value that we have set is 2 hours. We recommend that you verify with
a security expert or your security team before updating this value.


# Test Accounts

Test Accounts allow you to log in with a static OTP

<Warning>
  Test Accounts are only available in the
  [Sandbox](/developer-dashboard/sandbox-vs-live) environment
</Warning>

While testing projects in a Sandbox environment, you can use Test Accounts
to log in with a static verification code.

[Test Accounts](https://app.dynamic.xyz/dashboard/developer/test-accounts)
are enabled through the Developer Dashboard and allow you to use the
provided OTP code with:

1.  Any email including `+dynamic_test` right before the "@" sign, e.g. `example+dynamic_test@YOUR_DOMAIN`
2.  Any US phone number with an area code of `(555)`, e.g. `+1 (555)-123-4242`

<img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/test-account-otp.png" alt="Test Accounts OTP Screen" width="400" />


# SDK and API Keys

This page hosts the keys that you'll need to setup the SDK, validate the JWT, and generate tokens for API usage.

To access this page, navigate to the
[developer](https://app.dynamic.xyz/dashboard/developer/api) page in your
dashboard.

#### Environment ID

The environment ID is the key used for our platform to identify your SDK and
associate the users to your project environment. Each project has 2 keys, one
for Sandbox and one for Live. Whenever you want to setup a new instance of the
SDK, you simply need to copy the Environment ID and copy it into the settings
prop in the SDK.

```ts TypeScript
import {
  DynamicContextProvider,
  DynamicWidget,
} from "@dynamic-labs/sdk-react-core";

const App = () => (
  <DynamicContextProvider
    settings={{
      environmentId: "Enter your Environment ID here",
    }}
  >
    <DynamicWidget />
  </DynamicContextProvider>
);

export default App;
```

#### Public Key

The public key is what you can use to validate the JWT is authentic on your
backend. We recommend that you follow
[this guide](/authentication-methods/how-to-validate-users-on-the-backend) to properly validate your
users and ensure that users are using authenticated JWT's.

#### API Tokens

To use our API's, all you'll need to do is create a token and use it in your API
requests as a bearer Token. You can test out our API's
[here](/api-reference/overview).


# Visitors



<Note>
  ### 📘 Visitors are users that have connected a wallet through the connect-only path

  For more information on authenticated users compared to visitors, see [here](/wallets/advanced-wallets/connected-vs-authenticated).
</Note>

In your developer dashboard, you have full access to view all your visitors, ie, those users who have connected through the **connect-only path** and therefore didn't need to sign to login.

This is a simplified table from the Users table, those are users **who authenticated**, since minimal information is collected about visitors. One of the main points to highlight here is that no User ID is created and no information is associated to this visitor.

#### Visitors Table

Here you can view a list of your visitors and search by wallet address and filter. You can also sort by any column by simply click on the column header, or you filter by chains if you want to narrow your search criteria.

Note that since we don't associate a wallet to a user ID, the same user may have many visits if they choose to connect with different wallets.


# General



## Why webhooks?

Webhooks allow you to listen to events happening in your Dynamic environment and integrate applications to automatically receive information about those events.

To enable webhook events, you need to register webhook endpoints. After you register them, Dynamic can push real-time event data to your application’s webhook endpoint when events happen within your Dynamic environment.
Dynamic sends webhook events to your app as a JSON payload that includes an Event object.

## Events

Events are our way of letting you know when something happens in your Dynamic environment. When an event occurs, we create a new Event object. For example, when a user links a wallet, we create a `wallet.linked` event. Certain API requests might create multiple events. For example, when a user first signs in to your environment, we create a `user.created` event, and when a user successfully authenticates, we create a `user.session.created` event.

### Event object

**eventId**\
The unique id of the event that triggered the webhook message. A single event in a project can trigger one or more webhook messages if there is more than one webhook configured.

**messageId**\
The unique id of the message sent. This key should be used as the idempotency key inorder to handle redeliveries.

**webhookId**\
The unique id of the configured webhook that sent the message.

**userId**\
The unique id of the user who triggered the event. This can be different than `userId`'s in the event payload. For instance, when creating users via developer APIs the triggerer will be the developer's userId and the event payload will contain the id of the created user. This parameter will be `undefined` if the event is triggered using an API key.

**eventName**\
The name of the event that triggered the message. Events always conform to the following convention `{resourceType}.{action}`. For example with the event name of `user.created`, `user` is the resource type and `created` is the action performed on the resouce. The full list of event names can be retrieved by using the `/eventTypes` endpoint in the API [here](/api-reference/endpoints/events/getEventTypes).

**environmentId**\
The unique id of the environment from which the event originated.

**environmentName**\
The name of the environment from which the event originated. This can be used to have the same systems handle both `live` and `sandbox` events.

**timestamp**\
Timestamp when the event occurred.

**redelivery**\
If the message is a redelivery, this will be set to true.

**data**\
The data object contains the event payload. The structure of the data object will depend on the event that triggered the message. The full list of event payloads can be found [here](/developer-dashboard/eventTypes).

#### Example Event Object

Below is an example of a `user.created` event

```json
{
  "eventId": "2a92c161-3167-44ad-8fce-4c6cdaed8129",
  "messageId": "5a2a5360-bb7e-4ea6-9bd3-0146bf2f734f",
  "webhookId": "a86acea4-e050-4846-8e4f-0ae039f6e37c",
  "eventName": "user.created",
  "environmentId": "123e4567-e89b-12d3-a456-426614174000",
  "environmentName": "sandbox",
  "timestamp": "2023-10-26T14:30:59.210Z",
  "data": {
    "chain": "EVM",
    "origin": null,
    "ipAddress": "::1",
    "verifiedCredentialId": "b13f337d-a8dc-41a7-96f5-5e5d76ad864a",
    "userId": "a5914498-7a8b-4c58-b04c-9624fef2897c",
    "expiresAt": null,
    "walletPublicKey": "0x3FcE1F4F28DbA209344072867134A3a7F547C7f1",
    "createdAt": "2023-10-26T14:30:02.909Z",
    "projectEnvironmentId": "7181a853-fb76-4dc2-9af8-6aeb6d2b818b",
    "provider": "browserExtension",
    "walletName": "metamask",
    "id": "484e49ba-3026-4e2c-9bf0-ed98ae224833",
    "revokedAt": "2023-10-26T14:30:59.204Z",
    "verifiedCredentialType": "wallet"
  }
}
```

### What generates events?

| Source              | Trigger                                                                               |
| :------------------ | :------------------------------------------------------------------------------------ |
| Developer Dashboard | When you call an API by modifying Dynamic resources in the Developer Dashboard.       |
| SDK                 | When a user action in your app or website integrating the SDK results in an API call. |
| API                 | When you call an API directly.                                                        |

## Setting up Webhooks

<Steps>
  <Step title="Events">
    Identify what events you would like to monitor [here](/developer-dashboard/eventTypes).
  </Step>

  <Step title="Endpoint">
    Develop a webhook endpoint to receive event data POST requests, making sure it uses HTTPS.
  </Step>

  <Step title="Enable">
    Register your endpoint with Dynamic using the [Webhooks Developer Dashboard](https://app.dynamic.xyz/dashboard/developer/webhooks) or the API.
  </Step>
</Steps>

#### Signature validation

Dynamic follows general best practice when it comes to signature validation. As such, each payload includes a `x-dynamic-signature-256` header which has a hash signature value, generated from your secret token.

Each webhook has a unique secret token that is used to generate the message signature from the event object. This secret can be found on the webhook detail page in the Developer Dashboard.

![webhook secret](https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/webhook-secret.png)

To perform the validation, you can follow the guide [here](/guides/webhooks-signature-validation) for a javascript implementation. There are plenty of other template examples out there to help with different implementations. We recommend [the Github guide](https://docs.github.com/en/webhooks/using-webhooks/validating-webhook-deliveries) as it follows exactly the same format of signature as we do.

## Event delivery behavior

### Test payload

When you register a webhook, Dynamic sends a test payload to the webhook’s configured URL to verify that the endpoint exists and can receive payloads. The test payload is a `ping` event.

### Retry behavior

Webhook messages have built-in retry logic. All non-200 response codes will result in a retry.

Events are delivered at least once, but can be retried up to 5 times for live environments, make sure to handle idempotency using the `messageId` attribute.

Below are the request retry intervals for live environments

*   15 seconds
*   1 minute
*   10 minutes
*   1 hour
*   1 day

### Event ordering

Dynamic makes no guarantee of message ordering. Events can be sent in an order not congruent with the order in which they happened.

## Best Practices for implementing webhooks

### Idempotency

Configured endpoints may receive the same event multiple times. This can happen for a number of reasons, including network timeouts, duplicate events, or as a result of retry logic. As such, it is important to design your endpoint to be idempotent so that it can safely handle duplicate events. Using the `messageId` attribute in the event object is a good way to handle this.

### Events

Only select the events you are interested in. If you are only interested in `user` events, only select those event types. This will reduce the number of messages you receive and the number of messages you need to handle.

### Verify messages

Make sure to validate the `x-dynamic-signature-256` header to ensure the message is coming from Dynamic. This is a hash signature value, generated from your secret token.

### Security

Make sure your endpoint is using HTTPS. Dynamic will not send messages to endpoints that use HTTP.

## Limits

### Webhooks

10 per environment

### Webhook messages

After repeated failed delivery of messages within a 30 day period to a configured endpoint that responds with a non `2XX` HTTP status code the webhook will be automatically disabled. For live environments this is 6 failed attempts per message up to a maximum of 1500 failed messages, and for sandbox environments this is 3 failed attempts per message up to a maximum of 250 failed messages. Note that if any of the message retries are successful it is not counted against the failure limit. The webhook will remain disabled until it is re-enabled manually.

Messages in live environments will be available for 90 days and 30 days for sandbox environments.

### Endpoint response time

Configured payload urls must respond promptly, if the endpoint does not respond within 15 seconds, the message timeout and will be retried.


# Overview



Traditionally, embedded wallets were limited to single applications, hindering cross-app interoperability for end-users and developers. This restriction made it challenging to create seamless experiences across multiple web3 applications. Additionally, developers aiming to build ecosystems around their brand or chain faced limited tools for enabling universal wallet access from most wallet providers.

**Dynamic is designed to avoid both pitfalls and offer you solutions to improve end-user interoperability across web3 and solutions to let you build around your app or third-party ecosystem.**

This document outlines the different features and tools to enable cross-app connectivity for your embedded wallets and ecosystem growth.

## I'm building an ecosystem or chain

Designed for developers who want to foster unified login experiences across partner apps or associated third-party application developers. Ideal if you are building a chain, you are already a large brand that is extending reach to smaller partners, or you are a small company with a deep relationship with a single large entity in the ecosystem.

### 1: [Display your wallet within other apps](/ecosystem-wallets/web3-libraries)

We provide simple guides you can offer to your partners to add your wallet to their apps.

### 2: Create a full ecosystem around your brand

We are building multiple powerful features to let you build a full ecosystem around your brand. [Reach out to us to learn more](https://dynamic.xyz/slack).

## I'm building a single app

If you're building an app, Dynamic offers two features to let you enable your end-users to connect their Dynamic-powered wallets to other apps.

### 1: [Enable your end-users to connect to any app with QR code scanning (and URI codes)](/ecosystem-wallets/qr-scanning)

You can allow your end-users to connect to any app that uses WalletConnect. This provides end-users with the broadest web3 app coverage but is mostly suited for end-users who have some familiarity with WalletConnect and other apps across web3.

**How it works:**

1.  Mobile - Upon clicking "Connect," your end-users are prompted to go to their preferred app and scan a WalletConnect QR code or copy a URI code. Once scanned, they will confirm the connection, and their assets and wallet will now be connected to the app.
2.  Desktop - Your end-users will copy and paste WalletConnect's URI code to associate their embedded wallet with an external site.

### 2: Inject your end-users' wallets into an iframe

Coming soon.


# QR Scanning (Global Connectivity)



## Overview

Global connectivity allows end-users to use your app and their embedded wallet on any web3 app that integrates WalletConnect.
Transactions from the app will be forwarded to the end-user on the app that initiated the connection.
This feature is currently restricted to EVM embedded wallets.

<Note>
  To ensure all transactions from a connected app are approved first by the
  end-user, confirmation prompts will be forced for messages that come from a
  different app.
</Note>

## Getting started

In order to use Global Connectivity, we have provided an additional optional package:

```bash
npm i @dynamic-labs/global-wallet
```

```jsx
import { GlobalWalletExtension } from "@dynamic-labs/global-wallet";

return (
  <DynamicContextProvider
    settings={{
      environmentId: "XXXXX",
      walletConnectorExtensions: [GlobalWalletExtension],
    }}
  >
    <App />
  </DynamicContextProvider>
);
```

After passing in the extension, you should see a "Connect" button on the main page of the dynamic widget.
Follow the instructions on the widget to start connecting to other apps.

<Frame>
  <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/global-wallet.png" />
</Frame>

<Frame>
  <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/global-wallet-2.png" />
</Frame>

## Security

When users connect to third-party websites and initiate transactions, Dynamic automatically interacts with the Blockaid API ([www.blockaid.io](http://www.blockaid.io)) to run transaction simulations and verify that the associated URLs are safe and not malicious.

Please be aware that this feature has usage limits, and additional charges may apply for higher-volume usage. For details on scaling and pricing, please contact our support team.

## FAQ

### Headless and hooks support

Currently, global connectivity is not supported in headless mode or with hooks.
​

### Safety practices

For best safety practices, please refer to our security section above. In general:

*   Educate users to check which web3 apps they connect to.
*   Dynamic will be rolling out new integrations to warn users if they attempt to connect to malicious dApps in the near future.
    ​

### Using Account Abstraction with global wallets

Account Abstraction is compatible with global wallets. If using gas sponsorship, you should whitelist your own dApps/contracts. For more information, see our [Smart Wallets documentation](/smart-wallets/add-smart-wallets).
​

### What is a URI code?

A URI (Uniform Resource Identifier) code in the context of WalletConnect is a string that contains all the necessary information for a wallet to establish a connection with a dApp.
​

### Compatibility with other chains

Currently, global connectivity only work on EVM chains. Solana support may come in the future as more dApps support WalletConnect with Solana.


# Web3 Wallet Libraries



## RainbowKit

You can guide your partners to add your wallet as a recommended wallet in their app. To do so, they will create a custom connector, as described below, which will should your wallet. When clicked, the wallet will render a QR code, which will work with Dynamic's [QR scanning](/ecosystem-wallets/qr-scanning) feature.

### Step 1: Import `connectorsForWallets` and `getWalletConnectConnector`

```javascript
import { RainbowKitProvider, connectorsForWallets, Wallet, getWalletConnectConnector, darkTheme } from '@rainbow-me/rainbowkit';
```

### Step 2: Create a custom connector

```javascript
export interface MyWalletOptions {
  projectId: string;
}

export const legionkey = ({ projectId }: MyWalletOptions): Wallet => ({
  id: 'legion-key',
  name: 'LegionKey',
  iconUrl: './images/legion-key.png',
  iconBackground: '#985d3c',

  mobile: {
    getUri: (uri: string) => uri,
  },
  qrCode: {
    getUri: (uri: string) => uri,
    instructions: {
      learnMoreUrl: 'https://my-wallet/learn-more',
      steps: [
        {
          description:
            'We recommend putting My Wallet on your home screen for faster access to your wallet.',
          step: 'install',
          title: 'Open the My Wallet app',
        },
        {
          description:
            'After you scan, a connection prompt will appear for you to connect your wallet.',
          step: 'scan',
          title: 'Tap the scan button',
        },
      ],
    },
  },
  extension: {
    instructions: {
      learnMoreUrl: 'https://my-wallet/learn-more',
      steps: [
        {
          description:
            'We recommend pinning My Wallet to your taskbar for quicker access to your wallet.',
          step: 'install',
          title: 'Install the My Wallet extension',
        },
        {
          description:
            'Be sure to back up your wallet using a secure method. Never share your secret phrase with anyone.',
          step: 'create',
          title: 'Create or Import a Wallet',
        },
        {
          description:
            'Once you set up your wallet, click below to refresh the browser and load up the extension.',
          step: 'refresh',
          title: 'Refresh your browser',
        },
      ],
    },
  },
  createConnector: getWalletConnectConnector({ projectId }),
});
```

### Step 3: Config the connectors

```javascript
const connectors = connectorsForWallets(
  [
    {
      groupName: 'Recommended',
      wallets: [legionkey],
    },
    {
      groupName: 'Common',
      wallets: [rainbowWallet, walletConnectWallet],
    },
  ],
  {
    appName: 'My RainbowKit App',
    projectId: 'YOUR_PROJECT_ID',
  }
);
```

### Step 4: Add to the Rainbowkit Configuration

```javascript
const config = createConfig({
  connectors,
  ...
});
```

## ReOwn AppKit (formally WalletConnect)

To integrate ReOwn AppKit (formally WalletConnect), follow these steps to submit your wallet as an official wallet into the ReOwn ecosystem:

### Step 1: Create an account on reown.com

*   Visit [reown.com](https://reown.com) and sign up for a new account if you don't already have one.
*   Verify your email address and complete the account setup process.

### Step 2: Create a new walletKit project

*   Log in to your ReOwn account.
*   Navigate to the dashboard and look for the option to create a new walletKit project.
*   Fill in the required information about your wallet, including its name, description, and supported networks.

### Step 3: Submit your wallet for review

*   Once your walletKit project is set up, you'll need to submit your wallet for review.
*   Provide all necessary documentation, including your wallet's features, security measures, and any other relevant information.
*   Ensure that your wallet meets all of ReOwn's guidelines and requirements for official wallets.

### Step 4: Once approved, it will show up in any AppKit list

*   After submission, the ReOwn team will review your wallet.
*   When approved, your wallet will be added to the official ReOwn ecosystem.
*   It will then appear in AppKit lists, making it easily discoverable for users across various dApps that implement ReOwn AppKit.
*   Edit the WalletKit project ID in the Dynamic Dashboard [here](https://app.dynamic.xyz/dashboard/log-in-user-profile/#walletconnect).

By following these steps, you'll integrate your wallet with ReOwn AppKit, increasing its visibility and accessibility within the ReOwn ecosystem.


# Example apps



## Create Dynamic App

<Frame>
  <img className="block dark:hidden" src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/create-dynamic-app-light.svg" />

  <img className="hidden dark:block" src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/create-dynamic-app-dark.svg" />
</Frame>

Whether you need React, React Native or Next.js, whether you want to use Ethers or Viem, whether you want to use Wagmi or not, we've got you covered. Simply run `npx create-dynamic-app@latest` and follow the prompts!

<Tip>
  ```
  npx create-dynamic-app@latest
  ```
</Tip>


# Banxa



## Summary

At Dynamic, we make it incredibly simple to add a Fiat onramp solution to your SDK integration. We are currently working with [Banxa](https://banxa.com/) and other great onramp solutions will be made available soon.

### Usage

To enable an onramp solution:

1.  Go to your developer dashboard and find the [Onramp Providers](https://app.dynamic.xyz/dashboard/configurations#onramp) tab.
2.  **Review and agree** to Banxa's Terms and Conditions.
3.  Once confirmed, enable the Banxa onramp provider.
    1.  Note: if you enable it on Sandbox, then you'll be using Test Order. Reference Banxa's test information so you can easily [test in sandbox.](https://docs.banxa.com/docs/order-flow)

![Banxa](https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/banxa.png)

You're done. In the Dynamic widget, users will now see a "Buy" button that will open up a fiat onramper iFrame.


# Bring Your Own Onramp



## Introduction

If you want to embed any onramp provider in the widget, we've got your back! In this example we'll integrate [the Coinbase onramp](https://www.coinbase.com/en-gb/developer-platform/products/onramp) but the principle is exactly the same for any other.

## Pre-requisites

We assume you already have Dynamic integrated into a React based app, and are using the Dynamic Widget. The plan is to override the existing Buy button in the user profile page of the widget, so make sure you have [onramps enabled in your Dynamic dashboard]() for that to show up.

## Full Demo

You can find the full example app of this implementation code [here](https://github.com/dynamic-labs/custom-onramp-example/), and the deployment at [https://custom-onramp-example.vercel.app/](https://custom-onramp-example.vercel.app/).

## Implementation

### Install dependancies

The only other library we will need is the Coinbase Pay javascript SDK:

```bash
npm i @coinbase/cbpay-js
```

### Scaffold our override file

Create a new file in your codebase called `customOnramp.js`. In it let's add the imports and declarations needed to get started:

```javascript
// Method to initialize the Coinbase Pay instance.
import { initOnRamp } from "@coinbase/cbpay-js";

// We want to only run things once, and this variable will help.
let isSetupInitiated = false;

// Empty function that we will fill out in the next section.
export const setupCoinbaseButtonOverride = (options) => {
}
```

For the following sections, unless otherwise told, place the code inside the now empty `setupCoinbaseButtonOverride` function.

### Setup the pay instance

Inside the setupCoinbaseButtonOverride, let's set up a few things, including the CB pay instance:

```javascript
  // Stop if it already ran
  if (isSetupInitiated) {
    return;
  }
  
  // Set our flag to say the function has initiated
  isSetupInitiated = true;

  // Destructure the options needed for the onramp
  const {
    appId,
    addresses,
    assets,
    debug = false
  } = options;

  // Variable to hold the instance
  let onrampInstance = null;
  
  // Initialize the onramp
  initOnRamp({
    appId,
    widgetParameters: {
      addresses,
      assets,
    },
    // Transaction callback
    onSuccess: () => {
      if (debug) console.log('Coinbase transaction successful');
    },
    // Widget close callback
    onExit: () => {
      if (debug) console.log('Coinbase widget closed');
    },
    // General event callback
    onEvent: (event) => {
      if (debug) console.log('Coinbase event:', event);
    },
    experienceLoggedIn: 'popup',
    experienceLoggedOut: 'popup',
    closeOnExit: true,
    closeOnSuccess: true,
  }, (_, instance) => {
    // Set assign the instance to our variable
    onrampInstance = instance;
    if (debug) console.log('Coinbase instance initialized');
  });
```

### Find the current Buy button

Add a new function (still inside setupCoinbaseButtonOverride) called `findButtonInShadowDOM` which is responsible for detecting the button in the shadow dom:

```javascript
  const findButtonInShadowDOM = (root) => {
    const shadows = root.querySelectorAll('*');
    for (const element of shadows) {
      if (element.shadowRoot) {
        const button = element.shadowRoot.querySelector('[data-testid="buy-crypto-button"]');
        if (button) {
          return button;
        }
        const deepButton = findButtonInShadowDOM(element.shadowRoot);
        if (deepButton) {
          return deepButton;
        }
      }
    }
    return null;
  };
```

### Override the current button

Add another new function (still inside setupCoinbaseButtonOverride) called `setupOverride` which is responsible replacing the existing button functionality with our own:

```javascript
  const setupOverride = () => {
    // Call our previously declared function
    const button = findButtonInShadowDOM(document);
    
    // We're ready to override the button
    if (button && onrampInstance) {
      if (debug) console.log('Found button and Coinbase instance ready');
      
      // Remove disabled state
      button.classList.remove('disabled');
      button.removeAttribute('disabled');
      
      // Remove all existing click handlers
      const newButton = button.cloneNode(true);
      button.parentNode?.replaceChild(newButton, button);
      
      // Add our new click handler
      newButton.addEventListener('click', (event) => {
        event.preventDefault();
        event.stopPropagation();
        
        if (debug) console.log('Opening Coinbase widget');
        onrampInstance?.open();
      });

      return true;
    }
    
    return false;
  };
```

### Poll for the button and onramp

Since we need both the button to be present and the onramp instance to exist for us to complete the override, we must poll for that state:

```javascript
  // Set the current time 
  const startTime = Date.now();

  // Declare an interval of .5 seconds
  const checkInterval = setInterval(() => {
    
    // Run the override setup and if it succeeds or 30 seconds have passed...
    if (setupOverride() || Date.now() - startTime > 30000) {
      clearInterval(checkInterval);
      if (Date.now() - startTime > 30000) {
        if (debug) console.warn('Timeout reached while setting up Coinbase button override');
      }
    }
  }, 500);
```

### Return a cleanup function

We need to remember to tear things down again when we're finished:

```javascript
return () => {
  clearInterval(checkInterval);
  onrampInstance?.destroy();
  isSetupInitiated = false;
};
```

### Use setupCoinbaseButtonOverride

You've now finished with the setupCoinbaseButtonOverride method, so let's add it to one of our components. It doesn't matter to much which one as long as it's rendered at the same time as the Widget is. Note that it cannot be the same component you declare your DynamicContextProvider in, it must be inside the component tree.

#### Adding Dynamic & other imports

Here we'll do it in the same component (Main.js as we have our DynamicWidget). Let's do the relevant imports first, we're going to need a couple of hooks from Dynamic, as well as our setupCoinbaseButtonOverride:

```jsx
// Main.js
import { DynamicWidget, useDynamicContext, useIsLoggedIn } from "@dynamic-labs/sdk-react-core";
import { isEthereumWallet } from '@dynamic-labs/ethereum';
import { setupCoinbaseButtonOverride } from './customOnramp.js';
```

#### UseEffect, Dynamic hooks & widget

Next we'll scaffold the Main component itself and create an empty useEffect which depends on the relevant Dynamic hooks:

```jsx
export default const Main = () => {

  // We need to know if user is logged in
  const isLoggedIn = useIsLoggedIn();

  // We need to know that the user has a wallet
  const { primaryWallet } = useDynamicContext();
  
  // We will fill this in next
  useEffect(() => {
  }, [isLoggedIn, primaryWallet])

  return (
    <div>
      <DynamicWidget />
    </div>
  )
};
```

#### Conditionals and calling our init

Everything from now on will be added inside the useEffect we just declared.

```jsx
let cleanup;
  
const initOnramp = async () => {
  
  // We only want to support Eth here, you can do more
  if (!isEthereumWallet(primaryWallet)) {
    console.log('not an Eth Wallet');
    return;
  }

  if (cleanup) {
    cleanup();
  }

  // Fetch the currently supported EVM networks for that wallet
  const networks = primaryWallet.connector.evmNetworks.map(network => 
    network.name.toLowerCase()
  );

  cleanup = setupCoinbaseButtonOverride({
    appId: '12109858-450c-4be4-86b9-13867f0015a1',
    addresses: { 
      [primaryWallet.address]: networks
    },
    assets: ['ETH', 'USDC'],
    debug: true
  });
};

// Initialize our custom onramp
if (isLoggedIn && primaryWallet) {
  initOnramp();
}

return () => {
  if (cleanup) {
    cleanup();
  }
}
```

That's it! Your Buy button now opens the coinbase onramp widget, while passing in all the relevant parameters it needs!


# Dynamic Package



<Frame>
  <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/flutter.jpg" />
</Frame>

<Info>
  Please be aware that our Flutter SDK is in open alpha at the moment!

  Available now:

  *   email login (headless)
  *   sms login (headless)
  *   auth flow UI
  *   profile UI
  *   EVM embedded wallets
  *   web3dart integration
  *   social login

  Coming next:

  *   solana embedded wallets
</Info>

## Installation

Simply run the following in your terminal:

```
flutter pub add dynamic_sdk
```

This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):

```
dependencies:
  dynamic_sdk: ^0.0.1-alpha.2
```

## Set up

Getting started with `DynamicSDK` takes only three steps:

### 1. Initialize your client

First you have to start the client singleton with your data in `ClientProps`;

```dart
import 'package:dynamic_sdk/dynamic_sdk.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  DynamicSDK.init(
    props: ClientProps(
      environmentId: 'your-environment-id',
      appLogoUrl: 'your-logo-url',
      appName: 'your-app-name',
    ),
  );
  runApp(const MyApp());
}
```

### 2. Wait for the SDK to load

Add the `DynamicSDK.instance.dynamicWidget` and wait for the SDK to loaded using the `DynamicSDK.instance.sdk.readyChanges` stream;

```dart
class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: Stack(
        children: [
          // Make sure the SDK is ready before using it
          StreamBuilder<bool?>(
            stream: DynamicSDK.instance.sdk.readyChanges,
            builder: (context, snapshot) {
              final sdkReady = snapshot.data ?? false;
              return sdkReady
                  ? const MyHomePage(title: 'Flutter Demo Home Page')
                  : const SizedBox.shrink();
            },
          ),
          // DynamicSDK widget must be available all the time
          DynamicSDK.instance.dynamicWidget,
        ],
      ),
    );
  }
}
```

### 3. Do your stuff

That's it! Now you are good to go! See below how to authenticate using our UI:

```dart
class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: SingleChildScrollView(
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Column(
                children: [
                  // Listen to auth token changes
                  StreamBuilder<String?>(
                    stream: DynamicSDK.instance.auth.tokenChanges,
                    builder: (context, snapshot) {
                      final authToken = snapshot.data;
                      // Show the auth token when logged in
                      return authToken != null
                          ? Column(
                              children: [
                                const LogoutButton(),
                                const SizedBox(height: 24),
                                Text('AUTH TOKEN: $authToken'),
                              ],
                            )
                          // Show Dynamic UI for sign in
                          : const LoginButton();
                    },
                  ),
                ],
              ),
            ],
          ),
        ),
      ),
    );
  }
}

// Show Dynamic UI for sign in
class LoginButton extends StatelessWidget {
  const LoginButton({
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () => DynamicSDK.instance.ui.showAuth(),
      child: const Text('Dynamic Login'),
    );
  }
}

// Headless logout function
class LogoutButton extends StatelessWidget {
  const LogoutButton({
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () => DynamicSDK.instance.auth.logout(),
      child: const Text('Logout'),
    );
  }
}
```

## Available modules

Let's walk you through the available modules and how to use most of the features.

### SDK Module

1.  Get to know when the SDK is ready to be used by listening to `readyChanges` stream.

```dart
StreamBuilder<bool?>(
  stream: DynamicSDK.instance.sdk.readyChanges,
  builder: (context, snapshot) {
    final sdkReady = snapshot.data ?? false;
    return sdkReady
        ? const MyHomePage(title: 'Flutter Demo Home Page')
        : const SizedBox.shrink();
  },
),
```

### User Interface Module

1.  Use our interface to sign in

```dart
class LoginButton extends StatelessWidget {
  const LoginButton({
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () => DynamicSDK.instance.ui.showAuth(),
      child: const Text('Dynamic Login'),
    );
  }
}
```

2.  Use our interface to see the user's profile

```dart
class UserProfileButton extends StatelessWidget {
  const UserProfileButton({
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () => DynamicSDK.instance.ui.showUserProfile(),
      child: const Text('Show Profile'),
    );
  }
}
```

### Auth Module (headless authentication)

1.  Headless e-mail sign in

```dart
class EmailLoginButton extends StatelessWidget {
  const EmailLoginButton({
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () async => await DynamicSDK.instance.auth.email.sendOTP(
        'user@email.com'
      ),
      child: const Text('Email Login'),
    );
  }
}
```

2.  Headless SMS sign in

```dart
class SMSLoginButton extends StatelessWidget {
  const SMSLoginButton({
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () async => await DynamicSDK.instance.auth.sms.sendOTP(
        PhoneData(
          phone: phone, // User's phone number
          iso2: 'US',
          dialCode: '+1',
        ),
      ),
      child: const Text('SMS Login'),
    );
  }
}
```

3.  Verity OTP

```dart
class VerifyOTPButton extends StatelessWidget {
  const VerifyOTPButton({
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      // For email use await DynamicSDK.instance.auth.email.verifyOTP(code),
      onPressed: () async => await DynamicSDK.instance.auth.sms.verifyOTP(code), // Verification code
      child: const Text('Verify Code'),
    );
  }
}
```

### Wallets Module

1.  Get network

```dart
Future<String> getNetworkInfo({required BaseWallet wallet}) async {
  final network = await DynamicSDK.instance.wallets.getNetwork(wallet: wallet);
  final name = getNetworkName(network.value);

  return name;
}

String getNetworkName(networkId) {
  final evm = DynamicSDK.instance.networks.evm;

  bool isEvm = evm.any((network) => network.networkId == networkId);

  if (isEvm) {
    final network = evm.firstWhere(
      (network) {
        return network.networkId == networkId;
      },
    );

    return network.name;
  } else {
    return networkId;
  }
}
```

2.  Switch networks

```dart
void switchNetwork({
  required BaseWallet wallet,
  required int chainId,
}) async {
  await DynamicSDK.instance.wallets.switchNetwork(
    wallet: wallet,
    network: Network(chainId),
  );
}
```

3.  Sign message

```dart
Future<String?> signMessage({
  required String message,
  required BaseWallet wallet,
}) async {
  try {
    final signedMessage = await DynamicSDK.instance.wallets.signMessage(
      message: message,
      wallet: wallet,
    );
    
    return signedMessage;
  } catch (e) {
    print(e);
    rethrow;
  }
}
```

Other methods like `getBalance` and `setPrimary` are as straight forward as the ones above.

You can read more about our client package [here](/flutter/package-references/client).


# Deeplink URLs



In order to use most of our Flutter features (such as social connections), you will first
have to set up the list of mobile deeplink URLs Dynamic may use to redirect back to your app.

For security reasons, when deeplinking back to your app, we must verify whether the app's deeplink URL
is included in this list. If it isn't, Dynamic will not perform the deeplink.

## How to set up your app for deeplinking

### Configuring your app's custom scheme

First, you should follow Flutter's guide on enabling deeplinking for your app
[here](https://docs.flutter.dev/ui/navigation/deep-linking).

### Registering your app's deeplink URL with Dynamic

Afterwards, head to your Dynamic dashboard's
[Security page](https://app.dynamic.xyz/dashboard/security) and enable "Whitelist Mobile Deeplink".
Click "Save changes".

Next, click the cog in this same section to open up the
[Mobile Deeplink URL page](https://app.dynamic.xyz/dashboard/security#mobile-deeplink-urls), and there
you must add your app's deeplink URL with the same custom scheme you configured in the previous step.
It might look something like this: `myappcustomscheme://`.

With this, you're done! Dynamic is ready to deeplink back to your app.


# Client Reference



The base package that provides access to the Dynamic Client, which can be extended with extension
packages.

## Functions

### `DynamicSDK.init`

```
DynamicSDK init({
    required ClientProps props,
})
```

Initiates a singleton of a client object, which provides an interface to read state, trigger actions and
listen to events of our SDK.

## Objects

### `ClientProps`

The parameters that are acceptable by the [DynamicSDK.init](#DynamicSDK.init) method.

| Param           | Type           | Description                                                                 |
| --------------- | -------------- | --------------------------------------------------------------------------- |
| `environmentId` | `string`       | The ID of the environment of your dynamic application.                      |
| `apiBaseUrl`    | `string?`      | Allows you to override the URL to which the SDK will make its API requests. |
| `cssOverrides`  | `string?`      | Allows you to inject CSS into our UI modules (currently out of effect)      |
| `appName`       | `string?`      | How you'd like your app to be named in our copies.                          |
| `appLogoUrl`    | `string?`      | A URL of the logo of your app.                                              |
| `logLevel`      | `LoggerLevel?` | Allows you to set the level of the logs to the console                      |

### `DynamicSDK.instance`

The base object of a client.

Since clients can be extended (and thus have their objects composed with those of the extensions), this is considered the most basic kind of client:
the one that is returned from the [DynamicSDK.init](#DynamicSDK.init) method.

It is composed of properties which we call modules. Note that all modules have state properties and **implement one or more streams**.

<Note>
  For every property that is a state, there will always be a stream named with
  the same name plus `"Changes"`, which will be triggered when the property
  changes value. We will omit these from the docs below as they are implicit.
</Note>

#### `auth` module

Provides access to authentication related properties and methods of the SDK.

| Property            | Type                    | Description                                                                     |
| ------------------- | ----------------------- | ------------------------------------------------------------------------------- |
| `token`             | `string \| null`        | The JWT of the currently logged in user.                                        |
| `authenticatedUser` | `UserProfile \| null`   | The [UserProfile](/react-sdk/objects/userprofile) object of the logged in user. |
| `logout`            | `Future<void> logout()` | Allows you to log the current user out.                                         |

#### `auth.email` submodule

Provides methods to send, re-send and verify OTPs to email.

| Property    | Type                                   | Description                                                              |
| ----------- | -------------------------------------- | ------------------------------------------------------------------------ |
| `sendOTP`   | `Future<void> sendOTP(String email)`   | Sends an OTP token to the target email.                                  |
| `resendOTP` | `Future<void> resendOTP()`             | Sends another OTP token to the same email as the last call to `sendOTP`. |
| `verifyOTP` | `Future<void> verifyOTP(String token)` | Receives an OTP token and logs the user in if it is valid.               |

#### `auth.sms` submodule

Provides methods to send, re-send and verify OTPs to phone numbers.

| Property    | Type                                        | Description                                                                     |
| ----------- | ------------------------------------------- | ------------------------------------------------------------------------------- |
| `sendOTP`   | `Future<void> sendOTP(PhoneData phoneData)` | Sends an OTP token to the target [PhoneData](/react-sdk/objects/phone-data).    |
| `resendOTP` | `Future<void> resendOTP()`                  | Sends another OTP token to the same phone number as the last call to `sendOTP`. |
| `verifyOTP` | `Future<void> verifyOTP(String token)`      | Receives an OTP token and logs the user in if it is valid.                      |

#### `auth.social` submodule

Provides a method to connect social accounts.

| Property  | Type                                                                                 | Description                                                                                      |
| --------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------ |
| `connect` | `Future<void> connect({required SocialProvider provider, String? redirectPathname})` | Requests social connection to the provided [SocialProvider](/react-sdk/objects/social-provider). |

#### `sdk` module

Gives insight over the state of the SDK.

| Property | Type      | Description                                                 |
| -------- | --------- | ----------------------------------------------------------- |
| `loaded` | `boolean` | Whether the SDK has loaded and is ready to handle requests. |

#### `ui` module

Provides access to Dynamic's UI.

| Property          | Type   | Description                                                                                                |
| ----------------- | ------ | ---------------------------------------------------------------------------------------------------------- |
| `showAuth`        | `void` | Opens up Dynamic's authentication flow modal for your user to sign in. Automatically closes when finished. |
| `showUserProfile` | `void` | Opens up Dynamic's user profile modal, allowing your user to manage their profile and wallets.             |

#### `wallets` module

Provides access to the user's wallets.

| Property        | Type                                                                                 | Description                                                         |
| --------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------- |
| `userWallets`   | `BaseWallet[]`                                                                       | The array of all the user's [wallets](/react-sdk/objects/wallet).   |
| `primary`       | `BaseWallet \| null`                                                                 | The primary [wallet](/react-sdk/objects/wallet) of the user.        |
| `setPrimary`    | `Future<void> setPrimary({required String walletId})`                                | Sets primary \[wallet]/sdks(/react-sdk/objects/wallet) of the user. |
| `getBalance`    | `Future<String> getBalance({required BaseWallet wallet})`                            | Returns the balance of a wallet.                                    |
| `getNetwork`    | `Future<Network> getNetwork({required BaseWallet wallet})`                           | Returns the network the wallet is connected to.                     |
| `signMessage`   | `Future<String> signMessage({required BaseWallet wallet, required String message})`  | Signs a message with this wallet.                                   |
| `switchNetwork` | `Future<void> switchNetwork({required BaseWallet wallet, required Network network})` | Switches the wallet's network.                                      |

#### `wallets.embedded` submodule

Allows interacting with and creating an embedded wallet for the current user.

| Property       | Type                 | Description                                                                     |
| -------------- | -------------------- | ------------------------------------------------------------------------------- |
| `hasWallet`    | `boolean`            | Whether the logged in user has an embedded wallet.                              |
| `getWallet`    | `BaseWallet \| null` | Retrieves the embedded wallet of the current user, or null if it doesn't exist. |
| `createWallet` | `BaseWallet `        | Creates an embedded wallet for the current user. Throws if one already exists.  |


# Web3dart Reference



The package allows integrating [web3dart](https://pub.dev/packages/web3dart) to our [client](/react-native/client).

## Extensions

### `class DynamicRpcService extends RpcService`

```
DynamicRpcService({required this.chainId, required this.requestChannel})
```

Provides a method to make an `ethRequest` through the DynamicSDK.

| Property | Type                                                        | Description                                  |
| -------- | ----------------------------------------------------------- | -------------------------------------------- |
| `call`   | `Future<RPCResponse> call(String function, [List? params])` | Make an `ethRequest` through the DynamicSDK. |

#### `class DynamicCredential extends CredentialsWithKnownAddress implements CustomTransactionSender`

```
DynamicCredential({required this.requestChannel, required String address})
```

Provides methods to sign methods and send transactions for web3 wallets.

| Property          | Type                                                       | Description                                            |
| ----------------- | ---------------------------------------------------------- | ------------------------------------------------------ |
| `signMessage`     | `Future<String> signMessage({required Uint8List payload})` | Signs a message with `EthRequestWithAddressParams`     |
| `sendTransaction` | `Future<String> sendTransaction(Transaction transaction)`  | Sends a transaction with `EthRequestWithAddressParams` |


# Social Authentication



<Warning>
  This feature requires you set up the deeplink URLs whitelist for your Dynamic
  app. See [here](/flutter/deeplink-urls).
</Warning>

The dynamic client authentication module enables social authentication.

## Connecting to your users' social accounts

<Note>
  Notice that all social options you use in code **must be respectively
  enabled** in your environment's dashboard settings first!
</Note>

You can prompt a user to connect their social accounts with the `connect` method in our social module.
It returns a promise that resolves with no params on success, and rejects on failure.

Here's how you can connect a user's farcaster account, for example:

```dart
OutlinedButton(
    onPressed: () async =>
        await DynamicSDK.instance.auth.social.connect(
        provider: SocialProvider.farcaster,
        redirectPathname: '/login_screen',
    ),
    child: const Text(
        'Continue with Farcaster',
    ),
)
```

See [here](/react-sdk/objects/social-provider) for a list of all our supported social providers —
it will be the same list you see in your dashboard.

<Info>
  Not only can this method be used to sign a user in, but it is also able to
  link a social account to an already signed in user.
</Info>

## Adjusting app name in social connection dialogues

Social connection dialogues will derive the name of your app from the `appName` prop you used to initialize your client — read [here](/flutter/package-references/client#clientprops).

So in order for your app's name to be displayed correctly to your user when they are connecting their social accounts,
you need to provide this prop.

You can read more about the social module [here](/flutter/package-references/client#auth-social-submodule).


# Web3Dart Package



The Dynamic SDK Web3Dart is a dart package that provides a few methods for interacting with the Ethereum blockchain. It connects to an Ethereum through [web3dart](https://pub.dev/packages/web3dart) node to send transactions, interact with smart contracts and much more!

## Installation

Simply run the following in your terminal:

```
flutter pub add dynamic_sdk_web3dart
```

This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):

```
dependencies:
  dynamic_sdk_web3dart: ^0.0.1-alpha.2
```

## Main Features

### 1. Sign Message

```dart
Future<String> signMessage({
  required BaseWallet wallet,
  required String message,
}) async {
  
  final requestChannel = RequestChannel(
    DynamicSDK.instance.messageTransport,
  );
  
  final signedMessage = await DynamicCredential.fromWallet(
    requestChannel: requestChannel,
    wallet: wallet,
  ).signMessage(
    payload: Uint8List.fromList(
      message.codeUnits,
    ),
  );

  return signedMessage;
}
```

### 2. Send Transaction

```dart
Future<String> web3dartSendTransaction({
  required BaseWallet wallet,
  required String recipientAddress,
  required String amount,
}) async {
  final recipient = EthereumAddress.fromHex(
    recipientAddress,
  );

  final network = await DynamicSDK.instance.wallets.getNetwork(
    wallet: wallet,
  );

  final service = DynamicRpcService(
    requestChannel: RequestChannel(
      DynamicSDK.instance.messageTransport,
    ),
    chainId: network.intValue() ?? 0,
  );

  final client = Web3Client.custom(service);

  final gasPrice = await client.getGasPrice();

  final maxFeePerGas =
      gasPrice.getValueInUnitBI(EtherUnit.wei) * BigInt.from(2);

  final maxPriorityFeePerGas = gasPrice.getValueInUnitBI(EtherUnit.wei);

  final amountInWei =
      (double.parse(amount) * BigInt.from(10).pow(18).toDouble()).toInt();

  final transaction = Transaction(
    to: recipient,
    maxGas: 21000,
    gasPrice: gasPrice,
    maxFeePerGas: EtherAmount.inWei(
      maxFeePerGas,
    ),
    maxPriorityFeePerGas: EtherAmount.inWei(
      maxPriorityFeePerGas,
    ),
    value: EtherAmount.inWei(
      BigInt.from(amountInWei),
    ),
  );

  final requestChannel = RequestChannel(
    DynamicSDK.instance.messageTransport,
  );

  final dynamicCredential = DynamicCredential.fromWallet(
    requestChannel: requestChannel,
    wallet: wallet,
  );

  final signedTransaction = await dynamicCredential.sendTransaction(
    transaction,
  );

  return signedTransaction;
}
```

### 3. Sign Contract

```dart
Future<String> writeContract({
  required BaseWallet wallet,
  required String message,
}) async {
  final requestChannel = RequestChannel(
    DynamicSDK.instance.messageTransport,
  );

  final dynamicCredential = DynamicCredential.fromWallet(
    requestChannel: requestChannel,
    wallet: wallet,
  );

  final network = await DynamicSDK.instance.wallets.getNetwork(
    wallet: wallet,
  );

  final service = DynamicRpcService(
    requestChannel: RequestChannel(
      DynamicSDK.instance.messageTransport,
    ),
    chainId: network.intValue() ?? 0,
  );

  final client = Web3Client.custom(service);

  final gasPrice = await client.getGasPrice();

  final maxFeePerGas =
      gasPrice.getValueInUnitBI(EtherUnit.wei) * BigInt.from(2);

  final maxPriorityFeePerGas =
      gasPrice.getValueInUnitBI(EtherUnit.wei) * BigInt.from(2);

  final contract = DeployedContract(
    ContractAbi.fromJson(TestContract.contractAbi, ''),
    TestContract.deployedAddress,
  );

  final updateFunction = contract.function('update');

  final transaction = Transaction.callContract(
    contract: contract,
    maxFeePerGas: EtherAmount.inWei(
      maxFeePerGas,
    ),
    maxPriorityFeePerGas: EtherAmount.inWei(
      maxPriorityFeePerGas,
    ),
    function: updateFunction,
    parameters: [
      message,
    ],
  );

  final signedContract = await dynamicCredential.sendTransaction(
    transaction,
  );

  return signedContract;
}
```

You can read more about our client package [here](/flutter/package-references/client).


# Access Lists



## Overview

If you're launching a new site or want to restrict parts of your site to a predefined list of users, then access lists or waitlists are probably a high priority for you.

Leveraging gating with access lists through the Dynamic dashboard is a simple no-code option that gives you flexibility to define various and manage various lists easily. Through your dashboard, you can:

1.  Restrict site access based on a list of emails, wallet addresses, or other unique identifiers.
2.  Return a scope in the JWT of emails, wallet addresses, or other unique identifiers.

In our dashboard, uou can design precise gating rules by combining multiple elements such as various access access list with [NFT and token gates.](/gating/nft-token-gating)

## Usage

Find the [Access Control tab via the Configurations page](https://app.dynamic.xyz/dashboard/configurations#accesscontrol) of your developer dashboard.

Here you can create access lists based on emails or wallet addresses.

1.  Click "Create new gate"
2.  Name your gate
3.  Select the gating method:
    1.  Allow Site Access - this option will block users from access your site (we won't generate a JWT) unless the criteria is met
    2.  Return scope - this option will not block users, but instead will return the JWT with a predefined scope if the user has met the defined criteria
4.  Choose the type of identifier (email, wallet address, or other).
5.  Enter the identifier.
    1.  You can also add an alias to more easily keep track of these users.
6.  Click the "Add +" button to save the user to the list. Keep adding users as needed.
7.  Save and enable the toggle when you're ready.
8.  You're done!

You can create multiple lists to help keep track of different groups of users (ie, VIPs, beta users, internal users, etc). Note: a user only needs to be in one of the lists to pass.

## Working with scopes

To simplify working with scopes, we created a custom hook named useDynamicScopes. It allows you to easily checking the user scopes and to check for a specific scope on a user. To learn more about how to use this hook, see our docs [here](/react-sdk/hooks/usedynamicscopes).

## Customize the copy and button

You can customize the copy through props in our SDK by updating the `accessDeniedMessagePrimary` and `accessDeniedMessageSecondary`.

```tsx
<DynamicContextProvider
   settings={{....
      accessDeniedMessagePrimary: 'Your copy1',
      accessDeniedMessageSecondary: 'Your Copy2',
   }}
>
```

You can also return a completely custom button by passing a JSX/TSX element to the `accessDeniedButton` prop. Here's an example to link to a contact page:

```tsx
<DynamicContextProvider
   settings={{....
      accessDeniedButton: {
         action: () => window.open(`https://www.dynamic.xyz/talk-to-us`,'_blank'),
         title: `Book a demo`
      }
   }}
>
```

The button should adhere to the following type:

```tsx
type AccessDeniedCustomButton = {
   action?: () => void;
   title: string;
};
```


# NFT/Token Gating



NFT/Token Gating is a feature of access control, just like access lists. It is a way of restricting access to those who have certain NFTs or tokens. For example, you can:

*   restrict page access based on users NFTs or Tokens
*   give user list of scopes based on users NFTs or Tokens

The feature works by defining what are called "gates". You can have multiple gates based on different contract addresses. Inside of a gate you can add multiple criteria which all needs to be fulfilled for gate to be applied. Remember that between different gates we are adding an `OR` statement. That allows you to create a complex logic based on `AND` and `OR` gates, as you'll see in the example section on this page!

### How to create a new gate?

Visit [the Access Control tab of the Dynamic dashboard](https://app.dynamic.xyz/dashboard/configurations#accesscontrol).

1.  Click "Create new gate"
2.  Set a name for your gate
3.  Choose from two options:
    1.  Allow Site Access - users who fulfils the criteria won't be blocked from entering the site.
    2.  Return scope - users who fulfils the criteria will have a scope added to their `jwt` token.
4.  Select chain on which you want to apply the gate. Right now we only allow adding gates for `Ethereum`, `Polygon`, `Optimism`, `Arbitrum`. If you need another, let us know!
5.  Select the type of criteria for the gate:
6.  Token - for token criteria you need to select a token you want for users to have and amount.
7.  NFT - for NFT criteria you need to enter contract address of NFT you want for users to have.
8.  Save and enable the toggle when you're ready.
9.  Boom. You're done!

### Examples

**Block site for users without specific amount of tokens.**

Gate setup:

*   User needs to have at least 1 SHIB to enter the site.

<Frame>
  <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/access-gate-shib-config.png" />
</Frame>

*   User is blocked in Dynamic SDK:

<Frame>
  <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/token-gate-rejection.png" />
</Frame>

**Add scope for users `jwt` when having specific NFT**

Gate setup:

*   User needs to have specific NFT to have `admin` scope

<Frame>
  <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/access-gate-scope-config.png" />
</Frame>

*   User has an `admin` scope added to the `jwt`
    ```Text json
    {
      ...
      "scope": "admin",
      ...
    }
    ```

### Working with scopes

To simplify working with scopes, we created a custom hook named `useDynamicScopes`. It allows checking for users scopes and checking if one or many of them are in users `jwt` token. Check the docs [here](/react-sdk/hooks/usedynamicscopes).


# Go Live Checklist



<AccordionGroup>
  <Accordion title="Review Settings" icon="gears">
    *   Review the [general settings](/developer-dashboard/general) and apply the correct ones for your project.
    *   Review the [security settings](/developer-dashboard/security) and make sure your CORS is set up correctly for your live deployment.
  </Accordion>

  <Accordion title="Turn on MFA" icon="lock">
    If you haven't done so already, before going to production we highly recommend turning on MFA for your account. You can do this in the [security settings](https://app.dynamic.xyz/dashboard/security).
  </Accordion>

  <Accordion title="Consider Multi-wallet" icon="wallet">
    Multi Wallet allows users to connect and handle multiple wallets at once, and
    is a great UX boost for end users. Consider turning it on after reviewing the
    [multi-wallet support section](/wallets/advanced-wallets/multi-wallet).
  </Accordion>

  <Accordion title="Enable Information Capture" icon="info">
    Make sure you're capturing all the user information you need via [Information
    capture](/users/information-capture).
  </Accordion>

  <Accordion title="Choose your plan" icon="tags">
    Make sure to choose the right Dynamic
    [plan](https://app.dynamic.xyz/dashboard/admin#subscription) for your needs. -
    All features are free on sandbox, but if you're using [any advanced
    features](https://www.dynamic.xyz/pricing) you'll need to upgrade to a paid
    plan.
  </Accordion>

  <Accordion title="Switch to live mode" icon="toggle-on">
    There are two different modes to operate Dynamic in: Sandbox and Live. You can
    learn more about them [here](/developer-dashboard/sandbox-vs-live). To switch
    to live mode, you'll need to do so in the Dynamic dashboard. Remember that the
    environment ID differs between sanbox and live, so make sure to also update
    that in your own code.
  </Accordion>

  <Accordion title="Turn on MFA" icon="lock">
    If you haven't done so already, before going to production we highly recommend turning on MFA for your account. You can do this in the [security settings](https://app.dynamic.xyz/dashboard/security).
  </Accordion>

  <Accordion title="Test all the things" icon="check-double">
    We test all the time, but it's also worth you running through your implementation from a few different angles, here are some common testing cases that might be useful to double check:

    *   Cross device
    *   Embedded wallets (if you are using)
    *   Transactions
    *   New vs existing user flow
  </Accordion>

  <Accordion title="Drop us a line!" icon="hand-wave">
    Let us know how it's going! Drop a line to [hello@dynamic.xyz](mailto:hello@dynamic.xyz) or join
    [Slack](https://dynamiccustomers.slack.com/) - if you like, we can give your
    implementation a test and offer advice. We'll also check that your
    implementation is ready for launch.
  </Accordion>
</AccordionGroup>


# Phantom Redirect



## Introduction

This guide will help you use the Phantom redirect-based connect feature on mobile. Instead of deeplinking into the Phantom in-app browser, your users can enjoy a redirect-based connection, where they are automatically redirected between the app and their wallet to faciliate the connect, sign and additional method calls.

## Tutorial

### `mobileExperience` prop

Make sure you set `mobileExperience` to `redirect` on your DynamicContextProvider:

```JSX
<DynamicContextProvider
  settings={{
    mobileExperience: 'redirect'
    // ... other settings
  }}
>
```

With just this code, your users will connect with the redirect-based approach. If you want to learn how to make method calls (like `signAndSendTransaction`) and access the result, read on

### Signer methods

The redirect-based approach makes it a little trickier to access the result, and soon we will offer an easy-to-use hook to do so. For now, you will need this code in your app:

```JSX
import { useState, useEffect } from 'react';

import {
  VersionedTransaction,
  Transaction,
} from '@solana/web3.js';

import { useDynamicContext } from '@dynamic-labs/sdk-react-core';
import { ISolana } from '@dynamic-labs/solana-core';
import {
  isPhantomRedirectConnector,
  SignAndSendTransactionListener,
} from '@dynamic-labs/wallet-connector-core';

const useSignAndSendTransaction = () => {
  const { primaryWallet } = useDynamicContext();

  const [signature, setSignature] = useState<string | undefined>(undefined);
  const [errorCode, setErrorCode] = useState<string | undefined>(undefined);
  const [errorMessage, setErrorMessage] = useState<string | undefined>(
    undefined,
  );

  useEffect(() => {
    if (!isPhantomRedirectConnector(primaryWallet?.connector)) return;
    const handler: SignAndSendTransactionListener = (response) => {
      if (response.signature) {
        setSignature(response.signature);
      } else {
        setErrorCode(response.errorCode);
        setErrorMessage(response.errorMessage);
      }
    };

    primaryWallet.connector.on('signAndSendTransaction', handler);
    return () => {
      if (!isPhantomRedirectConnector(primaryWallet?.connector)) return;
      primaryWallet.connector.off('signAndSendTransaction', handler);
    };
  }, [primaryWallet?.connector]);

  const execute = async (transaction: Transaction | VersionedTransaction) => {
    if (!primaryWallet) return;
    const signer = await primaryWallet.getSigner<ISolana>();
    await signer.signAndSendTransaction(transaction);
  };

  return { errorCode, errorMessage, execute, signature };
};
```

You can render this hook in your app, and call `execute` on a button you render. This will trigger a redirect to the user's wallet for signing, and if they accept the transaction, they will be redirected back to your app. Upon your app re-loading, this hook should be rendered again, and it's state values will be populated – in this case the `signature` will contain the signature of the transcation.


# NextAuth & Dynamic



## Pre-requisites

*   Cloned [the nextAuth example repo](https://github.com/nextauthjs/next-auth-example) and installed dependencies (You can also see the end result after adding Dynamic [here](https://github.com/dynamic-labs/nextAuth-example)).

## Steps

### Add the right env variables

You'll need to define two environment variables in your `.env.local` file:

```bash
NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID=
NEXT_DYNAMIC_BEARER_TOKEN=
```

You'll be able to find both in [the SDK & API Keys page of the Dynamic dashboard](https://app.dynamic.xyz/dashboard/developer/api). The first will already be generated for you but for the API key, you'll need to generate your own via the UI on that page.

Make sure you add the values of each variable to the `.env.local` file, and you're good to go.

### Add the Dynamic Provider

We are going to use the Dynamic UI on the client, so the first thing we'll want to do is add an initialize the DynamicContextProvider and then we can use the DynamicWidget UI.

First make sure you have the appropriate dependencies installed:

```bash
npm install @dynamic-labs/sdk-react-core @dynamic-labs/ethereum
```

Next we will add the provider. Normally you would add the provider to one of your high level component files directly, wrapping everything else but in Next, this will likely cause some client - server issues especially because we're going to be utalizing callbacks, so we'll handle this in two ways:

Firstly, we'll create a file to export what we need from Dynamic and then we'll import that file in the client side code:

```js
// app/lib/dynamic.ts

"use client";

export * from "@dynamic-labs/sdk-react-core";
export * from "@dynamic-labs/ethereum";
```

Secondly, we'll create a wrapper for the provider itself:

```js
// app/components/dynamic-wrapper.ts

"use client";

import { DynamicContextProvider } from "../lib/dynamic";
import { EthereumWalletConnectors } from "../lib/dynamic";

export default function ProviderWrapper({ children }: React.PropsWithChildren) {
  return (
    <DynamicContextProvider
      settings={{
        environmentId: process.env.NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID,
        walletConnectors: [EthereumWalletConnectors],
      }}
    >
      {children}
    </DynamicContextProvider>
  );
}
```

Note that we are using only EthereumWalletConnectors, but you can add any of the other connectors you want to use.

Now we can add the wrapper to our `layout.tsx` file:

```js
// app/layout.tsx

import "./globals.css";
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import Footer from "@/components/footer";
import Header from "@/components/header";
import ProviderWrapper from "@/components/dynamic-wrapper";

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

export const metadata: Metadata = {
  title: "NextAuth.js Example",
  description:
    "This is an example site to demonstrate how to use NextAuth.js for authentication",
};

export default function RootLayout({ children }: React.PropsWithChildren) {
  return (
    <html lang="en">
      <ProviderWrapper>
        <body className={inter.className}>
          <div className="flex flex-col justify-between w-full h-full min-h-screen">
            <Header />
            <main className="flex-auto w-full max-w-3xl px-4 py-4 mx-auto sm:px-6 md:py-6">
              {children}
            </main>
            <Footer />
          </div>
        </body>
      </ProviderWrapper>
    </html>
  );
}
```

### Add the DynamicWidget

You don't need to add the widget at the same level as the provider, you can place this anywhere. For this example we'll add it to the header:

```js
// app/components/header.tsx

import { MainNav } from "./main-nav";
import UserButton from "./user-button";
import { DynamicWidget } from "../lib/dynamic";

export default function Header() {
  return (
    <header className="sticky flex justify-center border-b">
      <div className="flex items-center justify-between w-full h-16 max-w-3xl px-4 mx-auto sm:px-6">
        <MainNav />
        <DynamicWidget />
        <UserButton />
      </div>
    </header>
  );
}
```

Now we're almost done with the client side. We'll need to somehow send the JWT that Dynamic returns on login to our server functions so that we can validate it and create a session. To do this we'll use the `events` which Dynamic provides but let's come back to that and first add the server side code.

### Define the JWT decoding

NextAuth needs to know how to decode and validate the JWT which Dynamic sends back. To do this we'll create a custom JWT decoder inside a new helper file:

```js
// app/lib/authHelpers.ts

import jwt, { JwtPayload, Secret, VerifyErrors } from "jsonwebtoken";

export const validateJWT = async (
  token: string
): Promise<JwtPayload | null> => {
  try {
    const decodedToken =
      ((await new Promise()) < JwtPayload) |
      (null >
        ((resolve, reject) => {
          jwt.verify(
            token,
            getKey,
            { algorithms: ["RS256"] },
            (
              err: VerifyErrors | null,
              decoded: string | JwtPayload | undefined
            ) => {
              console.log("decoded the jwt");
              if (err) {
                reject(err);
              } else {
                // Ensure that the decoded token is of type JwtPayload
                if (typeof decoded === "object" && decoded !== null) {
                  resolve(decoded);
                } else {
                  reject(new Error("Invalid token"));
                }
              }
            }
          );
        }));
    return decodedToken;
  } catch (error) {
    console.error("Invalid token:", error);
    return null;
  }
};
```

You'll see that the above function depends on a few things, one of which is the external jsonwebtoken library. We'll need to install this:

```bash
npm install jsonwebtoken
```

Next we'll need to define the `getKey` function which is used to fetch the public key which you can use to decode the JWT. This function will make an API call to Dynamic. We'll add this to the same file:

```js
// app/lib/authHelpers.ts

export const getKey = (
  headers,
  callback: (err: Error | null, key?: Secret) => void
): void => {
  console.log("calling getKey");

  // Define the options for the fetch request
  const options = {
    method: "GET",
    headers: {
      Authorization: `Bearer ${process.env.NEXT_DYNAMIC_BEARER_TOKEN}`,
    },
  };

  // Perform the fetch request asynchronously
  fetch(
    `https://app.dynamicauth.com/api/v0/environments/${process.env.NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID}/keys`,
    options
  )
    .then((response) => {
      return response.json();
    })
    .then((json) => {
      const publicKey = json.key.publicKey;
      const pemPublicKey = Buffer.from(publicKey, "base64").toString("ascii");
      callback(null, pemPublicKey); // Pass the public key to the callback
    })
    .catch((err) => {
      console.error(err);
      callback(err); // Pass the error to the callback
    });
};
```

With this in place, there are just two steps left. Firstly we'll need to adapt the NextAuth configuration to use [the CredentialsProvider](https://next-auth.js.org/configuration/providers/credentials) so the JWT works, and then we'll need to trigger everything correct when a user logs in.

### Update the NextAuth configuration

You can copy the below code and paste it over the full existing auth.ts file in the demo repo:

```js
// ./auth.ts

import NextAuth from "next-auth";

import type { NextAuthConfig } from "next-auth";

import Credentials from "@auth/core/providers/credentials";
import { validateJWT } from "./authHelpers";

type User = {
  id: string;
  name: string;
  email: string;
  // Add other fields as needed
};

export const config = {
  theme: {
    logo: "https://next-auth.js.org/img/logo/logo-sm.png",
  },
  providers: [
    Credentials({
      name: "Credentials",
      credentials: {
        token: { label: "Token", type: "text" },
      },
      async authorize(
        credentials: Partial<Record<"token", unknown>>,
        request: Request
      ): Promise<User | null> {
        const token = credentials.token as string; // Safely cast to string; ensure to handle undefined case
        if (typeof token !== "string" || !token) {
          throw new Error("Token is required");
        }
        const jwtPayload = await validateJWT(token);

        if (jwtPayload) {
          // Transform the JWT payload into your user object
          const user: User = {
            id: jwtPayload.sub, // Assuming 'sub' is the user ID
            name: jwtPayload.name || "", // Replace with actual field from JWT payload
            email: jwtPayload.email || "", // Replace with actual field from JWT payload
            // Map other fields as needed
          };
          return user;
        } else {
          return null;
        }
      },
    }),
  ],
  callbacks: {
    authorized({ request, auth }) {
      const { pathname } = request.nextUrl;
      if (pathname === "/middleware-example") return !!auth;
      return true;
    },
  },
} satisfies NextAuthConfig;

export const { handlers, auth, signIn, signOut } = NextAuth(config);
```

### Trigger the JWT validation on login

We'll need to access the JWT which Dynamic sends back after a user has logged in so that we can co-ordinate with NextAuth. To do this we'll use one of the events which Dynamic provides. Back in the `dynamic-wrapper.ts` file we'll adjust the settings object passed to DynamicContextProvider as a prop to the following:

```js
// app/components/dynamic-wrapper.ts

...

<DynamicContextProvider
   settings={{
        environmentId: process.env.NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID,
        walletConnectors: [EthereumWalletConnectors],
        events: {
          onAuthSuccess: async (event) => {
            const authToken = getAuthToken();

            const csrfToken = await getCsrfToken();

            fetch("/api/auth/callback/credentials", {
              method: "POST",
              headers: {
                "Content-Type": "application/x-www-form-urlencoded",
              },
              body: `csrfToken=${encodeURIComponent(
                csrfToken
              )}&token=${encodeURIComponent(authToken)}`,
            })
              .then((res) => {
                if (res.ok) {
                  console.log('LOGGED IN', res);
                  // Handle success - maybe redirect to the home page or user dashboard
                } else {
                  // Handle any errors - maybe show an error message to the user
                  console.error("Failed to log in");
                }
              })
              .catch((error) => {
                // Handle any exceptions
                console.error("Error logging in", error);
              });
          },
        },
      }}
>
...
```

Note that we're using the `getCsrfToken` function which NextAuth provides. This is important because NextAuth uses CSRF tokens to prevent CSRF attacks.

### Token Scopes

A JWT token supports the concept of scopes, which are used to define the permissions that the token has. Dynamic uses various scopes to identify limitations for a token.

The most common and important scope value is `requiresAdditionalAuth` which signifies that the token requires MFA to be completed before the token is considered valid and the user is fully authenticated.

Our SDK handles this for the frontend, but for the backend you will need to check this scope and handle it accordingly.

To do this, you can add a check for the `requiresAdditionalAuth` scope in the `authorize` function in the `auth.ts` file:

```js
// ./auth.ts
...
    async authorize(
        credentials: Partial<Record<"token", unknown>>,
        request: Request
      ): Promise<User | null> {
        const token = credentials.token as string; // Safely cast to string; ensure to handle undefined case
        if (typeof token !== "string" || !token) {
          throw new Error("Token is required");
        }
        const jwtPayload = await validateJWT(token);

        if (jwtPayload) {
          
          // Transform the JWT payload into your user object
          const user: User = {
            id: jwtPayload.sub, // Assuming 'sub' is the user ID
            name: jwtPayload.name || "", // Replace with actual field from JWT payload
            email: jwtPayload.email || "", // Replace with actual field from JWT payload
            scopes: jwtPayload.scopes || [], // Add the scopes to the user object
            // Map other fields as needed
          };
          return user;
        } else {
          return null;
        }
      }
...
```

Alternatively, you can return `null` if you do not want to handle the token with the `requiresAdditionalAuth` scope.

### Run the example

```bash
npm run dev
```

You should now see a "Connect your wallet" button in the header, which you can use to log in with a wallet etc. Once you've logged in you should see "LOGGED IN" in the browser console.

### Going further

You'll see in auth.js that we are assigning certain JWT fields to a user object. You can add any fields you want to this object, and then access them in your pages via the `useSession` hook.


# Dynamic in PWA



In this guide we are going to setup a PWA using Dynamic and Vite, React & Typescript.

### Full code example

You can find the full code from this example [here](https://github.com/dynamic-labs/react-pwa).

## Pre-requisites

*   Node installed
*   Boilerplate project created with Vite

```
npm create vite@latest dynamic-pwa -- --template react-ts
```

## Dynamic Setup

With a basic project in place, we can now setup the Dynamic React SDK

### Install Dynamic Packages

```
npm install @dynamic-labs/sdk-react-core @dynamic-labs/ethereum
```

### Add the DynamicContextProvider

In the `src/main.tsx` file we can add the DynamicContextProvider with an environment ID

```tsx
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.tsx";
import "./index.css";

// Dynamic SDK imports
import { DynamicContextProvider } from "@dynamic-labs/sdk-react-core";
import { EthereumWalletConnectors } from "@dynamic-labs/ethereum";

ReactDOM.createRoot(document.getElementById("root")!).render(
  <React.StrictMode>
    <DynamicContextProvider
      settings={{
        environmentId: "96bae37d-7ed7-44aa-9081-79bb984508fe",
        walletConnectors: [EthereumWalletConnectors],
      }}
    >
      <App />
    </DynamicContextProvider>
  </React.StrictMode>
);
```

### Add the DynamicWidget

Over in the App file, we are going to use the DynamicWidget for users to be able to authenticate using the Dynamic UI

Now, in the `src/App.tsx` file, we can add the DynamicWidget component

```tsx
import { DynamicWidget } from "@dynamic-labs/sdk-react-core";

function App() {
  return (
    <DynamicWidget />
  );
}

export default App;
```

Great, now Dynamic is fully added and we can get start with the PWA setup!

## PWA Setup

### Install Dependencies

```
npm install -D vite-plugin-pwa
```

### Setup PWA

For this app to became a PWA we will need to generate a manifest file with some extra icons that the OS will use.

First, we can update our `vite.config.ts` file with vite-plugin-pwa:

```
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { VitePWA } from "vite-plugin-pwa";

export default defineConfig({
  publicDir: "public",
  plugins: [
    react(),
    VitePWA({
      registerType: "autoUpdate",
      devOptions: {
        enabled: true,
      },
      includeAssets: [
        "logo.png",
        "apple-touch-icon.png",
        "mask-icon.svg",
        "icon.svg",
      ],
      manifest: {
        name: "My Awesome DApp",
        short_name: "Dynamic PWA",
        description: "My Awesome DApp description",
        theme_color: "#ffffff",
        icons: [
          {
            src: "logo.png",
            sizes: "192x192",
            type: "image/png",
          },
          {
            purpose: "any maskable",
            sizes: "260x260",
            src: "/apple-touch-icon.png",
            type: "image/png",
          },
        ],
      },
    }),
  ],
});
```

We also need to add some images like `logo.png` and `apple-touch-icon.png` to our `public` to serve as the PWA icons.

Now we have to specify in the `index.html` file some basic information about the PWA:

So, add the following to the `head` tag

```
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width,initial-scale=1" />
  <title>Dynamic PWA</title>
  <meta name="description" content="My Awesome DApp description" />
  <link rel="icon" href="/logo.png" />
  <link rel="apple-touch-icon" href="/apple-touch-icon.png" sizes="180x180" />
  <link rel="mask-icon" href="/logo.png" color="#FFFFFF" />
  <meta name="theme-color" content="#ffffff" />
  <link rel="manifest" href="/manifest.webmanifest" />
</head>
```

## Add an Install Button

Let's add an install button so users can install our PWA.

We can add a hook to `src/useInstallPWA.ts` that allows us to trigger the PWA installation:

```ts
import { useCallback } from "react";

let deferredPrompt: Event | null = null;

const handleBeforeInstallPrompt = (event: Event) => {
  event.preventDefault();

  deferredPrompt = event;
};

window.addEventListener("beforeinstallprompt", handleBeforeInstallPrompt);

export const useInstallPWA = () =>
  useCallback(() => {
    if (deferredPrompt) {
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      (deferredPrompt as any).prompt();
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      (deferredPrompt as any).userChoice.then(() => {
        deferredPrompt = null;
      });
    }
  }, []);
```

Then we can use useInstallPWA in the `src/main.tsx` file to prompt the user to install the PWA:

```tsx
import { DynamicWidget } from "@dynamic-labs/sdk-react-core";
import { useInstallPWA } from "./useInstallPWA";

function App() {
  const installPWA = useInstallPWA();

  return (
    <>
      <DynamicWidget />

      <button onClick={installPWA}>Install PWA</button>
    </>
  );
}

export default App;
```

Perfect, now you can serve your DApp under `https` and install the PWA on your computer or phone, then authenticate with Dynamic!


# Create-web3-dapp & Dynamic



[Create Web3 Dapp](https://github.com/alchemyplatform/create-web3-dapp) is an excellent tool created by our friends over at Alchemy. It is described as "an npx tool that allows developers to create web3 applications in \~4 minutes."

We've created a version of a create-web3-dapp setup which has Dynamic integrated so you can start building apps with beautiful auth in no time!

Instructions all in the README: [https://github.com/dynamic-labs/create-web3-dapp-dynamic](https://github.com/dynamic-labs/create-web3-dapp-dynamic)


# Enhanced analytics with Bello



## Introduction

[Bello](https://bello.lol/) offers you analytics to help you understand your users and their behavior. You can use this data to make informed decisions about your app and improve your user experience.

To get set up, all you need to do it run an export of wallet addresses from Dynamic and upload it to Bello. Upon upload, Bello filters the data, retaining only EVM-compatible addresses and discarding any non-relevant fields such as emails, ensuring that only blockchain-relevant information is processed.

Upon launching a search, you as a free user gain access to features such as:

*   "Assets in Common," illuminating shared community memberships and fostering an understanding of collective affiliations.
*   "NFT Interests," which employs AI to tag users' collection habits, offering insights into their NFT collecting patterns.
*   A "Recently Purchased" segment, highlighting the most recent community NFTs acquired by a user.
*   "Web3 Social" insights, providing a comprehensive look at a user's interactions across platforms like Lens, Farcaster, and Warpcast.

<Accordion title="Go further with Bello Premium">
  To delve into more advanced insights and obtain a higher level of search functionality, users are encouraged to explore a 7-day free trial of premium services. This trial unlocks over 50 curated insights and tools, including:

  *   Advanced segmentation tools for sifting through user data, revealing key information such as percentages of users with specific Twitter handles, wallet ages, net worth, and holdings in NFTs, ERC20s, and POAPs, to name a few.
  *   Wallet messaging capabilities powered by XMTP, facilitating direct engagement with holders.
  *   Detailed dashboards that categorize AI-labeled interests, POAP holders, and more.
  *   The capacity to search across more than 20,000 addresses and over three cohorts, providing a broad view of purchasing behaviors, on-chain activity times and days, and recent NFT purchases, among others.

  These features offer an in-depth exploration of user engagement and preferences, enhancing the ability to tailor strategies and interactions within the Web3 ecosystem.

  <Tip>Use the keyword DYNAMIC for a 20% discount on premium Bello plans.</Tip>
</Accordion>

OK, let's get started! This is a simple two step process, as outlined above:

## Export wallet addresses from Dynamic

We offer both a drag and drop method (UI only, no code) as well as a programmatic method

<Tabs>
  <Tab title="Drag and drop">
    <iframe src="https://scribehow.com/embed/Export_User_Data_from_Dynamic_Dashboard__QFU4ndvdT_Kd9P1CHpMz2A" width="100%" height="640" allowfullscreen frameborder="0" />
  </Tab>

  <Tab title="Programmatic">
    Using [the `getEnvironmentUsers` endpoint](/api-reference/endpoints/users/getEnvironmentUsers), you can fetch all users for your environmentment like so:

    ```javascript
    const fetchUsers = async (bearer, environmentId) => {
      const options = {
        method: "POST",
        headers: {
          Authorization: `Bearer ${bearer}`,
          "Content-Type": "application/json",
        },
      };

      try {
        const response = await fetch(
          `https://app.dynamicauth.com/api/v0/environments/${environmentId}/users`,
          options
        );
        return await response.json();
      } catch (err) {
        console.error(err);
      }
    };
    ```

    You can then loop through the users and extract the wallet addresses:

    ```javascript
    const extractWallets = (users) => {
      const addresses = [];
      users.forEach((user) => {
        if (user.wallets && user.wallets.length > 0) {
          user.wallets.forEach((wallet) => {
            if (wallet.publicKey) {
              addresses.push(wallet.publicKey);
            }
          });
        }
        if (user.verifiedCredentials && user.verifiedCredentials.length > 0) {
          user.verifiedCredentials.forEach((credential) => {
            if (credential.address) {
              addresses.push(credential.address);
            }
          });
        }
      });
      return addresses;
    };
    ```

    ## Save wallets as CSV

    If you're using Node, you can use the `fs` module to write the wallet addresses to a CSV file:

    ```javascript
    const fs = require("fs").promises;
    const path = require("path");

    const writeToCSV = async (data, name) => {
      try {
        const csvContent = data.join("\n");
        const filePath = path.join(__dirname, name + ".csv");
        await fs.writeFile(filePath, csvContent, "utf8");
        console.log("CSV file has been written successfully");
      } catch (err) {
        console.log("Error writing CSV file", err);
      }
    };
    ```

    ## Put it all together

    Now we can chain our functions together to export the wallet addresses and save them as a CSV:

    ```javascript
    const exportWalletList = async (bearer, environmentId) => {
      const { users } = await fetchUsers(bearer, environmentId);
      const wallets = await extractWallets(users);
      return await writeToCSV(wallets, "wallets");
    };

    exportWalletList("BEARER_TOKEN", "ENVIRONMENT_ID");
    ```
  </Tab>
</Tabs>

## Upload wallet addresses to Bello

Once you have your CSV file, you can upload it to Bello through the dedicated Portal. You can access the portal by clicking the button below:

<div
  style={{
   display: "flex",
   justifyContent: "center",
   alignItems: "center",
  }}
>
  <a href="https://app.bello.lol/dashboard/0x749f5ddf5ab4c1f26f74560a78300563c34b417d?partner=dynamic">
    <button
      target="_blank"
      rel="noopener noreferrer"
      style={{
       backgroundColor: "#FFFFFF",
       color: "#000000",
       border: "1px solid #0000FF",
       borderRadius: "5px",
       padding: "0px 10px",
       textAlign: "center",
       display: "inline-block",
       fontSize: "16px",
      }}
    >
      Access Bello Portal
    </button>
  </a>
</div>

## Beyond the basics

Once you have your dashboard set up, you can start to explore the data and gain insights into your users' behavior. Here are some ideas to get you started:

*   [Metrics & Insights](https://help.bello.lol/en/collections/7887705-analytics-dashboards)
*   [Build segments](https://help.bello.lol/en/collections/7887701-segment)
*   [Broadcast messages](https://help.bello.lol/en/collections/7887702-broadcast-messages)


# Generate an multi-chain embedded wallet from a Farcaster frame



## Introduction

This guide shows you how to generate an embedded wallet in both Ethereum and Solana from a Farcaster frame based on an email address input.

We will use a frame building library in this example, specifically Frog.fm however you can use any frame builder you like.

<Tip>
  You can find a full demo of this implementation
  [here](https://warpcast.com/~/developers/frames?url=https%3A%2F%2Ffrog-frames-three.vercel.app%2Fapi)
  and the source code for the demo
  [here](https://github.com/dynamic-labs/frog-frames/blob/main/dynamic-embedded/api/index.tsx).
</Tip>

## Implementation

### Step 1: Configure your Dynamic app

<Frame>
  <iframe src="https://scribehow.com/embed/Configuring_your_Dynamic_app_for_Ethereum_and_Solana_embedded_wallet_generation__vB5YtCC0RNGQwsE-z7cLog?skipIntro=true&removeLogo=true" width="100%" height="640" allowfullscreen frameborder="0" />
</Frame>

### Step 2: Bootstrap your frame

First, you'll need to create a new frame template Frog.fm, they provide instructions on how to do this [here](https://frog.fm/getting-started). Specifically, they allow you to create a new frame and deploy to vercel with a single command. [See here for more info](https://frog.fm/getting-started#bootstrap-via-vercel).

### Step 3: Check out the Dynamic API endpoint

You'll be utilizing our new API endpoint built especially for frames. This endpoint is called `POST https://app.dynamicauth.com/api/v0/environments/{environmentId}/embeddedWallets/farcaster` and it takes in a JSON object with the following properties:

<Tip>
  The full reference for the endpoint can be found [here](/api-reference/endpoints/wallets/createEmbeddedWalletFromFarcaster)
</Tip>

```
{
  "chains": [
    "EVM"
  ],
  "email": "jsmith@example.com",
  "fid": 123
}
```

The `chains` property is an array of the chains you want to support with the wallet. Currently, the supported chains are `EVM` and `SOL`.

The `email` property is the email address of the user you want to generate the wallet for, it must be a valid email.

The `fid` property is the Farcaster user ID for whom you want to use to generate the wallet. You can find the FID for a user from within the Warpcast profile tab by clicking on the three dots and choosing "about".

### Step 4: Define our creation function a frame with an email input

In your frame code, you'll need to define a function that will be called when the user submits their email address. This function will call the Dynamic API endpoint to generate the wallet and then display the wallet to the user.

```tsx
import { configDotenv } from "dotenv";
import { ChainEnum } from "@dynamic-labs/sdk-api/models/ChainEnum";
import { UserResponse } from "@dynamic-labs/sdk-api/models/UserResponse";
configDotenv();

const key = process.env.KEY;
const environmentId = process.env.ENVIRONMENT_ID;
let newWallets: string[];

const createEmbeddedWallet = async (
  email: string,
  fid: number,
  chains: ChainEnum[]
) => {
  console.log("Creating embedded wallets for", email, fid, chains);
  const options = {
    method: "POST",
    headers: {
      Authorization: `Bearer ${key}`,
      "Content-Type": "application/json",
    },

    body: JSON.stringify({
      email,
      fid,
      chains,
    }),
  };

  const response = await fetch(
    `https://app.dynamic-preprod.xyz/api/v0/environments/${environmentId}/embeddedWallets/farcaster`,
    options
  ).then((r) => r.json());

  console.log(response);
  newWallets = (response as UserResponse).user.wallets.map(
    (wallet: any) => wallet.publicKey
  );

  return newWallets;
};
```

<Info>
  Note that the `key` should be a Dynamic.xyz API key. You can get one from the
  following page: [https://app.dynamic.xyz/dashboard/developer/api](https://app.dynamic.xyz/dashboard/developer/api) (you can find
  your ENVIRONMENT\_ID on the same page)
</Info>

### Step 5: Call the creation function when the user submits their email

For this we will need to define our frame UI and call the `createEmbeddedWallet` function on submit. it will look like this:

```tsx
import { Button, Frog, TextInput } from "frog";
import { handle } from "frog/vercel";

app.frame("/", async (c) => {
  const { frameData, inputText, status, buttonValue } = c;
  const isValidEmail = inputText
    ? /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(inputText)
    : false;

  const fid = frameData?.fid;
  let error = status != "initial" && (!isValidEmail || !fid);

  if (
    !error &&
    status != "initial" &&
    isValidEmail &&
    inputText &&
    fid &&
    buttonValue === "submit"
  ) {
    try {
      newWallets = await createEmbeddedWallet(inputText, fid, [
        ChainEnum.Sol,
        ChainEnum.Evm,
      ]);

      // Add any additional logic specific to your frame integration here. e.g. airdrop an NFT to the embedded wallet, etc.
    } catch (e) {
      error = true;
    }
  }

  return c.res({
    image: (
      <div
        style={{
          alignItems: "center",
          background: "linear-gradient(to right, #432889, #17101F)",
          backgroundSize: "100% 100%",
          display: "flex",
          flexDirection: "column",
          flexWrap: "nowrap",
          height: "100%",
          justifyContent: "center",
          textAlign: "center",
          width: "100%",
        }}
      >
        <div
          style={{
            alignItems: "center",
            background: "linear-gradient(to right, #432889, #17101F)",
            backgroundSize: "100% 100%",
            display: "flex",
            flexDirection: "column",
            flexWrap: "nowrap",
            height: "100%",
            justifyContent: "center",
            textAlign: "center",
            width: "100%",
            fontSize: 30,
            fontStyle: "normal",
          }}
        >
          {status === "initial" && !error ? (
            <div style={{ color: "white" }}>
              Create Dynamic-powered embedded wallets
            </div>
          ) : newWallets && newWallets.length > 0 ? (
            newWallets.map((wallet, index) => (
              <div key={index} style={{ color: "white" }}>
                {wallet}
              </div>
            ))
          ) : (
            <div style={{ color: "white" }}>
              No wallets created yet or an error occurred.
            </div>
          )}
        </div>
      </div>
    ),
    intents: [
      <TextInput placeholder="Enter a valid email" />,
      <Button value="submit">Create SOL + EVM Embedded Wallets</Button>,
    ],
  });
});

export const GET = handle(app);
export const POST = handle(app);
```

### Step 6: Test the frame

You can now test the frame by running it locally and entering an email address. You should see the wallet address displayed on the screen after you submit the email address.

### Step 7: Deploy the frame

With Frog.fm you can deploy quickly using Vercel. They have a great guide for it [here](https://frog.fm/platforms/vercel).

### Step 8: Connect to your dApp

Now that you've created embedded wallets for your users, which are linked to their email and Farcaster ID so you can set up Dynamic with your dApp, and the user can log into your dApp with either their email or Farcaster account to access their embedded wallets!


# Farcaster Overview



<Frame>
  <img className="block dark:hidden" src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/farcaster-light.png" />

  <img className="hidden dark:block" src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/farcaster-dark.png" />
</Frame>

## Introduction to Farcaster

[Farcaster](https://www.farcaster.xyz/) is a protocol for building sufficiently decentralized social networks. Clients (such as [Warpcast](https://warpcast.com/)) build on top of Farcaster to provide the user interface and user experience for interacting with the decentralized network.

This is a beautiful relationship because the underlying social graph is shared by all the clients. This means that users can switch between clients without losing their social graph. It also means that clients can be built by different teams, each focusing on different aspects of the user experience.

Farcaster is not a blockchain. It is a protocol that stores the social graph on a blockchain, currently Optimism. If you're wondering what exactly it means when Farcaster defines itself as "sufficiently decentralized", Varun Srinivasan, one of the co-founders of Farcaster [describes it as](https://www.varunsrinivasan.com/2022/01/11/sufficient-decentralization-for-social-networks):

"A social network achieves sufficient decentralization if two users can find each other and communicate, even if the rest of the network wants to prevent it. This implies that users can always reach their audience, which can only be true if developers can build many clients on the network. If only one client existed, it could stop users from communicating. Achieving this only requires three decentralized features: the ability to claim a unique username, post messages under that name, and read messages from any valid name."

Due to this decentralized and permissionless nature, Farcaster has been able to introduce a number of features that are not possible in traditional social networks, one being Frames which you can think of as "mini-apps". For example, you can purchase items directly from your social media feed or tip creators directly from your feed.

## Farcaster + Dynamic

Our mission is to create incredible onboarding experiences for anyone interacting with Web3. We believe that Farcaster is a key piece of the Web3 puzzle, and we are excited to be working with the Farcaster team to bring the best possible onboarding experience to users of Farcaster. You can do this in a number of different ways:

### Sign in with Farcaster

If you want to allow your users to sign into a dapp using their Farcaster identity, similar to how you would with Google or other, this is as easy as a toggle in the dashboard. You can learn about that [here](/guides/integrations/farcaster/sign-in-with-farcaster).

### Enable Write Access

You may want to create certain actions in Farcaster on behalf of your users. For example, you may want to post a message on their behalf. For this you will need your user to go through a write access flow, and you can learn about how to do that [here](/guides/integrations/farcaster/farcaster-write-access).

### Wallets in Frames

If you are building a Frame and want to allow users to interact in a way that requires a wallet, you can generate a wallet from just an email address on multiple chains. You can learn about that [here](/guides/integrations/farcaster/embedded-wallet-frame).


# Enable Write Access to Farcaster



## Introduction

This tutorial will help you enable Sign in with Farcaster in your application, as well as enabling write access to Farcaster on behalf of that user.

We've partnered with the wonderful team at Neynar on this use case who have implemented Dynamic as part of Sign in with Neynar. This means three things for you:

*   it's completely plug and play, drop in the code snippet and ship
*   warps are already sponsored by Neynar
*   you get the best of Dynamic and Neynar in one go

## Video walkthrough

<Frame>
  <iframe src="https://www.loom.com/embed/72ad193653374ec5a52e0e29fcefb721?sid=a24d135e-946a-4593-af8a-12a7be81ac75" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen width="100%" height="640" />
</Frame>

## Live demo

You can see this flow in action on the Neynar demo: [https://demo.neynar.com/](https://demo.neynar.com/). You can find the code for this demo [here](https://github.com/neynarxyz/farcaster-examples/tree/main/wownar).

## Step By Step Walkthrough

<Tip>
  We recommend referring to [the Neynar guide](https://docs.neynar.com/docs/how-to-let-users-connect-farcaster-accounts-with-write-access-for-free-using-sign-in-with-neynar-siwn) for the most up-to-date information.
</Tip>

### Configure Neynar

Go to [the Neynar Developer Portal settings tab](https://dev.neynar.com/app) and update the following:

Name - Displayed to the user in Step 3.
Logo URL - Displayed to the user in Step 3. Use a PNG or SVG format.
Authorized origins - Authorized origins are the HTTP origins that host your web application. e.g. [https://demo.neynar.com](https://demo.neynar.com) This is required to pass user credentials back to your application securely. This cannot contain wildcards or IP addresses.

### Drop in the code snippet

Add the following code snippet to your html, replacing `YOUR_NEYNAR_CLIENT_ID` with your Neynar client ID. You can find this in [the Neynar Developer Portal App tab](https://dev.neynar.com/app):

```html
<html>
  <body>
    <div
      class="neynar_signin"
      data-client_id="YOUR_NEYNAR_CLIENT_ID"
      data-success-callback="onSignInSuccess"
      data-theme="dark"> <!-- defaults to light, unless specified -->
    </div>
		<script src="https://neynarxyz.github.io/siwn/raw/1.2.0/index.js" async></script>
		<script>
      // Define the onSignInSuccess callback function
      function onSignInSuccess(data) {
        console.log("Sign-in success with data:", data);
        // Your code to handle the sign-in data
      }
	   </script>
  </body>
</html>
```

### Handle callback

Once the user is authenticated and a signer has been authorized by the user, the signer\_uuid and fid will be passed in via the data object in the callback function.

signer\_uuid is unique to your app and is used to write to Farcaster on behalf of the user (same uuid format)
fid: This is the unique Farcaster identifier for the user e.g. 6131
Store the signer\_uuid securely on your backend or the browser’s local storage, it's not meant to be exposed to the user or anyone other than you. Switch the app to the logged-in state for that Farcaster user.

### Ship it!

You’re all set! The user is now logged in and you should use the fid for any [read APIs](https://docs.neynar.com/docs/what-does-vitalikeths-farcaster-feed-look-like) and the signer\_uuid to do any [write actions](https://docs.neynar.com/docs/liking-and-recasting-with-neynar-sdk) on behalf of the user in your App.


# Sign in with Farcaster



## Introduction

This tutorial will guide you through the process of enabling read-only access to Farcaster using the Dynamic SDK (if you need write access, see [this guide](/guides/integrations/farcaster/farcaster-write-access)).

If you enable only social signup, and only Farcaster as a provider, the user will see the QR code immediately. If there are other providers enabled/other login methods, Farcaster will appear as a button, which will then open the QR code. Both UIs are shown below:

<Tabs>
  <Tab title="Farcaster only UI">
    <Frame>
      <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/farcaster-qr-code.png" />
    </Frame>
  </Tab>

  <Tab title="Farcaster as a button">
    <Frame>
      <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/farcaster-as-button.png" />
    </Frame>
  </Tab>
</Tabs>

## Video walkthrough

<Frame>
  <iframe src="https://www.loom.com/embed/5df7051ff32e494b9b555cb566545b84?sid=8cc2f6ac-0e40-4cf5-8657-24d3d1a95907" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen width="100%" height="640" />
</Frame>

## Step By Step Walkthrough

### Enable Farcaster Signup

Visit [Log in & User Profile page in the dashboard](https://app.dynamic.xyz/dashboard/log-in-user-profile) and under "Social", enable Farcaster.

### Configure your Dynamic app

You don't need to do anything extra in your Dynamic app to enable Farcaster sign-in. The Dynamic SDK will automatically handle the sign-in process for you.

The easiest way of seeing the UI is to use the DynamicWidget UI component:

```tsx
import { DynamicContextProvider, DynamicWidget } from "@dynamic-labs/sdk-react";

export default function App() {
  return (
    <DynamicContextProvider
        setttings={{
          environmentId: "YOUR_ENV"
        }}
    >
      <DynamicWidget />
    </DynamicContextProvider>
  );
}
```

You're done! Your users can now sign in with Farcaster.

## Further resources

*   [Enable write access to Farcaster](/guides/integrations/farcaster/farcaster-write-access)
*   [Create embedded wallet from Farcaster Frame](/guides/integrations/farcaster/embedded-wallet-frame)


# How to Use Dynamic in a Safe App



<Warning>
  This is an enterprise feature and access is gated. Please let us know [in Slack](https://dynamic.xyz/slack) if you'd like access.
</Warning>

## Implementation

In this guide, you'll learn how to integrate Dynamic with the Safe App environment. By using Dynamic’s Safe EVM wallet connector, you can enable seamless wallet integration for Safe users. Whether your app is set to connect-only mode or requires a Sign-In with Ethereum (SIWE) signature, Dynamic’s SDK allows you to quickly and securely connect users. Follow the steps below to get started and deploy your app on Safe.

### Step 1: Install Safe EVM Wallet Connector

Ensure you're using SDK version 3.4.3 or higher.

Install the Dynamic Safe EVM wallet connector with the following command:

```bash
npm install @dynamic-labs-connectors/safe-evm@3.0.0-alpha.0
```

### Step 2: Import the Safe Connector

In your project, import the `SafeEvmWalletConnectors` from the Dynamic Labs Safe EVM package:

```javascript
import { SafeEvmWalletConnectors } from "@dynamic-labs-connectors/safe-evm";
```

### Step 3: Add the Connector to Dynamic

Add the Safe EVM wallet connector into your `DynamicContextProvider` as a `walletConnectors` option:

```javascript
 <DynamicContextProvider
    settings={{
      walletConnectors: [EthereumWalletConnectors, SafeEvmWalletConnectors],
      initialAuthenticationMode: 'connect-only',
    }}
  >
    { /* Your app code here */ }
  </DynamicContextProvider>
```

### Step 4: Update `manifest.json`

Update the `manifest.json` file in your project (usually located in `public/manifest.json`) to include an app name, description, and icon.

Example:

```json
{
  "short_name": "Safe App Test",
  "name": "Safe App Test",
  "description": "This is a safe app test",
  "iconPath": "https://demo.dynamic.xyz/logo.png",
  "start_url": ".",
  "display": "standalone",
  "theme_color": "#000000",
  "background_color": "#ffffff"
}
```

Note: you can see the full guide [here](https://help.safe.global/en/articles/145503-how-to-create-a-safe-app-with-safe-apps-sdk-and-list-it).

### Step 5: Testing Your Safe App

To test your Safe app, follow these steps:

1.  Navigate to [Safe Apps](https://app.safe.global/apps).
2.  Add your app as a custom app by pasting the app’s URL.
3.  Your app will automatically log you in if you're using connect-only mode, or it will ask for a Sign-In with Ethereum (SIWE) signature if you're using connect-and-sign mode.

That's it! Your Dynamic-powered Safe App should now be ready to use.

## Troubleshooting

### Safe not auto-connecting/asking for signature

This can be because of two things:

1.  You have not been granted access to the feature on the environment ID you're using, please let us know [in Slack](https://dynamic.xyz/slack).
2.  You have mismatched Dynamic packages installed - if you have a V4 of the safe-evm connector lib, your other packages must also be on V4.


# Shield3 & Dynamic Integration

Public Beta

## Introduction

Shield3 provides automated security & compliance tools for developers to protect user transactions through a customizable policy engine delivered as a custom RPC.

Dynamic provides onboarding and login flows with a high level of customization and a focus on developer tooling.

With a couple of simple steps, customers of Dynamic can add custom rules to all transactions that are facilitated by their application through Shield3.

[Here's a short video walkthrough of the process and the result!](https://www.loom.com/share/8c344d27546747cf977d2a1bab583b51?sid=80bc7b14-1982-4357-a6e5-3198c177c629).

## Tutorial

### Step 1 - Get an API Key

Option 1. (Recommended)

Sign up at [https://www.shield3.com/auth/sign-in](https://www.shield3.com/auth/sign-in) to get a free API key & RPC URL

Get your API Key & Configure Policies

<Frame>
  <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/shield3-policies-url.png" />
</Frame>

<Frame>
  <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/shield3-api-key.png" />
</Frame>

Option 2.

Use this public shared key to test it out: [https://rpc.shield3.com/v3/UlihwLJtYY9bWgkuLA6KS6liL6iTpXKqhWvPBl80/0x01/rpc](https://rpc.shield3.com/v3/UlihwLJtYY9bWgkuLA6KS6liL6iTpXKqhWvPBl80/0x01/rpc)

### Step 2 - Change the RPC URL

On the Dynamic.xyz dashboard go to [the EVM Configuration tab](https://app.dynamic.xyz/dashboard/configurations#evm).

Toggle on Ethereum Mainnet, then click the down arrow on the far right.

<Frame>
  <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/shield3-arrow.png" />
</Frame>

The section will expand to show you the current RPC URL used and you can paste in the RPC URL you received in step 1 here.

<Frame>
  <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/shield3-rpc.png" />
</Frame>

<Tip>
  to test our Goerli change the 0x01 in the URL to 0x05, or select Goerli on the
  Shield3 Dashboard
</Tip>

<Frame>
  <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/shield3-evm-full.png" />
</Frame>

### Step 3 - Test it out!

You can now run your Dynamic Implementation normally and send a transaction while connected to Ethereum.

<Tip>
  The easiest way to get up and running if you haven’t already integrated
  Dynamic already is via [the Quickstart](/quickstart) or [the Sample
  Apps](/example-apps). You should just make sure you have [the
  EthereumWalletConnector
  implemented](/react-sdk/wallet-connectors#implementation).
</Tip>

The transaction should succeed if there are no violations and will show up on your Shield3 Dashboard. If any issues cause the transaction to be blocked or flagged you will receive an error message which you can learn more about [here](/guides/integrations/shield3#custom-error-codes).

Tell us how it went! Survey Link Telegram or email [info@shield3.com](mailto:info@shield3.com)

## All about policies

<Frame>
  <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/shield3-policies.png" />
</Frame>

1 - Unverified Contracts (Block | Flag | Alert)
If enabled, interactions with unverified contracts that cannot be decoded are forbidden. User can configure this policy to result in block, flag for MFA, or permit with an alert.

2 - OFAC SDN Block Native and ERC20 Transactions
Block native transfers, ERC20 transfers, and ERC20 approvals to OFAC addresses. As part of its enforcement efforts, OFAC publishes a list of individuals and companies owned or controlled by, or acting for or on behalf of, targeted countries. It also lists individuals, groups, and entities, such as terrorists and narcotics traffickers designated under programs that are not country-specific. Collectively, such individuals and companies are called “Specially Designated Nationals” or “SDNs.” Their assets are blocked and U.S. persons are generally prohibited from dealing with them.

3 - Native Transfers Spending Limits (Block | MFA)
If enabled, transactions with native values (ex. ETH) over a customizable threshold are forbidden. Users can configure this policy to result in a block, or flag for MFA.

4 - ERC20 Stablecoin Transfers Spending Limits (Block | MFA)
If enabled, transfers and approvals with verified stablecoins (ex. USDC, DAI, USDT) over a customizable threshold are forbidden. Users can configure this policy to result in a block, or flag for MFA.

5 - DEX Token Swap Protection
If enabled, token swaps are permitted with verified token pairs. Users can customize which pairs are allowed, and set a slippage limit to prevent trades that might result in MEV exploitation.

If you have additional requests for policies or custom use cases reach out to Shield3 at [info@shield3.com](mailto:info@shield3.com), join the Support Telegram or fill out the Survey

## Custom Error Codes

When Shield3 blocks or flags a transaction the RPC url returns a custom error message. The format is as follows:

```json
{
  "jsonrpc": "2.0",
  "error": {
    "code": "-XXXXXX",
    "message": "ERROR MESSAGE HERE"
  },
  "id": XX
}
```

These include:

**Transaction Blocked \[code -9999982]**

*   Raised when policy engine results in block.

**Transaction Flagged for MFA \[code -9999981]**

*   Raised when MFA result from policy engine.
*   Webhook is sent to user’s configured webhook.

**RPC Call Failed \[code -9999980]**

*   Raised when proxy call to user’s RPC fails.

**Invalid API Key \[code -9999979]**

*   Raised when API key does not match any account.

**Invalid Node \[code -9999977]**

*   Raised when network in RPC url does not match a valid network (0x01, 0x05, etc).

**Uncaught Error in Shield3 \[code -9999976]**

*   Something went wrong, raises alert at Shield3 internally as well.


# Telegram Auto-Wallets



Create a wallet in a single-click from your telegram mini-app.

## Introduction

<Warning>
  **Attention Required: Telegram Bot Secret Security**

  To use automatic wallet creation for Telegram, developers must create a Telegram bot configured with a bot secret, which serves as both the control mechanism for the bot and as a symmetric key for authentication. This key technically grants the developer an ability to sign authentication data and actions with a wallet. Any compromise of the bot secret internally or externally can expose users and their accounts to significant risk.
  Securing this symmetric key is critical to ensuring the integrity of all Telegram login sessions in your application.
</Warning>

<Info>
  Telegram Auto-Wallets is currently in closed beta. Please [reach out to us](https://www.dynamic.xyz/talk-to-us) for early access if you are interested in testing this feature.

  No transactional MFA must also be enabled to use this feature. This feature will also be enabled when requesting access to Telegram Auto-Wallets.
</Info>

[Seamless Telegram Login](https://core.telegram.org/api/url-authorization) with Telegram Web Apps improves user experience by enabling quick access directly within Telegram Messenger. Users can launch a mini app and get automatically logged in.

<Tip>
  Live example of a Dynamic Seamless Telegram Login *@DynamicMiniAppBot* on
  Telegram App.
</Tip>

## Live Demo

🤖 [Open Dynamic Mini App Bot in Telegram](tg://resolve?domain=DynamicMiniAppBot)

<Frame>
  <video height="60%" width="70%" autoPlay muted loop playsInline src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/tg-auto-wallets.mp4" />
</Frame>

## Integration guide

To begin, you can clone the provided [Mini App example and repository](https://github.com/dynamic-labs/telegram-miniapp-dynamic) as a foundation for your project.

### Step-by-Step Instructions

1.  **Create an Account on Dynamic**

    *   If you haven’t done so already, [sign up for an account on Dynamic](https://app.dynamic.xyz/) and take note of your Environment ID.

2.  **Clone the Repository and Deploy**

    *   Clone [Mini App example and repository](https://github.com/dynamic-labs/telegram-miniapp-dynamic) to your local machine.
    *   Follow steps in the [README](https://github.com/dynamic-labs/telegram-miniapp-dynamic?tab=readme-ov-file#telegram-mini-app--dynamic-connect)
    *   Deploy the application online. With Next.js, you can deploy a web app within minutes. Follow the deployment instructions [here](https://vercel.com/docs/deployments/git#deploying-a-git-repository).

3.  **Create a Telegram App and Bot**

    *   Use [BotFather](https://core.telegram.org/bots#botfather) to create a new app and bot. For detailed steps, refer to our comprehensive guide [here](https://docs.dynamic.xyz/social-providers/telegram#creating-a-new-app).

4.  **Configure Settings in Dynamic Dashboard**
    *   In your Dynamic dashboard, complete the following steps:
        *   Add your web app URL as authorized [CORS origin](https://app.dynamic.xyz/dashboard/security).
        *   [Enable Telegram Social Login](https://app.dynamic.xyz/dashboard/log-in-user-profile) and configure it with your Bot Name and Secret Token.

5.  **Update Bot Configuration**

    *   Use the Bot `TOKEN` from Telegram and set your web app URL as the `LOGIN_URL` in `scripts/bot.ts` or add them as environment variables.

6.  **Run the Telegram Bot**

    *   Run the Telegram bot using the following command:
        ```bash
        npx ts-node scripts/bot.ts
        ```

7.  **Start the Bot in Telegram**
    *   Open Telegram, go to your newly created bot, and type `/start`.

### Success!

You should now have Dynamic’s Seamless Telegram Login fully functional in your Mini App. 🎉

If you have any questions or need help with the integration, feel free to reach out to us in [Slack](https://dynamic.xyz/slack).

## Telegram Authentication Check for Existing Users

This feature avoids accidental duplicate account creation by **ensuring that users with existing accounts don't create unnecessary new ones** if their account is not yet linked to Telegram.
The `isAuthWithTelegram()` function verifies whether the user is authenticated through Telegram.

<img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/tg-modal.png" width="50%" style={{ margin: "auto" }} />

If the user is linked with Telegram, they are automatically logged in.
If not, a splash modal prompts the user to confirm if they have an account. Based on the user's response, they are either are prompted to log in or a new account is automatically created and logged in.
This ensures app owners can maintain a single user identity, avoiding duplicate accounts.

<Note>Hook is available from SDK version 3.1.3</Note>
Full working example in our [Telegram Mini App repository here](https://github.com/dynamic-labs/telegram-miniapp-dynamic/pull/18)

1.  Import the necessary Telegram authentication functions:

    ```javascript
    import { useTelegramLogin } from "@dynamic-labs/sdk-react-core";

    const { telegramSignIn, isAuthWithTelegram } = useTelegramLogin();
    ```

2.  Check if the user is authenticated via Telegram:

    Use isAuthWithTelegram() to determine if the user is linked with Telegram.

    ```javascript
    const checkTelegramConnection = async () => {
      const isLinkedWithTelegram = await isAuthWithTelegram();

      if (isLinkedWithTelegram) {
        // Auto login if Telegram is connected
        await telegramSignIn();
      } else {
        // Show modal splash page
      }
    };
    ```

3.  Handle the modal splash response:

    If the user has an account: Prompt developer's login into the app without creating a new account.
    If the user doesn't have an account: Automatically create a new account and log the user in.

    ```javascript
    const handleModalResponse = async (hasAccount: boolean) => {
      if (hasAccount) {
        // Prompt developer's login
        console.log("User already has an account on XYZ");
      } else {
        // Call signIn with autoCreate: true
        console.log(
          "User does not have an account on XYZ => Auto Create + Auto Login"
        );
        await telegramSignIn({ forceCreateUser: true });
      }
    };
    ```

You can clone the provided full working example in our [Telegram Mini App repository here](https://github.com/dynamic-labs/telegram-miniapp-dynamic/pull/18)

## Providing the Telegram auth token to Dynamic

The Telegram Auto-Wallets feature allows you to authenticate your end user with Dynamic using a Telegram authentication token directly. There are currently two
ways to provide this token to our SDK.

1.  Pass the auth token as a query parameter when the mini app is launched.

In this approach, your [Telegram Bot script](https://github.com/dynamic-labs/telegram-miniapp-dynamic/blob/main/scripts/bot.ts#L59) generates the Telegram auth token and appends it to your mini app URL.
When `telegramSignIn` is called within your mini app, the token is grabbed from the URL query parameters and used to authenticate the user

2.  Pass the auth token directly into the `telegramSignIn` hook

In this approach, you directly pass the Telegram auth token into the `telegramSignIn` hook. This is useful if you are generating the Telegram auth token somewhere outside of Telegram
such as your backend.

```javascript
await telegramSignIn({ forceCreateUser: true, authToken: 'telegram-auth-token-goes-here' });
```

## FAQ

### What do I need to do to ensure that Telegram Auto-Wallets is working correctly?

1.  **CORS Origin Setup**:
    Ensure that your CORS Origin is correctly configured to include your domain(s).
    Go to Dashboard > Security > CORS and verify that your domain(s) are listed.

2.  **Telegram Social Provider**:
    Verify that the Telegram Social Provider is enabled and configured with the correct Bot details.
    Navigate to Dashboard > Log In & User Profile > Telegram and confirm that it is turned on, with the BotName and Bot Token entered correctly.

3.  **Session keys**:
    Please reach out to us in [Slack](https://dynamic.xyz/slack) to request access.

4.  **Email Profile Information Requirement**:
    Make sure that the option to require profile information for email login is turned off.
    Go to Dashboard > Log In & User Profile > Email ⚙️ and confirm that Profile Information required is off.

5.  **Bot Code & Telegram Mini Web App**:
    Clone the provided [Mini App example and repository](https://github.com/dynamic-labs/telegram-miniapp-dynamic) as a foundation for your project.
    Double-check that the Telegram bot code and the Telegram Mini Web App are functioning without any errors.
    Check the environment variable are loaded correctly `LOGIN_URL` and `TOKEN`.
    Ensure that the logic for seamless login and user verification is working as expected, both server-side and on the front end.

6.  **SDK and Package Versions**:
    Verify that the SDK and any packages you’re using are up to date. Hook is available from SDK version 3.1.3

### How do I fix MetaMask wallet connection issues?

MetaMask wallet connection does not work on Telegram mobile devices. To resolve this issue, use the following workaround:

Open your app within the mobile version of the MetaMask app. This will allow the wallet connection to function correctly on mobile.
If you encounter further issues, ensure that MetaMask is up to date.


# Telegram Mini App



<Frame>
  <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/telegram-mini-app.jpg" />
</Frame>

## Introduction

Telegram Mini Apps (or TMAs) are web applications that run inside the Telegram messenger.

Dynamic works out of the box with TMAs, all you need to do is get your app scaffolded, and add Dynamic by following the quickstart guide.

<Tip>
  You can see a live example of a Dynamic integration in a TMA when using [Flooz](https://t.me/flooz_xyz).
</Tip>

## Intregration guide

To get started, you can use one of the TMA templates outlined [here](https://docs.telegram-mini-apps.com/packages/telegram-apps-create-mini-app).

The basic command to scaffold a new TMA is:

```bash
npx @telegram-apps/create-mini-app@latest
```

Cd into the `src/components/` directory in `Root.tsx` file and add the Dynamic Context Provider in the returned TSX:

<Tabs>
  <Tab title="Before">
    ```tsx
    <AppRoot
    appearance={miniApp.isDark ? 'dark' : 'light'}
    platform={['macos', 'ios'].includes(lp.platform) ? 'ios' : 'base'}
    >
        <Router location={location} navigator={reactNavigator}>
            <Routes>
            {routes.map((route) => <Route key={route.path} {...route} />)}
            <Route path='*' element={<Navigate to='/'/>}/>
            </Routes>
        </Router>
    </AppRoot>
    ```
  </Tab>

  <Tab title="After">
    ```tsx
    <AppRoot
        appearance={miniApp.isDark ? 'dark' : 'light'}
        platform={['macos', 'ios'].includes(lp.platform) ? 'ios' : 'base'}
    >
        <DynamicContextProvider
            settings={{
            environmentId: dynEnv as string,
            walletConnectors: [EthereumWalletConnectors],
            }}
        >
        <Router location={location} navigator={reactNavigator}>
        <Routes>
            {routes.map((route) => <Route key={route.path} {...route} />)}
            <Route path='*' element={<Navigate to='/'/>}/>
        </Routes>
        </Router>
        </DynamicContextProvider>
    </AppRoot>
    ```
  </Tab>
</Tabs>

Make sure that you've also added the correct imports and the variable for your Dynamic environment ID:

```tsx
import { DynamicContextProvider } from '@dynamic-labs/sdk-react-core';
import { EthereumWalletConnectors } from '@dynamic-labs/ethereum';

const dynEnv = import.meta.env.VITE_DYNAMIC_ENV_ID || process.env.DYNAMIC_ENV_ID;
```

That's it! You should now be able to use Dynamic in your Telegram Mini App..

You can customize the integration further by following the [documentation](/) in the generated project README file.

If you have any questions or need help with the integration, feel free to reach out to us in [Slack](https://dynamic.xyz/slack).


# XMTP & Dynamic Integration



![](https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/xmtp.png)

XMTP is a messaging protocol which helps you build apps that communicate with every wallet in the world, and we're huge fans here at Dynamic!

In this guide you'll learn how to set up an example XMTP application using Dynamic as the authentication layer, and learn how it all works.

The running version of what you're about to build can be found here: [https://xmtp-quickstart-reactjs.matthew18091.repl.co/](https://xmtp-quickstart-reactjs.matthew18091.repl.co/)

## Run the quickstart

All you'll need to get going is a code editor and a terminal! When you're ready, clone the official XMTP React quickstart, which ships with Dynamic:

```bash
git clone https://github.com/fabriguespe/xmtp-quickstart-reactjs/
```

Navigate to the xmtp-quickstart-reactjs directory, and run `npm i && npm run dev` to install the dependancies and start the app.

Open it up on whichever ports it displays for you, and you should see a simple app with a button that says "Connect Wallet".

Connect an Etherium supported wallet i.e. Metamask, and you should see a new button called "Connect XMTP" appear. Click it, and you'll be prompted to sign a message in your wallet.

Those two steps could be combined into one for ease of use, but we've kept them separate here to show you how it works under the hood.

That's it! You're authenticated and connected. You can now send messages to wallet addresses, and they'll be able to see them in real time.

## How it works

### The Dynamic integration

Take a look in App.tsx and you'll see that things get wrapped in DynamicContextProvider. This makes sure that Dynamic is available to your components.

If you check the import for this provider at the top of the file, you'll see it's imported from `sdk-react-core`. This is important.

The `-core` signifies that we are using the modular version of the SDK, where you pick and choose which chain/wallet providers you want to import and thus keep the bundle size as small as possible.

This is why you'll also see an `EthereumWalletConnectors` import, which is the Ethereum wallet connector from Dynamic. It's passed in through the settings prop on the DynamicContextProvider, like this:

```jsx
 <DynamicContextProvider
    settings={{
        walletConnectors: [ EthereumWalletConnectors ]
    }}
>
```

As you can see, The other setting passed in is the environment ID for the Dynamic environment you want to use. You can find this in the developer section of the Dynamic dashboard but we've given you a default demo ID to get started with.

Next take a look at the `Home` component, where you'll find a lot of different XMTP functionality If you want to learn more, the docs detail the entire SDK and methods available: [https://xmtp.org/docs/build/get-started](https://xmtp.org/docs/build/get-started)

### What next?

Discover some of the advanced use cases XMTP and Dynamic can provide! You can find use case examples for XMTP [here](https://xmtp.org/docs/use-cases/messaging) and Dynamic examples in the left hand nav [here](https://docs.dynamic.xyz/docs/getting-started).


# Add new users to your CRM



This guide will teach you how to automatically update a Web3 based CRM (in this case, Holder.xyz) once you have a new signup to your dapp.

Holder is a CRM and marketing automation platform built uniquely to handle web3 and wallet data. With Holder + Dynamic, you can:

*   Have a single view of your customers, unifying Dynamic user information with your CRM and marketing data.
*   Enable powerful on-chain automations and marketing campaigns (like XMTP messaging) based on Dynamic webhook events.

## Prerequisites

This guide builds on [the serverless guide](/guides/webhooks-serverless), so you should have already completed that guide and have a working serverless function. It also assumes you already have a [Holder](https://holder.xyz) account.

## Step 1: Get your Authentication token

For Holder.xyz, you'll need to follow a couple of steps in order to get the token you'll need to authenticate with the API. The fastest way is to follow this guide: [https://docs.holder.xyz/holder-api/authentication](https://docs.holder.xyz/holder-api/authentication). Come back here once you have your token!

You'll be passing this in the header of API requests like so:

```javascript
{
  "headers": {
    "Authorization": "Bearer Your_Token_Here"
  }
}
```

## Step 2: Define your API request

The choice is yours as to which library you use, we will use Fetch here.

First we need to know our base URL, which is `https://api.holder.xyz/`. We will be using the `POST` method to create a new user, and we will be hitting the `/contacts` endpoint. The full reference for that endpoint is [here](https://docs.holder.xyz/holder-api/~/revisions/gDfIZFl5wGxV0PcmHORf/api-reference/contacts).

We need to pass a primaryIdentifier which in our case will be email, and a value, which is the email itself. So putting it together it will look something like this:

```javascript
// holder.js

const token = XXXX;

const createUserInCrm = async (userEmail) => {
  const options = {
    method: "POST",
    headers: {
      Authorization: `Bearer ${token}`,
      "Content-Type": "application/json",
    },
    body: `{"primaryIdentifier":"email", "value": ${userEmail}}`,
  };

  try {
    return await fetch("https://api.holder.xyz/contacts", options);
  } catch (error) {
    console.error(error);
  }
};

module.exports = createUserInCrm;
```

## Step 3: Plug in your serverless function

Going back to our Serverless guide at [Step 3](/guides/webhooks-serverless#step-3-adapt-the-function-to-receive-a-dynamic-webhook-event), all we have to do now is add in our Holder function call:

```javascript
// index.js

const createUserInCrm = require("./holder");

module.exports.handler = async (event) => {
  const email = event.data.email;

  await createUserInCrm(email);

  return {
    statusCode: 200,
    body: JSON.stringify(
      {
        message: "Go Serverless v3.0! Your function executed successfully!",
        input: event,
      },
      null,
      2
    ),
  };
};
```

You can then test and redeploy your function as in [Step 4 of the serverless guide](/guides/webhooks-serverless#step-4-test-and-deploy-your-function), and you're done!

You should see a new contact in [the Holder app](https://app.holder.xyz/contacts) like so:

<Frame>
  <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/holder-contact.png" />
</Frame>

The next step you might want to take is to update the contact if the user adds more details to their Dynamic profile i.e. wallet addresses etc. We will cover this in a further guide, so stay tuned!


# Webhooks using a serverless function



This guide will teach you how to catch events from Dynamic webhooks using a serverless function.

## Prerequisites

*   A [Dynamic](https://dynamic.xyz) account
*   An [AWS](https://aws.amazon.com/) account

## Step 1: Scaffold a serverless function

You can do this directly through AWS, but we prefer to use [Serverless](https://www.serverless.com/) which creates a helpful layer of abstraction on top of Lambda.

First install their framework:

```bash
npm install -g serverless
```

Then run their setup command:

```bash
serverless
```

You will need to plug in your AWS credentials during setup, which you can find in your AWS console.

When asked which template you want to use, select `AWS - Node.js - HTTP API` as this tutorial will be using Node.js.

If you cd into the directory that was created, you should see an `index.js` file which is where we will be writing our code.

It will look something like this:

```javascript
module.exports.handler = async (event) => {
  return {
    statusCode: 200,
    body: JSON.stringify(
      {
        message: "Go Serverless v3.0! Your function executed successfully!",
        input: event,
      },
      null,
      2
    ),
  };
};
```

## Step 2: Create our webhook inside Dynamic

We need to create a webhook inside Dynamic that will send a POST request to our serverless function whenever a new user signs up.

To do this we can visit [the webhooks section of the dashboard](https://app.dynamic.xyz/dashboard/developer/webhooks) and click "Create Webhook". It will ask you for a URL and to mark the events you want to listen to.

For now we can use a fake URL, which we can generate from [webhook.site](https://webhook.site/). This will give us a URL that we can use to test our webhook, and will show us the data that is being sent to it.

For the events, you should make sure user.created is selected, and that's it! Save away and a test event will be sent to your URL.

## Step 3: Adapt the function to receive a Dynamic webhook event

Let's say in this instance We are most interested in [the user object](/api-reference/schemas/User), as this is returned by the user.created event. You can find the full list of events [here](/developer-dashboard/eventTypes).

As you'll see from the event reference, the only required field on the object is the ID, so if you want to collect more information you'll need to make sure you're doing [Info Capture](/users/information-capture) on signup.

<Tip>
  In this tutorial we will assume we are using email signup with embedded
  wallets, so we'll always have email available on the user object at time of
  signup
</Tip>

In the test event, when you inspect it using webhook.site, you'll see the payload comes with a `data` field, and it's inside this field that the user object will be present. Here's how we can use the email inside the function:

```javascript
module.exports.handler = async (event) => {

  const email = event.data.email;

  console.log(email);

  ...
};
```

Note that we are not adding error handling in the end response, you should do so by adapting the returned status code and message etc.

## Step 4: Test & deploy your function

Serverless hooks into the AWS local testing functionality using [invoke local](https://www.serverless.com/framework/docs/providers/aws/cli-reference/invoke-local).

You can find the function name in the serverless.yml file, and then run the following command to test it:

```bash
serverless invoke local --function FUNCTION_NAME_HERE --data '{data: {"email":"test@mail.com"}}'
```

If you get a log of the user email, you're ready to deploy your function so you can run:

```bash
serverless deploy
```

As long as this is successful, you should get a URL for your function and you can use that to update your webhook in Dynamic from the webhook.site URL.

Now you're ready to run it! Feel free to use one of [the example apps](https://docs.dynamic.xyz/example-apps) to test out creating a new user via the end UI!

## Step 5: Going further

You can use this approach to send notifications for any event, and you can use any service you like to handle the processing of the event.

Don't hesitate to check out [Zapier Webhooks](https://zapier.com/apps/webhook/integrations) to hook into many other services, CRMs and so on.


# Verifying webhook message signatures



This guide will teach you how to verify the webhook message signatures sent in the header of webhook message requests. This enables you to verify that the message originated from dynamic and the message payload has not been tampered with since only Dynamic and you have access to the secret used to sign messages.

Below is a simple typescript function that takes in the webhook secret, signature, and payload; and returns a boolean value indicating if the signature is valid or not.

```typescript
import * as crypto from "crypto";

export const verifySignature = ({
  secret,
  signature,
  payload,
}: {
  secret: string;
  signature: string;
  payload: any;
}) => {
  const payloadSignature = crypto
    .createHmac("sha256", secret)
    .update(JSON.stringify(payload))
    .digest("hex");
  const trusted = Buffer.from(`sha256=${payloadSignature}`, "ascii");
  const untrusted = Buffer.from(signature, "ascii");
  return crypto.timingSafeEqual(trusted, untrusted);
};
```

The following is a practical example of how to use the verifySignature function to verify a webhook message signature for a given payload.

<Note>
  The structure of the payload object matters; if the payload object is not structured in the manner in which the message was sent the signature verification will fail.
</Note>

```typescript
// webhook secret, unique per webhook
const secret = 'dyn_5LqRXtZsXit7Cjt9KktC8EGywoSkGSbGqtTud[S7AYd{LUTMcS38QRMT';

// signature sent in the header (x-dynamic-signature) of the webhook message
const signature = 'sha256=9c1eade367c30f371990918b8d79ecdd86ea2c8042849a1c22c94ad50b7d98fe';

// message payload sent in the body of the webhook POST request
const payload = {
  messageId: 'd487290b-9bbd-4b50-a5f5-6620634c8dd9',
  eventId: 'bfb51cec-345f-4539-b004-e3415223b86b',
  eventName: 'user.deleted',
  timestamp: '2024-05-31T17:21:14.890Z',
  webhookId: 'c51e4b22-193e-4989-9ce2-90851d57d861',
  userId: '001a4205-3d67-4956-a132-5ef41a06c78e',
  environmentId: 'ba1f6471-626b-45a1-ab63-c6bf4793a1c5',
  environmentName: 'sandbox',
  redelivery: true,
  data: {
    onboardingCompletedAt: '2024-01-22T20:38:29.049Z',
    metadata: {},
    createdAt: '2024-01-22T20:38:27.181Z',
    projectEnvironmentId: 'ba1f6471-626b-45a1-ab63-c6bf4793a1c5',
    id: '001a4205-3d67-4956-a132-5ef41a06c78e',
    firstVisit: '2024-01-22T20:38:27.169Z',
    email: 'dynamicuser@gmail.com',
    updatedAt: '2024-05-31T17:21:14.870Z',
    lastVisit: '2024-01-22T20:55:10.126Z',
    deletedAt: '2024-05-31T17:21:14.867Z',
  },
};

const isValid = verifySignature({ secret, signature, payload });
console.log('isValid', isValid);
```


# Slack notifications of new signups

Post in a slack channel when you have a new signup

This guide will teach you how to automatically post a message into a slack channel of your choice with details of a new signup to your dapp.

## Prerequisites

*   Completed [the serverless webhooks guide](/guides/webhooks-serverless)
*   A [Slack](https://slack.com/) account
*   A [Dynamic](https://dynamic.xyz) account

## Post a message to Slack

As long as you have completed the serverless webhook guide, you'll know how to access the email of a user within your serverless function. Now we have the email, we can post a message to Slack. To do this we'll need to create a [Slack app](https://api.slack.com/apps) and set it up to post messages using incoming webhooks.

[This guide](https://api.slack.com/messaging/webhooks) will get you set up, and once you've completed it you will end up with a webhook URL, that's all you need.

It's time to write the code to send our Slack message. We can do this with anything like Axios or Request, but here we will use [the Slack/webhook library](https://slack.dev/node-slack-sdk/webhook) to make things even easier.

```javascript
// Slack.js

const { IncomingWebhook } = require("@slack/webhook");

// Read a url from the environment variables
const url = YOUR_SLACK_WEBHOOK_URL;

// Initialize
const webhook = new IncomingWebhook(url);

const sendMessage = async (message) => {
  try {
    return await webhook.send({
      text: message,
    });
  } catch (error) {
    console.error(error);
  }
};

module.exports = sendMessage;
```

```javascript
// index.js

const webhook = require("./slack");

module.exports.handler = async (event) => {
  const email = event.data.email;

  const message = `New signup: ${email}`;

  await webhook(message);

  return {
    statusCode: 200,
    body: JSON.stringify(
      {
        message: "Go Serverless v3.0! Your function executed successfully!",
        input: event,
      },
      null,
      2
    ),
  };
};
```

## Test it out

As per the serverless webhook guide, we can test this out using:

```bash
serverless invoke local --function FUNCTION_NAME_HERE --data '{data: {"email":"test@mail.com"}}'
```

We can then deploy using `serverless deploy`, and you're good to go!


# Send new users an XMTP message



XMTP is a communication protocol for Web3, allowing you to build apps that communicate with
every wallet in the world. In this guide you will learn to programmatically send XMTP messages to users when they sign up using a wallet.

## Prerequisites

*   A [Dynamic](https://dynamic.xyz) account
*   Completed [the serverless webhooks guide](/guides/webhooks-serverless)

## Step 1: Access the wallet address

As long as you have completed the serverless webhook guide, you'll know how to access the email of a user within your serverless function. However in this instance, we need a wallet address.

A new user will have a wallet in two instances:

1.  They signup using an EOA
2.  They receive an embedded wallet

You can learn more about the different kinds of signup you can enable in the following section of the docs: [https://docs.dynamic.xyz/sign-in-sign-up/overview](https://docs.dynamic.xyz/sign-in-sign-up/overview).
Let's assume for now that you have both embedded wallets enabled, and EOA signups enabled so that the user object is guaranteed to have a wallet address.

When a user signs up, you can access their wallet address through the Verified Credentials array ([reference here](/react-sdk/objects/verified-credential)):

```js
const verifiedCredentials = event.data?.verifiedCredentials;

if (!verifiedCredentials) {
  return {
    statusCode: 400,
    body: JSON.stringify({
      error: "No verified credentials found",
    }),
  };
}

const blockchainVC = verifiedCredentials.find((vc) => vc.type === "blockchain");

if (!blockchainVC) {
  return {
    statusCode: 400,
    body: JSON.stringify({
      error: "No blockchain VC found",
    }),
  };
}

const address = blockchainVC.address;
```

## Step 2: Send the XMTP message

We will need to install the XMTP library for NodeJS first:

```bash
npm i @xmtp/xmtp-js
```

Then we can import the library and send a message:

```javascript
// xmtp.js
const { Client } = require("@xmtp/xmtp-js");

const sendMessage = (address) => {
    try {
        const conv = await xmtp.conversations.newConversation(address);
        return  await conv.send("gm");
    } catch(e) {
        console.log(e)
    }
}

export default sendMessage;
```

## Step 3: Call the function

Going back to our Serverless guide at [Step 3](/guides/webhooks-serverless#step-3-adapt-the-function-to-receive-a-dynamic-webhook-event), all we have to do now is add in our XMTP function call:

```javascript
// index.js

const sendMessage = require("./xmtp.js");

module.exports.handler = async (event) => {
  const verifiedCredentials = event.data?.verifiedCredentials;

  if (!verifiedCredentials) {
    return {
      statusCode: 400,
      body: JSON.stringify({
        error: "No verified credentials found",
      }),
    };
  }

  const blockchainVC = verifiedCredentials.find(
    (vc) => vc.type === "blockchain"
  );

  if (!blockchainVC) {
    return {
      statusCode: 400,
      body: JSON.stringify({
        error: "No blockchain VC found",
      }),
    };
  }

  const address = blockchainVC.address;

  await sendMessage(address);

  return {
    statusCode: 200,
    body: JSON.stringify(
      {
        message: "Go Serverless v3.0! Your function executed successfully!",
        input: event,
      },
      null,
      2
    ),
  };
};
```

You can then test and redeploy your function as in [Step 4 of the serverless guide](/guides/webhooks-serverless#step-4-test-and-deploy-your-function), and you're done!


# ETHGlobal Singapore



## Prizes

<Frame>
  <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/ethglobal-singapore-prizes.png" />
</Frame>

## Ideas

*   [Flutter](/flutter/client) or [React Native](/react-native/introduction) mobile apps

    *   [Flutter Example App](https://github.com/dynamic-labs/flutter-example)
    *   [React Native Example App (with Expo)](https://github.com/dynamic-labs/react-native-expo)

*   [Telegram Mini Apps](/guides/integrations/telegram/telegram-mini-app) or [Telegram Auto Wallets](/guides/integrations/telegram/telegram-auto-wallets)
    *   [TG Mini App with Auto Wallets Example Repo](https://github.com/dynamic-labs/telegram-miniapp-dynamic)

## Past Winners

*   [London 2024 Winners](https://x.com/dynamic_xyz/status/1769710456015647079)
*   [Brussels 2024 Winners](https://x.com/dynamic_xyz/status/1812532549601857693)


# Headless Email Signup



## Introduction

This guide will help you let a user signup/login with email, without any of our UI components. We've split the guide into two tabs as you'll see below. The first uses our SDK hooks (but none of the UI components) - we recommend this choice if you can. The second tab shows you how to handle email login without any SDK interaction at all i.e. API only.

## Tutorial

<Tabs>
  <Tab title="SDK Hooks">
    ### Configuring the SDK

    Let's start by installing our packages. Follow our [Quickstart guide](/quickstart) for a detailed walkthrough.

    When you're done, we'll update our App.js to include our environment ID in the setup and import the `DynamicContextProvider` and `EthereumWalletConnectors`.

    You app.js file should look like this (note that we are only importing the EthereumWalletConnectors for now):

    ```jsx
    import { DynamicContextProvider } from '@dynamic-labs/sdk-react-core';
    import { EthereumWalletConnectors } from "@dynamic-labs/ethereum";

    // Placeholder for our SMS signup/login form component
    import ConnectWithEmailView from './ConnectWithEmailView';


    function App() {
      return (
        <div className="App">
            <DynamicContextProvider
              settings={{
                environmentId: "YOUR_ENVIRONMENT_ID_GOES_HERE",
                walletConnectors: [ EthereumWalletConnectors ],

              }}
            >
            <ConnectWithEmailView />
            </DynamicContextProvider>
        </div>
      );
    }

    export default App;
    ```

    ### useConnectWithOtp

    All we will need for this use case is the [useConnectWithOtp hook](/react-sdk/hooks/useconnectwithotp). This exposes multiple methods, and we are interested primarily in the following:

    *   connectWithEmail
    *   verifyOneTimePassword

    Once you have those available in your component, the rest is as simple as building your form!

    ### Code Example

    ```tsx
    import { FC, FormEventHandler } from 'react';
    import { useConnectWithOtp, useDynamicContext } from '@dynamic-labs/sdk-react-core';

    const ConnectWithEmailView: FC = () => {
      const { user } = useDynamicContext()

      const { connectWithEmail, verifyOneTimePassword } = useConnectWithOtp();

      const onSubmitEmailHandler: FormEventHandler<HTMLFormElement> = async (
        event,
      ) => {
        event.preventDefault();

        const email = event.currentTarget.email.value;

        await connectWithEmail(email);
      };

      const onSubmitOtpHandler: FormEventHandler<HTMLFormElement> = async (
        event,
      ) => {
        event.preventDefault();

        const otp = event.currentTarget.otp.value;

        await verifyOneTimePassword(otp);
      };

      return (
        <div>
          <form key='email-form' onSubmit={onSubmitEmailHandler}>
            <input type='email' name='email' placeholder='Email' />
            <button type='submit'>Submit</button>
          </form>

          <form key='otp-form' onSubmit={onSubmitOtpHandler}>
            <input type='text' name='otp' placeholder='OTP' />
            <button type='submit'>Submit</button>
          </form>

          {!!user && (
            <p>Authenticated user:</p>
            <pre>
              {JSON.stringify(user, null, 2)}
            </pre>
          )}
        </div>
      )
    }
    ```
  </Tab>

  <Tab title="API Only">
    Here we follow three simple steps, creating the email verification, verifying the OTP, and getting the JWT.

    ### Basic UI

    First let's create a basic React component to help us handle the UI:

    ```JSX
    import React, { useState } from 'react';

    const EmailSignup = () => {

        const DYNAMIC_ENVIRONMENT_ID = "YOUR_ENVIRONMENT_ID";

        const [email, setEmail] = useState("");
        const [verifying, setVerifying] = useState(false);
        const [OTP, setOTP] = useState("");
        const [UUID, setUUID] = useState("");
        const [JWT, setJWT] = useState("");

        const sendEmailVerification = async () => {};
        const verify = async () => {};

        return (
            <div>
                <h1>Signup with Email</h1>
                <input
                type="text"
                onChange={(e) => setEmail(e.target.value)}
                placeholder="Enter your email"
                value={email}
                />
                <button onClick={() => sendEmailVerification()}>Submit</button>
                {verifying && (
                <div>
                    <input
                    type="text"
                    onChange={(e) => setOTP(e.target.value)}
                    placeholder="Enter your OTP"
                    value={OTP}
                    />
                    <button onClick={() => verify()}>Verify</button>
                </div>
                )}
                {JWT && <p>Your JWT is: {JWT}</p>}
            </div>
        );
    }
    ```

    We're creating a few state variables to help us keep track of the user's email, the OTP, the verificationUUID, and the JWT. We're also creating a couple of (currently empty) functions to handle the email verification and the OTP verification.

    You might be wondering what the UUID variable is for. When you create an email verification, we return a verificationUUID that you must pass into the API along with the OTP itself so that it's correctly verified.

    Lastly but most importantly, there's a variable called DYNAMIC\_ENVIRONMENT\_ID. We will need this to populate the URLs for the API calls. You can find your environment ID in the [Dynamic dashboard](https://app.dynamic.xyz/dashboard/developer/api).

    OK, let's fill in the empty functions now, going step by step:

    ### Create an email verification

    You will be interacting with [the emailVerification endpoint](/api-reference/endpoints/sdk/createEmailVerification) for this. This will send an email to the user with a One Time Password so they can verify their email address.

    You can find the full reference for the endpoint in the link above, but the main thing you need to pass is the email address of the user. We'll edit our `sendEmailVerification` function like so:

    ```javascript
    const sendEmailVerification = async () => {
      setVerifying(true);

      const options = {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ email: email }),
      };

      fetch(
        `https://app.dynamicauth.com/api/v0/sdk/${DYNAMIC_ENVIRONMENT_ID}/emailVerifications/create`,
        options
      )
        .then((response) => response.json())
        .then((response) => {
          setUUID(response.verificationUUID);
        })
        .catch((err) => console.error(err));
    };
    ```

    ### Verify the OTP

    With the above step complete, as long as the email address is valid, the user will receive an email with an OTP. They will enter the OTP in the UI, and we will verify it once they hit the "Verify" button.

    To verify the OTP, the next call will be to signIn endpoint, which you can find [here](/api-reference/endpoints/sdk/signInWithEmailVerification). You will need to pass the OTP from the email and the verificationUUID from the previous response.

    Let's fill in our `verify` function with that in mind:

    ```javascript
    const verify = async () => {
      const options = {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          verificationToken: OTP,
          verificationUUID: UUID,
        }),
      };

      fetch(
        `https://app.dynamicauth.com/api/v0/sdk/${DYNAMIC_ENVIRONMENT_ID}/emailVerifications/signIn`,
        options
      )
        .then((response) => response.json())
        .then((response) => {
          setVerifying(false);
          setJWT(response.jwt);
        })
        .catch((err) => console.error(err));
    };
    ```

    ### Get the JWT

    At this point, you'll have a JWT returned from the verify call and saved in state as the jwt variable. This JWT will be tied to a session in Dynamic, and we return details of the user from that JWT.

    Learn more about the JWT object [here](/react-sdk/objects/user-payload).

    ## Summary

    That's it! You now have a completely headless sign-in with email, using Dynamic OTP.
  </Tab>
</Tabs>


# Headless Embedded Wallets With OTP



## Summary

This guide will show you how to create an embedded wallet for a user, then help them secure it once they are ready to make their first transaction using One Time Codes, and optionally adding a passkey.

When using one time codes, we'll also create a session for that user so they can transact for a further period of time without any extra steps. for more information on the differences between passkeys and one codes, please see [this guide](/wallets/embedded-wallets/dynamic-embedded-wallets#signing-and-security-methods).

We will assume that the user has already signed up. If you need to handle the signup part headlessly, we recommend reading the [headless email guide](/headless/headless-email).

Here are the steps we need to follow:

1.  Check for active session
2.  Create the embedded wallet
3.  Secure the wallet pre-transaction
4.  Create a session for the user

For all of these steps we will need just one hook - [`useEmbeddedWallet`](/react-sdk/hooks/useembeddedwallet).

## Pre-requisites

*   You have already set up the Dynamic SDK and wrapped your app with the `DynamicContextProvider`.
*   You have enabled Dynamic-powered embedded wallets and toggled "Manual Mode" on in the configuration.
*   You do not have the DynamicWidget component in your application code.

## Session Notes

For security reasons, sessions are held in an iframe which gets deleted after page refresh and user log out.

To overcome sending users one-time codes each time they don't have an active session, we're able to restore it and developers should always try to do it before starting the one-time codes flow.

You'll see this in the code examples below, where we check for an active session before sending the one-time code i.e.

```jsx
useEffect(() => {
  const startSession = () => {
    try {
      if (isSessionActive) return;
      createOrRestoreSession();
    } catch (err) {
      return;
    }
  };
  startSession();
}, []);
```

## Full code examples

<Warning>
  You must add the following to your root CSS file to hide the iframe

  ```css
  iframe[id*="dyn-secure-enclave-element-id"] {
    display: none;
  }
  ```
</Warning>

<Tabs>
  <Tab title="One Time Codes">
    ```javascript
    import { useEmbeddedWallet, useDynamicContext } from “@dynamic-labs/sdk-react-core”

    // component declaration and all other logic you might need
    const { createEmbeddedWallet,
        createOrRestoreSession,
        isSessionActive,
        sendOneTimeCode,
        userHasEmbeddedWallet } = useEmbeddedWallet();

    const oneTimeCodeSent = useRef(false);

    useEffect(() => {
       const startSession = () => {
        try {
            if (isSessionActive) return;
            createOrRestoreSession();
        } catch (err) {
        return;
        }
    };

        startSession();
    }, []);

    const onSendOneTimeCodeHandler = async () => {

        // You do not need to call createEmbeddedWallet
        // if the Create on Sign up setting is enabled in the
        // Dynamic dashboard as the embedded wallets will be generated
        // automatically on user sign up.
        if(!userHasEmbeddedWallet()) await createEmbeddedWallet();

        if(!isSessionActive) {
        try {
            await sendOneTimeCode();
            oneTimeCodeSent.current = true;
            return;
        } catch(e) {
            console.error(e)
        }
        } else return;
    }

    const onCreateSessionHandler: FormEventHandler<HTMLFormElement> = async (event) => {
        try {
        event.stopPropagation();
        event.preventDefault();

        if (!primaryWallet || !userHasEmbeddedWallet()) return;

        const otc = event.currentTarget.otc.value;

        await createOrRestoreSession({oneTimeCode: otc})
            .then((result) => setResult(result))
            .catch((error) => setResult(JSON.stringify(error, null, 2)));
        } catch (err) {
        logger.error(err);
        }
    };

    return (
    <>
        {!isSessionActive && (
            <div>
                {!oneTimeCodeSent.current && <button onClick={onSendOneTimeCodeHandler}>Start session</button>}
                {oneTimeCodeSent.current && (
                <form onSubmit={onCreateSessionHandler} className='create-session-method'>
                <p>
                    Enter one-time code sent to email to create a session
                </p>

                <input required name='otc' type='text' placeholder='One-time code' />
                <br />
                <button type='submit'>Create session</button>

                </form>
                )}
            <div>
        )}
    </>
    )
    ```
  </Tab>

  <Tab title="Passkeys">
    ```javascript
    import { useEmbeddedWallet, useDynamicContext } from “@dynamic-labs/sdk-react-core”

    // component declaration and all other logic you might need
    const { createEmbeddedWallet,
        createPasskey,
        getPasskeys,
        userHasEmbeddedWallet } = useEmbeddedWallet();

    const onCreatePasskeyHandler: FormEventHandler<HTMLFormElement> = async (event) => {
        try {
        event.stopPropagation();
        event.preventDefault();

        if (!primaryWallet || !userHasEmbeddedWallet()) return;

        const otc = event.currentTarget.otc.value;

        return await createPasskey(otc)

        } catch (err) {
        console.error(err);
        }
    };

    return (
    <>
        {!oneTimeCodeSent.current && <button onClick={onSendOneTimeCodeHandler}>Start passkey creation</button>}
        {oneTimeCodeSent.current && (
        <form onSubmit={onCreatePasskeyHandler} className='create-session-method'>
        <p>
            Enter one-time code sent to email to create a new passkey
        </p>

        <input required name='otc' type='text' placeholder='One-time code' />
        <br />
        <button type='submit'>Create passkey</button>

        </form>
        )}
    </>
    )
    ```
  </Tab>
</Tabs>


# Headless embedded wallets using Passkeys



## Summary

This guide will show you how to create a secured embedded wallet with a passkey, completely headlessly. We will use the createPasskey and createEmbeddedWallet methods, available from [the useEmbeddedWallet hook](/react-sdk/hooks/useembeddedwallet).
Here are the steps we need to follow:

1.  Ensure the user is logged in
2.  Create a passkey for the user
3.  Create the embedded wallet with the passkey provided from the previous step

## Pre-requisites

*   You have already set up the Dynamic SDK and wrapped your app with the `DynamicContextProvider`.
*   You have enabled Dynamic-powered embedded wallets and toggled "Manual Mode" on in the configuration.
*   You do not have the DynamicWidget component in your application code.

## Step by step

### Ensure the user is logged in

You can do this by implementing signup/login i.e. via [email](/headless/headless-email), or via [SMS](/headless/headless-sms).

### Create a passkey for the user

To create a passkey for the user, we will use the createPasskey method from the useEmbeddedWallet hook. This method will request the user create a passkey through their device UI and return a WebAuthnAttestation object to be used next while creating the embedded wallet.

Method signature:

```typescript
createPasskey(): Promise<WebAuthnAttestation>
```

Implementation:

```tsx
const { createPasskey } = useEmbeddedWallet();

const createPasskeyHandler = async () => {
  const webAuthnAttestation = await createPasskey({});
  console.log(webAuthnAttestation);
};
```

### Create the embedded wallet

To create the embedded wallet, we will use the `createEmbeddedWallet` method from the `useEmbeddedWallet` hook. This method will create the wallet with the passkey we generated in the previous step.

Method signature:

```typescript
createEmbeddedWallet(chains?: ChainEnum[], options?: { webAuthnAttestation: WebAuthnAttestation }): Promise<Wallet | undefined>
```

Implementation:

```tsx
const { createEmbeddedWallet } = useEmbeddedWallet();

const createWalletHandler = async () => {
  const wallet = await createEmbeddedWallet(['EVM'], { webAuthnAttestation });
  console.log(wallet);
};
```

## Full code example

```tsx
export const CreateEmbeddedWalletWithPasskey: FC = () => {
  const { createEmbeddedWallet, createPasskey } = useEmbeddedWallet();
  const [result, setResult] = useState<string | undefined>(undefined);

  const onSubmitHandler = async (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    event.stopPropagation();

    const webAuthnAttestation = await createPasskey({});

    createEmbeddedWallet([ChainEnum.EVM, ChainEnum.Sol], { webAuthnAttestation })
      .then((result) => setResult(JSON.stringify(result, null, 2)))
      .catch((error) => setResult(JSON.stringify(error, null, 2)));
  };

  return (
    <form onSubmit={onSubmitHandler} className='create-session-method'>
      <p>Click to start the wallet creation flow</p>

      <button type='submit'>Create Wallet with passkey</button>

      {result}
    </form>
  );
};
```


# Headless Embedded Wallet Export



## Introduction

<Warning>
  This functionality is only available on V2.1.0-alpha.11 of the SDK and above.
</Warning>

You can use the SDK to headlessly reveal the private key or recovery phrase of an embedded wallet.

We will use the `revealWalletKey` method from the `useEmbeddedWallet` hook, which has the following signature:

```typescript
revealWalletKey: (options?: {
        type: "recoveryPhrase" | "privateKey";
        htmlContainerId?: string | undefined;
    } | undefined) => Promise<boolean>;
```

As you can see, the method takes an object with two properties:

*   `type`: The type of key you want to reveal. It can be either `recoveryPhrase` or `privateKey`.
*   `htmlContainerId`: The id of the HTML element where the key will be displayed. You must have a div present in your component with this id.

## Full code example

```tsx
import { FC, useState } from 'react';

import { useEmbeddedWallet, useIsLoggedIn } from '@dynamic-labs/sdk-react-core';

export const Reveal: FC = () => {
  const isLoggedIn = useIsLoggedIn();
  const { revealWalletKey } = useEmbeddedWallet();
  const [result, setResult] = useState<string | undefined>(undefined);

  const onSubmitHandler = async (
    selectedKey: 'recoveryPhrase' | 'privateKey',
  ) => {
    try {
      await revealWalletKey({
        htmlContainerId: 'reveal-example-container-id',
        type: selectedKey,
      });
    } catch (error) {
      setResult(JSON.stringify(error, null, 2));
    }
  };

  return (
    <div>
      {isLoggedIn ? 
        <div className='reveal-method'>
          <p>
            Choose which type of key you want to reveal from the embedded wallet
          </p>

          <button onClick={() => onSubmitHandler('privateKey')}>
            Reveal private key
          </button>
          <button onClick={() => onSubmitHandler('recoveryPhrase')}>
            Reveal recovery phrase
          </button>
          {result}
          <div id='reveal-example-container-id'></div>
        </div>
      : 
        <p>Please log in to continue.</p>
      }
    <div>
  );
};
```


# Headless MFA



## Introduction

This guide will show you how to add MFA to your app using your own custom UI. You can also refer to the general MFA guide [here](/authentication-methods/mfa) to learn more about this feature (note that this guide is for account level MFA, rather than transaction level MFA).

For a working example, see: [https://codesandbox.io/p/sandbox/kh8g8s](https://codesandbox.io/p/sandbox/kh8g8s)

## General Flow

1.  User logs in
2.  User is redirected to the MFA view
3.  User adds a device
4.  User is redirected to the OTP view
5.  User enters the OTP
6.  User is redirected to the backup codes view
7.  User acknowledges the backup codes

<Warning>
  The user must accept the backup codes before the MFA flow is complete.
</Warning>

## Step-by-Step Implementation

### Step 1: Import Necessary Modules and Components

Start by importing the required modules and components:

```typescript
import { EthereumWalletConnectors } from "@dynamic-labs/ethereum";

import { FC, useEffect, useState, ReactElement, useMemo, useRef } from "react";
import {
  DynamicContextProvider,
  DynamicWidget,
  useDynamicContext,
  useIsLoggedIn,
  useMfa,
  useSyncMfaFlow,
} from "@dynamic-labs/sdk-react-core";
import { MFADevice } from "@dynamic-labs/sdk-api-core";

import QRCodeUtil from "qrcode";
```

### Step 2: Define Types

Define the types for the MFA registration data.

```typescript
type MfaRegisterData = {
  uri: string;
  secret: string;
};
```

### Step 3: Create the `MfaView` Component

This component handles the main logic for MFA, including device management, QR code generation, OTP submission, and backup codes.

```typescript
export const MfaView = () => {
  const [userDevices, setUserDevices] = useState<MFADevice[]>([]);
  const [mfaRegisterData, setMfaRegisterData] = useState<MfaRegisterData>();
  const [currentView, setCurrentView] = useState<string>("devices");
  const [backupCodes, setBackupCodes] = useState<string[]>([]);
  const [error, setError] = useState<string>();

  const isLogged = useIsLoggedIn();
  const {
    addDevice,
    authDevice,
    getUserDevices,
    getRecoveryCodes,
    completeAcknowledgement,
  } = useMfa();

  const refreshUserDevices = async () => {
    const devices = await getUserDevices();
    setUserDevices(devices);
  };

  const { userWithMissingInfo, handleLogOut } = useDynamicContext();
  useEffect(() => {
    if (isLogged) {
      refreshUserDevices();
    }
  }, [isLogged]);

  useSyncMfaFlow({
    handler: async () => {
      if (userWithMissingInfo?.scope?.includes("requiresAdditionalAuth")) {
        getUserDevices().then(async (devices) => {
          if (devices.length === 0) {
            setError(undefined);
            const { uri, secret } = await addDevice();
            setMfaRegisterData({ secret, uri });
            setCurrentView("qr-code");
          } else {
            setError(undefined);
            setMfaRegisterData(undefined);
            setCurrentView("otp");
          }
        });
      } else {
        getRecoveryCodes().then(setBackupCodes);
        setCurrentView("backup-codes");
      }
    },
  });

  const onAddDevice = async () => {
    setError(undefined);
    const { uri, secret } = await addDevice();
    setMfaRegisterData({ secret, uri });
    setCurrentView("qr-code");
  };

  const onQRCodeContinue = async () => {
    setError(undefined);
    setMfaRegisterData(undefined);
    setCurrentView("otp");
  };

  const onOtpSubmit = async (code: string) => {
    try {
      await authDevice(code);
      getRecoveryCodes().then(setBackupCodes);
      setCurrentView("backup-codes");
      refreshUserDevices();
    } catch (e) {
      setError(e.message);
    }
  };

  return (
    <div className="headless-mfa">
      <DynamicWidget />
      <button onClick={handleLogOut}>log out</button>
      {error && <div className="headless-mfa__section error">{error}</div>}
      {currentView === "devices" && (
        <div className="headless-mfa__section">
          <p>
            <b>Devices</b>
          </p>
          <pre>{JSON.stringify(userDevices, null, 2)}</pre>
          <button onClick={() => onAddDevice()}>Add Device</button>
        </div>
      )}
      {currentView === "qr-code" && mfaRegisterData && (
        <QRCodeView data={mfaRegisterData} onContinue={onQRCodeContinue} />
      )}
      {currentView === "otp" && <OTPView onSubmit={onOtpSubmit} />}
      {currentView === "backup-codes" && (
        <BackupCodesView
          codes={backupCodes}
          onAccept={completeAcknowledgement}
        />
      )}
      <button
        onClick={async () => {
          const codes = await getRecoveryCodes(true);
          setBackupCodes(codes);
          setCurrentView("backup-codes");
        }}
      >
        Generate Recovery Codes
      </button>
    </div>
  );
};
```

**Explanation:**

*   **State Management:**

    *   `userDevices`: Stores the list of MFA devices associated with the user.
    *   `mfaRegisterData`: Stores the data required for MFA registration (QR code URI and secret).
    *   `currentView`: Tracks the current view (e.g., 'devices', 'qr-code', 'otp', 'backup-codes').
    *   `backupCodes`: Stores the backup codes for MFA.
    *   `error`: Stores any error messages.

*   **Hooks:**

    *   `useIsLoggedIn`: Checks if the user is logged in.
    *   `useMfa`: Provides functions for managing MFA (e.g., `addDevice`, `authDevice`, `getUserDevices`, `getRecoveryCodes`, `completeAcknowledgement`).
    *   `useDynamicContext`: Provides user context and logout function.
    *   `useEffect`: Refreshes user devices when the user logs in.
    *   `useSyncMfaFlow`: Synchronizes the MFA flow based on the user's authentication status.

*   **Functions:**

    *   `refreshUserDevices`: Fetches and sets the user's MFA devices.
    *   `onAddDevice`: Initiates the process of adding a new MFA device.
    *   `onQRCodeContinue`: Proceeds to the OTP view after displaying the QR code.
    *   `onOtpSubmit`: Authenticates the device using the provided OTP and fetches backup codes.

*   **Return Statement:**
    *   Renders the UI components based on the current view and state.

### Step 4: Create Supporting Components

Define the supporting components for displaying backup codes, QR code, and OTP input.

```typescript
const BackupCodesView = ({
  codes,
  onAccept,
}: {
  codes: string[];
  onAccept: () => void;
}) => (
  <>
    <p>Backup Codes</p>
    {codes.map((code) => (
      <p key={code}>{code}</p>
    ))}
    <button onClick={onAccept}>Accept</button>
  </>
);

const LogIn = () => (
  <>
    <p>User not logged in!</p>
    <DynamicWidget />
  </>
);

const QRCodeView = ({
  data,
  onContinue,
}: {
  data: MfaRegisterData;
  onContinue: () => void;
}) => {
  const canvasRef = useRef(null);

  useEffect(() => {
    if (!canvasRef.current) {
      return;
    }
    QRCodeUtil.toCanvas(canvasRef.current, data.uri, function (error: any) {
      if (error) console.error(error);
      console.log("success!");
    });
  });

  return (
    <>
      <div style={{ width: "320px", height: "320px" }}>
        <canvas id="canvas" ref={canvasRef}></canvas>
      </div>
      <p>Secret: {data.secret}</p>
      <button onClick={onContinue}>Continue</button>
    </>
  );
};

const OTPView = ({ onSubmit }: { onSubmit: (code: string) => void }) => (
  <form
    key="sms-form"
    onSubmit={(e) => {
      e.preventDefault();
      onSubmit(e.currentTarget.otp.value);
    }}
    className="headless-mfa__form"
  >
    <div className="headless-mfa__form__section">
      <label htmlFor="otp">OTP:</label>
      <input type="text" name="otp" placeholder="123456" />
    </div>
    <button type="submit">Submit</button>
  </form>
);
```

**Explanation:**

*   **BackupCodesView:**

    *   Displays the backup codes and provides an "Accept" button to acknowledge them.

*   **LogIn:**

    *   Displays a message indicating that the user is not logged in and shows the `DynamicWidget` for login.

*   **QRCodeView:**

    *   Displays the QR code for MFA setup and the secret key. Provides a "Continue" button to proceed to the OTP view.

*   **OTPView:**
    *   Provides a form for the user to enter the OTP. On form submission, it calls the `onSubmit` function with the entered OTP.

### Step 5: Create the Main `HeadlessMfaView` Component

This component checks if the user is logged in and displays the appropriate view.

```typescript
export const HeadlessMfaView: FC = () => {
  const { user, userWithMissingInfo } = useDynamicContext();

  return (
    <div className="headless-mfa">
      {user || userWithMissingInfo ? <MfaView /> : <LogIn />}
    </div>
  );
};

export default function App() {
  return (
    <div className="App">
      <DynamicContextProvider
        settings={{
          environmentId: "YOUR_ENV_ID",
          walletConnectors: [EthereumWalletConnectors],
        }}
      >
        <HeadlessMfaView />
      </DynamicContextProvider>
    </div>
  );
}
```

**Explanation:**

*   **HeadlessMfaView:**

    *   Checks if the user is logged in or has missing information that requires additional authentication.
    *   If the user is logged in, it renders the `MfaView` component.
    *   If the user is not logged in, it renders the `LogIn` component.

*   **App:**
    *   Your main app entry point.
    *   Make sure to set you environment id.

### Conclusion

By following this detailed breakdown, you should have a clear understanding of how the provided code implements a headless MFA solution using a custom UI. Each part of the code is explained, including the purpose of each component, hook, and function. This should help you understand the overall flow and how to customize it for your needs.


# Headless Overview



## Defining "headless"

While you can use the API to entirely decouple a frontend from Dynamic and still enable onboarding experiences, headless in this case refers to the ability to use the Dynamic SDK without our UI components.

This allows you to build your own UI components and still use the Dynamic SDK to manage the data and logic of your onboarding experience.

## Why go headless?

The Dynamic UI components handle a ton of edge cases, are actively maintained, and are designed to be easy to use while being completely flexible.

However, if for any reason these components are not fitting your use case, you might want to go headless.

## Getting started

You should still follow [the getting started guide](/quickstart) to add the SDK to your project.

Once your app is wrapped with the DynamicContextProvider, you can now start to use our hooks and context to build your own UI components.

This section outlines guides for each part of the onboarding experience, and how to build them headlessly. Specifically, they mimic building the same components as the Dynamic UI components, but with your own UI.

## Guides

*   [Headless Email Signup/Login](/headless/headless-email)

*   [Headless SMS Signup/Login](/headless/headless-sms)

*   [Headless Social Signup/Login](/headless/headless-social)

*   [Headless Embedded Wallets With OTP](/headless/headless-embedded-otp)

*   [Headless Embedded Wallets With Passkey](/headless/headless-embedded-passkey)

*   [Headless Export for Embedded Wallets](/headless/headless-embedded-reveal)


# Headless SMS signup



### Overview

Leveraging Dynamic's React package, this guide will help you create a sign in / sign up flow based on an sms login form.

We use [the `useConnectWithOtp` hook](/react-sdk/hooks/useconnectwithotp) to expose two methods: `connectWithSms` and `verifyOneTimePassword`.

The first method will send an SMS to the user's phone number, and the second will verify the OTP sent to the user's phone.

Once that's done, the user will be authenticated and you can access their information through the `useDynamicContext` hook!

### Dashboard Setup

#### SMS Signup/Login

The first thing you'll want to do is create a new Dynamic organization and setup Dashboard.

1.  Sign up at app.dynamic.xyz
2.  Fill in the modal to create your organization and project.
3.  Under the Developer menu, go to SDK & APIs and fetch the Environment ID. Keep this as we'll need this key in a little bit.

Next we'll want to enable SMS signup/login:

<iframe src="https://scribehow.com/embed/Configure_sms_signup__bMYwu7t8R26dcZaGSEwfnA?skipIntro=true" width="100%" height="640" allowfullscreen frameborder="0" />

For more information on how to configure SMS signup/login, check out our [SMS Signup/Login guide](/authentication-methods/email-social-sms).

### Configuring the SDK

Let's start by installing our packages. Follow our [Quickstart guide](/quickstart) for a detailed walkthrough.

When you're done, we'll update our App.js to include our environment ID in the setup and import the `DynamicContextProvider` and `EthereumWalletConnectors`.

You app.js file should look like this (note that we are only importing the EthereumWalletConnectors for now):

```jsx
import { DynamicContextProvider } from '@dynamic-labs/sdk-react-core';
import { EthereumWalletConnectors } from "@dynamic-labs/ethereum";

// Placeholder for our SMS signup/login form component
import ConnectWithSmsView from './ConnectWithSmsView';


function App() {
  return (
    <div className="App">
        <DynamicContextProvider
          settings={{
            environmentId: "YOUR_ENVIRONMENT_ID_GOES_HERE",
            walletConnectors: [ EthereumWalletConnectors ],

          }}
        >
        <ConnectWithSmsView />
        </DynamicContextProvider>
    </div>
  );
}

export default App;
```

### SMS Signup/Login UI

Now that we have our environment, we can create our SMS signup/login form. We use the [`useConnectWithOtp` hook](/react-sdk/hooks/useconnectwithotp) to handle the SMS setup.

```tsx
import { FC, FormEventHandler } from 'react';
import { useConnectWithOtp, useDynamicContext } from '@dynamic-labs/sdk-react-core';

const ConnectWithSmsView: FC = () => {
  const { user } = useDynamicContext()

  const { connectWithSms, verifyOneTimePassword } = useConnectWithOtp();

  const onSubmitSmsHandler: FormEventHandler<HTMLFormElement> = async (
    event,
  ) => {
    event.preventDefault();

    const phone = {
      dialCode: event.currentTarget.dialCode.value,
      iso2: event.currentTarget.iso2.value,
      phone: event.currentTarget.phone.value,
    };

    await connectWithSms(phone);
  };

  const onSubmitOtpHandler: FormEventHandler<HTMLFormElement> = async (
    event,
  ) => {
    event.preventDefault();

    const otp = event.currentTarget.otp.value;

    await verifyOneTimePassword(otp);
  };

  return (
    <div>
      <form key='sms-form' onSubmit={onSubmitSmsHandler}>
        <label htmlFor='iso2'>Country ISO Code:</label>
        <input type='text' name='iso2' placeholder='US' />

        <label htmlFor='dialCode'>Country Dial Code:</label>
        <input type='text' name='dialCode' placeholder='1' />

        <label htmlFor='phone'>Phone Number (without dial code):</label>
        <input type='text' name='phone' placeholder='5555555555' />
      
        <button type='submit'>Submit</button>
      </form>

      <form key='otp-form' onSubmit={onSubmitOtpHandler}>
        <input type='text' name='otp' placeholder='OTP' />
        <button type='submit'>Submit</button>
      </form>

      {!!user && (
        <p>Authenticated user:</p>
        <pre>
          {JSON.stringify(user, null, 2)}
        </pre>
      )}
    </div>
  )
}

export default ConnectWithSmsView
```


# Headless Social Login



This section shows you how to enable and implement social signup/login headlessly, using the Dynamic React SDK but without any of the UI components.

## Prerequisites

You must have at least one social provider enabled in the dashboard. For instructions on how to do this, check out [the configuring social providers section](/social-providers/overview).

## Implementation

You'll use [the `useSocialAccounts` hook](/react-sdk/hooks/usesocialaccounts) available from the `sdk-react-core` package:

<CodeGroup>
  ```npm
  npm i @dynamic-labs/sdk-react-core
  ```

  ```yarn
  yarn add @dynamic-labs/sdk-react-core
  ```
</CodeGroup>

This hook comes with a method called `signInWithSocialAccount` that you can utilize:

```tsx
import { FC } from 'react';
import {
  DynamicWidget,
  useDynamicContext,
  useSocialAccounts,
} from '@dynamic-labs/sdk-react-core';
import { ProviderEnum } from '@dynamic-labs/types';
import { FarcasterIcon, GoogleIcon, TwitterIcon } from '@dynamic-labs/iconic';

const SocialSignIn = () => {
  const { error, isProcessing, signInWithSocialAccount } = useSocialAccounts();

  return (
    <div className='headless-social-signin'>
      <div className='headless-social-signin__container'>
        <p>Log in or sign up</p>

        <button onClick={() => signInWithSocialAccount(ProviderEnum.Farcaster)}>
          <FarcasterIcon />
          Sign in with Farcaster
        </button>
        <button onClick={() => signInWithSocialAccount(ProviderEnum.Google)}>
          <GoogleIcon />
          Sign in with Google
        </button>
        <button onClick={() => signInWithSocialAccount(ProviderEnum.Twitter)}>
          <TwitterIcon />
          Sign in with Twitter
        </button>
        {isProcessing && <span className='processing'>Processing...</span>}
        {error && <span className='error'>{error.message}</span>}
      </div>
    </div>
  );
};

const LoggedInUser = () => {
  const { user } = useDynamicContext();

  return (
    <>
      <DynamicWidget />
      <p>user: {user?.email}</p>
    </>
  );
};

export const HeadlessSocialSignInView: FC = () => {
  const { user } = useDynamicContext();

  return (
    <div style={{ overflowY: 'scroll' }}>
      {user ? <LoggedInUser /> : <SocialSignIn />}
    </div>
  );
};
```


# Headless User Profile



## Introduction

This guide will help you to create a completely headless user profile including everything you see in the regular [Dynamic Widget UI component](/react-sdk/components/dynamicwidget)/[Dynamic Embedded Widget UI component](/react-sdk/components/dynamicembeddedwidget). If you'd like to see the Dynamic Widget/Embedded Widget in action, head over to [the live demo](https://demo.dynamic.xyz/).

## Prerequisites

Like with all this series of headless guides, "headless" is defined a way to use the Dynamic SDK without the need for Dynamic UI components (i.e. DynamicWidget, DynamicUserProfile).

You still need to add the SDK and set up the Dynamic Context Provider (complete [the quickstart](/quickstart) if you haven't done so already, or refer to [an example app](/example-apps))

### Setup

#### Show the user's profile based on whether they are logged in or not

How: Check if the user is logged in or not

Hook/Component: [useIsLoggedIn](/react-sdk/hooks/useisloggedin)

Notes: We start here assuming you have signup/login implemented already

```
import { useIsLoggedIn } from "@dynamic-labs/sdk-react-core";

const isLoggedIn = useIsLoggedIn();

return <>{isLoggedIn ? <Profile /> : </Login>}</>
```

### Profile Info

#### Show user profile information

How: Fetch info from user object on useDynamicContext

Hook/Component: [useDynamicContext](/react-sdk/hooks/usedynamiccontext)

Notes: The format of the user can be found here: [userProfile](/react-sdk/objects/userprofile)

```jsx
import { useDynamicContext } from "@dynamic-labs/sdk-react-core";

const { user } = useDynamicContext();

return (
  <div className="user-details">
    {user?.firstName && <p>First name: {user.firstName} </p>}
    {user?.email && <p>E-Mail: {user.email} </p>}
    {user?.alias && <p>Alias: {user.alias} </p>}
    {user?.lastName && <p>Last name: {user.lastName} </p>}
    {user?.jobTitle && <p>Job: {user.jobTitle} </p>}
    {user?.phoneNumber && <p>Phone: {user.phoneNumber} </p>}
    {user?.tShirtSize && <p>Tshirt size: {user.tShirtSize} </p>}
    {user?.team && <p>Team: {user.team} </p>}
    {user?.country && <p>Country: {user.country} </p>}
    {user?.username && <p>Username: {user.username} </p>}
  </div>
);
```

#### Allow user to update their profile information

How: useUserUpdateRequest hook

Hook/Component: [useUserUpdateRequest](/react-sdk/hooks/useuserupdaterequest)

Notes: We include the validation for email updates here

```
import { useUserUpdateRequest, useOtpVerificationRequest } from "@dynamic-labs/sdk-react-core";

const { verifyOtp } = useOtpVerificationRequest();
const { updateUser } = useUserUpdateRequest();

const [showUpdateForm, setShowUpdateForm] = useState(false);
const [showVerifyForm, setShowVerifyEmailForm] = useState(false);

const updateUserInfoFormSubmit = async (e) => {
   e.preventDefault();
   try {
     setLoading(true);
     // Call the updateUser function with the new values entered by the user
     const { isEmailVerificationRequired } = await updateUser({
       firstName: e.target[0].value,
       email: e.target[1].value,
     });
     // If email verification is required, show the email verification form
     if (isEmailVerificationRequired) {
       setShowVerifyEmailForm(true);
     }
   } catch (e) {
     console.log("Error", e);
   } finally {
     setLoading(false);
     setShowUpdateForm(false);
   }
 };

   // Handler for the email verification form submission
const onVerifyEmailFormSubmit = async (e) => {
    e.preventDefault();
    try {
      setLoading(true);
      const verificationToken = e.target[0].value;
      // Call the verifyEmail function with the entered verification token
      await verifyOtp(verificationToken);
    } catch (e) {
      console.log("Error", e);
    } finally {
      setLoading(false);
      // Hide the email verification form after the process is completed
      setShowVerifyEmailForm(false);
    }
    return false;
};


return (
    <div>
          {/* Render the profile update form */}
          {showUpdateForm && (
            <div>
              <form onSubmit={onProfileFormSubmit} className="form">
                <div className="form__row">
                  <label className="label" htmlFor="firstName">
                    First-Name
                  </label>
                  <input
                    id="firstName"
                    className="form__input"
                    defaultValue={user.firstName}
                    disabled={loading || showVerifyEmailForm}
                  />
                </div>
                <div className="form__row">
                  <label className="label" htmlFor="email">
                    E-Mail
                  </label>
                  <input
                    type="email"
                    id="email"
                    className="form__input"
                    defaultValue={user.email}
                    disabled={loading || showVerifyEmailForm}
                  />
                </div>
                <button
                  disabled={loading || showVerifyEmailForm}
                  className="form__button"
                  type="submit"
                >
                  Save
                </button>
              </form>
            </div>
          )}

         {/* Render the email verification form if needed */}
          {showVerifyEmailForm && (
            <form onSubmit={onVerifyEmailFormSubmit} className="form">
              <h6>Verify Email</h6>
              <div className="form__row">
                <label htmlFor="verificationToken">Verification Token</label>
                <input
                  disabled={loading}
                  pattern="^\d{6}$"
                  name="verificationToken"
                />
              </div>
              <button disabled={loading} className="form__button" type="submit">
                Send
              </button>
            </form>
          )}
    <div>
)
```

### Socials

#### Show users linked social accounts, allow linking/unlinking

How: useSocialAccounts hook from sdk-react-core

Hook/Component: [useSocialAccounts](/react-sdk/hooks/usesocialaccounts)

Notes: None

```jsx
import { useSocialAccounts } from "@dynamic-labs/sdk-react-core";
import { SocialIcon } from '@dynamic-labs/iconic';

const Avatar = ({ avatarUrl }) => {
  return (
    <div className="avatar">
      <img src={avatarUrl} alt="avatar" />
    </div>
  );
};


const Icon = ({ provider }) => {
  return (
    <div className="icon-container">
    <SocialIcon name={provider} />
    </div>
  );
};

const UserProfileSocialAccount = ({ provider }) => {
  const {
    linkSocialAccount,
    unlinkSocialAccount,
    isProcessing,
    isLinked,
    getLinkedAccountInformation,
  } = useSocialAccounts();

  const isProviderLinked = isLinked(provider);
  const connectedAccountInfo = getLinkedAccountInformation(provider);

  return (
    <Flex>
      <div className="icon">
        {isProviderLinked ? (
          <Avatar avatarUrl={connectedAccountInfo?.avatar} />
        ) : (
          <Icon provider={provider} />
        )}
      </div>
      <div className="label">
        <p>{connectedAccountInfo?.publicIdentifier ?? provider}</p>
      </div>
      {isProviderLinked ? (
        <button
          onClick={() => unlinkSocialAccount(provider)}
          loading={isProcessing}
        >
          Disconnect
        </button>
      ) : (
        <button
          onClick={() => linkSocialAccount(provider)}
          loading={isProcessing}
        >
          Connect
        </button>
      )}
    </Flex>
  );
};

const Socials = () => {
  const providers = [
    "discord",
    "facebook",
    "github",
    "google",
    "instagram",
    "twitch",
    "twitter",
  ];

  return (
    <Flex direction="column" align="stretch">
      {providers.map((provider) => (
        <UserProfileSocialAccount provider={provider} />
      ))}
    </Flex>
  );
};
```


# Headless Wallet Management



This guide covers how to implement the wallet aspect of the Dynamic Widget UI Component but using only our SDK hooks and your own UI implementation.

## Prerequisites

Like with all this series of headless guides, "headless" is defined a way to use the Dynamic SDK without the need for Dynamic UI components (i.e. DynamicWidget, DynamicUserProfile).

You still need to add the SDK and set up the Dynamic Context Provider (complete [the quickstart](/quickstart) if you haven't done so already, or refer to [an example app](/example-apps))

## Signup/Login

### Fetch available wallets

You can find the list of available wallets in the `walletOptions` prop returned by the [useWalletOptions hook](/react-sdk/hooks/usewalletoptions).

<Tip>
  When browsing wallets in the Dynamic Widget, you might see labels beside them like "Last Used", "Multichain" or "Recommended".

  Last used comes from the "dynamic\_last\_used\_wallet" value in localstorage.
  "Multichain" comes from the `chainGroup` node in each wallet (Remember to also add [the WalletConnectors](/chains/enabling-chains#enabling-a-chain-network) for each chain).
  "Recommendeded" from [the Recommended Wallets feature](/wallets/advanced-wallets/recommend-wallets).
</Tip>

### Display a wallet icon

Use the `@dynamic-labs/wallet-book` library to display a wallet icon using the exported `WalletIcon` component. This component takes a `walletKey` prop, which is the key of the wallet you want to display.

```tsx
import { WalletIcon } from '@dynamic-labs/wallet-book';

const WalletIconComponent = () => {
  return <WalletIcon walletKey="metamask" />;
};
```

### Connect to a wallet

[useWalletOptions](react-sdk/hooks/usewalletoptions) allows you to prompt the user to connect using a specific wallet (by passing in the key).

```
import { useWalletOptions } from "@dynamic-labs/sdk-react-core";

// component setup etc.

const { selectWalletOption } = useWalletOptions();

const connectWithWallet = async (walletKey) => {
  return await selectWalletOption(walletKey)
}
```

### Onramp

#### Show onramp button and open flow

How: Regular button attached to useFunding hook

Hook/Component: [useFunding](/react-sdk/hooks/usefunding)

Notes: Ensure Banxa is enabled in the Dynamic dashboard

```jsx
import { useFunding } from "@dynamic-labs/sdk-react-core";

const Onramp = () => {
  const { enabled, openFunding } = useFunding();

  return (
    <div>
      {enabled && (
        <button className="onramp-button" onClick={openFunding}>
          Onramp
        </button>
      )}
    </div>
  );
};
```

### Networks

#### Display current and available networks

How: Use evmNetworks and getNetwork to fetch, use supportsNetworkSwitching and switchNetwork to switch

Hook/Component: [useDynamicContext](/react-sdk/hooks/usedynamiccontext) & [getNetwork](/react-sdk/utilities/getnetwork)

Notes: This example is for Evm networks only

```jsx
import { useDynamicContext, getNetwork } from "@dynamic-labs/sdk-react-core";

const CustomNetworkPicker = () => {
  const [currentNetwork, setCurrentNetwork] = useState(null);
  const { primaryWallet } = useDynamicContext();

  const handleNetworkChange = async (event) => {
    const chainId = parseInt(event.target.value);

    if (primaryWallet?.connector?.supportsNetworkSwitching()) {
      try {
        return await primaryWallet?.connector?.switchNetwork({
          networkChainId: chainId,
        });
      } catch (error) {
        console.error("Error switching network", error);
      }
    }
  };

  useEffect(() => {
    if (!currentNetwork)
      getNetwork(primaryWallet?.connector).then((network) => {
        setCurrentNetwork(network);
      });
  }, [primaryWallet]);

  return (
    <>
      {currentNetwork && (
        <Select defaultValue={currentNetwork} onChange={handleNetworkChange}>
          {primaryWallet?.connector?.evmNetworks?.map((network) => (
            <option key={network.chainId} value={network.chainId}>
              {network.name}
            </option>
          ))}
        </Select>
      )}
    </>
  );
};
```

### Primary Wallet

Primary wallet is the wallet that is defined as the active one of all your connected wallets. All interactions, such as signing or creating transactions, will be made with this wallet. In addition, when leveraging our multiasset functionality, this is the wallet that will be display all available tokens.

#### Display primary wallet address

How: Fetch the primary wallet info from context

Hook/Component: [useDynamicContext](/react-sdk/hooks/usedynamiccontext)

Notes: There can be a moment after logging in when it’s not populated yet

```jsx
import { useDynamicContext } from "@dynamic-labs/sdk-react-core";

const { primaryWallet } = useDynamicContext();

const copyWalletAddress = () => {
  navigator.clipboard.writeText(primaryWallet?.address);
};

return (
  <button onCLick={() => copyWalletAddress()}>
    <p>...{primaryWallet?.address?.slice(-6)}</p>
  </button>
);
```

#### Display primary wallet icon

How: Utilize the Walletbook library

Hook/Component: WalletIcon (not publicly documented yet)

Notes: Requires the primaryWallet.connector object.

```jsx
import { primaryWallet } from "@dynamic-labs/sdk-react-core";
import { WalletIcon } from "@dynamic-labs/wallet-book";

const WalletIconWrapped = ({ connector }) => {

  return (
    <div className="wallet-icon-container">
      <WalletIcon walletKey={primaryWallet?.connector?.key} />
    </div>
  );
};
```

#### Show balance of primary wallet

How: getBalance method from useDynamicContext wallet connector

Hook/Component: [https://docs.dynamic.xyz/react-sdk/hooks/usedynamiccontext](https://docs.dynamic.xyz/react-sdk/hooks/usedynamiccontext)

Notes: None

```jsx
import { useDynamicContext } from "@dynamic-labs/sdk-react-core";

const { primaryWallet } = useDynamicContext();

const [balance, setBalance] = useState(null);

useEffect(() => {
  const fetchBalance = async () => {
    if (primaryWallet) {
      const value = await primaryWallet.getBalance();
      setBalance(value);
    }
  };
  fetchBalance();
}, [primaryWallet]);

return <p>{balance}</p>;
```

#### Show all tokens and balances

How: Mapping return from usetokenbalances hook

Hook/Component: [https://docs.dynamic.xyz/react-sdk/hooks/usetokenbalances](https://docs.dynamic.xyz/react-sdk/hooks/usetokenbalances)

Notes: Multi-asset is only supported on the following networks - Ethereum, Optimism, Polygon, Arbitrum, and Base.

```jsx
import { useTokenBalances } from "@dynamic-labs/sdk-react-core";

const TokenBalances = () => {
  const tokenBalances = useTokenBalances();

  return (
    <div>
      {tokenBalances.map((token) => (
        <div key={token.address}>
          <p>{token.symbol}</p>
          <p>{token.balance}</p>
        </div>
      ))}
    </div>
  );
};
```

#### Detect Wallet Locking

The wallet object has a field called `connected` which indicates whether the wallet is locked or not, and you can use this to trigger your own workflow to warn the user.

### Multi-Wallet

#### Show all linked wallets and allow unlinking

How: Mapping return from useUserWallets hook and handleUnlinkWallet from useDynamicContext

Hook/Component: [https://docs.dynamic.xyz/react-sdk/hooks/useuserwallets](https://docs.dynamic.xyz/react-sdk/hooks/useuserwallets) & [https://docs.dynamic.xyz/react-sdk/hooks/usedynamiccontext](https://docs.dynamic.xyz/react-sdk/hooks/usedynamiccontext)

Notes: Doesn’t yet show if a wallet is the primary wallet. Unlinking is also handled here.

```jsx
import {
  useDynamicContext,
  useUserWallets,
} from "@dynamic-labs/sdk-react-core";

import { Tooltip } from "react-tooltip";

const LinkedWallets = () => {
  const { handleUnlinkWallet } = useDynamicContext();

  const userWallets = useUserWallets();

  return (
    <>
      {userWallets.length > 0 && (
        <div className="profile-wallets-container">
          <h2>Connected Wallets</h2>
          {userWallets.length < 2 && <Tooltip id="unlink-tooltip" />}
          <div className="profile-wallets-inner-container">
            {userWallets.map((wallet) => (
              <Flex key={wallet.address}>
                <p>{wallet.address}</p>
                <Spacer />
                <a
                  data-tooltip-id="unlink-tooltip"
                  data-tooltip-content="Can't unlink your only wallet!"
                >
                  <Button
                    size="xs"
                    onClick={() => handleUnlinkWallet(wallet.id)}
                    disabled={userWallets.length < 2}
                  >
                    Unlink
                  </Button>
                </a>
              </Flex>
            ))}
          </div>
        </div>
      )}
    </>
  );
};
```

#### Detect which wallet is primary

How: You can grab it directly from useDynamicContext

Hook/Component: [useDynamicContext](/react-sdk/hooks/usedynamiccontext)

```jsx
import { useDynamicContext, useUserWallets } from "@dynamic-labs/sdk-react-core";

const { primaryWallet } = useDynamicContext();
console.log(primaryWallet);

```

#### Allow user to link a new wallet

How: Pop up our linking modal using a hook (setShowLinkNewWalletModal)

Hook/Component: [useDynamicModals](/react-sdk/hooks/usedynamicmodals)

Notes:

*   You will need to also use the DynamicMultiWalletPromptsWidget component

```jsx
import {
  useDynamicModals,
  DynamicMultiWalletPromptsWidget,
} from "@dynamic-labs/sdk-react-core";

const LinkWallet = ({ text }) => {
  const { setShowLinkNewWalletModal } = useDynamicModals();

  return (
    <>
      <div className="link-wallet-container">
        <Button
          className="profile-button"
          onClick={() => setShowLinkNewWalletModal(true)}
        >
          {text}
        </Button>
      </div>
      <DynamicMultiWalletPromptsWidget />
    </>
  );
};
```

#### Switch the primary wallet to another

How: useSwitchWallets hooks

Hook/Component: [https://docs.dynamic.xyz/react-sdk/hooks/useswitchwallet#useswitchwallet](https://docs.dynamic.xyz/react-sdk/hooks/useswitchwallet#useswitchwallet)

Notes: None

```jsx
import React from "react";
import { useSwitchWallet, useUserWallets } from "@dynamic-labs/sdk-react-core";

const WalletSwitcher = () => {
  const switchWallet = useSwitchWallet();
  const userWallets = useUserWallets();

  return (
    <div>
      {userWallets.map((wallet) => (
        <button key={wallet.id} onClick={() => switchWallet(wallet.id)}>
          {wallet.address}
        </button>
      ))}
    </div>
  );
};
```

## Wallet Interactions

To learn how to send balance, sign messages etc., please check out [the Wallet Interaction guides](/wallets/using-wallets/interacting-with-wallets) where we split into Read actions and Write actions, and we provide per chain examples for common functionality.


# Welcome

Welcome to Dynamic! We're so excited that you're here!

<video autoPlay loop muted playsInline src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/video.webm" />

### Versioning

This documentation is for V3 of the SDK. Therefore we assume you have [the latest version of V3](https://www.npmjs.com/package/@dynamic-labs/sdk-react-core?activeTab=versions) installed. If you are using V2, please refer to the [V2 documentation](https://v2.docs.dynamic.xyz).

Please also make sure you have all @dynamic/labs packages on exactly the same version to avoid any issues, and use [Dynamic Doctor](/troubleshooting/dynamic-doctor) for easy debugging. Happy building!

### What is Dynamic?

Dynamic offers smart and beautiful login flows for crypto-native users, simple
onboarding flows for everyone else, and powerful developer tools that go beyond
authentication.

<CardGroup cols={2}>
  <Card title="Overview video" icon="circle-play" color="#ea5a0c" href="https://www.youtube.com/watch?v=rBDkmzfaZnc">
    Watch a product walkthrough
  </Card>

  <Card title="Interactive demo" icon="bullseye-pointer" color="#0285c7" href="https://demo.dynamic.xyz">
    Play around with the SDK
  </Card>

  <Card title="Quickstart" icon="gauge-max" color="#dc2626" href="/quickstart">
    Get up and running in minutes
  </Card>

  <Card title="Join community" icon="messages" color="#16a34a" href="https://www.dynamic.xyz/join-slack">
    Be part of the Dynamic Slack group
  </Card>
</CardGroup>

#### Common Docs

Some of the common next steps after completing the quickstart etc. above are:

*   [Configuring the Signup page](/design-customizations/signup-login)
*   [Start interacting with wallets](/wallets/using-wallets/interacting-with-wallets)
*   [Customizing the look and feel of the UI](/design-customizations)
*   [Adding custom EVM Networks](/chains/evmNetwork)
*   [Collecting custom information](/custom-fields/overview)
*   [Adding smart wallet support](/smart-wallets/add-smart-wallets)

#### Support & Feedback

You can reach out to us at any time for questions, issues, concerns, product
ideas, or random thoughts. Here are some ways to do so:

<CardGroup>
  <Card title="Slack" icon="slack" href="https://www.dynamic.xyz/slack">
    Join our Slack community
  </Card>

  <Card title="Twitter" icon="x-twitter" href="https://twitter.com/dynamic%5Fxyz">
    Send us a tweet or DM us any time.
  </Card>

  <Card title="Email" icon="envelope" href="mailto: hello@dynamic.xyz">
    Email us at [hello@dynamic.xyz](mailto:hello@dynamic.xyz)
  </Card>

  <Card title="Founders" icon="comments" href="mailto:founders@dynamic.xyz">
    Bug our founders at any point at [founders@dynamic.xyz](mailto:founders@dynamic.xyz) (tell us anything that's on your mind - the good and the bad).
  </Card>
</CardGroup>


# Migrating to Dynamic



Migration from one auth and embedded wallet provider to another can seem daunting. You have users, they might have wallets, and a subset of them will have assets in their wallets, making it a fun adventure of a migration. But have no fear! We can help. Below, we put together a guide on migration strategies you can think through depending on your use case. At the end of the guide you'll also find some helpful tips and techniques to ensure the migration all goes smoothly.

<Tip>
  When in doubt, talk to us. Migrations can get complex, and we'd love to help
  guide you through them. You can [set up a call with us
  here](https://www.dynamic.xyz/talk-to-us)
</Tip>

<Info>
  If you don't want to migrate yet, but want to take advantage of Dynamic on top of your existing provider, you can also use [our Third Party Authentication feature](/authentication-methods/third-party-auth).
</Info>

We put together a simple flow chart to help you identify the right migration strategy:

<Frame>
  <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/migration-guide.png" />
</Frame>

## Migration Path A: You only have a front-end "connect wallet" flow

### Common scenarios

You built your own front end wallet connector or use Rainbowkit/ConnectKit. There is no SIWE, no issuing JWTs, and mostly just a front end.

### Recommended approach

You pretty much just swap out the modals. If you use wagmi, add our [wagmi connector](/react-sdk/using-wagmi#-install-wagmi) and follow our [rainbowkit guide](/migrations/tutorials/rainbowkit), but in general, no migration needed. After the initial update to the Dynamic modal, you can stay on our [connect-only mode](/wallets/advanced-wallets/connect) and not store any user info with Dynamic, or alternatively start adding more sophisticated features.

## Migration Path B: You store wallet info somewhere and a wallet is a user

### Common scenarios

You built your own front end wallet connector or use Rainbowkit/ConnectKit, and in addition implemented a SIWE on your own, authenticating users and creating sessions for them.

### Recommended approach

For front end changes, you can follow path A above. In addition, you can use our [create wallet](/api-reference/endpoints/wallets/createWallet) endpoint to generate users and add wallet records to them. When they first log in, they will verify wallet ownership via SIWE (we provide that and return a JWT - you don't need to do this), and you're done.

## Migration Path C: You have users, but no embedded wallets

### Common scenarios

You use Auth0, Firebase or another web2 user management system, or alternatively created something on your own. You store your users' emails and basic info.

### Recommended approach

You'll need to import your users into Dynamic

1.  [Bulk upsert your users into Dynamic](/api-reference/endpoints/users/bulkCreateUser), including their info capture/kyc information and their data
2.  For any profile information that Dynamic does not yet explicitly support, you can add these in the user.metadata, which is a key-value object
3.  When they log in, we verify their emails

And that's it. You're done. When your users log in next time around, we will verify their email addresses/social login information independently as part of the auth flow, and issue a JWT once you have successfully logged in.

## Migration Path D: You have users, and they have embedded EOA wallets

### Common scenarios

You use an embedded wallets provider, or have built a simple KMS-based key management system on your own

### Recommended approach

**There are really two options here:**

1.  Keep current users in their existing embedded wallets and generate Dynamic wallets for new users
2.  Migrate existing wallet users to Dynamic

**Keep current users:**

1.  Switch to Dynamic auth
2.  When user logs in with email, check if they have an existing account:

*   If so, redirect them to old login flow
*   If they don't, create a Dynamic-powered embedded wallet for them

```typescript
import { useEmbeddedWallet } from "@dynamic-labs/sdk-react-core";

// component declaration and all other logic you might need

const { createEmbeddedWallet } = useEmbeddedWallet();

const onCreateWalletHandler = async () => {
  try {
    await createEmbeddedWallet();
  } catch (e) {
    console.error(e);
  }
};
```

**Migrate users:**

1.  You'll need to import your users into Dynamic (the previous section)
2.  When a user logs in, check if they have a wallet in the previous system
3.  Check if wallet was ever used/is empty
4.  If it isn't, prompt user to transfer funds to new Dynamic wallet (this can feel like an account upgrade flow for the users)
5.  Next time that the user logs in, they go to their Dynamic wallet

## Migration Path E: You have users, and they have AA wallets

### Considerations

1.  Your users have an EOA embedded wallet, but it only serves as a signer for an AA layer (safe, zerodev etc).
2.  Hence, you'll want to add the Dynamic-powered embedded wallet as a second signer to the AA layer, or alternatively swap signers from the old embedded wallet one to the Dynamic one

**Main activities**
You'll want to have the end user add their new Dynamic wallet as a second signer to the smart contract wallet, or alternatively have them replace the current signer with a second signer

**Steps**

<Tip>
  This is non-trivial, and we recommend that we chat before starting this
  process. [You can book time here](https://www.dynamic.xyz/talk-to-us)
</Tip>

## Extra Migration Techniques/Tips

### Asset Transfers

If your users have assets in their existing embedded wallets, then you'll want to ensure they move those over to their new Dynamic-powered embedded wallets. To do this, you'll want to build a migration flow for your users either using your own UI to prompt asset transfer (recommended), or by sending them to a tool such as [BulkSender](https://bulksender.app/).

### Webhooks

As you import your users into Dynamic, you may want to notify other systems and take further action - webhooks allow you to do exactly this. We have events such as user.created, user.updated, wallet.created and many more (find the full list [here](/developer-dashboard/eventTypes)).

Setting them up is as simple as providing a URL to post to [in the Dynamic Dashboard](https://app.dynamic.xyz/dashboard/developer/webhooks) and selecting which events you'd like to subscribe to (there's a full guide [here](/developer-dashboard/webhooks)).


# Migrating Users via CSV



Dynamic.xyz offers a versatile platform for managing users and their associated data. If you're transitioning from another system, dynamic.xyz's API simplifies the process of migrating these users. The API is flexible and does not require any specific field to create a user, you only need at least one field. This guide will demonstrate how to migrate users from a data source, such as a CSV file, into dynamic.xyz's user management system using TypeScript.

### Step 1: Get an API Token

You'll first need to obtain an API token from the dynamic.xyz dashboard. Follow the instructions provided in the [API reference](/api-reference/overview#get-an-api-token-from-dashboard) to get your token.

### Step 2: Prepare Your Data Source

Prepare a data source, such as a CSV file, with your users' data. In this guide, we'll use a CSV file as an example, but remember that you can use any data source that you can read programmatically. The file needs to contain at least one field for each user.

### Step 3: Write a Script to Send Requests

You can use a script to read from your data source and send POST requests to the dynamic.xyz create user API endpoint. Here's an example script in TypeScript using `axios` and `csv-parser`:

```typescript
import axios from "axios";
import fs from "fs";
import csv from "csv-parser";

// Replace with your actual API token and environment ID
const API_TOKEN = "YOUR_API_TOKEN";
const ENVIRONMENT_ID = "<<sandboxEnvironmentId>>";

// URL for the API endpoint
const url = `https://app.dynamic.xyz/api/v0/environments/${ENVIRONMENT_ID}/users`;

// Set up headers for the request
const headers = {
  "Content-Type": "application/json",
  Authorization: `Bearer ${API_TOKEN}`,
};

// Open the CSV file and read the data
fs.createReadStream("users.csv")
  .pipe(csv())
  .on("data", async (row) => {
    // Prepare the user data for the request
    const user_data = {
      field: row["field"], // replace 'field' with the actual field name from your CSV
    };

    // Send the POST request to the API
    try {
      const response = await axios.post(url, user_data, { headers });
      console.log(
        `User created successfully: ${JSON.stringify(response.data)}`
      );
    } catch (error) {
      console.error(`Failed to create user. Error: ${error}`);
    }
  });
```

In the above TypeScript code, replace `'users.csv'` with the path to your actual data source, and replace `'YOUR_API_TOKEN'` and `'YOUR_ENVIRONMENT_ID'` with your actual API token and environment ID. Also, replace `'field'` with the name of the actual field you're using from your data source.

Please note that this is a basic script and does not handle errors robustly. In a production environment, you'd want to add error handling code to deal with potential issues such as network errors, API rate limits, and incorrect data in your data source.

Remember to install the necessary libraries if you haven't already done so:

```shell
npm install axios csv-parser
```

For more information on the user creation API endpoint, refer to the [API reference](/api-reference/endpoints/users/createUser).


# Migration from RainbowKit



Rainbowkit is a popular wallet adapter library built on Wagmi. That said, it has several limitations in terms of flexibility, customizability etc. As your company scales, it makes sense to explore transitioning to Dynamic for wallet auth. In this guide, we'll cover how you can quickly swap out Rainbowkit for Dynamic.

### A few notes

1.  Rainbowkit uses wagmi for all functions. Dynamic can leverage wagmi as well, requiring a quick connector to coordinate between the libraries.
2.  Rainbowkit requires that you manage your own authentication. Dynamic, on the other hand, comes with auth built in and will return a JWT if you select the connect and auth option.

### Overview

To start, we will assume your current setup looks similar to this:

```jsx
const App = () => {
  return (
    <WagmiConfig client={wagmiClient}>
      <RainbowKitProvider chains={chains}>
        <YourRoutes />
        <ConnectButton />
      </RainbowKitProvider>
    </WagmiConfig>
  );
};
```

In this setup, you have 3 dependencies:

1.  Ethers.js
2.  Wagmi
3.  Rainbowkit

### Step 1: Set up the Dynamic SDK

```bash Shell
npm install @dynamic-labs/sdk-react-core
```

> 📘 Note: Dynamic comes with ethers.js as part of its package, meaning you can remove any independent ethers.js installation.

You can read the full Dynamic set up guide [here](/quickstart).

### Step 2: Set up the Dynamic Wagmi connector

```bash Shell
npm install @dynamic-labs/wagmi-connector
```

You can read the full wagmi guide [here](/react-sdk/using-wagmi).

### Step 3: Configure Dynamic instead of Rainbowkit

#### Replace `RainbowKitProvider` with `DynamicContextProvider`

> 📘 Note: you'll need to grab a Dynamic environment ID from your [developer dashboard](https://app.dynamic.xyz)

First, we will swap out the Rainbowkit provider with the Dynamic one.

```jsx
const App = () => {
  return (
    <WagmiConfig client={wagmiClient}>
      <DynamicContextProvider
        settings={{
          environmentId: "Enter your Environment ID here",
        }}
      >
        <YourRoutes />
        <ConnectButton />
      </DynamicContextProvider>
    </WagmiConfig>
  );
};
```

#### Replace `WagmiConfig` with `DynamicWagmiConnector`

As mentioned in the full wagmi guide above, the `DynamicWagmiConnector` replaces `wagmiConfig` in the Dynamic setup. Note that the Dynamic provider wraps the Wagmi connector, vs wagmi wrapping the Rainbowkit adapter.

```jsx
const App = () => {
  return (
    <DynamicContextProvider
      settings={{
        environmentId: "Enter your Environment ID here",
      }}
    >
      <DynamicWagmiConnector>
        <YourRoutes />
        <ConnectButton />
      </DynamicWagmiConnector>
    </DynamicContextProvider>
  );
};
```

#### Replace `ConnectButton` with `DynamicWidget`

Dynamic uses the Dynamic widget to manage wallets, connect, user profile etc. We will replace the Rainbowkit ConnectButton with it:

```jsx
export const App = () => {
  return (
    <DynamicContextProvider
      settings={{
        environmentId: "Enter your Environment ID here",
      }}
    >
      <DynamicWagmiConnector>
        <DynamicWidget />
        <YourApp />
      </DynamicWagmiConnector>
    </DynamicContextProvider>
  );
};
```

**That's it! you should now have Dynamic working instead of Rainbowkit.**

### Things to know

#### Authenticating with Dynamic vs Rainbowkit

As mentioned above, Dynamic comes built in with authentication. That means that as part of a Dynamic setup, you receive optional access to user management functions, as well as a JWT that contains verified credentials for the user.

Optionally, you can chose to keep the only connect-only experience of Rainbowkit. To do so, you can add to the settings \`initialAuthenticationMode: 'connect-only'. You can read more about connect-only vs authentication[here.](/wallets/advanced-wallets/connected-vs-authenticated)

#### Chains and customization

Rainbowkit configures chains within code using wagmi. Dynamic, on the other hand, pulls its configuration from your [developer dashboard](https://app.dynamic.xyz/dashboard/configurations).


# Quickstart

Let's get you up and running with a full signup/login UI! Just [sign up](https://app.dynamic.xyz) and follow the guide below!

<Tip>
  You can also refer to one of [our sample apps](/example-apps) if you want to
  clone a full application rather than embed into an existing one!
</Tip>

## Select your SDK

<iframe frameBorder="0" width="100%" height="50px" src="https://readme-embeds.dynamic.xyz/quickstart-sdk?color-scheme=dark" allow="clipboard-write" class="hidden dark:block" />

<iframe frameBorder="0" width="100%" height="50px" src="https://readme-embeds.dynamic.xyz/quickstart-sdk?color-scheme=light" allow="clipboard-write" class="block dark:hidden" />

## Install the SDK

<iframe frameBorder="0" width="100%" height="400px" src="https://readme-embeds.dynamic.xyz/quickstart?color-scheme=dark" allow="clipboard-write" class="hidden dark:block">
  {' '}
</iframe>

<iframe frameBorder="0" width="100%" height="400px" src="https://readme-embeds.dynamic.xyz/quickstart?color-scheme=light" allow="clipboard-write" class="block dark:hidden">
  {' '}
</iframe>

<Tip>
  EthereumWalletConnectors also includes all EVM compatible chains including
  layer 2's i.e. Base as well as [Dynamic Embedded
  Wallets](/wallets/embedded-wallets/dynamic-embedded-wallets). Learn more about WalletConnectors [here](/react-sdk/providers/dynamiccontextprovider#walletconnectors).
</Tip>

## Initialize the SDK

<iframe frameBorder="0" width="100%" height="600px" src="https://readme-embeds.dynamic.xyz/quickstart-dynamic-context-provider?color-scheme=dark" allow="clipboard-write" class="hidden dark:block" />

<iframe frameBorder="0" width="100%" height="600px" src="https://readme-embeds.dynamic.xyz/quickstart-dynamic-context-provider?color-scheme=light" allow="clipboard-write" class="block dark:hidden" />

<Note>
  If you are using Wagmi, make sure to read our [full Wagmi
  guide](/react-sdk/using-wagmi) for more setup instructions.
</Note>

<Tip>
  If you're using Vite, you'll need to check out [the polyfills guide](/troubleshooting/react/vitejs-polyfills-necessary-for-dynamic-sdk).

  If you see any other errors, it's worth checking out [Dynamic Doctor](/troubleshooting/dynamic-doctor) for quick debugging tips!
</Tip>

<Tip>
  To quickly test the login flow, you can enable [Test
  Accounts](/developer-dashboard/test-accounts) in Sandbox mode.
</Tip>

## Celebrate & Learn More!

That's it! You should now have an initial working version of the Dynamic login flow. You can now configure different kinds of signup flows including [Embedded wallets](/wallets/embedded-wallets/dynamic-embedded-wallets) and [Email, Social & SMS](/authentication-methods/email-social-sms).

You can also [join our Slack Community](https://www.dynamic.xyz/slack) to ask us
anything on your mind.


# Account Abstraction with ZeroDev



When using account abstraction in your project to sponsor gas, batch transactions, or any other account abstraction features, you will likely want to get a ZeroDev kernel client to perform these operations.

You can use the `@dynamic-labs/zerodev-extension` package to create a ZeroDev kernel client for a wallet.

Here is how you can set up and create a kernel client.

<Note>
  The `@dynamic-labs/zerodev-extension` depends on the Viem Extension, so before going through this setup, make sure to have the Viem Extension set up and working. [Viem Extension Setup](/react-native/viem)
</Note>

# Setup

## Install ZeroDevExtension

Install the @dynamic-labs/zerodev-extension package:

<Tabs>
  <Tab title="expo">
    ```bash Shell
    npx expo install @dynamic-labs/zerodev-extension@alpha
    ```
  </Tab>

  <Tab title="npm">
    ```bash Shell
    npm install @dynamic-labs/zerodev-extension@alpha
    ```
  </Tab>

  <Tab title="yarn">
    ```bash Shell
      yarn add @dynamic-labs/zerodev-extension@alpha
    ```
  </Tab>
</Tabs>

## Resolve File Resolution Error (Optional)

When running the ZeroDevExtension in your React Native application, you might encounter an error where Metro cannot resolve the `paymasterClient.js` file. This issue occurs because Metro tries to load `paymasterClient.js`, but the actual file is named `paymasterClient.ts`.

To fix this, you need to customize Metro’s resolver in your `metro.config.js` file to handle TypeScript file extensions properly.

### Generate metro.config.js for Expo Projects

If you don’t have a `metro.config.js` file in your project, you can generate one using the following command:

```bash
npx expo customize metro.config.js
```

### Customize Metro Resolver

Add the following code to your `metro.config.js` file to instruct Metro to resolve `.ts` files when it cannot find the corresponding `.js` files:

```js
// metro.config.js

/**
 * Custom resolver to handle ZeroDev imports
 */
config.resolver.resolveRequest = (context, moduleName, platform) => {
  try {
    return context.resolveRequest(context, moduleName, platform);
  } catch (error) {
    if (moduleName.endsWith(".js")) {
      const tsModuleName = moduleName.replace(/\.js$/, ".ts");
      return context.resolveRequest(context, tsModuleName, platform);
    }
    throw error;
  }
};
```

This modification allows Metro to attempt to resolve `.ts` files when it fails to find `.js` files, which fixes the resolution error for the `paymasterClient` file.

## Events Polyfill

ZeroDevExtension requires a polyfill for the Node.js events module. You can install the [events](https://www.npmjs.com/package/events) polyfill using one of the following commands:

<Tabs>
  <Tab title="expo">
    ```bash Shell
    npx expo install events
    ```
  </Tab>

  <Tab title="npm">
    ```bash Shell
    npm install events
    ```
  </Tab>

  <Tab title="yarn">
    ```bash Shell
      yarn add events
    ```
  </Tab>
</Tabs>

## Integrate with your Dynamic client

To include the ZeroDev module in your Dynamic client, you need to extend the client with the ZeroDev extension:

```typescript
import { createClient } from "@dynamic-labs/client";
import { ViemExtension } from "@dynamic-labs/viem-extension";
import { ZeroDevExtension } from "@dynamic-labs/zerodev-extension";

const dynamicClient = createClient({
  environmentId: "",
})
  .extend(ViemExtension())
  .extend(ZeroDevExtension());
```

Now your setup is complete, and you have the ZeroDev module available in your Dynamic client.

# Usage

Now that you have the ZeroDev module in your Dynamic client, you can get the ZeroDev kernel client by using the `dynamicClient.zeroDev.createKernelClient` method. Here is an example:

```typescript
// dynamicClient.ts
const dynamicClient = createClient({
  environmentId: "",
})
  .extend(ViemExtension())
  .extend(ZeroDevExtension());

// getPrimaryWalletKernelClient.ts
const getPrimaryWalletKernelClient = async () => {
  const primaryWallet = dynamicClient.wallets.primaryWallet;

  if (!primaryWallet) {
    throw new Error('No primary wallet');
  }

  const kernelClient = await dynamicClient.zeroDev.createKernelClient({
    wallet: primaryWallet
  });

  return kernelClient;
}
```

# Examples

## Batch transaction example

This example demonstrates how you can use a ZeroDev kernel client to perform a batched transaction:

```typescript
import { encodeFunctionData } from 'viem';

const contractAddress = '0x123';

const contractABI = [
  {
    inputs: [{ internalType: 'address', name: '_to', type: 'address' }],
    name: 'mint',
    outputs: [],
    stateMutability: 'nonpayable',
    type: 'function',
  },
];

const sendBatchedTransactions = async () => {
  const primaryWallet = dynamicClient.wallets.primaryWallet;

  if (!primaryWallet) {
    throw new Error('No primary wallet');
  }

  const kernelClient = await client.zeroDev.createKernelClient({
    wallet: primaryWallet,
  });

  const { account } = kernelClient;

  const hash = await kernelClient.sendUserOperation({
    account,
    userOperation: {
      callData: await account.encodeCallData([
        {
          data: encodeFunctionData({
            abi: contractABI,
            args: [primaryWallet.address],
            functionName: 'mint',
          }),
          to: contractAddress,
          value: BigInt(0),
        },
        {
          data: encodeFunctionData({
            abi: contractABI,
            args: [primaryWallet.address],
            functionName: 'mint',
          }),
          to: contractAddress,
          value: BigInt(0),
        },
      ]),
    },
  });

  return hash;
}

```


# Setting up the Dynamic Client



The Dynamic Client is a vanilla javascript package that allows users to quickly set up all the configurations
needed to get an interface that allows them to interact with our features.

It provides a very simple, bare bones client object through the call to `createClient`.
This client can then be extended with many other features, like React Native support and viem integration.

## Installation

Simply run the following in your terminal:

<Tabs>
  <Tab title="npm">
    ```bash Shell
    npm install @dynamic-labs/client@alpha
    ```
  </Tab>

  <Tab title="expo">
    ```bash Shell
    npx expo install @dynamic-labs/client@alpha
    ```
  </Tab>

  <Tab title="yarn">
    ```bash Shell
    yarn install @dynamic-labs/client@alpha
    ```
  </Tab>
</Tabs>

## Set up

Now create your client with the following code:

```typescript
import { createClient } from '@dynamic-labs/client'

export const dynamicClient = createClient({
  environmentId: 'YOUR-ENVIRONMENT-ID',

  // Optional:
  appLogoUrl: 'https://demo.dynamic.xyz/favicon-32x32.png',
  appName: 'Dynamic Demo',
})
```

You can read more about our client package [here](/react-native/package-references/client).


# Deeplink URLs



In order to use most of our react native features (such as social connections), you will first
have to set up the list of mobile deeplink URLs dynamic may use to redirect back to your app.

For security reasons, when deeplinking back to your app, we must verify whether the app's deeplink URL
is included in this list. If it isn't, Dynamic will not perform the deeplink.

## How to set up your app for deeplinking

### Configuring your app's custom scheme

First, you should follow Expo's guide on enabling deeplinking for your app
[here](https://docs.expo.dev/guides/linking/#linking-to-your-app).

### Registering your app's deeplink URL with Dynamic

Afterwards, head to your Dynamic dashboard's
[Security page](https://app.dynamic.xyz/dashboard/security) and enable "Whitelist Mobile Deeplink".
Click "Save changes".

Next, click the cog in this same section to open up the
[Mobile Deeplink URL page](https://app.dynamic.xyz/dashboard/security#mobile-deeplink-urls), and there
you must add your app's deeplink URL with the same custom scheme you configured in the previous step.
It might look something like this: `myappcustomscheme://`.

With this, you're done! Dynamic is ready to deeplink back to your app.


# Using Dynamic's UI



Besides all of our headless methods, we also provide a compreehensive set of UI modals that can be easily leveraged
to achieve the same functionalities.

## Using Dynamic's modals

You can leverage our authentication flow modal to sign your users in — this will bring our sign in modal on top of
your app, and once the user has successfully signed in, it will automatically close.

You can show the authentication modal by calling:

```typescript
dynamicClient.ui.auth.show()
```

To hide the authentication modal programmatically, you can use:

```typescript
dynamicClient.ui.auth.hide()
```

After the user has signed in successfully, you can also bring up our user profile interface, which allows your user
to view their wallet's address and balance, as well as editing their fields like email and phone number.

To show the user profile modal, call:

```typescript
dynamicClient.ui.userProfile.show()
```

To hide the user profile modal programmatically, use:

```typescript
dynamicClient.ui.userProfile.hide()
```

You can read more about our UI modules [here](/react-native/package-references/client#ui-module).


# Email and SMS verification



The dynamic client authentication module enables user authentication via email or SMS.

<Note>
  Notice that email and SMS login methods **must be respectively enabled** in
  your environment's dashboard settings first!
</Note>

## Sending and verifying OTPs

The methods below can be used to send/resend and verify one time passwords

```typescript
// Send, retry and then verify OTP to an email
await dynamicClient.auth.email.sendOTP('someone@gmail.com')
await dynamicClient.auth.email.resendOTP()
await dynamicClient.auth.email.verifyOTP('received-token')

// Send, retry and then verify OTP to a phone number
await dynamicClient.auth.sms.sendOTP({
  dialCode: '1',
  iso2: 'us',
  phone: '2793334444',
})
await dynamicClient.auth.sms.resendOTP()
await dynamicClient.auth.sms.verifyOTP('received-token')
```

### Example

The example below demonstrates a React component that can send, resend and verify OTPs through email.

You can follow the same pattern for phone numbers but collect phone number, iso2 and dial code instead
of email address.

```typescript
import { dynamicClient } from '<path to client file>';

const EmailSignIn: FC = () => {
  const [email, setEmail] = useState('')
  const [otp, setOtp] = useState('')
  const [otpSent, setOtpSent] = useState(false)

  const handleSendOTP = async () => {
    await dynamicClient.auth.email.sendOTP(email)
    setOtpSent(true)
  }

  const handleResendOTP = () => {
    dynamicClient.auth.email.resendOTP()
  }

  const handleVerifyOTP = () => {
    dynamicClient.auth.email.verifyOTP(otp)
  }

  return (
    <View>
      {!otpSent ? (
        <View>
          <TextInput
            value={email}
            onChangeText={setEmail}
            placeholder="Enter your email"
          />

          <Button onPress={handleSendOTP}>Send OTP</Button>
        </View>
      ) : (
        <View>
          <TextInput
            value={otp}
            onChangeText={setOtp}
            placeholder="Enter OTP"
          />

          <Button onPress={handleVerifyOTP}>Verify OTP</Button>

          <Button onPress={handleResendOTP}>Resend OTP</Button>
        </View>
      )}
    </View>
  )
}
```

You can read more about these modules [here](/react-native/package-references/client#auth-email-submodule).


# Embedded wallets



The dynamic client provides an interface to easily interact with and create embedded wallets for your users.

<Note>
  Notice that embedded wallets **must be enabled** in your environment's
  dashboard settings first!
</Note>

## Creating and Getting the Wallet

This interface allows you to create new embedded wallets, check if any already exist and fetch the current wallet.

See the following example which renders a prompt to create a wallet or renders its address if it already exists:

```typescript
import { dynamicClient } from '<path to client file>';
import { useReactiveClient } from '@dynamic-labs/react-hooks';

const EmbeddedWallet: FC = () => {
  const { wallets } = useReactiveClient(dynamicClient)

  const wallet = wallets.userWallets[0]

  return (
    <View>
      {wallet && wallets.embedded.hasWallet ? (
        <View>Your wallet address: {wallet.address}</View>
      ) : (
        <Button onPress={() => wallets.embedded.createWallet()}>
          Create Wallet
        </Button>
      )}
    </View>
  )
}
```

## Creating a Wallet for a Specific Chain

The `createWallet` method also allows you to create an embedded wallet for a specific chain

See the example below:

```typescript
import { dynamicClient } from '<path to client file>';
import { useReactiveClient } from '@dynamic-labs/react-hooks';

const AddEvmWalletButton: FC = () => {
  return (
    <Button
      title="Add EVM wallet"
      onPress={() => dynamicClient
        .wallets
        .embedded
        .createWallet({ chain: 'Evm' })
      }
    />
  )
}
```

## Exporting Wallet Keys

If your app uses embedded wallets, you must also make sure to provide your users with some way to export their wallets' keys
to ensure they have absolute control over them.

Dynamic provides a quick and easy way to do so with our embedded wallet key export UI flow!

<Note>
  This flow will export the keys from your primary wallet. See
  [here](/react-native/managing-users-wallets#working-your-primary-wallet) how
  to set your primary wallet.
</Note>

```typescript
const ExportEmbeddedWalletKeyButtons: FC = () => {
  return (
    <View>
      <TouchableOpacity
        onPress={() =>
          dynamicClient.ui.wallets.revealEmbeddedWalletKey({
            type: 'private-key',
          })
        }
      >
        Reveal private key
      </TouchableOpacity>

      <TouchableOpacity
        onPress={() =>
          dynamicClient.ui.wallets.revealEmbeddedWalletKey({
            type: 'recovery-phrase',
          })
        }
      >
        Reveal recovery phrase
      </TouchableOpacity>
    </View>
  )
}
```

You can read more about the embedded wallets module [here](/react-native/package-references/client#wallets-embedded-submodule).


# Listening to events



Our client was built with an event-first approach, and every module has its own set of events that you can listen to.

## Types of events

There are two types of events that you can listen to:

*   State change events: These events are triggered when a module's variable changes value.
    All variables are observable, and their corresponding events will always have the same name as the variable, followed by `Changed`.
*   Custom events: These events represent some specific action that has occurred in the module.

## Listening to events

To listen to a module's event, you need to call the `on` method on the module instance,
passing the event name and a callback function as arguments.

You can just as easily remove the listener by calling the `off` method on the module instance,
passing the event name and the same callback function as arguments.

```typescript
const handleAuthSuccess = (user) => {
  console.log('User logged in', user)
}

dynamicClient.auth.on('authSuccess', handleAuthSuccess)

// When you no longer need to listen to the event

dynamicClient.auth.off('authSuccess', handleAuthSuccess)
```

<Tip>
  You can add a one-off listener by calling the `once` method on the module
  instance instead of `on`.
</Tip>

## List of events

You can find all the events that a module emits in the module's documentation — for instance, take a look at the
auth module's documentation [here](/react-native/package-references/client#auth-module).

However, for your convenience, below is a list of all the events that the client emits.
Please refer to the module's documentation for more information on each event.

### [Auth module](/react-native/package-references/client#auth-module)

*   `authInit`: Emitted when the user initializes authentication, but before it either completes or fails.
*   `authSuccess`: Emitted when the user successfully logs in.
*   `authFailed`: Emitted when the user fails to log in.
*   `loggedOut`: Emitted when the user logs out.
*   `userProfileUpdated`: Emitted when the user's profile is updated.
*   `authenticatedUserChanged`: State change event for the `authenticatedUser` variable.
*   `tokenChanged`: State change event for the `token` variable.

#### [Email auth module](/react-native/package-references/client#auth-email-submodule)

*   `emailVerificationFinished`: Emitted when the email verification process is completed.

#### [SMS auth module](/react-native/package-references/client#auth-sms-submodule)

*   `smsVerificationFinished`: Emitted when the SMS verification process is completed.

### [Wallets module](/react-native/package-references/client#wallets-module)

*   `messageSigned`: Emitted when a message is signed.
*   `walletAdded`: Emitted when a wallet is added to the `userWallets` variable.
*   `walletRemoved`: Emitted when a wallet is removed from the `userWallets` variable.
*   `primaryChanged`: State change event for the `primary` variable.
*   `userWalletsChanged`: State change event for the `userWallets` variable.

#### [Embedded wallets module](/react-native/package-references/client#wallets-embedded-submodule)

*   `embeddedWalletCreated`: Emitted when an embedded wallet is created.
*   `hasWalletChanged`: State change event for the `hasWallet` variable.

### [Networks module](/react-native/package-references/client#networks-module)

*   `evmChanged`: State change event for the `evm` variable.
*   `solanaChanged`: State change event for the `solana` variable.

### [SDK module](/react-native/package-references/client#sdk-module)

*   `error`: Emitted when an error occurs.
*   `loadedChanged`: State change event for the `loaded` variable.

### [UI module](/react-native/package-references/client#ui-module)

*   `authFlowCancelled`: Emitted when the user cancels the authentication flow (closes it before completing it).
*   `authFlowClosed`: Emitted when the authentication flow is closed, regardless of whether it was successfully completed or not.
*   `authFlowOpened`: Emitted when the authentication flow is opened.


# Signing in with Third-party Authentication



<Note>
  This is an enterprise-only feature. Please contact us [in
  slack](https://dynamic.xyz/slack) or via email ([hello@dynamic.xyz](mailto:hello@dynamic.xyz)) to enable.
</Note>

Dynamic supports signing in with third-party JWTs — read about it
[here](/authentication-methods/third-party-auth).

In this page we'll show how that can be achieved with our React Native SDK!

<Note>
  Notice that third-party auth **must be enabled** in your environment's
  dashboard settings first! See how to
  [here](/authentication-methods/third-party-auth#dashboard-configuration).
</Note>

## Providing an external JWT to dynamic

Once you have access to your user's JWT and ID, just pass them both to Dynamic by calling:

```typescript
await dynamicClient.auth.signInWithExternalJwt({ externalJwt, externalUserId })
```

or

```typescript
await dynamicClient.auth.verifyWithExternalJwt({ externalJwt, externalUserId })
```

This promise resolves when the sign in is complete — and that's it! You're done.

You can find the reference for this method [here](/react-native/package-references/client#auth-module).


# Introduction to the React Native SDK



<Frame>
  <img className="block dark:hidden" src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/react-native-light.png" />

  <img className="hidden dark:block" src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/react-native-dark.png" />
</Frame>

<Info>
  Please be aware that our React Native SDK is in open alpha at the moment!
</Info>

The dynamic SDK can now be consumed in React Native apps through our new **client** package.

> Soon enough, this package will become the new standard and will be usable in any javascript environments, as it is
> framework agnostic.

It is still in a very early stage of development, and does not yet have feature parity with our regular React SDK.
Here are the features it supports as of right now:

*   Headless email and SMS login.
*   Embedded wallets with passkeys.
*   Viem wallet and public client integrations.
*   Headless or Dynamic UI.

Though you might be able to get away with a different setup, as of right now we only support the following:

*   React Native `^0.73.6`
*   React Native WebView `^13.6.4`
*   Expo `50.0.14`

Follow these pages in order and you should learn all you need in order to kick off your first React Native app with Dynamic!

1.  [Initializing your Dynamic Client](/react-native/client)
2.  [Making your Dynamic Client ready for use with React Native](/react-native/react-native-extension)
3.  [Adding email and SMS verification to your Dynamic Client](/react-native/email-and-sms-verification)
4.  [Enabling deeplinking to your app](/react-native/deeplink-urls)
5.  [Adding social authentication to your Dynamic Client](/react-native/social)
6.  [Install & Setup Passkey](/react-native/setup-passkey)
7.  [Creating embedded wallets for your users](/react-native/embedded-wallets)
8.  [Displaying the user's data in your UI](/react-native/react-hooks)
9.  [Reacting to user actions by listening to events](/react-native/events)
10. [Integrating Viem with your users' wallets](/react-native/viem)
11. [Integrating Solana with your users' wallets](/react-native/solana)
12. [Using Dynamic's UI instead of headless](/react-native/dynamic-ui)
13. [Use Account Abstraction with ZeroDev](/react-native/account-abstraction)


# Managing your User's Wallets



If you have multi-wallet enabled, your users might have multiple wallets connected at once.
Read [here](/wallets/advanced-wallets/multi-wallet#multiple-wallet-connections-multi-wallet) to learn more about multi-wallet.

You can use our `wallets` module to get access to all your user's wallets and perform actions with them:

```typescript
import { dynamicClient } from '<path to client file>';
import { useReactiveClient } from '@dynamic-labs/react-hooks';

const UserWallets: FC = () => {
  const { wallets } = useReactiveClient(dynamicClient)

  return (
    <View>
      {wallets.userWallets.map((wallet) => (
        <Text key={wallet.id}>{wallet.address}</Text>
      ))}
    </View>
  )
}
```

## Working your Primary Wallet

You can access the primary wallet via `client.wallets.primary` as well as set it with `client.wallets.setPrimary`.

This example sets the first user wallet as the primary wallet on click:

```typescript
import { dynamicClient } from '<path to client file>';
import { useReactiveClient } from '@dynamic-labs/react-hooks';

const SetFirstWallet: FC = () => {
  const { wallets } = useReactiveClient(dynamicClient)

  return (
    <View>
      <Text>Current primary wallet address: {wallets.primary.address}</Text>

      <TouchableOpacity
        onPress={() =>
          wallets.setPrimary({ walletId: wallets.userWallets[0].id })
        }
      >
        Set wallet
      </TouchableOpacity>
    </View>
  )
}
```

The wallets module also provides a collection of useful methods to perform actions with your users' wallets.
You can read more about the module [here](/react-native/package-references/client#wallets-module).


# client



The base package that provides access to the Dynamic Client, which can be extended with extension
packages.

## Functions

### `createClient`

```typescript
createClient(params: ClientProps): BaseClient
```

Returns an instance of a client object, which provides an interface to read state, trigger actions and
listen to events of our SDK.

## Types

### `ClientProps` type

The parameters that are acceptable by the [createClient](#createclient) method.

| Param           | Type      | Description                                                                 |
| --------------- | --------- | --------------------------------------------------------------------------- |
| `environmentId` | `string`  | The ID of the environment of your dynamic application.                      |
| `apiBaseUrl`    | `string?` | Allows you to override the URL to which the SDK will make its API requests. |
| `cssOverrides`  | `string?` | Allows you to inject CSS into our UI modules (currently out of effect)      |
| `appName`       | `string?` | How you'd like your app to be named in our copies.                          |
| `appLogoUrl`    | `string?` | A URL of the logo of your app.                                              |

### `BaseClient` type

The base type of a client.

Since clients can be extended (and thus have their types composed with those of the extensions), this is considered the most basic kind of client:
the one that is returned from the [createClient](#createclient) method.

Besides the `.extend` method which is inherited from the [Extendable](#extendable-class) class, it is composed of properties which we call modules.

Note that all client modules **implement an event emitter interface** that allows you to call `on`, `off` and `once` to create and destroy
event listeners.

<Note>
  For every property that is a state, there will always be an event name with
  the same name plus `"Changed"`, which will be triggered when the property
  changes value. We will omit these from the docs below as they are implicit.
</Note>

#### `auth` module

Provides access to authentication related properties and methods of the SDK.

| Property                | Type                                                                 | Description                                                                                                                            |
| ----------------------- | -------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `token`                 | `string \| null`                                                     | The JWT of the currently logged in user.                                                                                               |
| `authenticatedUser`     | `UserProfile \| null`                                                | The [UserProfile](/react-sdk/objects/userprofile) object of the logged in user.                                                        |
| `logout`                | `() => Promise<void>`                                                | Allows you to log the current user out.                                                                                                |
| `signInWithExternalJwt` | `({ externalJwt: string; externalUserId: string }) => Promise<void>` | Logs the user in with a third-party JWT. This must be [enabled in dashboard](/authentication-methods/third-party-auth) first.          |
| `verifyWithExternalJwt` | `({ externalJwt: string; externalUserId: string }) => Promise<void>` | Links the dynamic user in with a third-party JWT. This must be [enabled in dashboard](/authentication-methods/third-party-auth) first. |

| Event name                 | Type                                                                               | Description                                                                                |
| -------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| `authInit`                 | `(data: AuthEventPayload) => void`                                                 | Emitted when the user initializes authentication, but before it either completes or fails. |
| `authSuccess`              | `(user: UserProfile) => void`                                                      | Emitted when the user successfully logs in.                                                |
| `authFailed`               | `(data: AuthEventPayload, reason: 'user-cancelled' \| { error: unknown }) => void` | Emitted when the user fails to log in.                                                     |
| `loggedOut`                | `(user: UserProfile \| null) => void`                                              | Emitted when the user logs out.                                                            |
| `userProfileUpdated`       | `(user: UserProfile) => void`                                                      | Emitted when the user's profile is updated.                                                |
| `authenticatedUserChanged` | `(authenticatedUser: UserProfile \| null) => void`                                 | State change event for the `authenticatedUser` variable.                                   |
| `tokenChanged`             | `(token: string \| null) => void`                                                  | State change event for the `token` variable.                                               |

#### `auth.email` submodule

Provides methods to send, re-send and verify OTPs to email.

| Property    | Type                                | Description                                                              |
| ----------- | ----------------------------------- | ------------------------------------------------------------------------ |
| `sendOTP`   | `(target: string) => Promise<void>` | Sends an OTP token to the target email.                                  |
| `resendOTP` | `() => Promise<void>`               | Sends another OTP token to the same email as the last call to `sendOTP`. |
| `verifyOTP` | `(token: string) => Promise<void>`  | Receives an OTP token and logs the user in if it is valid.               |

| Event name                  | Type                                                            | Description                                               |
| --------------------------- | --------------------------------------------------------------- | --------------------------------------------------------- |
| `emailVerificationFinished` | `(params: { isSuccess: boolean; destination: string }) => void` | Emitted when the email verification process is completed. |

#### `auth.sms` submodule

Provides methods to send, re-send and verify OTPs to phone numbers.

| Property    | Type                                   | Description                                                                     |
| ----------- | -------------------------------------- | ------------------------------------------------------------------------------- |
| `sendOTP`   | `(target: PhoneData) => Promise<void>` | Sends an OTP token to the target [PhoneData](/react-sdk/objects/phone-data).    |
| `resendOTP` | `() => Promise<void>`                  | Sends another OTP token to the same phone number as the last call to `sendOTP`. |
| `verifyOTP` | `(token: string) => Promise<void>`     | Receives an OTP token and logs the user in if it is valid.                      |

| Event name                | Type                                                               | Description                                                                                            |
| ------------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------ |
| `smsVerificationFinished` | `(params: { isSuccess: boolean; destination: PhoneData }) => void` | Emitted when the SMS verification process is completed. See [PhoneData](/react-sdk/objects/phone-data) |

#### `auth.social` submodule

Provides a method to connect social accounts.

| Property  | Type                                                    | Description                                                                                      |
| --------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| `connect` | `(args: { provider: SocialProvider }) => Promise<void>` | Requests social connection to the provided [SocialProvider](/react-sdk/objects/social-provider). |

#### `networks` module

Provides access to the current network configurations.

| Property | Type               | Description                                                                                             |
| -------- | ------------------ | ------------------------------------------------------------------------------------------------------- |
| `evm`    | `GenericNetwork[]` | Which EVM networks are currently configured. See [GenericNetwork](/react-sdk/objects/GenericNetwork)    |
| `solana` | `GenericNetwork[]` | Which Solana networks are currently configured. See [GenericNetwork](/react-sdk/objects/GenericNetwork) |

| Event name      | Type                                 | Description                                   |
| --------------- | ------------------------------------ | --------------------------------------------- |
| `evmChanged`    | `(evm: GenericNetwork[]) => void`    | State change event for the `evm` variable.    |
| `solanaChanged` | `(solana: GenericNetwork[]) => void` | State change event for the `solana` variable. |

#### `sdk` module

Gives insight over the state of the SDK.

| Property | Type      | Description                                                 |
| -------- | --------- | ----------------------------------------------------------- |
| `loaded` | `boolean` | Whether the SDK has loaded and is ready to handle requests. |

| Event name      | Type                             | Description                                   |
| --------------- | -------------------------------- | --------------------------------------------- |
| `error`         | `(error: Error \| null) => void` | Emitted when an error occurs.                 |
| `loadedChanged` | `(loaded: boolean) => void`      | State change event for the `loaded` variable. |

#### `ui` module

Provides access to Dynamic's UI.

| Property                          | Type                                                             | Description                                                                                                |
| --------------------------------- | ---------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `auth.show`                       | `() => void`                                                     | Opens up Dynamic's authentication flow modal for your user to sign in. Automatically closes when finished. |
| `auth.hide`                       | `() => void`                                                     | Hide the Dynamic's authentication flow modal.                                                              |
| `userProfile.show`                | `() => void`                                                     | Opens up Dynamic's user profile modal, allowing your user to manage their profile and wallets.             |
| `userProfile.hide`                | `() => void`                                                     | Hide the Dynamic's user profile modal                                                                      |
| `wallets.revealEmbeddedWalletKey` | `(params: { type: "recovery-phrase" \| "private-key" }) => void` | Opens up Dynamic's export embedded wallet key flow for the primary wallet.                                 |

| Event name          | Type         | Description                                                                                                 |
| ------------------- | ------------ | ----------------------------------------------------------------------------------------------------------- |
| `authFlowCancelled` | `() => void` | Emitted when the user cancels the authentication flow (closes it before completing it).                     |
| `authFlowClosed`    | `() => void` | Emitted when the authentication flow is closed, regardless of whether it was successfully completed or not. |
| `authFlowOpened`    | `() => void` | Emitted when the authentication flow is opened.                                                             |

#### `wallets` module

Provides access to the user's wallets.

| Property        | Type                                                                                      | Description                                                       |
| --------------- | ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------- |
| `userWallets`   | `BaseWallet[]`                                                                            | The array of all the user's [wallets](/react-sdk/objects/wallet). |
| `primary`       | `BaseWallet \| undefined`                                                                 | The primary [wallet](/react-sdk/objects/wallet) of the user.      |
| `setPrimary`    | `(params: { walletId: string }) => Promise<void>`                                         | Sets primary [wallet](/react-sdk/objects/wallet) of the user.     |
| `getBalance`    | `(params: { wallet: BaseWallet }) => Promise<{ balance: string }>`                        | Returns the balance of a wallet.                                  |
| `getNetwork`    | `(params: { wallet: BaseWallet }) => Promise<{ network: string \| number }>`              | Returns the network the wallet is connected to.                   |
| `signMessage`   | `(params: { wallet: BaseWallet; message: string }) => Promise<{ signedMessage: string }>` | Signs a message with this wallet.                                 |
| `switchNetwork` | `(params: { wallet: BaseWallet; chainId: string \| number }) => Promise<void>`            | Switches the wallet's network.                                    |

| Event name           | Type                                                                  | Description                                                       |
| -------------------- | --------------------------------------------------------------------- | ----------------------------------------------------------------- |
| `messageSigned`      | `(params: { messageToSign: string; signedMessage: string }) => void`  | Emitted when a message is signed.                                 |
| `walletAdded`        | `(params: { wallet: BaseWallet; userWallets: BaseWallet[] }) => void` | Emitted when a wallet is added to the `userWallets` variable.     |
| `walletRemoved`      | `(params: { wallet: BaseWallet; userWallets: BaseWallet[] }) => void` | Emitted when a wallet is removed from the `userWallets` variable. |
| `primaryChanged`     | `(primary: BaseWallet \| null) => void`                               | State change event for the `primary` variable.                    |
| `userWalletsChanged` | `(userWallets: BaseWallet[]) => void`                                 | State change event for the `userWallets` variable.                |

#### `wallets.embedded` submodule

Allows interacting with and creating an embedded wallet for the current user.

| Property       | Type                                                   | Description                                                                     |
| -------------- | ------------------------------------------------------ | ------------------------------------------------------------------------------- |
| `hasWallet`    | `boolean`                                              | Whether the logged in user has an embedded wallet.                              |
| `getWallet`    | `() => BaseWallet \| null`                             | Retrieves the embedded wallet of the current user, or null if it doesn't exist. |
| `createWallet` | `(params?: { chain?: "Evm" \| "Sol" }) => BaseWallet ` | Creates an embedded wallet for the current user.                                |

| Event name              | Type                                                          | Description                                      |
| ----------------------- | ------------------------------------------------------------- | ------------------------------------------------ |
| `embeddedWalletCreated` | `(verifiedCredential: JwtVerifiedCredential \| null) => void` | Emitted when an embedded wallet is created.      |
| `hasWalletChanged`      | `(hasWallet: boolean) => void`                                | State change event for the `hasWallet` variable. |

### `Extendable` class

An object that can be extended by calling its `.extend` method with an [Extension](#extension-method).

To extend here means to add or replace the existing properties of this object, as well as kicking off any related procedures.
Like a structure on which you can attach lego bricks.

Since our client extends this class, you are able to easily pick and choose exactly which features you will need, maximizing the control,
simplicity and package size of your project.

| Property | Type                                            | Description                                                                                |
| -------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------ |
| `extend` | `<T>(extension: Extension<T>): T & typeof this` | Allows you to extend the properties of the calling object and kick off related procedures. |

### `Extension` method

```typescript
Extension<T>(extendable: Extendable, core: Core): T
```

A method that can be passed to the `.extend` method of an [Extendable](#extendable-class) object to extend its properties and trigger related procedures.

<Note>
  We do not currently provide support on how to create your own custom
  extensions, though it can be achieved.
</Note>


# react-hooks



The package that provides a hook to make the [client](/react-native/client)'s state reactive.

## Hooks

### `useReactiveClient`

```typescript
useReactiveClient<T>(target: T) => T
```

Receives the client and returns it wrapped in a proxy.

In each rerender, the proxy keeps track of which variables you have accessed.
As soon as one of these variables' state mutates, React automatically rerenders, and the cycle restarts.

> This means **you only trigger rerenders for variables you are actually reading from**, keeping rerenders to a minimum


# react-native-extension



The package that gives access to an [Extension](/react-native/package-references/client#extension-method)
that allows adding react native support to our [client](/react-native/client).

## Functions

### `ReactNativeExtension` method

```typescript
ReactNativeExtension(props?: ReactNativeExtensionProps): Extension<IReactNativeExtension>
```

A method that, when passed to the client instance, injects the following modules into it:

#### `reactNative` module

Provides access to the webview that renders our SDK in the background of your app.

The client itself is only an interface to our SDK, so it must be rendered to your app in order for the client
to work.

| Property  | Type                | Description                                                        |
| --------- | ------------------- | ------------------------------------------------------------------ |
| `WebView` | `() => JSX.Element` | The react native component that renders our SDK in the background. |

## Types

### `ReactNativeExtensionProps` type

Properties which you can pass to the extension.

*   `appOrigin`: Allows you to specify the URL origin of your app. Necessary for use with passkeys and social auth.

```typescript
type ReactNativeExtensionProps = {
  appOrigin?: string
}
```

### `IReactNativeExtension` type

Type of the react native extension.

```typescript
type IReactNativeExtension = {
  reactNative: {
    WebView: () => JSX.Element
  }
}
```


# solana-extension



The package that gives access to an [Extension](/react-native/package-references/client#extension-method)
that allows integrating Solana/web3.js to our [client](/react-native/client).

## Functions

### `SolanaExtension` method

```typescript
SolanaExtension(): Extension<ISolanaExtension>
```

A method that, when passed to the client instance, injects the following modules into it:

#### `solana` module

Provides methods to create solana connection and signer for solana wallets.

<Info>
  The reference types below are simplified for the sake of readability. See the
  [type specification](#isolanaextension-type) for details.
</Info>

| Property        | Type                                                                | Description                                                                                                                                       |
| --------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `getClient`     | `(props: { wallet: Wallet }) => ISolanaSigner`                      | Crate a Signer for the wallet that allows for sign messages and transactions.                                                                     |
| `getConnection` | `(connectionConfig?: ConnectionConfig \| Commitment) => Connection` | Create a `@solana/web3.js` Connection object to query the blockchain. Accepts an same argument that is Solana's Connection class second argument. |

## Types

### `ISolanaExtension` type

Type of the solana extension.

```typescript
import {
  Connection,
  PublicKey,
  SendOptions,
  Transaction,
  TransactionSignature,
  VersionedTransaction,
} from '@solana/web3.js'

import { type Wallet } from '@dynamic-labs/client'

type SignedMessage = {
  signature: Uint8Array
}

type ISolanaSigner = {
  publicKey: PublicKey
  signTransaction<T extends Transaction | VersionedTransaction>(
    transaction: T
  ): Promise<T>
  signAllTransactions<T extends Transaction | VersionedTransaction>(
    transactions: T[]
  ): Promise<T[]>
  signAndSendTransaction<T extends Transaction | VersionedTransaction>(
    transaction: T,
    options?: SendOptions
  ): Promise<{ signature: TransactionSignature }>
  signMessage(message: Uint8Array, encoding?: string): Promise<SignedMessage>
}

type ISolanaExtension = {
  solana: {
    getConnection: (
      connectionConfig?: ConnectionConfig | Commitment
    ) => Connection
    getSigner: (props: { wallet: Wallet }) => ISolanaSigner
  }
}
```


# viem-extension



The package that gives access to an [Extension](/react-native/package-references/client#extension-method)
that allows integrating Viem to our [client](/react-native/client).

## Functions

### `ViemExtension` method

```typescript
ViemExtension(): Extension<IViemExtension>
```

A method that, when passed to the client instance, injects the following modules into it:

#### `viem` module

Provides methods to create viem clients.

<Info>
  The reference types below are simplified for the sake of readability. See the
  [type specification](#iviemextension-type) for details.
</Info>

| Property             | Type                                                              | Description                                                             |
| -------------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------------- |
| `createPublicClient` | `(params: { chain: Chain }) => PublicClient`                      | Creates a public viem client to interact with the given chain.          |
| `createWalletClient` | `(params: { wallet: BaseWallet, chain?: Chain }) => WalletClient` | Creates a wallet viem client to interact with the given wallet address. |

## Types

### `IViemExtension` type

Type of the react native extension.

```typescript
import {
  Account,
  Chain,
  CustomTransport,
  PublicClient,
  PublicClientConfig,
  WalletClient,
  WalletClientConfig,
} from 'viem'

type PublicClientConfigDynamic<C extends Chain | undefined> = Omit<
  PublicClientConfig<CustomTransport, C>,
  'transport'
> & { chain: NonNullable<PublicClientConfig<CustomTransport, C>['chain']> }

type WalletClientConfigDynamic<C extends Chain | undefined> = Omit<
  WalletClientConfig<CustomTransport, C>,
  'transport' | 'account'
> & { wallet: BaseWallet }

type IViemExtension = {
  viem: {
    createPublicClient: <C extends Chain | undefined = undefined>(
      params: PublicClientConfigDynamic<C>
    ) => PublicClient<CustomTransport, C>

    createWalletClient: <C extends Chain | undefined = undefined>(
      params: WalletClientConfigDynamic<C>
    ) => WalletClient<CustomTransport, C, Account>
  }
}
```


# zero-dev-extension



The package that gives access to an [Extension](/react-native/package-references/client#extension-method)
that allows integrating ZeroDev Kernel to our [client](/react-native/client).

## Functions

### `ZeroDevExtension` method

```typescript
ZeroDevExtension(): Extension<IZeroDevExtension>
```

A method that, when passed to the client instance, injects the following modules into it:

#### `zeroDev` module

Provides method to create zerodev kernel client.

| Property             | Type                                                                    | Description                                     |
| -------------------- | ----------------------------------------------------------------------- | ----------------------------------------------- |
| `createKernelClient` | `(args: CreateKernelAccountClientArgs) => Promise<KernelAccountClient>` | Creates the ZeroDev Kernel client for a wallet. |

## Types

### `IZeroDevExtension` type

```typescript
import { KernelAccountClient } from "@zerodev/sdk/clients";

type CreateKernelAccountClientArgs = {
  wallet: Wallet;
  bundlerProvider?: BundlerProvider;
  bundlerRpc?: string;
  paymasterRpc?: string;
  paymaster?: PaymasterType;
};

type IZeroDevExtension = {
  zeroDev: {
    createKernelClient: (
      args: CreateKernelAccountClientArgs
    ) => ReturnType<KernelAccountClient>;
  };
};
```


# Rendering client state to React



Our client was built in vanilla javascript, and therefore its state is not able to trigger React
rerenders out of the box upon mutation.

But fear not, for we have a simple, lightweight solution to this problem: the react-hooks package.
It provides you with the `useReactiveClient` hook, which wraps the client in a proxy that is ready to be used
directly inside JSX.

<Note>
  Since our client was built with a modular approach, this package comes as a
  standalone, so to reduce the client's package size.
</Note>

## Installation

Simply run the following in your terminal:

<Tabs>
  <Tab title="expo">
    ```bash Shell
    npx expo install @dynamic-labs/react-hooks@alpha
    ```
  </Tab>

  <Tab title="npm">
    ```bash Shell
    npm install @dynamic-labs/react-hooks@alpha
    ```
  </Tab>

  <Tab title="yarn">
    ```bash Shell
      yarn add @dynamic-labs/react-hooks@alpha
    ```
  </Tab>
</Tabs>

## Using client's state inside JSX

All you need to do is wrap your client with `useReactiveClient` inside your component.

> This wraps the client in a proxy that keeps track of, for each render, which variables you have accessed.
> As soon as one of these variables' state mutates, React automatically rerenders, and the cycle restarts.
> This means **you only trigger rerenders for variables you are actually reading from**, keeping rerenders to a minimum.

```typescript
import { createClient } from '@dynamic-labs/client';
import { useReactiveClient } from '@dynamic-labs/react-hooks'

const dynamicClient = createClient({
  environmentId: ''
});

export const useDynamic = () => useReactiveClient(client)
```

```typescript
import { useDynamic } from '<path to client file>';

export function Home() {
  const { auth } = useDynamic();

  return (
    <>
      <View>
        <Text>JWT:</Text>
        <Text>{auth.token}</Text>
      </View>

      <View>
        <Text>User:</Text>
        <Text>{auth.authenticatedUser?.email}</Text>
      </View>

      <Button title="Logout" onPress={() => auth.logout()} />
    </>
  )
}
```

You can read more about our react hooks package [here](/react-native/package-references/react-hooks).


# Adding React Native support



The way Dynamic integrates with React Native is by extending our [client](/react-native/client)
with the React Native Extension. This adds the `reactNative` module, which provides access to a *webview* component
that must be rendered to your app.

<Note>
  Since our client was built with a modular approach, each extension must be
  installed as a separate package, so to keep the client's package size to a
  minimum.
</Note>

## Installation

Simply run the following in your terminal:

<Tabs>
  <Tab title="expo">
    ```bash Shell
    npx expo install @dynamic-labs/react-native-extension@alpha react-native-webview expo-web-browser expo-linking expo-secure-store
    ```
  </Tab>

  <Tab title="npm">
    ```bash Shell
    npm install @dynamic-labs/react-native-extension@alpha react-native-webview expo-web-browser expo-linking expo-secure-store
    ```
  </Tab>

  <Tab title="yarn">
    ```bash Shell
    yarn install @dynamic-labs/react-native-extension@alpha react-native-webview expo-web-browser expo-linking expo-secure-store
    ```
  </Tab>
</Tabs>

## Usage with React Native

First, extend your client with our extension:

```typescript
import { ReactNativeExtension } from '@dynamic-labs/react-native-extension'

export const dynamicClient = createClient({
  environmentId: 'YOUR-ENVIRONMENT-ID',
}).extend(ReactNativeExtension())
```

Next, render the webview injected into the client by the extension:

```typescript
import { dynamicClient } from '<path to client file>';

export function App() {
  return (
    <>
      <dynamicClient.reactNative.WebView />

      <SafeAreaView>
        { ... }
      </SafeAreaView>
    </>
  )
}
```

You can read more about our react native package [here](/react-native/package-references/react-native-extension).


# Install & Setup Passkey



To use embedded wallets in your React Native app, you must first connect your mobile application with your website so users can use passkeys in both your app and website.

The setup process involves installing a few React Native packages and setting up your domain's `.well-known` public endpoint.

### Installation

Install the required package using your preferred package manager:

<Tabs>
  <Tab title="expo">
    ```bash Shell
    npx expo install react-native-passkey@2.1.1
    ```
  </Tab>

  <Tab title="npm">
    ```bash Shell
    npm install react-native-passkey@2.1.1
    ```
  </Tab>

  <Tab title="yarn">
    ```bash Shell
      yarn add react-native-passkey@2.1.1
    ```
  </Tab>
</Tabs>

<Note>
  After installing the react-native-passkey package, you need to link the native packages to your project by running the following commands:

  For iOS, run:

  ```bash
  cd ios && pod install
  ```

  If you are using Expo, you may need to run a prebuild or create a new build:

  ```bash
  expo prebuild
  ```

  Or use the EAS service to create a new build for your app

  ```bash
  eas build
  ```
</Note>

### ReactNativeExtension Settings

To create the passkeys associated with the correct website, the `appOrigin` must be added to the ReactNativeExtension.

```typescript
const dynamicClient = createClient({
  environmentId: 'YOUR-ENVIRONMENT-ID',
}).extend(
  ReactNativeExtension({
    appOrigin: 'https://\{\{yourdomain\}\}'
  })
)
```

The appOrigin will be used to create the passkeys, so make sure it matches your website, e.g., [https://example.com](https://example.com).

## OS Configuration

### iOS

There are iOS-specific steps to configure Passkey support. If you have already set up an associated domain for your application, you can skip this step.

#### Set Up an Associated Domain for Your Application

Follow the [Apple documentation](https://developer.apple.com/documentation/xcode/supporting-associated-domains) to associate a domain with your application. On your webserver, set up this route:

```
GET https://\{\{yourdomain\}\}/.well-known/apple-app-site-association
```

This route should serve a static JSON object containing your team ID and bundle identifier. Example (replace XXXXXXXXXX with your team identifier and "YYY.YYYYY.YYYYYYYYYYYYYY" with your bundle ID, e.g., H123456789.com.example.mobileApp):

```tsx
{
  "applinks": {
    "details": []
  },
  "webcredentials": {
    "apps": ["XXXXXXXXXX.YYY.YYYYY.YYYYYYYYYYYYYY"]
  }
}
```

In Xcode, under Signing & Capabilities, add a new Capability of type Associated Domains. Now add this and replace XXXXXX with your domain (e.g., apple.com):

```tsx
webcredentials:XXXXXX
```

If you are using Expo, add the webcredentials:\{\{yourdomain}} to your app.json file. See the [Expo documentation](https://docs.expo.dev/guides/deep-linking/#native-apple-configuration) for more details.

```tsx
{
  "expo": {
    "ios": {
      "associatedDomains": ["webcredentials:\{\{yourdomain\}\}"]
    }
  }
}

```

### Android

The Android-specific configuration is similar to iOS. If you have already set up Digital Asset Links for your application, you can skip this step.

#### Associate Your App with a Domain

Follow the [Android documentation](https://developer.android.com/training/sign-in/passkeys?hl=pt-br#add-support-dal) to associate a domain with your application. On your webserver, set up this route:

```
GET https://\{\{yourdomain\}\}/.well-known/assetlinks.json
```

This route should serve a static JSON object containing the following information (replace with your data, and replace SHA\_HEX\_VALUE with the SHA256 fingerprints of your Android signing certificate):

```json
[{
  "relation": [
    "delegate_permission/common.get_login_creds",
    "delegate_permission/common.handle_all_urls"
  ],
  "target": {
    "namespace": "android_app",
    "package_name": "com.example",
    "sha256_cert_fingerprints": [
      SHA_HEX_VALUE
    ]
  }
}]
```

## Conclusion

By following these steps, you can successfully integrate passkey functionality with the Dynamic SDK into your React Native app


# Social authentication



<Warning>
  This feature requires you set up the deeplink URLs whitelist for your Dynamic
  app. See [here](/react-native/deeplink-urls).
</Warning>

The dynamic client authentication module enables social authentication.

## Connecting to your users' social accounts

<Note>
  Notice that all social options you use in code **must be respectively
  enabled** in your environment's dashboard settings first!
</Note>

You can prompt a user to connect their social accounts with the `connect` method in our social module.
It returns a promise that resolves with no params on success, and rejects on failure.

Here's how you can connect a user's farcaster account, for example:

```typescript
const connectFarcaster = async () => {
  await dynamicClient.auth.social.connect({ provider: 'farcaster' })
}
```

See [here](/react-sdk/objects/social-provider) for a list of all our supported social providers —
it will be the same list you see in your dashboard.

<Info>
  Not only can this method be used to sign a user in, but it is also able to
  link a social account to an already signed in user.
</Info>

## Adjusting app name in social connection dialogues

Social connection dialogues will derive the name of your app from the `appOrigin` prop of the `ReactNativeExtension`
extension you used to initialize your client — read [here](/react-native/package-references/react-native-extension#reactnativeextensionprops-type).

So in order for your app's name to be displayed correctly to your user when they are connecting their social accounts,
you need to provide this prop.

You can read more about the social module [here](/react-native/package-references/client#auth-social-submodule).


# Solana integration



The `@dynamic-labs/solana-extension` integrates your application with the `@solana/web3.js` library, providing a `Connection` and `Signer` to interact with the Solana blockchain.

### Installation

To install the Solana extension, run the following command:

```
npm install @dynamic-labs/solana-extension@alpha
```

### Setup

Incorporate the Solana extension into your client using the extend method to add getSigner and getConnection methods:

```ts
import { createClient } from '@dynamic-labs/client'
import { SolanaExtension } from '@dynamic-labs/solana-extension'

/**
 * Creates and extends the client with Solana blockchain functionality.
 */
export const dynamicClient = createClient({
  environmentId: 'YOUR-ENVIRONMENT-ID',
}).extend(SolanaExtension())
```

### Polyfils

Your React Native project might require polyfills for certain global objects. Install the necessary polyfills and set them up as follows

**Installation**

```
npm install text-encoding react-native-get-random-values buffer
```

Include the following code in your project's entry point to set up the polyfills:

```ts
import { Buffer } from 'buffer'
import 'react-native-get-random-values'

global.TextEncoder = require('text-encoding').TextEncoder
global.Buffer = Buffer
```

### Usage

After setup, you can interact with the Solana blockchain. Below is an example of a component that sends 1 SOL to a specified wallet address:

```tsx
import { Button } from 'react-native'
import { FC } from 'react'
import { dynamicClient } from '<path to client file>'

import {
  LAMPORTS_PER_SOL,
  PublicKey,
  SystemProgram,
  TransactionMessage,
  VersionedTransaction,
} from '@solana/web3.js'

interface Send1SolButtonProps {
  destinationAddress: string
}

/**
 * Renders a button that sends 1 SOL to a given address.
 */
const Send1SolButton: FC<Send1SolButtonProps> = ({ destinationAddress }) => {
  const send = async () => {
    const wallet = dynamicClient.wallets.primary

    if (!wallet) return

    const connection = dynamicClient.solana.getConnection({
      commitment: 'singleGossip',
    })
    const signer = dynamicClient.solana.getSigner({ wallet })

    const { blockhash } = await connection.getLatestBlockhash()
    const amountInLamports = 1 * LAMPORTS_PER_SOL
    const fromKey = new PublicKey(wallet.address)
    const toKey = new PublicKey(destinationAddress)

    const instructions = [
      SystemProgram.transfer({
        fromPubkey: fromKey,
        lamports: amountInLamports,
        toPubkey: toKey,
      }),
    ]

    const messageV0 = new TransactionMessage({
      instructions,
      payerKey: fromKey,
      recentBlockhash: blockhash,
    }).compileToV0Message()

    const transaction = new VersionedTransaction(messageV0)
    const { signature } = await signer.signAndSendTransaction(transaction)

    console.log('Successful transaction signature:', signature)
  }

  return <Button title="Send" onPress={send} />
}
```


# Troubleshooting Common Issues



You might come across some issues when integrating our client. Here are some common cases and how you can handle them:

### After installing a new package, app fails to initialize with error "Maximum call stack size exceeded"

We have noticed this might happen with some packages due to import order complications.
In order to fix this, please ensure you are importing from our `@dynamic-labs/` packages *before* you import other react native related libraries.

Known libraries with this issue for now include only `react-native-gesture-handler`.

### Expo Web Support

At this time, our SDK only supports Expo for iOS and Android. Unfortunately, Expo Web is not yet fully supported. If you are working with an Expo project and intend to target web, you may encounter compatibility issues. We are actively working to expand support in future updates


# Viem integration



We provide a lightweight viem extension to our client which introduces 2 simple methods to integrate with Viem:
`client.viem.createPublicClient()` and `client.viem.createWalletClient()`.

<Note>
  Since our client was built with a modular approach, this package comes as a
  standalone, so to reduce the client's package size.
</Note>

## Installation

Simply run the following in your terminal:

```
npm i @dynamic-labs/viem-extension@alpha
```

## Creating Viem clients

First, extend your client with our extension:

```typescript
import { ViemExtension } from '@dynamic-labs/viem-extension'

export const dynamicClient = createClient({
  environmentId: 'YOUR-ENVIRONMENT-ID',
}).extend(ViemExtension())
```

Now you can easily leverage all the power of Viem by creating wallet and public clients:

```typescript
import { mainnet } from 'viem/chains'

// Create a public client using one of viem's provided chains
const publicViemClient = dynamicClient.viem.createPublicClient({ chain: mainnet })

const lookupBalance = (address: `0x${string}`) =>
  publicViemClient.getBalance({ address })

// Create a wallet client
const walletViemClient = dynamicClient.viem.createWalletClient({
  wallet: dynamicClient.wallets.primary,
})

const signMessage = (message: string) =>
  walletViemClient.signMessage({ message })
```

You can read more about our viem integration [here](/react-native/package-references/viem-extension).


# Components Introduction



<Tip>
  You can see the full UI implemented at any time in our demo environment:
  [https://demo.dynamic.xyz](https://demo.dynamic.xyz)
</Tip>

### UI Components

You have the choice of using the all in one UI component (Dynamic Widget), or individual components. Note that the widget does not render the bridge widget, this must always be added seperately.

<Warning>
  You should never use the DynamicWidget and the standalone components together,
  except for the Bridge Widget.
</Warning>

#### Dynamic Widget

The [**DynamicWidget**](/react-sdk/components/dynamicwidget) combines all three parts of the UI into one out of the box widget.

The three parts of the UI which are bundled into the Widget are listed below along with their standalone components if you wish to use them seperately:

#### The Connect button

[**DynamicConnectButton**](/react-sdk/components/dynamicconnectbutton) is a standalone button to handle connecting wallets. It shows up by default with the text "Connect Wallet".

#### The User Profile

[**DynamicUserProfile**](/react-sdk/components/dynamicuserprofile) is responsible for displaying the dialog or dropdown of the user's profile information, connected wallets, and more.

#### The Navigation

[**DynamicWidgetNav**](/react-sdk/components/dynamicwidgetnav) is responsible for rendering the network picker and the connected account field.

### Dynamic Embedded Widget

[**DynamicEmbeddedWidget**](/react-sdk/components/dynamicembeddedwidget) is a standalone component that renders the signup/login flow along with the user profile directly.

#### Bridge Widget

The [**DynamicBridgeWidget**](/react-sdk/components/dynamicbridgewidget) is a standalone component that renders a bridge UI so that users can bridge accross different chains.

#### The Multi Wallet Prompts Widget

The [**DynamicMultiWalletPromptsWidget**](/react-sdk/components/dynamicmultiwalletpromptswidget) displays the multi wallet prompts in a modal, useful when UserProfile is not available


# DynamicBridgeWidget

The `DynamicBridgeWidget` component allows you to handle multi chain bridging

## Usage

To get the bridge component setup, you’ll want to have your desired chains enabled In the Dynamic Dashboard, for example Ethereum and Starknet. Then, in your `index.tsx` (or wherever you render `DynamicContextProvider`, specify the `initialAuthenticationMode ` and [`bridgeChains`](/react-sdk/objects/wallets-by-chain) prop like so:

```typescript
<DynamicContextProvider
  settings={{
    environmentId: "ENV_ID",
    initialAuthenticationMode: "connect-only",
    bridgeChains: [
      {
        chain: "EVM",
      },
      {
        chain: "STARK",
      },
    ],
  }}
>
  <App />
</DynamicContextProvider>
```

Then, in `app.tsx`:

```typescript
import { DynamicBridgeWidget } from "@dynamic-labs/sdk-react-core";

export default function App() {
  return (
    <div className="App">
      <DynamicBridgeWidget />
    </div>
  );
}
```

And that's it! Now you're rendering our bridge widget.


# DynamicConnectButton



## Settings

Settings available when you first initialize `DynamicWidget` in your App.

| Method                            | Description                                                                                      |
| --------------------------------- | ------------------------------------------------------------------------------------------------ |
| buttonContainerClassName: string; | This is an optional component passed in to DynamicConnectButton.                                 |
| buttonClassName: string;          | Specifies the type of display used when a user clients on the Dynamic Widget. Defaults to modal. |
| children: ReactNode;              | React components to be passed as children of the DynamicConnectButton                            |

## Examples

#### Add children

```TypeScript
const children = (
  <div data-testid='exampleChild'>Connect a new wallet!</div>
) as React.ReactElement;

export const LoginView = () => (
  <DynamicConnectButton>{children}</DynamicConnectButton>
);
```


# DynamicEmbeddedWidget



### Summary

This component will render the signup/login UI as well as the user profile all in one on the page. It's useful as an alternative to DynamicWidget, which first renders a button that brings up the flow.

You can see this component in action on our demo: [https://demo.dynamic.xyz/](https://demo.dynamic.xyz/)

| Parameter     | Type                                   | Description                                                                                                                                                  |
| ------------- | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `background?` | `'default' \| 'none' \| 'with-border'` | Specifies what kind of background to apply , defaults to 'default'. Check the playground section to see the differences.                                     |
| `className?`  | `string`                               | Allows setting a className to the container component. Note: this does not work when [Shadow Dom](/design-customizations/css/custom-css#summary) is enabled! |
| `style?`      | `CSSProperties`                        | Allows passing explicit style to the container, such as setting a fixed height.                                                                              |

### Playground

See how it works live! Play with the background options to see their effect!

<iframe frameBorder="0" width="100%" height="600px" src="https://readme-embeds.dynamic.xyz/embedded-widget" />

### Usage

```jsx
import {
  DynamicEmbeddedWidget,
  DynamicContextProvider,
} from "@dynamic-labs/sdk-react-core";
import { EthereumWalletConnectors } from "@dynamic-labs/ethereum";
import "./App.css";

function App() {
  return (
    <div className="App">
      <DynamicContextProvider
        settings={{
          environmentId: "XXXX",
          walletConnectors: [EthereumWalletConnectors],
        }}
      >
        <div className="widget-container">
          {/* background can be none, default or with-border */}
          <DynamicEmbeddedWidget background="default" />
        </div>
      </DynamicContextProvider>
    </div>
  );
}

export default App;
```


# DynamicMultiWalletPromptsWidget



### Summary

The DynamicMultiWalletPromptsWidget provides a UI for multi-wallet without it being bundled as part of the DynamicWidget or DynamicUserProfile.

<Note>
  This component should not be used with DynamicWidget or DynamicUserProfile.
</Note>

This component provides the same multi-wallet prompts which are found in the DynamicWidget and used when the user changes wallets or starts linking a new wallet, but in a standalone modal.

### Example

```tsx
import {
  DynamicContextProvider,
  DynamicMultiWalletPromptsWidget,
} from '@dynamic-labs/sdk-react-core';

const App = () => {
  return (
    <DynamicContextProvider settings={{ environmentId: '<env id>' }}>
      <DynamicMultiWalletPromptsWidget />

      <Views />
    </DynamicContextProvider>
  )
}
```

Now the multi-wallet prompts will open automatically in a modal when the user switches their wallet or starts linking a new wallet

<Frame>
  <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/headless-multi-wallet-1.png" />
</Frame>

<Frame>
  <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/headless-multi-wallet-2.png" />
</Frame>


# DynamicUserProfile



### Summary

The DynamicUserProfile component is responsible for displaying the dialog or dropdown of the user's profile information, connected wallets, and more.

*Note: it should be placed as high as possible in the component tree to guarantee that the component is always rendered.*

### Settings

| Prop                             | Description                                                                                                                                                           |
| -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| variant?: 'modal' \| 'dropdown'; | Specifies whether to show DynamicUserProfile as a centered modal or a dropdown. The component in the dropdown type renders relative to the parent. Defaults to modal. |

### Usage

#### Control opening and closing

You can simply call `setShowDynamicUserProfile(boolean)` to show or close the DynamicUserProfile.

```TypeScript
import React from "react";
import {
  DynamicUserProfile,
  useDynamicContext
} from "@dynamic-labs/sdk-react-core";

export const Example = () => {
  const { setShowDynamicUserProfile } = useDynamicContext();
  return (
    <>
      <button onClick={() => setShowDynamicUserProfile(true)}>
        Click to open DynamicUserProfile!
      </button>
      <DynamicUserProfile />
    </>
  );
};
```

#### Callback at closing or opening

You can use the `showDynamicUserProfile` property to trigger the logic after opening/closing the widget.

```java
import React from "react";
import {
  DynamicWidgetUserProfile,
  useDynamicContext
} from "@dynamic-labs/sdk-react-core";

export const Example = () => {
    const { showDynamicUserProfile } = useDynamicContext();
  React.useEffect(() => {
        if (showDynamicUserProfile) {
        /* On widget opens */
    }   else {
      /* On widget closes */
    }
  },
  [showDynamicUserProfile])
  return null;
}
```


# DynamicWidget



### Summary

Once a user connects their wallet, you may want to display a navigation component to show the current wallet and connected network. Additionally, if you collected information during onboarding, you may want to display the user's profile so they can edit their information, add additional wallets (if you [enabled multiwallet](/wallets/advanced-wallets/multi-wallet)), and more.

The DynamicWidget is the parent component that has all these features available in it. If you want to explore sub-components for more granular control, then see [DynamicConnectButton](/react-sdk/components/dynamicconnectbutton) [DynamicUserProfile](/react-sdk/components/dynamicuserprofile) and the [DynamicNav](/react-sdk/components/dynamicwidgetnav) components.

If you need to embed the modal directly into your site without the need for button first, then we recommend exploring [the EmbeddedWidget](/react-sdk/components/dynamicembeddedwidget) instead.

### Settings

There are two settings available when you initialize the `DynamicWidget` in your App.

| Method                            | Description                                                                                                              |
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| innerButtonComponent?: ReactNode; | This is an optional component passed in to DynamicConnectButton. Defaults to Connect your wallet                         |
| variant?: 'modal' \| 'dropdown';  | Specifies whether to show DynamicWidget as a centered modal or a dropdown from the DynamicNav button. Defaults to modal. |

### Examples

#### variant = modal

```TypeScript
import { DynamicContextProvider, DynamicWidget} from '@dynamic-labs/sdk-react-core';

const App = () => (
  <DynamicContextProvider
     settings={{
       environmentId: 'Enter your Environment ID here'
    }}>
    <DynamicWidget variant='modal' />
  </DynamicContextProvider>
);

export default App;
```

<img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/DynamicWidgetVariantModal.gif" />

#### variant = dropdown

```TypeScript
import { DynamicContextProvider, DynamicWidget} from '@dynamic-labs/sdk-react-core';

const App = () => (
  <DynamicContextProvider
     settings={{
       environmentId: 'Enter your Environment ID here'
    }}>
    <DynamicWidget variant='dropdown' />
  </DynamicContextProvider>
);

export default App;
```

<img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/DynamicWidgetVariantDropdown.gif" />


# DynamicNav



### Summary

The DynamicNav component is responsible for rendering the network picker and the connected account field.

Typically, if you want to use the DynamicNav, we recommend using the DynamicWidget since it's connected to the DynamicUserProfile.

<Note>
  ### 🚧 A duplicate widget is displayed.

  Make sure you do not render `DynamicWidget` along with `DynamicNav`. These components should not be used together.
</Note>

### Usage

```JavaScript
import React from "react";
import {
  useDynamicContext,
  DynamicNav
} from "@dynamic-labs/sdk-react-core";

export const AppNavigation = () => {
  const { user } = useDynamicContext();
  return (
    <nav className="nav">
        <a className="nav__element" href="/">Home</a>
        {
            user && (
                <div className="nav_element">
                <DynamicNav />
            </div>
            )
        }
    </nav>
  );
};
```


# Dynamic Events



### Summary

An alternative to using [event callbacks](/react-sdk/events/events-introduction#event-callbacks) or [`useDynamicEvents`](/react-sdk/hooks/usedynamicevents) react hook is to use `dynamicEvents` emitter directly.

It can used anywhere, even outside of a React component.

If you want to listen to events in a React component, you might want to use the `useDynamicEvents` hook instead, as it will automatically unsubscribe when the component is unmounted.

### Usage

```TypeScript
import { dynamicEvents } from "@dynamic-labs/sdk-react-core";

dynamicEvents.on("primaryWalletChanged", (newPrimaryWallet) => {
  console.log('primaryWalletChanged was called', newPrimaryWallet);
});

dynamicEvents.on("primaryWalletNetworkChanged", (newNetwork) => {
  console.log('primaryWalletNetworkChanged was called', newNetwork);
});
```

<Note>
  You can see the available public events
  [here](/react-sdk/hooks/usedynamicevents)
</Note>


# Events Introduction



Events are a way to listen for SDK events and run your own code at certain points.

### Dynamic Events

An alternative to using event callbacks or [`useDynamicEvents`](/react-sdk/hooks/usedynamicevents) react hook is to [use `dynamicEvents` emitter directly.](/react-sdk/events/dynamic-events)

```jsx
dynamicEvents.on(eventName, listener);
```

### Event callbacks

You do not need to import anything extra to make use of event callbacks, instead simply add them using the events field inside the settings prop on [DynamicContextProvider](/react-sdk/providers/dynamiccontextprovider).

If you are looking for synchronous actions that will block the user until completion, please look at [**Handlers**](/react-sdk/handlers/handlers-introduction)

```jsx
<DynamicContextProvider
  settings={{
    events: {
      // events callbacks go here
    },
  }}
>
  ...
</DynamicContextProvider>
```

[**onAuthFlowCancel**](/react-sdk/events/onauthflowclose): Called when the auth modal is closed before authentication is successful (alongside `onAuthFlowClose`).

[**onAuthFlowClose**](/react-sdk/events/onauthflowclose): Called whenever the auth flow modal is closed.

[**onAuthFlowOpen**](/react-sdk/events/onauthflowopen): Called after the auth flow modal is opened.

[**onAuthFailure**](/react-sdk/events/onauthfailure): Called when authentication fails.

[**onAuthInit**](/react-sdk/events/onauthinit): Called when an authentication process begins.

[**onAuthSuccess**](/react-sdk/events/onauthsuccess): Called after the user successfully authenticates.

[**onEmbeddedWalletCreated**](/react-sdk/events/onembeddedwalletcreated): Called after embedded wallet created for user.

[**onLogout**](/react-sdk/events/onlogout): Called after the user logs out.

[**onOtpVerificationResult**](/react-sdk/events/onotpverificationresult): Called when an email or SMS OTP verification succeeds or fails.

[**onSignedMessage**](/react-sdk/events/onsignedmessage): Called when the user signs a message and it allows you to act on that message.

[**onUserProfileUpdate**](/react-sdk/events/onuserprofileupdate): Called when the user successfully updated their profile.

[**onWalletAdded**](/react-sdk/events/onwalletadded): Called when a new wallet is added to the user wallets.

[**onWalletRemoved**](/react-sdk/events/onwalletremoved): Called when a wallet is removed from the user wallets.


# onAuthFailure



### Summary

This callback will be called when an authentication process fails.

This can be either caused by an error or by the user cancelling the process.

### Usage

```TypeScript
<DynamicContextProvider
  settings={{
    events: {
      onAuthFailure: (method, reason) => {
        console.log('onAuthFailure was called', method, reason);
      }
    }
  }}
>
 {/* ... rest of your app ... */}
</DynamicContextProvider>
```

### Callback Argument Values

First argument `method`:

```TypeScript
{
  type: "email";
  email: string;
  isEmailRecoveryFlow?: boolean;
  // Same as "email"
  option: string;
} | {
  type: "social";
  provider: ProviderEnum;
  // Same as "provider"
  option: ProviderEnum;
} | {
  type: "wallet";
  address: string;
  connectorName: string;
  // Same as "connectorName"
  option: string;
} | {
  type: "sms";
  phone: PhoneData;
  // Same as "phone"
  option: PhoneData;
}
```

Second argument `reason`:

```TypeScript
'user-cancelled' | { error: unknown }
```

<Note>
  For details about ProviderEnum, see [here](/react-sdk/objects/verified-credential#providerenum)

  For details about PhoneData, see [here](/react-sdk/objects/phone-data)
</Note>


# onAuthFlowCancel



### Summary

This callback will be called when the auth modal is closed before authentication is successful.
Note that `onAuthFlowClose` will also still be called — if you want an event that is fired whenever the auth modal is closed,
you should listen to it.

### Usage

```TypeScript
<DynamicContextProvider
  settings={{
    events: {
      onAuthFlowCancel: () => {
        console.log('Authentication was cancelled');
      }
    }
  }}
>
 {/* ... rest of your app ... */}
</DynamicContextProvider>
```

### Callback Argument Value

None


# onAuthFlowClose



### Summary

This callback will be called after the auth flow modal is closed.

### Usage

```TypeScript
<DynamicContextProvider
  settings={{
    events: {
      onAuthFlowClose: () => {
        console.log('onAuthFlowClose was called');
      }
    }
  }}
>
 {/* ... rest of your app ... */}
</DynamicContextProvider>
```

### Callback Argument Value

None


# onAuthFlowOpen



### Summary

This callback will be called after the auth flow modal is opened.

### Usage

```TypeScript
<DynamicContextProvider
  settings={{
    events: {
      onAuthFlowOpen: () => {
        console.log('onAuthFlowOpen was called');
      }
    }
  }}
>
 {/* ... rest of your app ... */}
</DynamicContextProvider>
```

### Callback Argument Value

None


# onAuthInit



### Summary

This callback will be called when an authentication process initiates.

*   For wallets, this is when signature is requested.
*   For email and SMS, this is as soon as user provides the phone number or email address.
*   For social, this is as soon as user selects a social provider.

### Usage

```TypeScript
<DynamicContextProvider
  settings={{
    events: {
      onAuthInit: (args) => {
        console.log('onAuthInit was called', args);
      }
    }
  }}
>
 {/* ... rest of your app ... */}
</DynamicContextProvider>
```

### Callback Argument Value

```TypeScript
{
  type: "email";
  email: string;
  isEmailRecoveryFlow?: boolean;
  // Same as "email"
  option: string;
} | {
  type: "social";
  provider: ProviderEnum;
  // Same as "provider"
  option: ProviderEnum;
} | {
  type: "wallet";
  address: string;
  connectorName: string;
  // Same as "connectorName"
  option: string;
} | {
  type: "sms";
  phone: PhoneData;
  // Same as "phone"
  option: PhoneData;
}
```

<Note>
  For details about ProviderEnum, see [here](/react-sdk/objects/verified-credential#providerenum)

  For details about PhoneData, see [here](/react-sdk/objects/phone-data)
</Note>


# onAuthSuccess



### Summary

This callback will be called after the user successfully authenticates.

### Usage

```TypeScript
<DynamicContextProvider
  settings={{
    events: {
      onAuthSuccess: (args) => {
        console.log('onAuthSuccess was called', args);
        // you can get the jwt by calling the getAuthToken helper function
        const authToken = getAuthToken();
        console.log('authToken', authToken);
      }
    }
  }}
>
 {/* ... rest of your app ... */}
</DynamicContextProvider>
```

### Callback Argument Value

```TypeScript
{
  ~authToken: string;~ // deprecated - use getAuthToken() instead
  handleLogOut: () => Promise<void>;
  isAuthenticated: boolean;
  primaryWallet: Wallet | null
  user: UserProfile;
  ~walletConnector: WalletConnector;~
}
```

<Note>
  For details about UserProfile, see [here](/react-sdk/objects/userprofile)

  For details about WalletConnector, see [here](/react-sdk/objects/walletconnector)

  For details about Wallet, see [here](/react-sdk/objects/wallet)
</Note>


# onEmbeddedWalletCreated



### Summary

This callback will be called when the user successfully has an embedded wallet created for them. For more info, see the [embedded wallets section of the documentation](/wallets/embedded-wallets/dynamic-embedded-wallets).

### Usage

```TypeScript
<DynamicContextProvider
  settings={{
    events: {
      onEmbeddedWalletCreated: (jwtVerifiedCredential, ~authToken~) => {
        console.log('onEmbeddedWalletCreated', jwtVerifiedCredential);
        console.log('onEmbeddedWalletCreated authToken', getAuthToken());
      },
    }
  }}
>
 {/* ... rest of your app ... */}
</DynamicContextProvider>
```

### Callback Argument Value

```
JwtVerifiedCredential
~authToken: string;~ // deprecated - use getAuthToken() instead
```


# onLogout



### Summary

This callback will be called after the user logs out.

It will be called either when the user explicitly logged out by calling `handleLogOut`
or when the user's token expired.

### Usage

```TypeScript
<DynamicContextProvider
  settings={{
    events: {
      onLogout: (args) => {
        console.log('onLogout was called', args);
      }
    }
  }}
>
 {/* ... rest of your app ... */}
</DynamicContextProvider>
```

### Callback Argument Value

```TypeScript
user: UserProfile | undefined;
```

If your auth mode is connect-only, then the user argument will be undefined, otherwise the user argument will be the user object that just logged out.

<Note>
  For details about UserProfile, see [here](/react-sdk/objects/userprofile)
</Note>


# onOtpVerificationResult



### Summary

This event will be called whenever an OTP token is submitted for verification.

It provides paramteres that will indicate whether the verification was successful or not,
and also to differentiate whether it was an email or SMS OTP, through the following signature:

```typescript
type onOtpVerificationResult = (
  isSuccess: boolean,
  destination: 'email' | 'sms'
) => void
```

<Warning>
  This event will **not** be called by the `useOtpVerificationRequest` hook,
  or by email/phone number verification from `updateUser`, under the
  `useUserUpdateRequest` hook. You are expected to decide when verification
  should be declared "successful" when using these methods
</Warning>

### Usage

```jsx
<DynamicContextProvider
  settings={{
    events: {
      onOtpVerificationResult: (isSuccess, destination) => {
        console.log(
          destination + ' verification ' + (isSuccess ? 'success' : 'failed')
        )
      },
    },
  }}
>
  {/* ... rest of your app ... */}
</DynamicContextProvider>
```


# onSignedMessage



### Summary

This callback will be called after a user has signed a message, and it allows you to act on that message.

### Usage

```jsx
<DynamicContextProvider
  settings={{
    events: {
      onSignedMessage: ({ signatureHash, signedMessage }) => {
        console.log(
          `onSignedMessage was called: ${signatureHash}, ${signedMessage}`
        );
      },
    },
  }}
>
  {/* ... rest of your app ... */}
</DynamicContextProvider>
```

### Callback Argument Value

String


# onUserProfileUpdate



## Summary

This callback will be called when user profile information is updated. To learn more about information capture, see the [here](/users/information-capture).

## Usage

```typescript
<DynamicContextProvider
  settings={{
    events: {
      onUserProfileUpdate: (user) => {
        console.log('onUserProfileUpdate was called', user);
      }
    }
  }}
>
 {/* ... rest of your app ... */}
</DynamicContextProvider>
```

## Callback Argument Value

```typescript
user: UserProfile
```


# onWalletAdded



### Summary

This callback will be called when a new wallet is added to the userWallets when signing in with a wallet, linking a new wallet (`connect-and-sign` mode) or connecting a new wallet (`connect-only` mode).

### Usage

```TypeScript
<DynamicContextProvider
  settings={{
    eventsCallbacks: {
      onWalletAdded: (args) => {
        console.log('onWalletAdded was called', args);
      }
    }
  }}
>
 {/* ... rest of your app ... */}
</DynamicContextProvider>
```

### Callback Argument Value

```TypeScript
{
  wallet: Wallet;
  userWallets: Wallet[];
}
```

This is called with the wallet that was just added and the updated list of wallets.

<Note>
  For details about userWallets, see
  [here](/react-sdk/hooks/useuserwallets)
</Note>


# onWalletRemoved



### Summary

This callback will be called when a wallet is removed from the userWallets by either unlinking the wallet (`connect-and-sign` mode) or disconnecting the wallet (`connect-only` mode).

### Usage

```TypeScript
<DynamicContextProvider
  settings={{
    eventsCallbacks: {
      onWalletRemoved: (args) => {
        console.log('onWalletRemoved was called', args);
      }
    }
  }}
>
 {/* ... rest of your app ... */}
</DynamicContextProvider>
```

### Callback Argument Value

```TypeScript
{
  wallet: Wallet;
  userWallets: Wallet[];
}
```

This is called with the wallet that was just removed and the updated list of wallets.

<Note>
  For details about userWallets, see
  [here](/react-sdk/hooks/useuserwallets)
</Note>


# Examples



You'll find lots of examples throughout the guides in the documentation, from wallets to users to headless etc. Here are some of the most common:

## Wallets

[Switch primary wallet](/wallets/using-wallets/general/switch-primary-wallet)

[Get balance for all connected wallets](/wallets/using-wallets/evm/get-balance-for-all-wallets)

[Get balance for a single wallet](/wallets/using-wallets/evm/get-wallet-balance)

[Fetch token balances](/wallets/using-wallets/general/get-token-balances)

[Send a transaction](/wallets/using-wallets/evm/send-a-transaction)

[Send a transaction with Wagmi](/wallets/using-wallets/evm/send-a-transaction-wagmi)

[Send balance using embedded wallet](/wallets/using-wallets/evm/send-balance)

[Sign a message](/wallets/using-wallets/evm/sign-a-message)

[Send a legacy Solana transaction](/wallets/using-wallets/solana/send-legacy-solana-transaction)

[Send a versioned Solana transaction](/wallets/using-wallets/solana/send-versioned-solana-transaction)

## Chains

[Switch networks](/chains/network-switching#example)

## Users

[Get the user profile](/users/examples/get-the-users-profile)

## UI

[Programmatically adapt authentication methods](/design-customizations/tutorials/login-views-guide)

[Programmatically adapt the wallet selection view](/design-customizations/tutorials/wallet-list-views-guide)


# handleAuthenticatedUser



### Summary

A handler runs after the user is authenticated and allows for a custom code to handle the user object before the Dynamic SDK finishes the authentication and closes the UI.

### Usage

```jsx
<DynamicContextProvider
  settings={{
    handlers: {
      handleAuthenticatedUser: async (args) => {
        console.log("handleAuthenticatedUser was called", args);

        await customUserObjectProcess(args.user);
      },
    },
  }}
>
  {/* ... rest of your app ... */}
</DynamicContextProvider>
```

### Handler Argument Value

[UserProfile](/react-sdk/objects/userprofile) Authenticated user


# handleConnectedWallet



### Summary

A callback that runs after the wallet is connected, but before we create a user session. You can use this callback to run your logic and reject (by returning boolean false). For example, running a fraud check before establishing the connection.

### Usage

```jsx
<DynamicContextProvider
  settings={{
    handlers: {
      handleConnectedWallet: (args) => {
        console.log("handleConnectedWallet was called", args);
        // if runYourOwnLogic return true, the connection will be established, otherwise it will not
        return runYourOwnLogic();
      },
    },
  }}
>
  {/* ... rest of your app ... */}
</DynamicContextProvider>
```

### Callback Argument Value

[Wallet](/react-sdk/objects/wallet) but without the `connected` boolean.


# Handlers Introduction



Handlers is a way to programmatically customize synchronous behaviors in the SDK.
They allow running code synchronously and wait until completion, unlike [**Events**](/react-sdk/events/events-introduction) that run a-synchronous and do not block the authentication flow.

Handlers are configured in the DynamicContextProvider:

```tsx
<DynamicContextProvider
  settings={{
    handlers: {
      // handles callbacks go here
    },
  }}
>
  ...
</DynamicContextProvider>
```

[**handleConnectedWallet**](/react-sdk/handlers/handleconnectedwallet): Handler that runs before Dynamic establishes a wallet connection (Connect wallet before the authentication step).
For example This is a good place to run compliance checks.

[**handleUserOnboarding**](/react-sdk/handlers/handleuseronboarding): Handler that runs whenever the user is still in the process of authentication, but has some missing kyc data to complete before they can be fully authenticated.
If [**information capture**](https://app.dynamic.xyz/dashboard/log-in-user-profile) is enabled and has required fields or optional fields without the option to skip optional fields during onboarding turned on, the user must be updated with the required information before they're considered authenticated.
This handler is always used when it's set, which means the sdk will not display the modal to prompt for user information.
The handler must be set when using the sdk in a headless mode or if you wish to handle onboarding on your own.
For example: When implementing headless sign-in and information capture is enabled, you must have this handler set.

[**handleAuthenticatedUser**](/react-sdk/handlers/handleauthenticateduser): Handler that runs after the user authenticated but before Dynamic's finished the authentication flow.
For example: It is a good place to perform blocking actions with the user on your backend before Dynamic's SDK finished the authentication flow.


# handleUserOnboarding



### Summary

A Handler that runs whenever the user is still in the process of authentication, but has some missing kyc data to complete before they can be fully authenticated.

If [**information capture**](https://app.dynamic.xyz/dashboard/log-in-user-profile) is enabled and has required fields or optional fields without the option to skip optional fields during onboarding turned on, the user must be updated with the required information before they're considered authenticated.

This handler is always used when it's set, which means the sdk will not display the modal to prompt for user information.
The handler must be set when using the sdk in a headless mode or if you wish to handle onboarding on your own.

### Usage

```jsx
<DynamicContextProvider
  settings={{
    handlers: {
      handleUserOnboarding: async (missingFields: UserOnboardingFieldRequest[]) => {
        console.log("handleUserOnboarding was called", missingFields);
        
        /** Example
         * missingFields: [
         *  {
         *    key: 'email',
         *    required: true, 
         *    isCustom: false, // only true for custom fields
         *  },
         *  {
         *    key: 'city',
         *    required: false,
         *    isCustom: true, // only true for custom fields
         *    label: 'City',
         *  }
         * ]
        */
        
        // TODO: collect user information to return to dynamic 

        // Note: isCustom value should be the same as the one received
        return [
          { key: 'email', value: 'email@test.com', isCustom: false }, 
          { key: 'city', value: 'New York', isCustom: true }, 
        ];
      },
    },
  }}
>
  {/* ... rest of your app ... */}
</DynamicContextProvider>
```

### Types

```jsx
type HandleUserOnboarding = (
  missingFields: UserOnboardingFieldRequest[],
) => Promise<UserOnboardingFieldResponse[]>;

type UserOnboardingFieldRequest = {
  key: string;
  required: boolean;
  isCustom: boolean;
  label?: string;
};

type UserOnboardingFieldResponse = {
  key: string;
  value: string;
  isCustom: boolean;
};
```


# Hooks Introduction



Dynamic offers many different custom [React hooks](https://react.dev/learn/reusing-logic-with-custom-hooks#extracting-your-own-custom-hook-from-a-component) for different tasks within your code.

You can import them directly from the @dynamic-labs/sdk-react-core package, for example:

```jsx
import { useDynamicContext } from "@dynamic-labs/sdk-react-core";
```

Here is a list of all of the hooks and links to their documentation pages:

[**useAuthenticateConnectedUser**](/react-sdk/hooks/useauthenticatedconnecteduser)
Can be used to authenticate already connected user.

[**useConnectWithOtp**](/react-sdk/hooks/useconnectwithotp)
Connect with email or SMS OTP in headless mode.

[**useDynamicContext**](/react-sdk/hooks/usedynamiccontext)
Gives you access to the full context which is initially set via the `DynamicContextProvider` component.

[**useDynamicEvents**](/react-sdk/hooks/usedynamicevents)
Hook to subscribe to events emitted by Dynamic

[**useDynamicModals**](/react-sdk/hooks/usedynamicmodals)
Allow to programmatically show/hide some dynamic modals.

[**useDynamicScopes**](/react-sdk/hooks/usedynamicscopes)
Check if user has specific scope set using our Access List scopes or NFT/Token gating.

[**useEmbeddedReveal**](/react-sdk/hooks/useembeddedreveal)
Trigger the embedded wallet export flow manually.

[**useEmbeddedWallet**](/react-sdk/hooks/useembeddedwallet)
Helps you handle the creation of embedded wallets.

[**useFunding**](/react-sdk/hooks/usefunding)
Allow for programmatically request of the onramp UI.

[**useIsLoggedIn**](/react-sdk/hooks/useisloggedin)
Checks if user is logged in or not.

[**useOtpVerificationRequest**](/react-sdk/hooks/useotpverificationrequest)
Handle email and SMS verification and securely update user profiles.

[**usePasskeyRecovery**](/react-sdk/hooks/usepasskeyrecovery)
Trigger the passkey generation flow manually.

[**useRefreshUser**](/react-sdk/hooks/userefreshuser)
Manually refresh the user state at any point in time.

[**useReinitialize**](/react-sdk/hooks/usereinitialize)
Manually reinitialize the SDK at any point in time.

[**useSendBalance**](/react-sdk/hooks/usesendbalance)
Request Send Balance UI and optionally prepopulate the form for the user.

[**useSmartWallets**](/react-sdk/hooks/usesmartwallets)
Provides smart contract wallet related utilities.

[**useSocialAccounts**](/react-sdk/hooks/usesocialaccounts)
Trigger social sign-in, or social account linking and unlinking for an authenticated user.

[**useSwitchNetwork**](/react-sdk/hooks/useswitchnetwork)
Switches the network of a wallet

[**useSwitchWallet**](/react-sdk/hooks/useswitchwallet)
Switches the primary wallet by wallet ID.

[**useTokenBalances**](/react-sdk/hooks/usetokenbalances)
Fetch the token balances of an account on a specified network.

[**useUserUpdateRequest**](/react-sdk/hooks/useuserupdaterequest)
Update user profile information in your application.

[**useUserWallets**](/react-sdk/hooks/useuserwallets)
Get an array of the wallets for the current user/session.

[**useWalletConnectorEvent**](/react-sdk/hooks/usewalletconnectorevent)
Hook to listen for wallet connector events like account change, chain change or disconnect.

[**useWalletItemActions**](/react-sdk/hooks/usewalletitemactions)
Set of utility functions to sign with a specific wallet.

[**useWalletOptions**](/react-sdk/hooks/usewalletoptions)
Allows you to start the process of connecting to a specific wallet. It provides a function to select a wallet by wallet key.


# useAuthenticateConnectedUser



### Summary

A hook which can be used to authenticate an already connected user. It's useful when there is a need to authenticate a user who is currently in [connect-only](/wallets/advanced-wallets/connect) mode.

<Note>
  If you are using [connect-and-sign](/wallets/advanced-wallets/connect-and-sign)
  authentication mode, you don't need to use this hook . Learn more about the
  different authentication modes
  [here](/wallets/advanced-wallets/connected-vs-authenticated).
</Note>

The hook needs to be initialized within a child of `DynamicContextProvider`

### Usage

#### Available functions and states:

*   `authenticateUser` - when called starts signing already connected user.
*   `isAuthenticating` - boolean which indicates the state of authentication.

#### Example usage:

```TypeScript
const AuthenticateConnectedUserButton = () => {
  const { authenticateUser, isAuthenticating } = useAuthenticateConnectedUser();

  return (
    <button onClick={ authenticateUser } disable={ isAuthenticating }>
    { isAuthenticating ? 'Authenticating ...' : 'Authenticate connected wallet' }
    </button>
  )
}
```


# useConnectWithOtp

Allow for headless email or sms OTP authentication

### Summary

This hook allows for authenticating a user using email or sms OTP without the standard Dynamic SDK.

### How it works

The `useConnectWithOtp` hook exposes the `connectWithEmail`, `connectWithSms` and `verifyOneTimePassword` functions, with these functions you can send an OTP to a user and then verify the OTP code the user received.

The hook works with [Dynamic User Verification](/users/email-verification) provider.

| Method                | Type                                                           | Description                                                                                                                          |
| --------------------- | -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| connectWithEmail      | (email: string) => Promise                                     | Sends the OTP to the provided email                                                                                                  |
| connectWithSms        | (phone: [PhoneData](/react-sdk/objects/phone-data)) => Promise | Sends the OTP to the provided phone                                                                                                  |
| verifyOneTimePassword | (oneTimePassword: string) => Promise                           | Verify the provided OTP.<br />The returned promise will be resolved if the OTP is valid or it will be rejected if the OTP is invalid |

### Examples

#### Verify user with email OTP headless

```tsx
import { FC, FormEventHandler } from 'react';
import { useConnectWithOtp, useDynamicContext } from '@dynamic-labs/sdk-react-core';

const ConnectWithOrpView: FC = () => {
  const { user } = useDynamicContext()

  const { connectWithEmail, verifyOneTimePassword } = useConnectWithOtp();

  const onSubmitEmailHandler: FormEventHandler<HTMLFormElement> = async (
    event,
  ) => {
    event.preventDefault();

    const email = event.currentTarget.email.value;

    await connectWithEmail(email);
  };

  const onSubmitOtpHandler: FormEventHandler<HTMLFormElement> = async (
    event,
  ) => {
    event.preventDefault();

    const otp = event.currentTarget.otp.value;

    await verifyOneTimePassword(otp);
  };

  return (
    <div>
      <form key='email-form' onSubmit={onSubmitEmailHandler}>
        <input type='email' name='email' placeholder='Email' />
        <button type='submit'>Submit</button>
      </form>

      <form key='otp-form' onSubmit={onSubmitOtpHandler}>
        <input type='text' name='otp' placeholder='OTP' />
        <button type='submit'>Submit</button>
      </form>

      {!!user && (
        <p>Authenticated user:</p>
        <pre>
          {JSON.stringify(user, null, 2)}
        </pre>
      )}
    </div>
  )
}
```

This will display a field to input the email when the user is not authenticated or a field to verify the OTP code if the user is already authenticated.
Once the OTP is verified, the Dynamic SDK will be authenticated and you can find the user object using the [useDynamicContext](/react-sdk/hooks/usedynamiccontext) hook.

#### Verify user with sms OTP headless

```tsx
import { FC, FormEventHandler } from 'react';
import { useConnectWithOtp, useDynamicContext } from '@dynamic-labs/sdk-react-core';

const ConnectWithOrpView: FC = () => {
  const { user } = useDynamicContext()

  const { connectWithSms, verifyOneTimePassword } = useConnectWithOtp();

  const onSubmitSmsHandler: FormEventHandler<HTMLFormElement> = async (
    event,
  ) => {
    event.preventDefault();

    const phone = {
      dialCode: event.currentTarget.dialCode.value,
      iso2: event.currentTarget.iso2.value,
      phone: event.currentTarget.phone.value,
    };

    await connectWithSms(phone);
  };

  const onSubmitOtpHandler: FormEventHandler<HTMLFormElement> = async (
    event,
  ) => {
    event.preventDefault();

    const otp = event.currentTarget.otp.value;

    await verifyOneTimePassword(otp);
  };

  return (
    <div>
      <form key='sms-form' onSubmit={onSubmitSmsHandler}>
        <label htmlFor='iso2'>Country ISO Code:</label>
        <input type='text' name='iso2' placeholder='US' />

        <label htmlFor='dialCode'>Country Dial Code:</label>
        <input type='text' name='dialCode' placeholder='1' />

        <label htmlFor='phone'>Phone Number (without dial code):</label>
        <input type='text' name='phone' placeholder='5555555555' />
      
        <button type='submit'>Submit</button>
      </form>

      <form key='otp-form' onSubmit={onSubmitOtpHandler}>
        <input type='text' name='otp' placeholder='OTP' />
        <button type='submit'>Submit</button>
      </form>

      {!!user && (
        <p>Authenticated user:</p>
        <pre>
          {JSON.stringify(user, null, 2)}
        </pre>
      )}
    </div>
  )
}
```

This will display three fields to input the phone data when the user is not authenticated: country iso code (e.g: 'US'), country dial code (e.g. '1') and phone number (e.g. '5555555555')
If user is already authenticated, it will display a field to verify the OTP code.
Once the OTP is verified, the Dynamic SDK will be authenticated and you can find the user object using the [useDynamicContext](/react-sdk/hooks/usedynamiccontext) hook.


# useDynamicContext

Dynamic's React Context which is set via DynamicContextProvider

## accountSwitchState

Type: `AccountSwitchState`

This enum type specifies the current state of switching or linking wallets in a multi-wallet enabled environment. This can be idle, linking\_new\_wallet, switching\_primary, primary\_not\_connected

## authMode

Type: `AuthModeType`

This enum type specifies whether the SDK's users will need to sign to authenticate and prove ownership of their account, or just connect is enough. This can be connect-only or connect-and-sign

## authToken

Type: `getAuthToken()`

JSON web token (JWT) stored if the user is authenticated. This is the authenticated user's token. See: [backend verification](/authentication-methods/how-to-validate-users-on-the-backend#option-2-do-it-yourself-verification).

## awaitingSignatureState

Type: `AwaitingSignatureState`

This enum type specifies the current state while waiting for a signature of a new wallet to link in a multi-wallet enabled environment. This can be idle, linking\_new\_wallet, transferring\_wallet

## handleLogOut

Type: `() => Promise`

A helper-method to log-out the currently authenticated user account.

## handleUnlinkWallet

Type: `(walletId: string) => Promise`

Helper function to unlink a wallet given a wallet ID from the user account.

## isVerificationInProgress

Type: `boolean`

Whether any verifications are in progress for the current user (ex. connect, sign and email login verifications).

## loadingNetwork

Type: `boolean`

Returns true when the SDK is looking for the network of the primary wallet's wallet connector.

## multiWalletWidgetState

Type: `MultiWalletWidgetState`

This enum type specifies the current state of the widget in a multi-wallet enabled environment. This can be idle, awaiting\_account\_switch, awaiting\_connection, awaiting\_signature, detected\_known\_secondary\_wallet, detected\_new\_wallet

## network

Type: `number | undefined`

This represents the current network selected for the primary wallet

## networkConfigurations

Type: `NetworkConfigurationMap | undefined`

This contains the current network configurations for various EVM and Solana chains. These information includes chain ID, RPC URLs, and other important information.

## primaryWallet

Type: `Wallet | null`

An instance of a Wallet that represents the most recent connected Wallet.

## qrcodeUri

Type: `string`

URI for the QR code to scan in the current modal.

## redirectUrl

Type: `string | undefined`

URL used for redirecting back after connecting with farcaster

## rpcProviders

Type: `DynamicRPCProviders`

List of RPC providers and their settings to communicate with web3 services. These providers are configured on Dynamic's dashboard.

## sdkHasLoaded

Type: `boolean`

Whether the sdk's data is done loading — useful for avoiding stale data flashes by not rendering until the sdk is done loading.

## selectedTabIndex

Type: `number`

The selected tab index when using the [wallet list view tabs](/design-customizations/tutorials/wallet-list-views-guide) feature

## setAuthMode

Type: `Dispatch<SetStateAction<AuthModeType>>`

Sets the current authentication mode of branded wallets ("connect-and-sign" or "connect-only"). Note: does nothing if the user is already logged in. You might also want to toggle the [initialAuthenticationMode](/react-sdk/providers/dynamiccontextprovider) prop.

## setMultiWalletWidgetState

Type: `MultiWalletWidgetStateSetter`

It controls the intended state for a multi-wallet enabled environment.

## setSelectedTabIndex

Type: `Dispatch<SetStateAction<number>>`

Sets the selected tab index when using the [wallet list view tabs](/design-customizations/tutorials/wallet-list-views-guide) feature

## setShowAuthFlow

Type: `Dispatch<SetStateAction>`

It controls whether or not to display the DynamicAuthFlow SDK component.

## setShowQrcodeModal

Type: `Dispatch<SetStateAction>`

It controls whether or not to display a QR code modal.

## showAuthFlow

Type: `boolean`

The value set by setShowAuthFlow, it controls whether or not to display the DynamicAuthFlow SDK component.

## showQrcodeModal

Type: `boolean`

Value set by setShowQrcodeModal, it controls whether or not to display a QR code modal.

## user

Type: `UserProfile | null`

The user object of the currently authenticated user.

## userWithMissingInfo

Type: `UserProfile | undefined`

Present if the user is authenticated but hasn't finished onboarding (i.e. mandatory info capture/MFA), undefined if the user hasn't started the login/signup process or has already fully completed it.

<Tip>
  If you're looking to access the current user or session's wallets, like you
  would with `linkedWallets`, `secondaryWallets` and `connectedWallets` in
  previous versions, check out the new
  [useUserWallets](/react-sdk/hooks/useuserwallets) hook.
</Tip>

### Examples

#### `setShowAuthFlow` - use it to start signature request from user

```TypeScript
const ConnectButton = () => {
  const { setShowAuthFlow } = useDynamicContext();
  return (
    <button
      onClick={() => setShowAuthFlow(true)}
    >
      Connect your wallet
    </button>
  );
};
```

#### `primaryWallet` - use it to make operations on the currently active wallet

*   method to simply fetch balance of users wallet:

```TypeScript
const getBalance = async () => {
  const balance = await primaryWallet.getBalance();
  return balance;
};
```

*   get users primary wallet

```TypeScript
const getAddress = () => {
  const address = primaryWallet.address;
  return address;
};
```

*   get all connected wallets by primary wallet connector

```TypeScript
const getConnectedAccounts = async () => {
  const connectedAccounts = await primaryWallet?.connector.getConnectedAccounts();
  return connectedAccounts;
};
```

#### `handleLogOut` - use it to log out currently logged in user

```TypeScript
const LogoutButton = () => {
  const { handleLogOut } = useDynamicContext();
  return (
    <button
      onClick={() => handleLogOut()}
    >
      Log out
    </button>
  );
};
```

#### `authToken` - use it to get authenticated user jwt

```TypeScript
const getJwt = () => {
  const jwt = authToken;
  return jwt;
};
```

#### `isVerificationInProgress` - use it along with [**onAuthFlowClose**](/react-sdk/events/onauthflowclose) to tell whether it was manually closed or closed due to a verification process

```TypeScript
<DynamicContextProvider
  settings={{
    events: {
      onAuthFlowClose: () => {
        if (isVerificationInProgress) console.log('Closed due to verification process');
        else console.log('Manually closed by client');
      }
    }
  }}
>
 {/* ... rest of your app ... */}
</DynamicContextProvider>
```


# useDynamicEvents



### Summary

The useDynamicEvents hook can be used to subscribe to events emitted by Dynamic.

### Usage

Available function and states

| Event                       | Arguments                      | Description                                           |
| --------------------------- | ------------------------------ | ----------------------------------------------------- |
| primaryWalletChanged        | `newPrimaryWallet: Wallet`     | Called whenever the primary wallet changes            |
| primaryWalletNetworkChanged | `newNetwork: number \| string` | Called whenever the primary wallet network changes    |
| walletAdded                 | `newWallet: Wallet`            | Called whenever a wallet is added to the user profile |

### Example

```tsx
import { useDynamicEvents } from "@dynamic-labs/sdk-react-core";

const useDynamicEventsExample = () => {
  useDynamicEvents("primaryWalletChanged", async (newPrimaryWallet) => {
    console.log(newPrimaryWallet);
  });

  useDynamicEvents("primaryWalletNetworkChanged", async (newNetwork) => {
    console.log(newNetwork);
  });

  useDynamicEvents("walletAdded", async (newWallet) => {
    console.log(newWallet);
  });
};
```


# useDynamicModals



### Summary

This hook can be used to programmatically show/hide some dynamic modals.
The hook needs to be initialized within a child of [DynamicContextProvider](/react-sdk/providers/dynamiccontextprovider).

### Usage

Available functions and states:

| Method                    | Type                       | Description                                            |
| ------------------------- | :------------------------- | :----------------------------------------------------- |
| setShowLinkNewWalletModal | `Dispatch<SetStateAction>` | Use it to link a new wallet when the user is logged in |

### Example

```TypeScript
const LinkNewWalletButton = () => {
  const { setShowLinkNewWalletModal } = useDynamicModals();
  return (
    <button
      onClick={() => setShowLinkNewWalletModal(true)}
    >
      Link a new wallet
    </button>
  );
};
```


# useDynamicScopes



If you want to prevent users from reaching specific parts of your app you can use this hook to check if user has specific scope set using our Access List scopes or NFT/Token gating.

### How it works?

The scopes are send as a standard JWT scopes field. Using this hook you can easily check if user has access to a specific scope or an array of scopes connected by logical operators like: `AND`, `OR`.

### Hook return values

| Name          | Type                                                                    | Description                                                                                                                                 |
| :------------ | :---------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------ |
| userScopes    | string\[]                                                               | All scopes returned in JWT for specific user                                                                                                |
| userHasScopes | (scopes: string\[] \| string, logicOperator?: 'AND' \| 'OR') => boolean | Function used to check if logged user has specific scope or list of scopes connected by logicOperator. By default the logicOperator is `OR` |

### Limitations

Combinations of AND & OR are not supported. You can only use one logic operator at a time. Let us know in slack if this is something you need!

### Examples

*   Check if user has `'signing'` scope

    ```javascript TypeScript
    import { useDynamicScopes } from "@dynamic-labs/sdk-react";

    const App = () => {
      const { userHasScopes } = useDynamicScopes();

      return (
        <div>
          {userHasScopes("signing")
            ? "This user can sign something"
            : "This user cannot sign something"}
        </div>
      );
    };
    ```

*   Check if user has `'signing'` and `'creating'` scopes

    ```javascript TypeScript
    import { useDynamicScopes } from "@dynamic-labs/sdk-react";

    const App = () => {
      const { userHasScopes } = useDynamicScopes();

      return (
        <div>
          {userHasScopes(["signing", "creating"], "AND")
            ? "This user can sign and create something"
            : "This user cannot sign and create something"}
        </div>
      );
    };
    ```

*   Check if user has `'signing'` or `'creating'` scopes

    ```javascript TypeScript
    import { useDynamicScopes } from "@dynamic-labs/sdk-react";

    const App = () => {
      const { userHasScopes } = useDynamicScopes();

      return (
        <div>
          {userHasScopes(["signing", "creating"], "OR")
            ? "This user can sign or create something"
            : "This user cannot sign or create something"}
        </div>
      );
    };
    ```


# useEmbeddedReveal



### Summary

Used to manually open the export flow and let a user export their wallet credentials. Read more in the [embedded wallet export section](/wallets/embedded-wallets/reveal-export).

### Usage

```jsx
import { useEmbeddedReveal } from "@dynamic-labs/sdk-react-core";

const { initExportProcess } = useEmbeddedReveal();

<button onClick={() => initExportProcess()}></button>;
```


# useEmbeddedWallet



This hook gives you control over when you create an embedded wallet for any given user, the ability to check if a user currently has an embedded wallet, the ability to create and validate user sessions, and other helpful methods.

You can also utilize the methods in combination with each other to create a fully headless implementation of embedded wallet onboarding flows, check out the [headless embedded guides](/headless) for more.

## Usage

```javascript
import { useEmbeddedWallet } from “@dynamic-labs/sdk-react-core”

 // component declaration and all other logic you might need
const { 
    createEmbeddedWallet,
    createEmbeddedWalletAccount,
    createOrRestoreSession,
    createPasskey,
    getPasskeys,
    isLoadingEmbeddedWallet,
    isSessionActive,
    sendOneTimeCode,
    userHasEmbeddedWallet
} = useEmbeddedWallet();

const oneTimeCodeSent = useRef(false);
```

### Create embedded wallet

Create an embedded wallet at any point.

#### Method Signature

<Warning>webAuthnAttestation argument only available from V2.1.0-alpha.9 of the SDK</Warning>

```typescript
createEmbeddedWallet( chains?: ChainEnum[], options?: { webAuthnAttestation: WebAuthnAttestation }): Promise<Wallet | undefined>
```

#### Example

```javascript
const onClick = async () => {
    if(!userHasEmbeddedWallet()) {
      try {
        const walletId = await createEmbeddedWallet();
        // do whatever you want with that Id
      } catch(e) {
        // handle error
      }
    }
}

return (
    <button onClick={() => onClick()}>Create Wallet</button>
)
```

### Create embedded wallet account

Used to create an embedded wallet on an additional chain if a user already has an embedded wallet on another chain.

<Warning>createEmbeddedWalletAccount method is only available from 3.0.0-alpha.37 of the SDK</Warning>

```typescript
import { EmbeddedWalletChainEnum } from '@dynamic-labs/sdk-api-core';

createEmbeddedWalletAccount({ chain: EmbeddedWalletChainEnum.Evm }): Promise<UserProfile | undefined>
```

### Create session

Create an embedded wallet session whenever you need to validate the user authenticity to perform transactions without the need to prompt user confirmation every single time.

#### Steps

1.  First you need to trigger the sending of a one-time code for authenticity validation, the `sendOneTimeCode` method handles this.
2.  Once the user enters the one-time code, the `createOrRestoreSession` method will initiate a session as long as the code is valid.

```javascript
const onSendOneTimeCodeHandler = async () => {
    if(!isSessionActive) {
      try {
        await sendOneTimeCode();
        oneTimeCodeSent.current = true;
        // do whatever you want with that Id
      } catch(e) {
        // handle error
      }
    }
}

const onCreateSessionHandler: FormEventHandler<HTMLFormElement> = async (event) => {
    try {
      event.stopPropagation();
      event.preventDefault();

      if (!primaryWallet || !userHasEmbeddedWallet()) return;

      const otc = event.currentTarget.otc.value;

      await createOrRestoreSession({ oneTimeCode: otc })
        .then((result) => setResult(result))
        .catch((error) => setResult(JSON.stringify(error, null, 2)));
    } catch (err) {
      logger.error(err);
    }
  };

return (
  <>
    {!oneTimeCodeSent.current && <button onClick={onSendOneTimeCodeHandler}>Start session</button>}
    {oneTimeCodeSent.current && (
      <form onSubmit={onCreateSessionHandler} className='create-session-method'>
      <Typography>
        Enter one-time code sent to email to create a session
      </Typography>

      <input required name='otc' type='text' placeholder='One-time code' />
      <br />
      <Button type='submit'>Create session</Button>

    </form>
    )}
  </>
)
```

### Create passkey

Create a passkey whenever you need to validate the user authenticity to perform a transaction or to access a specific feature as well as to handle cases where the user lost access to their device.

#### Steps

1.  If the user is not in an active session, we first need to send them a one-time code for authenticity validation.
2.  Once the user enters the one-time code, the `createPasskey` method will validate it and create a passkey.

```javascript
const onSendOneTimeCodeHandler = async () => {
    if(!isSessionActive) {
      try {
        await sendOneTimeCode();
        oneTimeCodeSent.current = true;
        // do whatever you want with that Id
      } catch(e) {
        // handle error
      }
    }
}

const onCreatePasskeyHandler: FormEventHandler<HTMLFormElement> = async (event) => {
    try {
      event.stopPropagation();
      event.preventDefault();

      if (!primaryWallet || !userHasEmbeddedWallet() || !isSessionActive) return;

      const otc = event.currentTarget.otc.value;

      await createPasskey({ oneTimeCode: otc })
        .then((result) => setResult(result))
        .catch((error) => setResult(JSON.stringify(error, null, 2)));
    } catch (err) {
      logger.error(err);
    }
  };

return (
  <>
    {!oneTimeCodeSent.current && <button onClick={onSendOneTimeCodeHandler}>Start passkey creation</button>}
    {oneTimeCodeSent.current && (
      <form onSubmit={onCreatePasskeyHandler} className='create-session-method'>
      <Typography>
        Enter one-time code sent to email to create a new passkey
      </Typography>

      <input required name='otc' type='text' placeholder='One-time code' />
      <br />
      <Button type='submit'>Create passkey</Button>

    </form>
    )}
  </>
)
```

## Return Values

*   **createEmbeddedWallet** returns a promise which will resolve to a wallet ID once the creation flow has been successful.
*   **createEmbeddedWalletAccount** returns a promise which will resolve to a user once the creation flow has been successful.
*   **createOrRestoreSession** returns a promise which will resolve to a string `session_created` or `session_restored` if the session has been created or restored successfully.
*   **createPasskey** returns a promise which will resolve to a passkey data `(attestation, challenge, displayName)` once the creation flow has been successful.
*   **getPasskeys** returns a promise which will resolve to an array of passkeys info.
*   **isLoadingEmbeddedWallet** returns a boolean which will be true if the embedded wallet is being created.
*   **isSessionActive** returns a boolean which will be true if the user has an active session.
*   **sendOneTimeCode** returns a promise which will resolve to a string `code_sent` if the code has been sent successfully.
*   **userHasEmbeddedWallet** returns a boolean which will be true if the user has an embedded wallet at that time.


# useExternalAuth



<Note>
  ### This is an enterprise-only feature. Please contact us if you are interested.
</Note>

<Note>
  You must update your SDK version to v3.0.0-alpha.6 or above for this feature
</Note>

### Summary

This hook provides a way for clients to sign-in to Dynamic or link with Dynamic using a third-party issued JWT auth token.

The hook needs to be initialized within a child of [DynamicContextProvider](/react-sdk/providers/dynamiccontextprovider).

### Usage

Available methods:

*   `signInWithExternalJwt`: Sign in to Dynamic by providing the external user ID and JWT. This will automatically set the logged-in Dynamic `user` when successful.
*   `verifyWithExternalJwt`: Verify and link an external user in Dynamic by providing the external user ID and JWT.

### Usage

```TypeScript
const { signInWithExternalJwt, verifyWithExternalJwt } = useExternalAuth();

try {
  // `externalUserId`: User ID in the external auth system
  // `externalJwt`: Raw encoded JWT issued by external auth system
  const userProfile = await signInWithExternalJwt({
    externalUserId,
    externalJwt
  });

  if (userProfile) {
    // You should be logged in at this point
  }
} catch (e: any) {
  console.error('Dynamic login failed:', e);
}

try {
  const verifiedProfile = await verifyWithExternalJwt({
    externalUserId,
    externalJwt
  });

  if (verifiedProfile) {
    // User verification successful
  }
} catch (e: any) {
  console.error('Dynamic verification failed:', e);
}
```

### Details

In order to use this hook, the proper [Third-Party Auth](/authentication-methods/third-party-auth) settings need to be configured.


# useFunding



<Tip>Only available from SDK V1</Tip>

### Summary

This hook can be used to trigger an Onramp UI so that users can immediately buy crypto with fiat.

### How it works

After setting up your [onramp provider](/fiat-onboarding/banxa), you can use the useFunding to prompt your user to fund their wallet.

UseFunding exposes the following attributes:

| Method      | Type     | Description                                                                                                                            |
| ----------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| enabled     | boolean  | Weather funding is enable and ready to use                                                                                             |
| openFunding | function | Trigger the onramp UI with optional `address` and `token` attributes, returns a Promise that will resolve once the onramp UI is closed |

### Examples

Example on a custom onramp button.

```tsx
import { useFunding } from "@dynamic-labs/sdk-react-core";

const FundMyWalletButton = () => {
  const { enabled, openFunding } = useFunding();

  const onClick = () => {
    openFunding({
      token: "USDT",
      address: "0x1234567890123456789012345678901234567890",
    }).then(() => window.alert("Success!"));
  };

  return (
    <button onClick={onClick} disabled={!enabled}>
      Buy USDT
    </button>
  );
};
```


# useIsLoggedIn



### Summary

This hook determines whether a user is logged in or not.
The hook needs to be initialized within a child of [DynamicContextProvider](/react-sdk/providers/dynamiccontextprovider).

### Usage

```TypeScript
import { useIsLoggedIn } from '@dynamic-labs/sdk-react-core';

const MyComponent = () => {
  const isLoggedIn = useIsLoggedIn();

  return (
    <div>
      {isLoggedIn ? (
        <p>You are logged in!</p>
      ) : (
        <p>Please log in to continue.</p>
      )}
    </div>
  );
};

export default MyComponent;
```

### Return Value

The hook returns a boolean value indicating whether the user is logged in or not.

### Details

The hook first checks if the user object exists or if the authMode is 'connect-only' and a primaryWallet exists. If either of these conditions is true, the user is considered logged in.

Next, it checks the user should have a primaryWallet due to embedded wallets being enabled. If the user should have a promaryWallet and it doesn't exist, the user is not considered logged in, otherwise, they are considered logged in.


# useOtpVerificationRequest



The `useOtpVerificationRequest` hook simplifies OTP verification processes in your application.
It provides an easy-to-use interface for developers to handle OTP verification and securely update user profiles.
The hook returns a `verifyOtp` function that can be used to finalize the OTP verification process.

This hook is particularly useful when, for example, you need to verify a user's email address after updating it in their profile,
or when they sign up for the first time.

## `VerifyOtp` Function

The `verifyOtp` function, provided by the `useOtpVerificationRequest` hook, allows you to handle the OTP verification process
for your users by submitting the OTP verification token they received, along with which method it applies to.

| Parameters        | Type               | Description                                      |
| :---------------- | :----------------- | :----------------------------------------------- |
| verificationToken | `string`           | The OTP verification token received by the user. |
| destination       | `"email" \| "sms"` | Where the OTP was sent to.                       |

<Warning>
  The `verifyOtp` function does **not** trigger either
  `onOtpVerificationFailure` or `onOtpVerificationSuccess` callbacks. This way,
  when it fails you can decide whether to try again or declare a "failure"
  yourself.
</Warning>

## Example Usage

```typescript
import { useOtpVerificationRequest } from '@dynamic-labs/sdk-react-core';

function EmailVerification() {
  const { verifyOtp } = useOtpVerificationRequest();

  const handleVerify = async (verificationToken) => {
    try {
      const verifyOtpResponse = await verifyOtp(verificationToken, "email");
      // Handle successful OTP verification, e.g., show a success message or redirect
    } catch (error) {
      // Handle errors, e.g., show an error message or prompt for the correct token
    }
  };

  return (
    // Render your component with the verification token input and verify button
    // ...
  );
}
```


# usePasskeyRecovery



### Summary

Used to manually open the recovery flow and let a user create a new passkey so they can recover their account.

### Usage

```jsx
import { usePasskeyRecovery } from "@dynamic-labs/sdk-react-core";

const { initPasskeyRecoveryProcess, addRecoveryEmail } = usePasskeyRecovery();

// Opens the recovery flow UI
<button onClick={() => initPasskeyRecoveryProcess()}></button>;

// Adds a recovery email
<button onClick={() => addRecoveryEmail('recovery-email@gmail.com')}></button>;
```


# useRefreshUser



<Warning>
  Available from SDK V3.0+
</Warning>

## Summary

Used to manually refresh the user state at any point in time, for example if you have multiple seperate windows (things like a Chrome extension popup and a Chrome page for the same extension) where Dynamic is active and you want to ensure they are all in sync.

The SDK state as a whole will be maintained i.e. the `sdkHasLoaded` boolean on `useDynamicContext` will stay true. If you want to reset the whole SDK including wallets, then take a look at [`useReinitialize`](/react-sdk/hooks/usereinitialize).

## Usage

```tsx
import {
  DynamicContextProvider,
  useRefreshUser,
} from '@dynamic-labs/sdk-react-core';

const RefreshButton = () => {
  const refresh = useRefreshUser();

  return (
    <button onClick={refresh}>Reinitialize</button>
  );
}

const App = () => {
  return (
    <DynamicContextProvider>
      <RefreshButton />
    </DynamicContextProvider>
  )
}
```


# useReinitialize



<Warning>
  Available from SDK V3.0+
</Warning>

## Summary

Used to manually reinitialize the whole SDK at any point in time. It will reset not just the user state (as [useRefreshUser](/react-sdk/hooks/userefreshuser) does), but the whole SDK state including wallets, the primaryWallet, user state, etc.

An example of where this hook can be helpful is if you have multiple seperate windows (things like a Chrome extension popup and a Chrome page for the same extension) where Dynamic is active and you want to ensure they are all in sync.

While in process, the `sdkHasLoaded` boolean on [`useDynamicContext`](/react-sdk/hooks/usedynamiccontext) will be false, and once completed, it will be true.

## Usage

```tsx
import {
  DynamicContextProvider,
  useReinitialize,
} from '@dynamic-labs/sdk-react-core';

const ReInitButton = () => {
  const reinitialize = useReinitialize();

  return (
    <button onClick={reinitialize}>Reinitialize</button>
  );
}

const App = () => {
  return (
    <DynamicContextProvider>
      <ReInitButton />
    </DynamicContextProvider>
  )
}
```


# useRpcProviders



### Summary

This hook provides acces to EVM and Solana RPC providers.

<Note>Check out our guide on [Using RPC Providers](/chains/rpc-urls)</Note>

### Usage

This hook must be fed with an RPC provider selector, of which you can chose among two:

*   `evmProvidersSelector`, exported from `@dynamic-labs/ethereum-core`
*   `solanaProvidersSelector`, exported from `@dynamic-labs/solana-core`

```typescript
import { useRpcProviders } from '@dynamic-labs/sdk-react-core'
import { evmProvidersSelector } from '@dynamic-labs/ethereum-core'
import { solanaProvidersSelector } from '@dynamic-labs/solana-core'

const App = () => {
  const evmProviders = useRpcProviders(evmProvidersSelector)
  const solanaProviders = useRpcProviders(solanaProvidersSelector)
}
```

### Return Value

This hook's return value depends on the selector with which it was fed:

*   [EvmRpcProviderMethods](/react-sdk/objects/EvmRpcProviderMethods) when fed with `evmProvidersSelector`
*   [SolanaRpcProviderMethods](/react-sdk/objects/SolanaRpcProviderMethods) when fed with `solanaProvidersSelector`


# useSendBalance



In case you need to programmatically open the send balance widget, you can use our useSendBalance hook to request the UI and optionally prepopulate the form for the user.

### How it works

The useSendBalance hook depends on the DynamicContextProvider, so it has to be used as a child of the Dynamic context. When you use the hook, you will get a function named `open`. That method accepts the follow options:

| Parameter        | Type             | Description                    |
| :--------------- | :--------------- | :----------------------------- |
| recipientAddress | String           | The initial recipient address  |
| value            | ethers.BigNumber | The initial transaction amount |

The `open` function will return a promise; If successful, the promise will be resolved with the transaction; if not successful, the promise will be rejected accompanied by the error collected from the provider.

Here is an example of a custom Send button

```typescript
import { useSendBalance } from "@dynamic-labs/sdk-react-core";

const MySendButton = () => {
  const { open } = useSendBalance();

  return <button onClick={() => open()}>Send</button>;
};
```

From this example, when the user clicks the button, they will be prompted to fill the amount and recipient fields, review the transaction, and they will see a confirmation at the end of the flow.

For the second example, we will pre-populate the recipient and amount fields for the user.

```typescript
import { useSendBalance } from "@dynamic-labs/sdk-react-core";
import { parseEther } from "ethers";

const MySendButton = () => {
  const { open } = useSendBalance();

  const onCickSend = async () => {
    try {
      const tx = await open({
        recipientAddress: "<address>",
        value: parseEther("1"),
      });
    } catch (err) {
      // Handle the promise rejection
    }
  };

  return <button onClick={onCickSend}>Send</button>;
};
```

Here, when the user clicks the button, they will be prompted with the same UI, but now it will be pre-populated with the recipient address and amount.


# useSmartWallets



# useSmartWallets

The `useSmartWallets` hook provides utility functions for working with smart contract wallets and their corresponding EOA (Externally Owned Account) wallets.

## Usage

```typescript
import { useSmartWallets } from '@dynamic-labs/sdk-react';

function MyComponent() {
  const { isSmartWallet, getEOAWallet, getSmartWallet } = useSmartWallets();
  // ...
}
```

## Return Value

The hook returns an object with the following properties:

*   isSmartWallet: (wallet: Wallet) => boolean;
    Allows you to tell whether a wallet is a Smart Contract Wallet.

*   getEOAWallet: (wallet: Wallet) => Wallet | undefined;
    Given a smart wallet, returns the correspondent EOA wallet. If a non smart wallet is provided, returns undefined.

*   getSmartWallet: (wallet: Wallet) => Wallet | undefined;
    Given an EOA wallet, returns the corresponding smart wallet. If a non EOA wallet is provided, returns undefined.


# useSocialAccounts



### Summary

A hook which can be used to trigger sign-in with social or social account linking and unlinking for an authenticated user.
The hook needs to be initialized within a child of [DynamicContextProvider](/react-sdk/providers/dynamiccontextprovider).

### Usage

Available functions and states:

| Method                      | Type                                                   | Description                                                                                                                                        |
| --------------------------- | :----------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------- |
| error                       | `SocialOAuthError \| undefined`                        | Contains the code and message in case of an error during the link or unlink process                                                                |
| getLinkedAccountInformation | `(provider: ProviderEnum) => SocialAccountInformation` | Returns an object with the information about the linked account for the specified provider if there's a linked account. If not, returns undefined. |
| isLinked                    | `(provider: ProviderEnum) => boolean`                  | Returns true if user has an account linked for the specified provider. Otherwise returns false.                                                    |
| isProcessing                | `boolean`                                              | Indicates whether a link or unlink request is in process                                                                                           |
| linkSocialAccount           | `(provider: ProviderEnum) => Promise<void>`            | When called, will trigger the oauth linking for the specified provider                                                                             |
| signInWithSocialAccount     | `(provider: ProviderEnum) => Promise<void>`            | When called, will trigger the oauth sign-in for the specified provider                                                                             |
| unlinkSocialAccount         | `(provider: ProviderEnum) => Promise<void>`            | When called, it will unlink the user account for the specified provider                                                                            |

### Types

```jsx
ProviderEnum =
  "discord" |
  "facebook" |
  "farcaster" |
  "github" |
  "google" |
  "instagram" |
  "twitch" |
  "twitter";
```

```jsx
SocialAccountInformation = {
  id: string;
  provider: ProviderEnum;
  accountId: string;
  publicIdentifier: string;
  avatar?: string;
  displayName?: string;
  email?: string;
  username?: string;
};
```

```jsx
SocialOAuthErrorCode =
  "account_already_linked" | // trying to link an account for a provider that already has an account linked
  "account_already_linked_to_different_profile" | //trying to link an account that is already linked to a different user
  "invalid_provider" | // the provider is not a valid social provider
  "no_account_linked" | // trying to unlink the account for a provider that has no linked accounts
  "no_auth_code" | // oauth provider did not return a valid authorization code
  "no_oauth_url" | // could not find the oauth url to initiate linking flow
  "no_provider" | // a hook menthod was called without passing a provider when required
  "oauth_error" | // user denied access to account
  "oauth_window_timeout" | // user closed oauth window or left if open for too long without finishing auth flow
  "provider_not_enabled" | // the provider has not been enabled in the dashboard for the social account linking feature
  "session_timeout" | // user tried to authorize oauth after the timeout period
  "signin_error" | // could not sign-in with social account
  "social_linking_not_enabled" | // the social account linking feature has not been enabled in the dashboard so it cannot be used
  "unlink_error" | // could not unlink social account
  "verification_error" | // could not validate authorization code
  "general_error";
```

### Linking/unlinking example

```jsx
import {
  useSocialAccounts,
} from '@dynamic-labs/sdk-react-core';
import { ProviderEnum } from '@dynamic-labs/types';
import { GoogleIcon } from '@dynamic-labs/iconic';

const UserProfileSocialAccount = () => {
  const {
    linkSocialAccount,
    unlinkSocialAccount,
    isProcessing,
    isLinked,
    getLinkedAccountInformation,
  } = useSocialAccounts();

  const provider = ProviderEnum.Google;
  const isGoogleLinked = isLinked(provider);
  const connectedAccountInfo = getLinkedAccountInformation(provider);

  return (
    <div>
      <div className="icon">
        {isGoogleLinked ? (
          <Avatar avatarUrl={connectedAccountInfo?.avatar} />
        ) : (
          <GoogleIcon />
        )}
      </div>
      <div className="label">
        <Typography>
          {connectedAccountInfo?.publicIdentifier ?? provider}
        </Typography>
      </div>
      {isGoogleLinked ? (
        <Button
          onClick={() => unlinkSocialAccount(provider)}
          loading={isProcessing}
        >
          Disconnect
        </Button>
      ) : (
        <Button
          onClick={() => linkSocialAccount(provider)}
          loading={isProcessing}
        >
          Connect
        </Button>
      )}
    </div>
  );
};
```

### Sign-in example

```jsx
import { FC } from 'react';
import {
  DynamicWidget,
  useDynamicContext,
  useSocialAccounts,
} from '@dynamic-labs/sdk-react-core';
import { ProviderEnum } from '@dynamic-labs/types';
import { FarcasterIcon, GoogleIcon, TwitterIcon } from '@dynamic-labs/iconic';

const SocialSignIn = () => {
  const { error, isProcessing, signInWithSocialAccount } = useSocialAccounts();

  return (
    <div className='headless-social-signin'>
      <div className='headless-social-signin__container'>
        <p>Log in or sign up</p>

        <button onClick={() => signInWithSocialAccount(ProviderEnum.Farcaster)}>
          <FarcasterIcon />
          Sign in with Farcaster
        </button>
        <button onClick={() => signInWithSocialAccount(ProviderEnum.Google)}>
          <GoogleIcon />
          Sign in with Google
        </button>
        <button onClick={() => signInWithSocialAccount(ProviderEnum.Twitter)}>
          <TwitterIcon />
          Sign in with Twitter
        </button>
        {isProcessing && <span className='processing'>Processing...</span>}
        {error && <span className='error'>{error.message}</span>}
      </div>
    </div>
  );
};

const LoggedInUser = () => {
  const { user } = useDynamicContext();

  return (
    <>
      <DynamicWidget />
      <p>user: {user?.email}</p>
    </>
  );
};

export const HeadlessSocialSignInView: FC = () => {
  const { user } = useDynamicContext();

  return (
    <div style={{ overflowY: 'scroll' }}>
      {user ? <LoggedInUser /> : <SocialSignIn />}
    </div>
  );
};
```


# useSwitchNetwork



### Summary

The useSwitchNetwork hook is designed to facilitate the process of switching the active network of a wallet.

### Usage

Available function and states

| Method        | Type                                                                     | Description                                                 |
| ------------- | ------------------------------------------------------------------------ | ----------------------------------------------------------- |
| switchNetwork | `(args: { wallet: Wallet; network: number \| string }) => Promise<void>` | Function to switch the given wallet to the given network id |

### Example

```tsx
import React from "react";
import {
  useSwitchNetwork,
  useDynamicContext,
} from "@dynamic-labs/sdk-react-core";
import { base } from "viem/chains";

const NetworkSwitcher = () => {
  const switchNetwork = useSwitchNetwork();
  const { primaryWallet } = useDynamicContext();

  return (
    <>
      <button
        onClick={() =>
          switchNetwork({ wallet: primaryWallet, network: base.id })
        }
      >
        Switch to Base
      </button>
    </>
  );
};
```

### Hook Details

**Function: switchNetwork**

The switchNetwork function switches the network of the given wallet. It takes an object containing the wallet who's network should be switched, as well as the network id to switch to.


# useSwitchWallet



### Summary

The useSwitchWallet hook is designed to facilitate the process of switching between wallets of an authenticated user. It provides a function to switch the primary wallet.

### Usage

Available function and states

| Method       | Type                                  | Description                                        |
| ------------ | ------------------------------------- | -------------------------------------------------- |
| switchWallet | `(walletId: string) => Promise<void>` | Function to switch the primary wallet by wallet ID |

### Example

```tsx
import React from "react";
import { useSwitchWallet, useUserWallets } from "@dynamic-labs/sdk-react-core";

const WalletSwitcher = () => {
  const switchWallet = useSwitchWallet();
  const userWallets = useUserWallets();

  return (
    <div>
      {userWallets.map((wallet) => (
        <button key={wallet.id} onClick={() => switchWallet(wallet.id)}>
          {wallet.address}
        </button>
      ))}
    </div>
  );
};
```

### Hook Details

**Function: switchWallet**

The switchWallet function switches the primary wallet of the authenticated user. It takes a single argument, walletId, which is the ID of the wallet to switch to.


# useTokenBalances



<Note>Hook is available from version 2.2.4 and Solana support from 2.2.9</Note>

### Summary

Used to fetch the token balances of an account on a specified network. The default behavior is to return the token balances of the primary account on the current network, but optionally the account, network, includeFiat and includeNativeBalance can be specified.

<Note>
  Currently only supported on Ethereum, Optimism, Polygon, Arbitrum, Base and
  Solana networks for logged in users.
</Note>

### Usage

```jsx
import { useTokenBalances } from "@dynamic-labs/sdk-react-core";

const { tokenBalances, isLoading, isError, error } = useTokenBalances();
```

```json
// tokenBalances
[
  {
    "networkId": 1,
    "address": "0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0",
    "name": "Polygon",
    "symbol": "MATIC",
    "decimals": 18,
    "logoURI": "https://assets.coingecko.com/coins/images/4713/thumb/polygon.png?1698233745",
    "balance": 0.7851804304793578,
    "rawBalance": 785180430479357800,
    "price": 0.703229,
    "marketValue": 0.5521616489455683
  },
  {
    "networkId": 1,
    "address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
    "name": "USDC",
    "symbol": "USDC",
    "decimals": 6,
    "logoURI": "https://assets.coingecko.com/coins/images/6319/thumb/usdc.png?1696506694",
    "balance": 50,
    "rawBalance": 50000000,
    "price": 1,
    "marketValue": 50
  }
]
```

#### With arguments

| Parameter            | Type      | Description                   |
| :------------------- | :-------- | :---------------------------- |
| networkId            | Number    | The network ID                |
| chainName            | ChainEnum | The chain used                |
| tokenAddresses       | String\[] | The token addresses           |
| includeFiat          | Boolean   | Should include Fiat prices    |
| includeNativeBalance | Boolean   | Should include native balance |

Optionally, you can pass an object with the account address and network id specified. Additionally, you can pass an array of token addresses to filter the results.

```jsx
import { useTokenBalances } from "@dynamic-labs/sdk-react-core";

const { tokenBalances, isLoading, isError, error } = useTokenBalances({
  networkId: 1,
  accountAddress: "0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0",
  includeFiat: true,
  includeNativeBalance: true,
});

return (
  <ul>
    {tokenBalances?.map((tokenBalance) => (
      <li key={tokenBalance.address}>
        {tokenBalance.name} {tokenBalance.balance} {tokenBalance.symbol} ($
        {tokenBalance.price}) | ${tokenBalance.marketValue}
      </li>
    ))}
  </ul>
);
```

```jsx
// with token addresses filter
import { useTokenBalances } from "@dynamic-labs/sdk-react-core";

const { tokenBalances, isLoading, isError, error } = useTokenBalances({
  networkId: 1,
  accountAddress: "0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0",
  tokenAddresses: ["0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0"],
});
```

Solana support with Fiat prices (with SDK version 2.2.9)

```jsx
import { useTokenBalances } from "@dynamic-labs/sdk-react-core";
import { ChainEnum } from "@dynamic-labs/sdk-api";

const { tokenBalances, isLoading, isError, error } = useTokenBalances({
  chainName: ChainEnum.Sol,
  accountAddress: address,
  includeFiat: true,
  includeNativeBalance: true,
});
```


# useUserUpdateRequest

Programmatically update the user profile

The `useUserUpdateRequest` hook is a custom React hook designed for updating user profile information in your application.
It provides an easy-to-use interface for developers to securely modify various user properties. It can be used in scenarios
where you want to allow users to update their profile information, such as when they edit their account settings,
complete onboarding steps, or update their preferences.

This hook returns two different methods for your convenience:

*   The first method, `updateUserWithModal`, is a higher-level option that leverages our modals to perform all information editing — and email verification — seamlessly with a single method call.
*   The second method, `updateUser`, is a lower-level option that requires you to build your own UI for information editing and email verification, but still does all the heavy lifting for you.

## UserFields interface

Both `updateUser` and `updateUserWithFunction` functions use objects with properties that match the `UserFields` interface.
The `UserFields` interface includes the following properties:

| Property    | Description                                                                        |
| :---------- | :--------------------------------------------------------------------------------- |
| email       | The user's email address.                                                          |
| alias       | The user's alias.                                                                  |
| firstName   | The user's first name.                                                             |
| lastName    | The user's last name.                                                              |
| jobTitle    | The user's job title.                                                              |
| phoneNumber | The user's phone number.                                                           |
| tShirtSize  | The user's t-shirt size.                                                           |
| team        | The user's team name.                                                              |
| country     | The user's country, using the standard ISO 3166-1 alpha-2 two-letter country code. |
| username    | The user's username.                                                               |
| metadata    | The user's metadata (maximum size of 2kb)                                          |

By providing an object following the `UserFields` interface, the `updateUser` function allows you to update the necessary properties in the user profile
while ensuring the restricted properties remain untouched.

## Function `updateUserWithModal`

The `updateUserWithModal` function, provided by the `useUserUpdateRequest` hook, allows you to conveniently and securely
handle the entire process of user profile updates — including email verification when necessary — with a single method call.

> This function is the higher-level option returned from the `useUserUpdateRequest` hook, and it actually uses `updateUser` internally.

When called, this function will open one of our modals and prompt the user for the new values. If email verification is required,
it also prompts the user for the verification code.

| Parameter | Type                                                          | Description                                          |
| :-------- | :------------------------------------------------------------ | :--------------------------------------------------- |
| `fields`  | `(keyof UserFields)[]`                                        | An array of the properties' names you want to update |
| `options` | `{ title?: string; subtitle?: string; submitText?: string; }` | Options to customize the modal's text                |

### Output

The `updateUserWithModal` function returns a `Promise<UserFields>`. It resolves if and only if all fields were successfully updated,
and resolves to an object with the new values mapped to their fields. If OTP verification was required but failed, no fields are updated
and the promise rejects.

If the promise rejects, it provides a helpful message explaining the reason.

> One notable error cause is if you forget to previously enable one of the fields you are trying to update in your app's dashboard.

<Note>
  As opposed to `updateUser`, `updateUserWithModal` does trigger
  [`onOtpVerificationResult`](/react-sdk/events/onotpverificationresult).
</Note>

### Example Usage

A simple example of how to use this function to update a user's email and alias:

```typescript
updateUserWithModal(['email', 'alias'])
```

For a more complete example, the `useUserUpdateRequest` hook is imported and used in the `UserProfileEdit` component.
The `updateUserWithModal` function is called with the `fields` array when the user requests a profile information update.
If email or SMS verification is required, it is automatically handled by the function. Once the returned promise is resolved,
you can handle the successful update, such as by showing a success message or redirecting the user to another page.

```typescript
import { useUserUpdateRequest } from '@dynamic-labs/sdk-react-core';

function UserProfileEdit() {
  const { updateUserWithModal } = useUserUpdateRequest();

  const handleUpdate = (fields) => {
    updateUserWithModal(fields)
      .then((updatedFields) => {
        // Handle successful update, e.g., show a success message or redirect
      })
      .catch((errorMessage) => {
        // Handle failure, e.g., show an error message or redirect
      });
  };

  return (
    // Render your component with buttons to update your user's profile information
    // ...
  );
}
```

> If you wish to know whether email or SMS verification was necessary for the update, and whether it succeeded/failed,
> you can subscribe to the event [**onOtpVerificationResult**](/react-sdk/events/onotpverificationresult)

## Function `updateUser`

The `updateUser` function, provided by the `useUserUpdateRequest` hook, allows you to conveniently and securely
handle user profile updates with new values directly, and also provides a method for OTP verification, when necessary.

| Parameter    | Type         | Description                                                                                                      |
| :----------- | :----------- | :--------------------------------------------------------------------------------------------------------------- |
| `userFields` | `UserFields` | An object containing the user properties you want to update — keys are the fields, values are the updated values |

### Output

The `updateUser` function returns an object with the following properties:

| Property                      | Type                                             | Description                                                                                                                                                                                                                                                             |
| :---------------------------- | :----------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `isEmailVerificationRequired` | `boolean`                                        | A boolean indicating whether email verification is necessary after updating the user profile. If `true`, initiate the email verification process.                                                                                                                       |
| `isSmsVerificationRequired`   | `boolean`                                        | A boolean indicating whether email verification is necessary after updating the user profile. If `true`, initiate the email verification process.                                                                                                                       |
| `updateUserProfileResponse`   | `object`                                         | An object containing the server response from the user profile update request.                                                                                                                                                                                          |
| `missingFields`               | `ProjectSettingsKyc[]`                           | An array of items for each field that is required but was still not provided.                                                                                                                                                                                           |
| `verifyOtp`                   | `(verificationToken: string) => Promise<object>` | A scoped function for performing OTP verification, provided only if `isEmailVerificationRequired` or `isSmsVerificationRequired` is `true`. This function's single argument, `verificationToken`, should be the email or SMS verification token received from the user. |

<Warning>
  The `verifyOtp` function does **not** trigger
  [**onOtpVerificationResult**](/react-sdk/events/onotpverificationresult).
  This way, when it fails you can decide whether to try again or abort.
</Warning>

### Example Usage

A simple example of how to use this function to update a user's email and alias:

```typescript
updateUser({ email: 'foo@bar.com', alias: 'John Doe' })
```

For a more complete example, the `useUserUpdateRequest` hook is imported and used in the `UserProfileEdit` component.
The `updateUser` function is called with the `userFields` object when the user saves their profile information.
If email verification is required (`isEmailVerificationRequired` is `true`), the `verifyOtp` function is used
to handle the email verification process. Once the email is verified or if email verification is not required,
you can handle the successful update, such as by showing a success message or redirecting the user to another page.

```typescript
import { useUserUpdateRequest } from '@dynamic-labs/sdk-react-core';

function UserProfileEdit() {
  const { updateUser } = useUserUpdateRequest();

  const handleSave = async (userFields) => {
    const {
      isSmsVerificationRequired,
      isEmailVerificationRequired,
      updateUserProfileResponse,
      verifyOtp,
    } = await updateUser(userFields);

    if (isEmailVerificationRequired) {
      // Trigger the email verification process
      // Obtain the verificationToken from the user (e.g., from a user-input field)
      const verificationToken = 'user-provided-verification-token';
      await verifyOtp(verificationToken);
      // Handle successful email verification, e.g., show a success message or redirect
    } else if (isSmsVerificationRequired) {
      // Trigger the SMS verification process
      // Obtain the verificationToken from the user (e.g., from a user-input field)
      const verificationToken = 'user-provided-verification-token';
      await verifyOtp(verificationToken);
      // Handle successful email verification, e.g., show a success message or redirect
    } else {
      // Handle successful update without email verification, e.g., show a success message or redirect
    }
  };

  return (
    // Render your component with user fields input and save button
    // ...
  );
}
```

## Live preview

<iframe src="https://readme-embeds.dynamic.xyz/use-user-update-request" height="600px" width="100%">
  {' '}
</iframe>

## Integration with `useOtpVerificationRequest`

The `useUserUpdateRequest` hook is designed to work seamlessly with the [useOtpVerificationRequest](/react-sdk/hooks/useotpverificationrequest) hook.

To handle email or SMS verification in another view or component, you can directly use the `useOtpVerificationRequest` hook, which provides the verifyOtp function.
Using both hooks together ensures a streamlined user experience while maintaining security and data integrity.


# useUserWallets

Get access to the current user/session wallets

Use this hook whenever you need access to all the current wallets in your app — it returns an array of wallets, with type `Wallet[]`.

The array represents all wallets that were connected in the current session + all wallets authenticated by the current user. See [this section](#when-is-a-wallet-added-to-this-array) for more details.

### Example: listing which wallets are currently connected

```javascript
import { FC } from 'react'
import { useUserWallets } from '@dynamic-labs/sdk-react-core'

export const ListConnectedWallets: FC = () => {
  const userWallets = useUserWallets()

  return (
    <div>
      <h1>Wallets</h1>

      {userWallets.map((wallet) => (
        <p key={wallet.id}>
          {wallet.address}
        </p>
      ))}
    </div>
  )
}
```

### What does the Wallet type look like?

You can inspect the type using your code editor, but here's a summary:

```Typescript
    id: string;
    key: string;
    address: string;
    additionalAddresses: WalletAdditionalAddress[];
    chain: string;
    isAuthenticated: boolean;
    connector: walletConnector
```

<Info>
  Find the reference for the `walletConnector` type [here](/wallets/using-wallets/interacting-with-wallets#walletconnector).
</Info>

### When is a wallet added to this array?

There are currently 2 ways a wallet can be added to the array:

1.  When the a new wallet is connected to the current session.
2.  When the user signs in, all wallets authenticated to his account are added.

> Notice the intentional distinction between the *user* and the *current session*: if your end-user connects in `connect-only` mode, [he doesn't get a jwt](/wallets/advanced-wallets/connected-vs-authenticated#the-difference-in-practice). This means we have no access to the authenticated wallets.

### When is a wallet removed from this array?

Wallets are only removed explicitly by the user, be it through log-out, unlinking, or disconnecting in `connect-only`.


# useWalletConnectorEvent



### Summary

The `useWalletConnectorEvent` hook is used for handling events from a `WalletConnector` or an array of `WalletConnectors`. This hook allows you to specify an event to listen to and a handler function that will be called whenever the event is emitted. The handler will receive the event arguments followed by the instance of the `WalletConnector` that emitted the event.

| Parameter     | Type                                  | Description                                                                                                                                                                                          |
| ------------- | ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| eventEmitters | WalletConnector or WalletConnector\[] | The WalletConnector instance(s) to attach the event listener to. If an array is provided, the event listener is attached to all provided connectors.                                                 |
| eventName     | string                                | The name of the event to listen for.                                                                                                                                                                 |
| handler       | function                              | The callback function to execute when the event is emitted. The arguments to the callback are the arguments emitted with the event, followed by the WalletConnector instance that emitted the event. |

### Example

Listen to primary wallet connector account changed

```tsx
import {
  useWalletConnectorEvent,
  useDynamicContext
} from '@dynamic-labs/sdk-react-core'

const App = () => {
  const { primaryWallet } = useDynamicContext()

  useWalletConnectorEvent(
    primaryWallet?.connector,
    'accountChange',
    ({ accounts }, connector) => {
      console.group('accountChange');
      console.log('accounts', accounts);
      console.log('connector that emitted', connector);
      console.groupEnd();
    },
  );

  return null;
}
```

Listen to all wallets connectors for the disconnect event

```tsx
import {
  useWalletConnectorEvent,
  useUserWallets
} from '@dynamic-labs/sdk-react-core'

const App = () => {
  const wallets = useUserWallets();

  const walletsConnectors = wallets.map(({ connector }) => connector);

  useWalletConnectorEvent(
    walletsConnectors,
    'disconnect',
    (connector) => {
      console.log(`Connector ${connector} disconnected`);
    }
  );

  return null;
}
```


# useWalletItemActions



### Summary

A hook with utility functions to sign with a specific wallet.

The hook needs to be initialized within a child of `DynamicContextProvier`

#### `openWallet` - available from version `v0.14.32`

Initiates the signing process for a specific wallet without opening the dynamic auth flow.

For example, you can create a button that will sign the user with MetaMask specifically.

```TypeScript
const SignWithMetaMaskButton = () => {
  const { openWallet } = useWalletItemActions();

  return (
    <button onClick={() => openWallet('metamask')}>
      Sign with MetaMask
    </button>
  )
}
```


# useWalletOptions



### Summary

The useWalletOptions hook allows you to start the process of connecting to a specific wallet. It provides a function to select a wallet by wallet key.

Once this function is called, the connection process begins with the caveat that:

*   If you pass in a `walletKey` that supports multiple chains, such as `magiceden`, a user will first be asked to select which chain they want to connect with. Once a chain is selected, then the user will be prompted to connect.
*   If you pass in a `walletKey` that includes a chain, such as `magicedenbtc`, then the user will skip to the connection step.
*   If a wallet does not support multiple chains, such as `xverse`, then the user will simply go to the connection step.

### Usage

Available props

| Prop               | Type                                                                                | Description                                                                   |
| ------------------ | ----------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| selectWalletOption | `(walletKey: string) => Promise<void>`                                              | Function to select a specific wallet to connect with                          |
| walletOptions      | `Array<{isInstalledOnBrowser: boolean; key: string; name: string; group?: string}>` | List of avaiable wallet options with their keys, names and group (if defined) |

### Examples

Example 1: Harcoded options

```tsx
import { useWalletOptions } from "@dynamic-labs/sdk-react-core";

const WalletList = () => {
  const { selectWalletOption } = useWalletOptions();

  const wallets = [
    { key:'metamask', name: 'MetaMask' }, 
    { key: 'magiceden', name: 'Magic Eden' }
  ];

  return (
    <div>
      {wallets.map((wallet) => (
        <button key={wallet.key} onClick={() => selectWalletOption(wallet.key)}>
          {wallet.name}
        </button>
      ))}
    </div>
  );
};
```

Example 2: Dynamically populated options

```tsx
import { useWalletOptions } from "@dynamic-labs/sdk-react-core";

const WalletList = () => {
  const { selectWalletOption, walletOptions } = useWalletOptions();

  const groupedWallets = walletOptions.reduce((options, wallet) => {
    const key = wallet.group || wallet.key;
    const name = wallet.groupName || wallet.name;
    
    if (!options[key]) {
      options[key] = name;
    }
    
    return options;
  }, {});

  return (
    <div>
      {Object.entries(groupedWallets).map(([key, name]) => (
        <button key={key} onClick={() => selectWalletOption(key)}>
          {name}
        </button>
      ))}
    </div>
  );
};
```

Example 3: Checking if a wallet is instaled in browser

```tsx
import { useWalletOptions } from "@dynamic-labs/sdk-react-core";

const Main = () => {
  const { walletOptions } = useWalletOptions();

  const isMagciEdenInstalled = walletOptions.find((wallet) => wallet.key === 'magiceden')?.isInstalledOnBrowser;
  
  return (
    <div>
      {isMagciEdenInstalled ? 'Magic Eden is installed' : 'Magic Eden is not installed'}
    </div>
  );
};
```

### Hook Details

**Function: selectWalletOption**

The selectWalletOption function select a wallet to connect with. It takes a single argument, `walletKey`, which is the key of the wallet to connect with.

You can dynamically find the available wallet keys in `walletOptions` or all the supported wallet keys either from [wallet-book](https://dynamic-static-assets.com/wallet-book/v1/latest/wallet-book.json) (object keys in groups or wallets) or in the [chains pages in the dashboard](https://app.dynamic.xyz/dashboard/chains-and-networks#evm).


# SDK/User Loading States



## Overview

Before using the SDK through hooks etc., you need to ensure that it has loaded. In addition, you might also want to wait for a user to have logged in to call certain hooks.

This guide will show you how to check if the SDK has loaded and how to handle the different loading states for the login process.

## Wait for SDK to load (variable)

The sdkHasLoaded variable is available from the [useDynamicContext](/react-sdk/hooks/usedynamiccontext) hook and can be used to check if the SDK has loaded.

```javascript
import { useDynamicContext } from "@dynamic-labs/sdk-react-core";

const { sdkHasLoaded } = useDynamicContext();

sdkHasLoaded ? console.log("SDK has loaded") : console.log("SDK is still loading");
```

## Check if user is logged in (hook)

The [useisloggedin hook](/react-sdk/hooks/useisloggedin) will tell you if the user has finished the whole onboarding process i.e. they signed in/up and completed any required [information capture](/users/information-capture).

Note that Dynamic has two modes for a user to be in: connect-only and connect-and-sign (learn more [here](/wallets/advanced-wallets/connected-vs-authenticated)). In connect-only mode, the user is considered logged in once they have connected their wallet(s) and in connect-and-sign mode, the user is considered logged in once they have connected their wallet(s) and signed a message.

useIsLoggedIn will return true for connect-only when a wallet is connected but there is no UserActivation, and for connect-and-sign when a wallet is connected and a valid user is present.

```jsx
import { useIsLoggedIn } from '@dynamic-labs/sdk-react-core';

const MyComponent = () => {
  const isLoggedIn = useIsLoggedIn();

  return (
    <div>
      {isLoggedIn ? (
        <p>You are logged in!</p>
      ) : (
        <p>Please log in to continue.</p>
      )}
    </div>
  );
};

export default MyComponent;
```

## Check if user is authenticated but hasn't finished onboarding

useIsLoggedIn tells you if the user has finished the whole onboarding process, but if you want to know if the user has authenticated but hasn't finished the process, you can use \[userWithMissingInfo] from the [useDynamicContext](/react-sdk/hooks/usedynamiccontext) hook.

This will be undefined if the user is not authenticated or if they have finished the onboarding process and will contain the user object otherwise. Conversely, the user object from the same hook will be defined if the user is authenticated and has finished the onboarding process.

```jsx
import { useDynamicContext, useIsLoggedIn } from '@dynamic-labs/sdk-react-core';

const MyComponent = () => {
  const isLoggedIn = useIsLoggedIn();
  const { userWithMissingInfo } = useDynamicContext();

  let message = "";

  if (userWithMissingInfo) {
    message = "You are authenticated but need to complete the onboarding process.";
  } else if (!isLoggedIn) {
    message = "Please log in to continue.";
  } else {
    message = "You are logged in!";
  }

  return (
    <div>
      <p>{message}</p>
    </div>
  );
};
```

## Access user during login (handler)

The [handleAuthenticatedUser handler](/react-sdk/handlers/handleauthenticateduser) is a handler that can be used to intercept the workflow once a user has been authenticated but before the auth process completes and we close the UI.

```jsx
<DynamicContextProvider
  settings={{
    handlers: {
      handleAuthenticatedUser: async (args) => {
        console.log("handleBeforeAuth was called", args);

        await customUserObjectProcess(args.user);
      },
    },
  }}
>
  {/* ... rest of your app ... */}
</DynamicContextProvider>
```

<Info>
  It gives you a [UserProfile](/react-sdk/objects/userprofile) object for the given user.
</Info>

## Check when login has started (callback)

The [onAuthInit](/react-sdk/events/onauthinit) callback gives you information about a login process that has just begun.

```jsx
<DynamicContextProvider
  settings={{
    events: {
      onAuthInit: (args) => {
        console.log('onAuthInit was called', args);
      }
    }
  }}
>
 {/* ... rest of your app ... */}
</DynamicContextProvider>
```

<Info>
  Check out [the full reference](/react-sdk/events/onauthinit) for the return value (args).
</Info>

## Check when authentication has completed (callback)

The [onAuthSuccess](/react-sdk/events/onauthsuccess) callback runs once the user has authenticated and is signed in (i.e. completed any mandatory info capture & MFA).

```jsx
<DynamicContextProvider
  settings={{
    events: {
      onAuthSuccess: (args) => {
        console.log('onAuthSuccess was called', args);
        // you can get the jwt by calling the getAuthToken helper function
        const authToken = getAuthToken();
        console.log('authToken', authToken);
      }
    }
  }}
>
 {/* ... rest of your app ... */}
</DynamicContextProvider>
```

<Info>
  Check out [the full reference](/react-sdk/events/onauthsuccess) for the return value (args).
</Info>

## Check if login failed (callback)

There are two callbacks which you can use for this:

1.  The [onAuthFailure](/react-sdk/events/onauthfailure) callback will be called when an authentication process fails, either by error or user closing the modal.
2.  The [onAuthCancel](react-sdk/events/onauthflowcancel) callback will be called specifically if the modal is closed before login is completed, it is not called on error.


# EvmRpcProvider



An object representing a provider for a specific EVM chain.

Exported by `@dynamic-labs/ethereum-core`.

```typescript
import { Chain, PublicClient, Transport } from 'viem'

type EvmRpcProvider = {
  chainId: number
  chainName: string
  provider: PublicClient<Transport, Chain>
}
```


# EvmRpcProviderMethods



Methods and properties available to EVM rpc providers.

Exported by `@dynamic-labs/ethereum-core`.

```typescript
type EvmRpcProviderMethods = {
  defaultProvider: EvmRpcProvider | undefined
  providers: EvmRpcProvider[] | undefined
  getProviderByChainId: (chainId: number) => EvmRpcProvider | undefined
}
```

<Info>
  Check out the reference for
  [EvmRpcProvider](/react-sdk/objects/EvmRpcProvider)
</Info>


# GenericNetwork



An object representing a generic network, such as an EVM or Solana L2.

Exported by `@dynamic-labs/sdk-api-core`.

```typescript
type GenericNetwork = {
  blockExplorerUrls: Array<string>
  chainId: number | string
  chainName?: string
  iconUrls: Array<string>
  lcdUrl?: string
  name: string
  nameService?: NameService
  nativeCurrency: NativeCurrency
  networkId: number | string
  privateCustomerRpcUrls?: Array<string>
  rpcUrls: Array<string>
  vanityName?: string
}
```

See also [NameService](/react-sdk/objects/NameService) and [NativeCurrency](/react-sdk/objects/NativeCurrency).

<Note>The `lcdUrl` field is only required for Cosmos networks.</Note>


# NameService



An object representing a name service.

Exported by `@dynamic-labs/sdk-api-core`.

```typescript
type NameService = {
  registry?: string
}
```


# NativeCurrency



An object representing a name service.

Exported by `@dynamic-labs/sdk-api-core`.

```typescript
type NativeCurrency = {
  decimals: number
  denom?: string
  name: string
  symbol: string
}
```


# SolanaRpcProvider



An object representing a provider for a specific Solana chain.

Exported by `@dynamic-labs/solana-core`.

```typescript
import { Connection } from '@solana/web3.js'

type SolanaRpcProvider = {
  chainId: string
  chainName: string
  provider: Connection
}
```


# SolanaRpcProviderMethods



Methods and properties available to Solana rpc providers.

Exported by `@dynamic-labs/solana-core`.

```typescript
type SolanaRpcProviderMethods = {
  defaultProvider: SolanaRpcProvider | undefined
  providers: SolanaRpcProvider[] | undefined
  getProviderByChainId: (chainId: string) => SolanaRpcProvider | undefined
}
```

<Info>
  Check out the reference for
  [SolanaRpcProvider](/react-sdk/objects/SolanaRpcProvider)
</Info>


# AccessDeniedCustomButton



An object containing custom text and action for access denied modal.

| Field                | Description                                               |
| :------------------- | :-------------------------------------------------------- |
| title: string;       | Defines the button's text.                                |
| action?: () => void; | Defines the action of the button when click is triggered. |

## Usage

```Text TypeScript
<DynamicContextProvider
  settings={{
    accessDeniedMessagePrimary: 'Not authorized',
    accessDeniedMessageSecondary: 'Unfortunately you are not allowed to proceed.',
    accessDeniedButton: {
       action: () => window.open('https://www.mywebsite.com/contact-us', '_blank'),
       title: 'Contact us',
    }
  }}
>
 {/* ... rest of your app ... */}
</DynamicContextProvider>
```


# EvmNetwork



Object which represents a network on the Ethereum Virtual Machine (EVM). It can be used in the [DynamicContextProvider](/react-sdk/providers/dynamiccontextprovider) or in the [DynamicWagmiConnector](/react-sdk/providers/dynamicwagmiconnector).

## Definition

| Attribute              | Value            | Required/Optional |
| ---------------------- | ---------------- | ----------------- |
| blockExplorerUrls      | `string[]`       | Required          |
| chainId                | `number`         | Required          |
| name                   | `string`         | Required          |
| iconUrls               | `string[]`       | Required          |
| nativeCurrency         | `NativeCurrency` | Required          |
| networkId              | `number`         | Required          |
| privateCustomerRpcUrls | `string[]`       | Optional          |
| rpcUrls                | `string[]`       | Required          |
| vanityName             | `string`         | Optional          |

### NativeCurrency

| Attribute | Value    | Required/Optional |
| --------- | -------- | ----------------- |
| decimals  | `number` | Required          |
| name      | `string` | Required          |
| symbol    | `string` | Required          |
| denom     | `string` | Optional          |

## Example Usage

```jsx
const evmNetworks = [
  {
    blockExplorerUrls: ['https://etherscan.io/'],
    chainId: 1,
    name: 'Ethereum Mainnet',
    iconUrls: ['https://app.dynamic.xyz/assets/networks/eth.svg'],
    nativeCurrency: { decimals: 18, name: 'Ether', symbol: 'ETH' },
    networkId: 1,
    privateCustomerRpcUrls: ['https://mainnet.infura.io/v3/your-api-key'],
    rpcUrls: ['https://cloudflare-eth.com'],
    vanityName: 'Ethereum',
  },
]

return (
  <DynamicContextProvider
    settings={{
      environmentId: 'XXXX',
      overrides: { evmNetworks },
    }}
  >
    ...
  </DynamicContextProvider>
)
```


# LocaleResource



An object used with the locale prop on DynamicContextProvider, you can find a tutorial on how to use this type [here](/design-customizations/customizing-copy-translations).

## Properties

The key of the object must be one of the following valid "lang"s:

\| "ar"
\| "da"
\| "de"
\| "en"
\| "es"
\| "fi"
\| "fr"
\| "he"
\| "it"
\| "ja"
\| "nl"
\| "pl"
\| "pt"
\| "ru"
\| "uk"
\| "zh"

The value of the object must be a valid translation object - see below.

### Translations

The translation object should follow the shape found in "@dynamic-labs/sdk-react-core/src/lib/locale/en/translation.js" which you can reach by going to the definition of LocaleResource in your IDE once imported (`import { LocaleResource } from '@dynamic-labs/sdk-react-core'`)

Here is an example excerpt from that file:

```jsx
    dyn_account_exists: {
        connect: 'Connect with {{socialOauth}}',
        description: 'It looks like an account already exists using',
        title: 'Account already exists',
        trail_message_email: '. Please log in with your email.',
        trail_message_social: 'through {{socialOauth}}',
    },
```


# PhoneData



An object containing phone information required for sms verification.

| Attribute | Type     | Description                                                     |
| :-------- | :------- | :-------------------------------------------------------------- |
| dialCode  | `string` | Country dial code (e.g: '1')                                    |
| iso2      | `string` | Country iso code (e.g: 'US')                                    |
| phone     | `string` | Phone number, numbers only, without dial code (e.g: 5555555555) |


# React Objects Introduction



The SDK uses a number of objects to represent different things. Here is a list of all of the objects used by the SDK and links to their reference pages.

[**AccessDeniedCustomButton**](/react-sdk/objects/access-denied-custom-button): An object containing custom text and action for access denied modal.

[**EvmNetwork**](/react-sdk/objects/evmNetwork): Object which represents a network on the Ethereum Virtual Machine (EVM).

[**Locale**](/react-sdk/objects/locale): Used within the locale prop of the [DynamicContextProvider](/react-sdk/providers/dynamiccontextprovider) to edit the copy displayed by the SDK

[**JWT Payload**](/react-sdk/objects/user-payload): JWT to verify some claims about the end user.

[**JwtVerifiedCredential**](/react-sdk/objects/verified-credential): A representation of a verified credential (Wallet, email, farcaster, phone, etc.).

[**PhoneData**](/react-sdk/objects/phone-data): The phone information required for sms verification.

[**SdkViews**](/react-sdk/objects/views): Used within the [DynamicContextProvider](/react-sdk/providers/dynamiccontextprovider) `overrides` prop to adapt the view shown to the user programmatically.

**[UserProfile](/react-sdk/objects/userprofile)**: An object containing attributes about the
user such as the wallet address, email, first name, etc.

[**Wallet**](/react-sdk/objects/wallet): An object containing the attributes about the
connected wallet. This object would be available immediately after a wallet has
been connected.

[**WalletsByChain**](/react-sdk/objects/wallets-by-chain): Array of objects, used primarily for bridging.

[**WalletConnector**](/react-sdk/objects/walletconnector): This is Dynamic's abstraction over
the wallet interface. This can be used to interact with the users wallet.


# SocialProvider



A union of all social provider names available for connection.

Exported by `@dynamic-labs/client`.

```typescript
type SocialProvider =
  | 'apple'
  | 'coinbaseSocial'
  | 'discord'
  | 'facebook'
  | 'farcaster'
  | 'github'
  | 'google'
  | 'telegram'
  | 'tiktok'
  | 'twitch'
  | 'twitter'
```


# JWT Payload



When an end user connects their wallet, you, the developer, get a [JSON Web Token (JWT)](https://jwt.io/introduction) that can be used to verify some claims about the end user, notably a proof of ownership over a wallet public address.

After authenticating the JWT token, see [Server-side verification](/authentication-methods/how-to-validate-users-on-the-backend), you may want to leverage user and wallet information provided in the JWT. Below we have the content defined with the aim of following the JWT standards.

##### Standard JWT claims:

See: [https://www.rfc-editor.org/rfc/rfc7519#section-4.1](https://www.rfc-editor.org/rfc/rfc7519#section-4.1)

| Field | Description                                                                                   |
| ----- | --------------------------------------------------------------------------------------------- |
| aud   | Audience for the JWT token. This claim shows what domain of the indended audience of the JWT. |
| iss   | Issuer of the JWT token. This claim shows app.dynamic.xyz generated and issued the JWT.       |
| sub   | Subject of the JWT token. userId in the deprecated info claim.                                |
| iat   | Timestamp when the JWT token was issued.                                                      |
| exp   | Timestamp when the JWT token will expire.                                                     |

##### Dynamic-specific claims:

These fields are **optional** and you depends on whether you want to collect this information during onboarding. For more information about collecting this information, see [here](/users/information-capture).

| alias                 | Alias field from customer information capture.                                                                                                                                     |
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| email                 | Email field from customer information capture.                                                                                                                                     |
| environment\_id       | Unique ID of the project environment for the SDK, from [https://app.dynamic.xyz/dashboard/api](https://app.dynamic.xyz/dashboard/api). environmentId in the deprecated info claim. |
| given\_name           | First name field from customer information capture. firstName in the deprecated info claim.                                                                                        |
| family\_name          | Last name field from customer information capture. lastName in the deprecated info claim.                                                                                          |
| lists                 | Names of access lists enabled for this user.                                                                                                                                       |
| verified\_credentials | List of all [verified credentials](/react-sdk/objects/verified-credential) connected to this user.                                                                                 |
| verified\_account     | If present, this was the most recently signed and verified account.                                                                                                                |

#### Example

```JSON{
  "alias": "john",
  "aud": "https://dashboard.hello.xyz",
  "verified_credentials": [
    {
      "address": "0x000123abc",
      "chain": "eip155",
      "id": "af615228-99e5-48ee-905d-4575f0a6bfc9",
      "wallet_name": "metamask"
    }
  ],
  "email": "[[email protected]](/cdn-cgi/l/email-protection)",
  "environment_id": "fb6dd9d1-09f5-43c3-8a8c-eab6e44c37f9",
  "family_name": "bot",
  "given_name": "jon",
  "iss": "app.dynamic.xyz/fb6dd9d1-09f5-43c3-8a8c-eab6e44c37f9",
  "lists": [ "Community dashboard acess list" ],
  "sub": "d261ee91-8ea0-4949-b8bb-b6ab4f712a49",
  "verified_account": {
    "address": "0x000123abc",
    "chain": "eip155",
    "id": "af615228-99e5-48ee-905d-4575f0a6bfc9",
    "wallet_name": "metamask"
  },
  "iat": 1660677597,
  "exp": 1660684797
}
```


# UserProfile

An object containing attributes about the user such as id, email, first name, etc.

| Field                                                                                    | Description                                           |
| ---------------------------------------------------------------------------------------- | ----------------------------------------------------- |
| userId?: string                                                                          | \[Optional] - Dynamic's UUID                          |
| sessionId: string                                                                        | \[Optional] - The current session ID                  |
| environmentId?: string                                                                   | \[Optional] - Dynamic environment ID                  |
| newUser?: boolean                                                                        | \[Optional] - Is it first time user created/logged in |
| email?: string                                                                           | \[Optional] - User's email                            |
| alias?: string                                                                           | \[Optional] - User's alias                            |
| firstName?: string                                                                       | \[Optional] - User's first name                       |
| lastName?: string                                                                        | \[Optional] - User's last name                        |
| phoneNumber? string                                                                      | \[Optional] - User's phone number                     |
| lists?: string\[]                                                                        | \[Optional] - User's access lists                     |
| verifiedCredentials?: [JWTVerifiedCredential](/react-sdk/objects/verified-credential)\[] | \[Optional] - User's verified credentials             |
| lastVerifiedCredentialId: string \|\ undefined                                           | \[Optional] - User's last verified credential ID      |
| scope?: string                                                                           | \[Optional] - User's scope                            |


# JwtVerifiedCredential



| Field                                    | Description                                                                                                                                                                                      |
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| id: string                               | internal ID for this VC                                                                                                                                                                          |
| address?: string                         | \[Optional] - wallet address associated with the VC                                                                                                                                              |
| chain?: string                           | \[Optional] - chain associated with the VC                                                                                                                                                       |
| refId?: string                           | \[Optional] - if using oauth, this is the embedded wallet\_id associated with the social account                                                                                                 |
| signerRefId?: boolean                    | \[Optional] - for smart contract wallets, signerRefId is the wallet ID of the signer for this SCW                                                                                                |
| email?: string                           | \[Optional] - email associated with the VC                                                                                                                                                       |
| nameService?: NameServiceData            | \[Optional] - ens data name and avatar url, if one is associated with the wallet in the VC, see [NameServiceData](/react-sdk/objects/verified-credential#nameservicedata) below for more details |
| publicIdentifier?: string                | \[Optional] - common way to reference the VC. i.e. email address for email VCs, name of user for Google VC                                                                                       |
| walletName?: string                      | \[Optional] - wallet name associated with the VC                                                                                                                                                 |
| walletProvider?: WalletProviderEnum      | \[Optional] - type of wallet, see [WalletProviderEnum](/react-sdk/objects/verified-credential#walletproviderenum) below for more details                                                         |
| walletProperties?: WalletProperties      | \[Optional] - generally only used for embedded wallets with Turnkey, see [WalletProperties](http://localhost:3000/react-sdk/objects/verified-credential#walletproperties) below for more details |
| format: JwtVerifiedCredentialFormatEnum  | \[Optional] - the type of VC i.e. email, see [JwtVerifiedCredentialFormatEnum](/react-sdk/objects/verified-credential#jwtverifiedcredentialformatenum) below for more details                    |
| oauthProvider?: SocialSignInProviderEnum | \[Optional] - the social provider associated with the VC i.e. apple, see [ProviderEnum](/react-sdk/objects/verified-credential#providerenum) below for more details                              |
| oauthUsername?: string                   | \[Optional] - username associated with the social provider for the VC                                                                                                                            |
| oauthDisplayName?: string                | \[Optional] - display name associated with the social provider for the VC                                                                                                                        |
| oauthAccountId?: string                  | \[Optional] - ID associated with the social provider for the VC                                                                                                                                  |
| oauthAccountPhotos?: Array\<string>      | \[Optional] - photos associated with the social provider for the VC                                                                                                                              |
| oauthEmails?: string                     | \[Optional] - emails associated with the social provider for the VC                                                                                                                              |
| previousUsers?: Array\<string>           | \[Optional] - user IDs who were previously associated with the VC                                                                                                                                |
| embeddedWalletId?: string \| null        | \[Optional] - ID of the embedded wallet associated with the VC                                                                                                                                   |

## NameServiceData

| Field           | Description                                  |
| --------------- | -------------------------------------------- |
| avatar?: string | \[Optional] - avatar associated with the ens |
| name?: string   | \[Optional] - name associated with the ens   |

## WalletProviderEnum

'browserExtension' || 'custodialService' || 'walletConnect' || 'qrCode' || 'deepLink' || 'embeddedWallet' || 'smartContractWallet'

## WalletProperties

| Field                             | Description                                                        |
| --------------------------------- | ------------------------------------------------------------------ |
| turnkeySubOrganizationId?: string | \[Optional] - turnkey sub organization ID associated with wallet   |
| turnkeyHDWalletId?: string        | \[Optional] - turnkey HD wallet ID associated with the the wallet  |
| isAuthenticatorAttached?: boolean | \[Optional] - is authenticator i.e. passkey attached to the wallet |

## JwtVerifiedCredentialFormatEnum

'blockchain' || 'email' || 'oauth' || 'passkey'

## ProviderEnum

'emailOnly' || 'magicLink' || 'apple' || 'bitbucket' || 'discord' || 'facebook' || 'github' || 'gitlab' || 'google' || 'instagram' || 'linkedin' || 'microsoft' || 'twitch' || 'twitter' || 'blocto' || 'banxa' || 'dynamic' || 'alchemy' || 'zerodev' || 'turnkey'


# SdkViews



Views are a new feature as of SDK V0.19, please be aware there may be breaking changes in the future.

## What is it?

Views are used to customize the kind of UI that shows up at any point in time in your application.

## How does it work?

Views are used primarily in the overrides prop of the [DynamicContextProvider](/react-sdk/providers/dynamiccontextprovider). You pass in an array of configurations for each view you want to customize, each view has its own set of options.

## Supported views:

*   [Login](/react-sdk/objects/views#login-view)
*   [Wallet List](/react-sdk/objects/views#wallet-list)

## Login View

The `SdkViewType.Login` is used to adjust the login/signup UI options programmatically.

When using the login view, you add an object to the views array. This object should have `type: SdkViewType.Login` and `sections` which is an array of SdkViewSection objects.

Please read [here](/design-customizations/views#types-of-views) for a comprehensive guide on using this feature.

## Wallet List

The `wallet-list` configuration enables you to define tabs with predetermined labels, icons, filters, and recommended wallets, enhancing your application's wallet selection interface. This feature is particularly useful for grouping wallets.

<Tip>
  Available from version 2.0.0+
</Tip>

#### Configuring Wallet List Tabs

In the `DynamicContextProvider` setup, the `overrides` field is used to configure each tab in the wallet list. The configuration options available for each tab allow for detailed customization:

*   **Label and Icon**: Customize the tab's appearance with a `label` for text and an `icon` for visual representation. The `icon` can be one of the following options:
    *   A icon from the dynamic iconic package
        <Accordion title="Example using Dynamic Iconic icons">
          ```tsx
          import { BitcoinIcon } from '@dynamic-labs/iconic';

          <DynamicContextProvider
            settings={{
              overrides: {
                views: [
                  {
                    type: 'wallet-list',
                    tabs: {
                      items: [
                        {
                          text: 'Ethereum',
                          icon: <BitcoinIcon />
                        }
                      ]
                    }
                  }
                ]
              }
            }}
          />
          ```
        </Accordion>
    *   A image URL
        <Accordion title="Example custom image URL">
          ```tsx
          <DynamicContextProvider
            settings={{
              overrides: {
                views: [
                  {
                    type: 'wallet-list',
                    tabs: {
                      items: [
                        {
                          text: 'My own tab',
                          icon: 'https://example.org/my-image.png'
                        }
                      ]
                    }
                  }
                ]
              }
            }}
          />
          ```
        </Accordion>
    *   Or you can bring your own React icon
        <Accordion title="Example using custom react icon">
          ```tsx
          <DynamicContextProvider
            settings={{
              overrides: {
                views: [
                  {
                    type: 'wallet-list',
                    tabs: {
                      items: [
                        {
                          text: 'My own tab',
                          icon: <i />
                        }
                      ]
                    }
                  }
                ]
              }
            }}
          />
          ```
        </Accordion>

*   **Wallets Filter**: This option enables to dynamic display of wallets based on the selected tab. Clients have the flexibility to write custom filter functions or utilize predefined ones, for more information read the [sort and filter wallets](/wallets/advanced-wallets/sort-and-filter-wallets) doc

*   **Recommended Wallets**: Specify recommended wallets for each tab by providing [wallet option](/react-sdk/objects/wallet-option) keys and optional labels. This feature is designed to highlight preferred wallets, steering users towards secure and suitable options for their specific needs.

*   **Style**: An optional field that determines how the tabs are displayed within the wallet list. Currently, the only supported style is `"grid"`.

#### Example Configuration

Below is an example showcasing the setup for tabs that categorize wallets by blockchain network, utilizing both custom and predefined filter functions:

```tsx
import { DynamicContextProvider, FilterChain } from '@dynamic-labs/sdk-react-core'
import {
  BitcoinIcon,
  EthereumIcon,
  FlowIcon,
  SolanaIcon,
} from '@dynamic-labs/iconic';

const App = () => {
  return (
    <DynamicContextProvider
      settings={{
        environmentId: 'env-id',
        // Additional settings...

        overrides: {
          views: [
            {
              type: 'wallet-list',
              tabs: {
                items: [
                  {
                    label: { text: 'All chains' },
                  },
                  {
                    label: { icon: <EthereumIcon /> },
                    walletsFilter: FilterChain('EVM'),
                    recommendedWallets: [
                      {
                        walletKey: 'phantomevm',
                      },
                    ],
                  },
                  {
                    label: { icon: <SolanaIcon /> },
                    walletsFilter: FilterChain('SOL'),
                  },
                  {
                    label: { icon: <BitcoinIcon /> },
                    walletsFilter: FilterChain('BTC'),
                  },
                  {
                    label: { icon: <FlowIcon /> },
                    walletsFilter: FilterChain('FLOW'),
                  },
                ]
              }
            }
          ]
        }
      }}
    >

    </DynamicContextProvider>
  )
}
```

This is the wallet list view with the tabs

| All chains tab selected                                                                                                | Ethereum selected                                                                                                    |
| ---------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/wallet-list-tabs-all-chains-selected.png" /> | <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/wallet-list-tabs-ethereum-selected.png" /> |


# Wallet

An object containing the attributes about the user wallets.

### Props

| Field                                           | Description                                                                                             |
| ----------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| additionalAddresses: WalletAdditionalAddress\[] | Additional addresses associated to the wallet (e.g. ordinals and payment addresses for bitcoin wallets) |
| address: string                                 | Public address of the connected wallet                                                                  |
| chain: string                                   | Current BlockChain name (e.g: 'ETH', 'SOL', 'BTC', etc)                                                 |
| connector: WalletConnector                      | The wallet connector object                                                                             |
| id: string                                      | The wallet's unique id (matches thw wallet verified credential on connect-anf-sign)                     |
| isAuthenticated: boolean                        | True if the user is authenticated, otherwise it's false                                                 |
| key: string                                     | The wallet key (e.g. metamask, phantom, etc)                                                            |

### Methods

| Method                                                                | Description                                                                                                          |
| --------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| getBalance(): Promise\<string \| undefined>                           | Retrieves the balance of the wallet.                                                                                 |
| getNameService(): Promise\<NameServiceData \| undefined>              | Retrieves the name service data associated with the wallet.                                                          |
| isConnected(): Promise\<boolean>                                      | If the wallet is connected.                                                                                          |
| proveOwnership(messageToSign: string): Promise\<string\ \| undefined> | Proves ownership of the wallet by signing a message.                                                                 |
| signMessage(messageToSign: string): Promise\<string\ \| undefined>    | Signs a message using the wallet.                                                                                    |
| switchNetwork(networkChainId: number \| string): Promise\<void>       | Switches the network that the wallet is connected to.                                                                |
| sync(): Promise\<void>                                                | Prompts the user to reconnect the wallet if not connected and only resolves when the wallet is connected and active. |


# WalletOption



A WalletOption type is used to represent a wallet option in the wallet selection modal. This is as opposed to a [Wallet](/react-sdk/objects/wallet) type, which is used to represent a wallet that is currently connected.

| Field                | Optional/Required | Type                                                  | Description                                                                                                                                     |
| -------------------- | ----------------- | ----------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| isInstalledOnBrowser |                   | boolean                                               | Is this wallet installed via a browser extension                                                                                                |
| key                  |                   | string                                                | Normalization of the wallet name                                                                                                                |
| name                 |                   | string                                                | The name of the given wallet                                                                                                                    |
| walletConnector      |                   | [WalletConnector](/react-sdk/objects/walletconnector) | The wallet connector interface for the given wallet                                                                                             |
| group                | Optional          | string                                                | The wallet which can connect to multiple chains, not present if the wallet is not multi-chain compatible, or if multiple chains are not enabled |
| groupedWallets       | Optional          | WalletOption\[]                                       | Where a wallet supports multiple chains, this array contains t                                                                                  |

## Example

```jsx
isInstalledOnBrowser: true;
key: 'phantom';
name: 'Phantom';
walletConnector: WalletConnector;
group: 'phantom';
groupedWallets: [
    {
        isInstalledOnBrowser: true;
        key: '';
        name: 'phantom EVM';
        walletConnector: WalletConnector;
    },
    {
        isInstalledOnBrowser: true;
        key: '';
        name: 'phantom Solana';
        walletConnector: WalletConnector;
    },
    {
        isInstalledOnBrowser: true;
        key: '';
        name: 'phantom Ledger';
        walletConnector: WalletConnector;
    },
];
```


# WalletConnectConnector

This is Dynamic's WalletConnect connector over the basic wallet interface. This can be used to interact with the users WalletConnect wallets. It has all methods available in the [WalletConnector](https://docs.dynamic.xyz/react-sdk/objects/walletconnector) and some additional method specific to WalletConnect wallets.

<Warning>IWalletConnectConnector is available in SDK v2.0.0+.</Warning>

| Field                                     | Description                                                                                                                                                                              |
| ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| getSupportedNetworks: Promise\<string\[]> | A method to retrieve the supported/approved networks for the WalletConnect wallet. Some wallets will only allow approve a network if the user manually switches in the wallet app first. |

## Interface definition

```ts
interface IWalletConnectConnector {
  getSupportedNetworks: Promise<string[]>
};
```

### How to use it

In this example, we are going to return all supported networks for the wallet connector.

```JavaScript
import { useDynamicContext } from '@dynamic-labs/sdk-react-core';
import { isWalletConnectConnector } from '@dynamic-labs/wallet-connector-core';

const MyComponent = () => {
  const { primaryWallet } = useDynamicContext();

  const getWCSupportedNetworks = async () => {
    if (!isWalletConnectConnector(primaryWallet?.connector)) {
      return;
    }

    const supportedNetworks = await primaryWallet.connector.getSupportedNetworks();

    console.log('supportedNetworks', supportedNetworks);

    return supportedNetworks;
  };

  ...
};
```


# WalletConnector

This is Dynamic's abstraction over the wallet interface. This can be used to interact with the users wallet.

| Field                                                                                                                | Description                                                                                                                                                                                                                                               |
| -------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| fetchPublicAddress: Promise\<string \| undefined>                                                                    | A method to fetch the public address (alternatively the address could be fetched from useDynamicContext's user object)                                                                                                                                    |
| getPublicClient(): Viem Public Client \| undefined                                                                   | The provider should be used for read only actions on the block chain.Ethereum - A [Viem Public Client](https://viem.sh/docs/clients/public.html) SOLANA -[solana/web3js.Connection](https://solana-labs.github.io/solana-web3.js/classes/Connection.html) |
| getWalletClient() : Promise\<Viem Wallet Client\| Solana Wallet>\\                                                   | Representation of the signer (the wallet) for actions that require signatures with the private key.Ethereum - [Viem Wallet Client](https://viem.sh/docs/clients/wallet.html), Solana - The active wallet                                                  |
| name: string                                                                                                         | The wallet name                                                                                                                                                                                                                                           |
| signMessage(messageToSign: string): Promise\<string \| undefined>                                                    | A method to sign a message                                                                                                                                                                                                                                |
| supportedChains: Chain\[]                                                                                            | The chains that the wallet supports                                                                                                                                                                                                                       |
| getDeepLink(): string                                                                                                | A method to get a deep link to a wallet for mobile                                                                                                                                                                                                        |
| getAccountAbstraction(): Smart Wallet Provider                                                                       | A method to get the smart wallet provider for more advanced usage                                                                                                                                                                                         |
| supportsNetworkSwitching(): boolean                                                                                  | Whether the connector supports network switching.                                                                                                                                                                                                         |
| switchNetwork(\{ networkChainId, networkName }: \{ networkChainId?: number; networkName?: string; }): Promise\<void> | Switch to another network by provider either the network name or chain id specified in the list of EvmNetwork                                                                                                                                             |


# WalletsByChain



## Description

An Array of objects, in which each object contains the chain name to be connected for the chain. Primarily used for [bridging](/building-bridges/dynamic-bridge-widget) purposes.

| Field         | Description |
| ------------- | ----------- |
| chain: string | Chain name  |

## Example

```jsx
[
  {
    chain: "EVM",
  },
  {
    chain: "STARK",
  },
];
```


# Introduction to the SDK

The SDK allows you to build authentication and authorization into your application in a few lines of code.

## Getting Started

We provider step by step guides on getting started with the SDK [here](/quickstart).

You'll also want to check out the [UI components customization guides](/ui-components), or [the headless guides](/headless/headless-overview) if you need to use your own UI components.

Below we'll introduce you to the main parts of the SDK and how they can be used.

## Main Concepts

### Loading States

[Our Loading/Login States guide](/react-sdk/loading-login-states) show you how do everything from wait for the SDK to be loaded to checking if a user is authenticated.

### Components

[Components](/react-sdk/components/components-introduction) exist as three main types:

1.  UI components (create UI for different parts of the onboarding flow)
2.  Context providers (wrap your app in a React context to access SDK functionality)
3.  Connectors (help integrate different parts of the SDK i.e. Wagmi or specific chains)

### Handlers

[Handlers](/react-sdk/handlers/handlers-introduction) are a way to  programmatically customize synchronous behaviors of the SDK (i.e. run a blocking fraud check during wallet connection).

### Events

[Events](/react-sdk/events/events-introduction) are a way to listen for SDK events and run your own asynchronous code at certain points.

### Hooks

[Dynamic hooks](/react-sdk/hooks/hooks-introduction) are custom React hooks that allow you to access SDK functionality in your React components.

### Utilities

[Utilities](/react-sdk/utilities/utilities-introduction) are functions that allow you to do certain tasks in your application quickly i.e. check the network of a wallet.


# DynamicContextProvider



## Settings

Passed in using the "settings" prop, available when you first initialize `DynamicContextProvider` in your App.

### accessDeniedMessagePrimary

**Type:** `string`

**Description:** Custom main error message used when a wallet attempts to authenticate via Dynamic and is rejected because it does not have access. Defaults to "Access denied"

### accessDeniedMessageSecondary

**Type:** `string`

**Description:** Custom secondary error message used when a wallet attempts to authenticate via Dynamic and is rejected because it does not have access. Defaults to "We couldn't find your wallet address on our access list of customers."

### accessDeniedButton

**Type:** `AccessDeniedCustomButton`

**Description:** Custom secondary error button text and action when a wallet attempts to authenticate via Dynamic and is rejected because it does not have access. Defaults to "Try another method" and allow user to choose another login option. Please see: [AccessDeniedCustomButton](/react-sdk/objects/access-denied-custom-button)

### coinbaseWalletPreference

**Type:** `'all' | 'smartWalletOnly' | 'eoaOnly'`

**Description:** Determines which connection options users will see. Defaults to all. Please see: [https://www.smartwallet.dev/sdk/makeWeb3Provider#options-optional](https://www.smartwallet.dev/sdk/makeWeb3Provider#options-optional)

### cssOverrides

**Type:** `string | JSX.Element`

**Description:** Allows for custom CSS overrides via ShadowDom. Please see: [Custom CSS](/design-customizations/css/custom-css)]

### debugError

**Type:** `boolean`

**Description:** When enabled, errors caught during the authentication step and their stack trace will be set in a state and displayed in the front end.

### deepLinkPreference

**Type:** `'native' | 'universal'`

**Description:** Controls the type of deep link used when connecting a mobile wallet. Defaults to 'native'. This is useful for example if your app is running in a webview of a native mobile app, and you want to be able to link out to any wallet without having to modify your iOS build config. In this case, you can set this to 'universal'.

### displaySiweStatement

**Type:** `boolean`

**Description:** When enabled, this will show a message on terms of service and privacy policy in the signing message on the authentication step.

### enableVisitTrackingOnConnectOnly

**Type:** `boolean`

**Description:** When the Dynamic SDK is being used with auth mode = connect-only, we require this to be set to "true" to track visits of connected wallets in this environment.

### environmentId

**Type:** `string`

**Description:** You will need to specify your app's environment ID, which refers to a unique environment and its settings in Dynamic. To get your environment ID, go to [dashboard's API tab](https://app.dynamic.xyz/dashboard/api)

### events

**Type:** `DynamicEvents`

**Description:** This prop allows custom event callbacks after important events during the authentication flows for Dynamic's React SDK. For more information, please see [the main React SDK reference](/react-sdk)

### initialAuthenticationMode

**Type:** `AuthModeType`

**Description:** Sets the initial SDK authentication mode to either connect-only or connect-and-sign. connect-only does not require users to authenticate to prove ownership of their wallet. connect-and-sign will require an additional step for users to prove ownership of their wallet. Defaults to connect-and-sign. See also the [setAuthMode](/react-sdk/hooks/usedynamiccontext) method, which allows you to toggle this after the app has loaded.

### logLevel

**Type:** `keyof typeof LogLevel`

**Description:** The log level to use for client side logging with our SDK. Defaults to WARN

### mobileExperience

**Type:** `'in-app-browser' | 'redirect'`

**Description:** This setting determines how users connect on mobile devices. By default, it is set to 'in-app-browser', which means the connection will open within the wallet's in-app browser. If you prefer to have users connect via WalletConnect, set this option to 'redirect'. This will prompt users to accept connection requests in their wallet app and, for Phantom users, automatically redirect them back to their mobile browser. [See here for examples](#setting-mobile-experience)

### newToWeb3WalletChainMap

**Type:** `ChainToWalletMap`

**Description:** When provided, this is used in the Get your first wallet view in the wallet list modal. This can be helpful to steer initial customers who do not have a wallet to download and use a specific chain and wallet.

### networkValidationMode

**Type:** `'always' | 'sign-in' | 'never'`

**Description:** Note: Supported only in connect-only. Defines how the Dynamic SDK will enforce the wallet network.

*   **always** - requires the wallet to be on an enabled network while connecting and while the session is active
*   **sign-in** - will only enforce the network on connect
*   **never** - completely turn off the network validation. Defaults to `sign-in`.

### onboardingImageUrl

**Type:** `string`

**Description:** When provided, this image will be shown during the customer information capture step after a wallet successfully authenticates with Dynamic and the environment requires additional information from the user.

### policiesConsentInnerComponent

**Type:** `ReactNode | ReactNode[]`

**Description:** For environments with the username setting enabled, you will need to pass in a value for this prop to show a custom prompt or label for the policies contest checkboxes displayed during customer information capture after signing.

### privacyPolicyUrl

**Type:** `string`

**Description:** When provided, this will display a privacy policy URL on the signing step. This should be set to a URL of your organization's privacy policy web page.

### recommendedWallets

**Type:** `RecommendedWallet[]`

**Description:** Available from V1.2 only. An array of wallet keys that will be recommended to the user. See more in [our section on recommending wallets](/wallets/advanced-wallets/recommend-wallets).

### redirectUrl

**Type:** `string`

**Description:** When provided, this will redirect the user to the specified URL after the user has successfully gone through an oauth flow (social login or social account linking).

### shadowDOMEnabled

**Type:** `boolean`

**Description:** Shadow DOM allows the SDK to look as intended wherever it is hosted and it plays nicely with your existing styling system. For more information, please see: [Custom CSS](/design-customizations/css/custom-css)

### siweStatement

**Type:** `string`

**Description:** When provided, this custom message will be shown on the message to sign for the wallet signing step.

### termsOfServiceUrl

**Type:** `string`

**Description:** When provided, this will display a terms of service URL on the signing step. This should be set to a URL of your organization's terms of service web page.

### walletConnectors

**Type:** `[]walletConnector`

**Description:** When provided, will enable whatever connectors you pass so that your end user can signup/login using those wallets. For the list of available connectors, see the walletConnectors section below.

### walletConnectPreferredChains

**Type:** Not specified

**Description:** Relevant to Wallet Connect only, used to determine which chains to establish a connection with first. The value must be an array containing [CAIP-2](https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-2.md) chain ID's. The format for this is `{namespace-goes-here}:{reference-goes-here}`. Currently we only support Ethereum, so it will always be `eip155:{reference-goes-here}`. For example, Ethereum mainnet being \['eip155:1']

### walletsFilter

**Type:** `(options: WalletOption[]) => WalletOption[]`

**Description:** When specified, this is a custom function that allows clients of Dynamic SDK to filter out wallet options based on a function on the wallet options. For example: `walletsFilter: (wallets) => wallets.filter((w) => w.key !== 'walletx')` will exclude walletx from showing up on the wallet list.

### bridgeChains

**Type:** `WalletsByChain`

**Description:** (Only use with bridging) Which chains should be used for bridging.

### socialProvidersFilter

**Type:** `(providers: SocialOAuthProvider[]) => SocialOAuthProvider[]`

**Description:** When specified, this is a custom function that allows clients of Dynamic SDK using social auth to filter or modify the order of the social options displayed to the user. For example, we can only show github oauth option: `socialProvidersFilter: (providers) => (['github'])`.

### overrides

**Type:** `{ views: SdkView[], evmNetworks: EvmNetwork[] }`

**Description:** Used for passing in [Views](/react-sdk/objects/views) or [evmNetworks](/chains/network-switching#evmnetworks).

### enableConnectOnlyFallback

**Type:** `boolean`

**Description:** When `true`, enables the SDK to fallback to wallet connect-only auth mode if connection to Dynamic's servers is not possible. Available in version 1.1 and above

### defaultPhoneInputIso2

**Type:** `string`

**Description:** Used to define which phone number country code should be used as the default in all phone inputs, ex: `defaultPhoneInputIso2: "fr"`

### social

**Type:** `{ strategy: 'redirect' | 'popup' }`

**Description:** Allow to customize the default social behavior from 'redirect' to 'popup'

## walletConnectors

Here are the possible options for the walletConnectors array. For each one, you must make sure you have installed the package first:

<Tip>
  Please note that @dynamic-labs/ethereum (EthereumWalletConnectors) contains
  all EVM chains, not just Ethereum. It also includes Dynamic-powered embedded wallets,
  as these are EVM based too.
</Tip>

| Package Name           | Chain  | WalletConnector to include |
| :--------------------- | :----- | -------------------------- |
| @dynamic-labs/ethereum | EVM    | `EthereumWalletConnectors` |
| @dynamic-labs/algorand | ALGO   | `AlgorandWalletConnectors` |
| @dynamic-labs/solana   | SOL    | `SolanaWalletConnectors`   |
| @dynamic-labs/flow     | FLOW   | `FlowWalletConnectors`     |
| @dynamic-labs/starknet | STARK  | `StarknetWalletConnectors` |
| @dynamic-labs/cosmos   | COSMOS | `CosmosWalletConnectors`   |

##### EVM Addon Wallets

| Package Name         | Which Wallets | WalletConnector to include  |
| :------------------- | :------------ | :-------------------------- |
| @dynamic-labs/magic  | *magic*       | `MagicWalletConnectors`     |
| @dynamic-labs/blocto | *blocto*      | `BloctoEvmWalletConnectors` |

## Locale

This prop is for editing copy and adding translations to the SDK. For more information, please see [the customizing copy guide](/design-customizations/customizing-copy-translations) and [reference](/react-sdk/objects/locale).

## Examples

#### Initiate Dynamic using only defaults

```JavaScript
<DynamicContextProvider
    settings={{
      environmentId: 'YOUR_ENVIRONMENT_ID'
  }}>
  <MyChildComponents />
</DynamicContextProvider>
```

#### Initiate Dynamic with Ethereum and Starknet wallets enabled

```JavaScript
import { EthereumWalletConnectors } from "@dynamic-labs/ethereum";
import { StarknetWalletConnectors } from "@dynamic-labs/starknet";

<DynamicContextProvider
    settings={{
      environmentId: 'YOUR_ENVIRONMENT_ID',
      walletConnectors: [EthereumWalletConnectors, StarknetWalletConnectors]
  }}>
  <MyChildComponents />
</DynamicContextProvider>
```

#### Initiate Dynamic using all available methods

```JavaScript
<DynamicContextProvider
    settings={{
      environmentId: 'YOUR_ENVIRONMENT_ID',
      accessDeniedMessagePrimary: 'Custom main error message',
      accessDeniedMessageSecondary: 'Custom secondary error message',
      cssOverrides: ".wallet-list-item__tile { background-color: lightblue; }",
      debugError: true,
      displaySiweStatement: true,
      enableVisitTrackingOnConnectOnly: true,
      events: {
        onAuthFlowClose: () => {
          console.log('in onAuthFlowClose');
        },
        onAuthFlowOpen: () => {
          console.log('in onAuthFlowOpen');
        },
        onAuthSuccess: () => {
          navigate('/dashboard/overview');
        },
        onLogout: () => {
          console.log('in onLogout');
        },
      },
      initialAuthenticationMode: 'connect-only',
      logLevel: 'DEBUG',
      newToWeb3WalletChainMap: {
        1: ['metamask', 'walletconnect'],
        137: ['metamask', 'walletconnect'],
        56: ['metamask', 'walletconnect'],
        80001: ['metamask', 'walletconnect'],
      },
      onboardingImageUrl: 'https://i.imgur.com/3g7nmJC.png',
      policiesConsentInnerComponent: (
        <div>
          <p>
            By clicking "Connect", you agree to our{' '}
            <a href="https://www.dynamic.xyz/terms-of-service" target="_blank">
              Terms of Service
            </a>{' '}
            and{' '}
            <a href="https://www.dynamic.xyz/privacy-policy" target="_blank">
              Privacy Policy
            </a>
            .
          </p>
        </div>
      ),
      privacyPolicyUrl: 'https://www.dynamic.xyz/privacy-policy',
      shadowDOMEnabled: true,
      siweStatement: 'Custom message to sign',
      termsOfServiceUrl: 'https://www.dynamic.xyz/terms-of-service',
      walletsFilter: (wallets) => wallets.filter((w) => w.key !== 'walletx'),
  }}>
  <MyChildComponents />
</DynamicContextProvider>
```

#### Do not show `walletx` on the wallet list

```JavaScript
<DynamicContextProvider
  settings={{
    environmentId: '<<sandboxEnvironmentId>>'
    walletsFilter: (wallets) => wallets.filter((w) => w.key !== 'walletx'),
  }}
>
  <MyChildComponents />
</DynamicContextProvider>
```

#### With events callbacks

```JavaScript
<DynamicContextProvider
    settings={{
      environmentId: '<<sandboxEnvironmentId>>',
      events: {
        onAuthFlowClose: () => {
          console.log('in onAuthFlowClose');
        },
        onAuthFlowOpen: () => {
          console.log('in onAuthFlowOpen');
        },
        onAuthSuccess: () => {
          navigate('/dashboard/overview');
        },
        onLogout: () => {
          console.log('in onLogout');
        },
      },
  }}>
  <MyChildComponents />
</DynamicContextProvider>
```

#### Setting mobile experience

##### Globally for all wallets

```JavaScript
<DynamicContextProvider
    settings={{
      environmentId: '<<sandboxEnvironmentId>>',
      mobileExperience: 'redirect' | 'in-app-browser'
  }}>
  <MyChildComponents />
</DynamicContextProvider>
```

##### On a wallet by wallet basis with optional default for all non-specified wallets

**Note**: The walletKey can be found on the chains and networks page of the dashboard [here](https://app.dynamic.xyz/dashboard/chains-and-networks#evm)

```JavaScript
<DynamicContextProvider
    settings={{
      environmentId: '<<sandboxEnvironmentId>>',
      mobileExperience: {
        '<<walletKey>>': 'redirect' | 'in-app-browser',
        default: 'in-app-browser'
      }
  }}>
  <MyChildComponents />
</DynamicContextProvider>
```


# DynamicWagmiConnector

The `DynamicWagmiConnector` component integrates your Dynamic project settings with Wagmi

# Usage

```typescript
import { DynamicWagmiConnector } from "@dynamic-labs/wagmi-connector";
import { DynamicContextProvider } from "@dynamic-labs/sdk-react-core";

function App() {
  return (
    <DynamicContextProvider settings={{ environmentId: "your_env_id" }}>
      <DynamicWagmiConnector>
        <YourRoutes />
      </DynamicWagmiConnector>
    </DynamicContextProvider>
  );
}
```

# Props

## evmNetworks

You can pass a static `evmNetworks` array. This will be passed down to the [Wagmi client config](https://wagmi.sh/react/client#configuration).

The reference for an EvmNetwork can be found [here](/react-sdk/objects/evmNetwork).

Example:

```typescript
const evmNetworks: EvmNetwork[] = [
  {
    blockExplorerUrls: ["https://etherscan.io/"],
    chainId: 1,
    chainName: "Ethereum Mainnet",
    iconUrls: ["https://app.dynamic.xyz/assets/networks/eth.svg"],
    nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
    networkId: 1,
    privateCustomerRpcUrls: ["https://mainnet.infura.io/v3/your-api-key"],
    rpcUrls: ["https://cloudflare-eth.com"],
    vanityName: "Ethereum",
  },
];
```

### WebSocket

For webSocket support with Wagmi, you can pass an object as the privateCustomerRpcUrls with your webSocket url configuration

```typescript
const evmNetworks: EvmNetwork[] = [
  {
    ...
    privateCustomerRpcUrls: [
      {
        url: "https://mainnet.infura.io/v3/your-api-key",
        webSocket: "wss://mainnet.infura.io/v3/your-api-key"
      }
    ],
    ...
  },
];
```


# Providers and Connectors Introduction



<Tip>
  You can see the full UI implemented at any time in our demo environment:
  [https://demo.dynamic.xyz](https://demo.dynamic.xyz)
</Tip>

#### Dynamic Context Provider

The [**DynamicContextProvider**](/react-sdk/providers/dynamiccontextprovider) is a provider component provides the context for the rest of the components to work. It must wrap all Dynamic related components of your application.

#### Dynamic Wagmi Connector

The [**DynamicWagmiConnnector**](/react-sdk/providers/dynamicwagmiconnector) is responsible for helping your Dynamic application to integrate with Wagmi out of the box.


# Upgrade from V0 to V1



## From V0.19 to V1

This upgrade guide is specific to implementing the SDK itself.

### Breaking Changes

#### userWallets hooks

*   connectedWallets , secondaryWallets and linkedWallets all been removed in favor of userWallets hook. See [here](/react-sdk/hooks/useuserwallets) for more info.

<Tabs>
  <Tab title="Before">
    ```jsx
    import { useDynamicContext, getNetwork } from '@dynamic-labs/sdk-react-core';

    const App = () => {
    const { connectedWallets } = useDynamicContext();

    useEffect(() => {
      connectedWallets.forEach(async ({ connector }) => {
      const network = await getNetwork(connector);
            ...
          });
    }, [connectedWallets]);

    ...
    }
    ```
  </Tab>

  <Tab title="After">
    ```jsx
    import { useUserWallets, getNetwork } from '@dynamic-labs/sdk-react-core';

    const App = () => {
    const userWallets = useUserWallets();

    useEffect(() => {
      userWallets.forEach(async ({ connector }) => {
      const network = await getNetwork(connector);
            ...
          });
    }, [userWallets]);

    ...
    }
    ```
  </Tab>
</Tabs>

#### EthereumSmartWalletConnectors -> ZeroDevSmartWalletConnectors

*   (v1 alpha -> v1) EthereumSmartWalletConnectors is now renamed to ZeroDevSmartWalletConnectors. You use this connector when you want to use AA wallets.

<Tabs>
  <Tab title="After">
    ```jsx
    import {DynamicContextProvider} from "@dynamic-labs/sdk-react-core";
    import { EthereumWalletConnectors } from '@dynamic-labs/ethereum';
    import { ZeroDevSmartWalletConnectors } from '@dynamic-labs/ethereum-aa';

    const App = () => (

      <DynamicContextProvider
        settings={{
          ...
          walletConnectors: [ EthereumWalletConnectors, ZeroDevSmartWalletConnectors ],
          ...
        }}>
        <DynamicWidget />
      </DynamicContextProvider>
    );

    export default App;
    ```
  </Tab>

  <Tab title="Before">
    ```jsx
    import {DynamicContextProvider} from "@dynamic-labs/sdk-react-core";
    import { EthereumWalletConnectors } from "@dynamic-labs/ethereum";
    import { EthereumSmartWalletConnectors } from "@dynamic-labs/ethereum-aa";

    const App = () => (

      <DynamicContextProvider
        settings={{
          ...
          walletConnectors: [ EthereumWalletConnectors, EthereumSmartWalletConnectors ],
          ...
        }}>
        <DynamicWidget />
      </DynamicContextProvider>
    );

    export default App;
    ```
  </Tab>
</Tabs>

#### wallets -> walletConnectorOptions

*   Wallets property returned by useDynamicContext now renamed to walletConnectorOptions

<Tabs>
  <Tab title="After">
    `````jsx
    import { useDynamicContext } from "@dynamic-labs/sdk-react-core";

    const App = () => {
    const { walletConnectorOptions } = useDynamicContext();
    return (

    <div>
      {walletConnectorOptions.map((wallet) => (
        <div>{wallet.name}</div>
      ))}
    </div>
    ); };

    export default App
    ;````

    </Tab >

    <Tab title="Before">
    ```jsx
    import { useDynamicContext } from "@dynamic-labs/sdk-react-core";

    const App = () => {
    const { wallets } = useDynamicContext();
    return (

    <div>
      {wallets.map((wallet) => (
        <div>{wallet.name}</div>
      ))}
    </div>
    ); };

    export default App;
    `````
  </Tab>
</Tabs>

#### walletsByChain -> bridgeChains

*   walletsByChain has been renamed to bridgeChains

<Tabs>
  <Tab title="After">
    ```jsx
    import { DynamicContextProvider } from "@dynamic-labs/sdk-react-core";

    const App = () => {
    return (

    <DynamicContextProvider
      settings={{
        environmentId: 'ENV_ID',
        bridgeChains: [
          {
            chain: 'EVM',
          },
        ],
      }}
    >
      <DynamicWidget />
    </DynamicContextProvider>
    ); };

    export default App;
    ```
  </Tab>

  <Tab title="Before">
    ```jsx
    import { DynamicContextProvider } from "@dynamic-labs/sdk-react-core";

    const App = () => {
    return (

    <DynamicContextProvider
      settings={{
        environmentId: 'ENV_ID',
        walletsByChain: [
          {
            chain: 'EVM',
          },
        ],
      }}
    >
      <DynamicWidget />
    </DynamicContextProvider>

    ); };

    export default App;
    ```
  </Tab>
</Tabs>

### Non Breaking Changes

*   Turnkey wallets are now created as Turnkey HD wallets.

### New Features

*   eip6963 is now fully supported without any need for changes on your end!

*   Listening to changes in wallet connectors is now supported via a hook called useWalletConnectorEvent. See [here](/react-sdk/hooks/usewalletconnectorevent) for more info.

*   You can now trigger the onramp flow programmatically using the useFunding hook. See [here](/react-sdk/hooks/usefunding) for more info.

*   Support for account recovery via a hook has been added. See [here](/react-sdk/hooks/usepasskeyrecovery) for more info.

*   We now generate HD wallets by default, there is nothing you need to do to take advantage of this.

*   You can now generate smart contract wallets (SCWs) using our Smart Wallet integrations. See [here](/smart-wallets/add-smart-wallets) for more info.

*   All of the copy which is used in the UI from the SDK is now completely editable. See [here](/design-customizations/customizing-copy-translations) for more info.

*   You can control what kind of signup/login options are shown in your UI on a programmatic basis regardless of what is enabled in the dashboard. See [here](/design-customizations/views) for more info.

*   Embedded wallets has been split up into the generation step and the claim step which means you can now give a user a wallet but without them adding a passkey until they make their first transaction.

*   Along the same lines as above, you can also pregenerate a wallet for a user without any interaction from them by using our API directly. See [here](/api-reference/endpoints/wallets/createWallet) for more info.

*   Webhooks are now offered out of the box! You do not need to make any SDK changes to take advantage of this. See [here](/developer-dashboard/webhooks) for more info.

## From V0.18 to V0.19

### *Summary*

This upgrade guide is specific to implementing the SDK itself.

There are a number of breaking changes in v0.19, mainly to the way the SDK is structured. To upgrade you should follow three steps, which are outlined in the breaking changes section below.

<Steps>
  <Step title="Switch to sdk-react-core">
    Uninstall sdk-react (if used) and [install the latest sdk-react-core](/react-sdk/upgrade#sdk-react-sdk-react-core).
  </Step>

  <Step title="Add walletConnectors">
    [Install the WalletConnectors you need](/react-sdk/upgrade#walletconnectors)
    alongside the SDK and [use them in the
    DynamicContextProvider](/react-sdk/upgrade#using-walletconnectors).
  </Step>

  <Step title="Viem as default">
    If you're happy for Viem to be the default instead of Ethers, do nothing. Otherwise, [use the Ethers extension](/react-sdk/upgrade#ethers-viem).
  </Step>
</Steps>

### Breaking Changes

#### sdk-react -> sdk-react-core

<Frame>
  <video autoPlay loop muted playsInline src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/sdk.webm" />
</Frame>

Previously you could use the SDK in two ways. The first was by installing `sdk-react` which came with all of the possible WalletConnectors pre-bundled. The second was by installing `sdk-react-core` and then adding WalletConnectors yourself alongside it.

While the first was helpful in getting started, the general feedback was that the bundle size was too large. So we've decided to remove the `sdk-react` package and make `sdk-react-core` the main way of installing the SDK.

To get started in this way, you should uninstall `sdk-react` and install `sdk-react-core` instead. If you're already using `sdk-react-core`, it's worth upgrading it to the latest version.

```bash
npm uninstall @dynamic-labs/sdk-react

npm install @dynamic-labs/sdk-react-core
```

#### WalletConnectors

As mentioned above, `sdk-react-core` doesn't come with any WalletConnectors pre-bundled.

There are also no more `all` packages available.

You should install them according to the table below.

| Package Name           | Chain  | WalletConnector to include |
| :--------------------- | :----- | -------------------------- |
| @dynamic-labs/ethereum | EVM    | `EthereumWalletConnectors` |
| @dynamic-labs/algorand | ALGO   | `AlgorandWalletConnectors` |
| @dynamic-labs/solana   | SOL    | `SolanaWalletConnectors`   |
| @dynamic-labs/flow     | FLOW   | `FlowWalletConnectors`     |
| @dynamic-labs/starknet | STARK  | `StarknetWalletConnectors` |
| @dynamic-labs/cosmos   | COSMOS | `CosmosWalletConnectors`   |

##### EVM Addon Wallets

| Package Name             | Which Wallets | WalletConnector to include  |
| :----------------------- | :------------ | :-------------------------- |
| @dynamic-labs/magic      | *magic*       | `MagicWalletConnectors`     |
| @dynamic-labs/blocto-evm | *blocto*      | `BloctoEvmWalletConnectors` |

<Warning>
  Magic and Blocto were previously available via the `ethereum-all` package
  which is now deprecated, please note that you will no longer get Magic &
  Blocto with the `ethereum` package - use the above packages instead.
</Warning>

Here's an example for Ethereum & Flow:

```bash
npm install @dynamic-labs/ethereum @dynamic-labs/flow
```

#### Using WalletConnectors

Using WalletConnectors looks like the following (ethereum and flow example):

```jsx
import { EthereumWalletConnectors } from "@dynamic-labs/ethereum";
import { FlowWalletConnectors } from "@dynamic-labs/flow";


const App = () => (
  <DynamicContextProvider
     settings={{
       ...
       walletConnectors: [ EthereumWalletConnectors, FlowWalletConnectors ],
  		 ...
    }}>
    <DynamicWidget />
  </DynamicContextProvider>
);

export default App;
```

#### Ethers -> Viem

<Frame>
  <video autoPlay loop muted playsInline src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/library.webm" />
</Frame>

We've also changed the way we interact with the BlockChain RPC and the Wallet. Previously we used Ethers.js by default and gave you a Viem version if you needed, but with Viem now rising further in popularity and functionality, we've now moved to Viem as the default library.

<Info>
  Please not that since Viem is now a peer dependancy of the SDK, it will be
  installed even if you use Ethers.
</Info>

If you're happy to use Viem, this should not have much of an effect on you. If you would still like to use Ethers or do not want to migrate your own application to using Viem, we have provided an extension for accessing Ethers within our Connectors:

```bash
npm i @dynamic-labs/ethers-v5
```

```jsx
import { EthersExtension } from '@dynamic-labs/ethers-v5'

return (
  <DynamicContextProvider
    settings={{
      environmentId: 'XXXXX',
      walletConnectorExtensions: [EthersExtension],
    }}
  >
    <App />
  </DynamicContextProvider>
)
```

You can then access the signer like so:

```jsx
const signer = await primaryWallet.connector.ethers?.getSigner()
```

<Tip>
  If you want to use Ethers V6 instead of V5, it's as simple as using
  `@dynamic-labs/ethers-v6` instead of `@dynamic-labs/ethers-v5`.
</Tip>

#### WalletConnector methods naming

Since the move to Viem, we have also updated the method names on the WalletConnector as they previously used Ethers Terminology.

| V0.18               | V0.19               |
| :------------------ | :------------------ |
| `getWeb3Provider()` | `getPublicClient()` |
| `getRpcProvider()`  | `getWalletClient()` |

#### onConnectSuccess -> onConnect

We've also renamed the `onConnectSuccess` callback to `onConnect`.

#### WalletIcon

| V0.18                                                                | V0.19                                         |
| :------------------------------------------------------------------- | :-------------------------------------------- |
| `<WalletIcon walletName={normalizeWalletName(walletConnector.name)}` | `<WalletIcon walletKey={walletConnector.key}` |

```bash
npm i @dynamic-labs/wallet-book
```

```jsx
import { useDynamicContext } from '@dynamic-labs/sdk-react-core'
import { WalletIcon } from '@dynamic-labs/wallet-book'

const { primaryWallet } = useDynamicContext()

return <WalletIcon walletKey={primaryWallet.connector.key} />
```

### Non-breaking changes

#### New Callbacks added

*   [onSignedMessage](/react-sdk/events/onsignedmessage)

*   onEmailVerificationSuccess (since replaced with "onotpverificationresult")

*   onEmailVerificationFailure (since replaced with "onotpverificationresult")

#### New Hooks added

*   [useEmbeddedWallet](/react-sdk/hooks/useembeddedwallet)

## From V0.17 to V0.18

#### *Summary*

There are no major breaking changes in V0.18. There are a few minor changes in V0.18.x, which you can find below.

#### *Updates to API URIs*

Our api is served now from [www.dynamicauth.com](http://www.dynamicauth.com) instead of [www.dynamic.xyz](http://www.dynamic.xyz), which should reduce issues for end users behind specific firewall providers.. Please update any rules and validations that you might have on the dynamic.xyz URIs.

#### *Changes to ​​DynamicContextProvider*

##### *Prop rename from: displayTermsOfService to: displaySiweStatement.*

This affects `DynamicContextProvider`. If you're passing `displayTermsOfService` to `DynamicContextProvider`, you should update your code like so:

<CodeGroup>
  ```jsx Before
  <DynamicContextProvider
    settings={{
      environmentId: "1234abcd-1234-abcd-1234-abcd12343abc",
      displayTermsOfService: false,
      ...
    }}
  >
  </DynamicContextProvider>
  ```

  ```jsx After
  <DynamicContextProvider
    settings={{
      environmentId: "1234abcd-1234-abcd-1234-abcd12343abc",
      displaySiweStatement: false,
      ...
    }}
    >
  </DynamicContextProvider>
  ```
</CodeGroup>

<br />

##### *Multiwallet prop has been removed.*

If you have multiwallet enabled please enable it in your dashboard:

<CodeGroup>
  ```jsx Before
  <DynamicContextProvider
    settings={{
      multiWallet: true,
      ...
    }}
  >
    {...}
  </DynamicContextProvider>
  ```

  ```jsx After
  {/* Enable multiwallet in your dashboard */}
  {/* (Advanced wallet options) section of Configurations tab*/}
  <DynamicContextProvider
    settings={{
      ...
    }}
  >
    {...}
  </DynamicContextProvider>
  ```
</CodeGroup>

#### *Changes to DynamicContext*

##### *ConnectedWallets*

connectedWallets now represents all the wallets that are actually connected

```jsx
const { connectedWallets } = useDynamicContext()
```

##### *LinkedWallets*

Used to be called ConnectedWallets now only represents that wallets that are linked to the account (only relevant to connect-and-auth flow)

```jsx
const { linkedWallets } = useDynamicContext()
```

#### *Updates to Walletbook*

If you have a dependancy on [@dynamic-labs/walletbook](https://www.npmjs.com/package/@dynamic-labs/wallet-book?activeTab=versions) \< v0.19, please remove it from your dependancies as it should now be included by default alongside the sdk-react package.

## From V0.16 to V0.17

### Breaking Changes

*   In favor of supporting MagicLink Wallets we removed the support for Fortmatic.

*   `chainName` on EvmNetwork is deprecated in favor of `name`. If you're passing `evmNetworks` to `DynamicContextProvider` or `DynamicWagmiConnector`, you should update your code like so:

```ts
<DynamicContextProvider
  settings={{
    environmentId: 'YOUR_ENV_ID',
    evmNetworks: [
      {
        blockExplorerUrls: [],
        chainId: 1,
        iconUrls: [],
     -  chainName: 'Ethereum',
     +  name: 'Ethereum',
        nativeCurrency: {
          decimals: 18,
          name: 'Ether',
          symbol: 'ETH',
        },
        networkId: 1,
        rpcUrls: [],
      },
    ],
  }}
>
  <HomePage />
</DynamicContextProvider>
```

<Note>
  {' '}

  `chainName` is a deprecated field on the EVMchain (and will be removed on v18),
  but if you pass a custom EVM network you will need to change to `name`.
</Note>

*   the `multiWallet` prop on DynamicContextProvider is deprecated in favor of the Dashboard settings. See [here](/wallets/advanced-wallets/multi-wallet) for more info.

## From V0.15 to V0.16

### Breaking Change

*   With the introduction of email login and social verification we moved to `verifiedCredentials` instead of `blockchainAccounts`. `blockChainAccounts` was removed from the [user's object](/react-sdk/objects/userprofile).

## From V0.14 to V0.15

### Breaking Change

*   Based on your feedback, our team has been hard at work to completely redesign our SDK UI and move it to ShadowDom. Now in addition, to the default customizations that we provide in the our admin dashboard, you have full control and ability to customize any element in the Dynamic's SDK to your liking. If you previously used any CSS Classes to override our previous SDK modal you will need to update them to the current CSS classes.
    For more info see: [Custom CSS](/design-customizations/css/custom-css).

*   As we introduced more callbacks, we now require all the callbacks to be nested under events. See [Callbacks](/react-sdk/events) for more info.

*   We removed user.walletPublicKey, instead please use primaryWallet.address, which you can access from [useDynamicContext](/react-sdk/hooks/usedynamiccontext).


# Upgrade from V1 to V2



### Breaking changes

*   `setShowAuthFlow` opens dynamic profile in non-multi-wallet, logged in states

#### Packages

*   sdk-api to sdk-api-core

The Dynamic SDK uses a number of other Dynamic packages, among them is the sdk-api library which contains several internal APIs that are not relevant to making the SDK communicate properly with Dynamic's backend.

Therefore we've created a new package called sdk-api-core which only contains endpoints and models relevant for the SDK, and this is what the V2 SDK uses.

If you need to interact with Dynamic's backend for admin-related API calls or bulk work, among other things, you should explicitly install sdk-api, otherwise the only effect this has is lowering your bundle size!

*   Wagmi V2

We now use Wagmi V2 by default as opposed to V1 in V1 of our SDK, you can find the implementation guide [here](/react-sdk/using-wagmi) to upgrade that package implementation and integration.

*   Viem V2

Viem V2 is now the default, generally you don't need to do anything as the SDK will install it for you but if you have Viem installed manually alongside the SDK, you'll want to either uninstall it or ensure you're using the latest version.

#### Renamed variables/props

*   useConnectWithEmailOtp to useConnectWithOtp

With the introduction of sms based login in addition to email we've adjusted some method naming to reflect this. Thus, useConnectWithEmailOtp becomes useConnectWithOtp.

*   fetchPublicAddress to getAddress

This renaming is simply to align with standard terminology.

*   hideEmbeddedWalletUIs prop to hideEmbeddedWalletTransactionUIs

This prop was not specific enough so we renamed it to include "Transaction" as this is exactly what it hides - the transaction confirmation screens.

*   canConnectViaEmail to requiresNonDynamicEmailOtp

This flag is actually used to decide whether a wallet connector requires an external OTP provider instead of Dynamic's default email OTP and therefore it made sense to rename it as such.

#### Removed variables/props

*   isVerificationInProgress

This was a boolean, available from useDynamicContext, which indicated whether any verifications are in progress for the current user (ex. connect, sign and email login verifications). However, this leads to the need for useEffects, which [the React team recommends avoiding](https://react.dev/learn/you-might-not-need-an-effect). Therefore we've remove this state and request that you add listeners to [our callbacks](/react-sdk/events/events-introduction) in order to trigger functionalities in an event driven manner.

*   isFullyConnected

You can now use [useIsLoggedIn](/react-sdk/hooks/useisloggedin) instead of isFullyConnected to check the user's login status.

#### Replaced variables/props

*   enableForcedNetworkValidation -> networkValidationMode

The type looks like the following:

```typescript
export type NetworkValidationMode = 'always' | 'sign-in' | 'never'
```

Use looks like this:

<Tabs>
  <Tab title="Before">
    ```jsx
    const settings = {
      enableForcedNetworkValidation: true,
    }
    ```
  </Tab>

  <Tab title="After">
    ```jsx
    const settings = {
      networkValidationMode: 'always'
    }
    ```
  </Tab>
</Tabs>

*   onEmailVerificationSuccess & onEmailVerificationFailure -> onOtpVerificationResult

With the introduction of SMS authentication as well as pre-existing email authentication, it makes more sense to generalise the verification callbacks for you, rather than having 4 individual ones. Therefore we've created `onOtpVerificationResult` so you can easily listen to success and failure for email and sms.

The signature of this new callback looks like the following

```typescript
export type OnOtpVerificationResult = (
  isSuccess: boolean,

  destination:
    | {
        type: Extract<OtpVerificationDestination, 'email'>
        value: string
      }
    | {
        type: Extract<OtpVerificationDestination, 'sms'>
        value: PhoneEventData
      }
) => void
```

The usage of the callback is outlined as such:

<Tabs>
  <Tab title="Before">
    ```jsx
    const DynamicSettings = {
      eventsCallbacks: {
        onEmailVerificationSuccess: (email) => handleSuccess(email),
        onEmailVerificationFailure: (email) => handleFailure(email),
      },
    };
    ```
  </Tab>

  <Tab title="After">
    ```jsx
    const DynamicSettings = {
      eventsCallbacks: {
        onOtpVerificationResult: (isSuccess, destination) => {
          if (destination.type !== "email") return;
          if (isSuccess) handleSuccess(destination.value);
          else handleFailure(destination.value);
        },
      },
    };
    ```
  </Tab>
</Tabs>

#### Moved variables/props

*   setShowLinkNewWalletModal

setShowLinkNewWalletModal is not part of DynamicContext anymore, but is available as part of the new useDynamicModals hook.

<Tabs>
  <Tab title="Before">
    `const {setShowLinkNewWalletModal} = useDynamicContext();`
  </Tab>

  <Tab title="After">
    `const {setShowLinkNewWalletModal} = useDynamicModals();`
  </Tab>
</Tabs>

*   evmNetworks

[evmNetworks](/react-sdk/objects/evmNetwork) now lives inside the `overrides` prop on the DynamicContextProvider settings object, whereas before it lived directly in the settings object.

<Tabs>
  <Tab title="Before">
    ```jsx
    <DynamicContextProvider
      settings={{
        environmentId: 'XXXXX',
        evmNetworks: [
          {
            blockExplorerUrls: [],
            chainId: 1,
            iconUrls: [],
            name: 'Ethereum',
            nativeCurrency: {
              decimals: 18,
              name: 'Ether',
              symbol: 'ETH',
            },
            networkId: 1,
            rpcUrls: [],
          },
        ],
      }}
    >
    </DynamicContextProvider>

    ```
  </Tab>

  <Tab title="After">
    ```jsx
    <DynamicContextProvider
      settings={{
        environmentId: 'XXXXX',
        overrides: {
          evmNetworks: [
            {
              blockExplorerUrls: [],
              chainId: 1,
              iconUrls: [],
              name: 'Ethereum',
              nativeCurrency: {
                decimals: 18,
                name: 'Ether',
                symbol: 'ETH',
              },
              networkId: 1,
              rpcUrls: [],
            },
          ],
        },
      }}
    ></DynamicContextProvider>
    ```
  </Tab>
</Tabs>

### ERC 1155 support

Our NFT Gating feature helps you gate by NFT ownership. We've now added support for ERC 1155 tokens, so you can now gate by NFT ownership for both ERC 721 and ERC 1155 token types!

### New [webhooks](/developer-dashboard/webhooks) added

*   user.passkeyRecovery.started
*   user.passkeyRecovery.completed
*   user.social.linked
*   user.social.unlinked
*   wallet.transferred
*   visit.created

### userId in all webhook payloads

You wanted an easier way to always have the userId of the user that triggered the event - now we also include the userId of the trigger in all webhook event objects!

### New hooks & methods

*   [showDynamicUserProfile](https://docs.dynamic.xyz/react-sdk/components/dynamicuserprofile#callback-at-closing-or-opening)
    Currently the setShowDynamicUserProfile method exists on useDynamicContext which helps you trigger the user profile modal, however you had no way of knowing if the modal was open or not. Now you can use the showDynamicUserProfile boolean to get this information.

```jsx
export const Example = () => {
  const { showDynamicUserProfile } = useDynamicContext()
  React.useEffect(() => {
    if (showDynamicUserProfile) {
      /* On widget opens */
    } else {
      /* On widget closes */
    }
  }, [showDynamicUserProfile])
  return null
}
```

*   [useConnectWithEmailOtp](https://docs.dynamic.xyz/react-sdk/hooks/useconnectwithemailotp)
    Now you can handle headless email signup with the useConnectWithEmailOtp hook! It exposes `connectWithEmail` and `verifyOneTimePassword` functions. It supports both Dynamic native login and our Magic integration.

*   [setShowLinkNewWalletModal](https://docs.dynamic.xyz/react-sdk/hooks/usedynamiccontext#setshowlinknewwalletmodal-use-it-to-link-a-new-wallet-when-the-user-is-logged-in)
    This new method comes as part of `useDynamicContext` and allows you to trigger the link new wallet method programmatically.

```jsx
const LinkNewWalletButton = () => {
  const { setShowLinkNewWalletModal } = useDynamicContext()
  return (
    <button onClick={() => setShowLinkNewWalletModal(true)}>
      Link a new wallet
    </button>
  )
}
```

*   [setAuthMode](https://docs.dynamic.xyz/react-sdk/hooks/usedynamiccontext)
    Previously, you could set the auth mode via the `initialAuthenticationMode` prop on `DynamicContextProvider` but this would be set in stone once the app loads. Now you can also set it programmatically via the `setAuthMode` method on `useDynamicContext`.

```jsx
const Example = () => {
  const { setAuthMode } = useDynamicContext()
  return (
    <button onClick={() => setAuthMode('connect-only')}>Connect Only</button>
  )
}
```

### New callbacks

*   [onAuthFlowCancel](https://docs.dynamic.xyz/react-sdk/events/onauthflowcancel)
    This callback is triggered when the user cancels the auth flow before successful completion. It will get called alongside [onAuthFlowClose](https://docs.dynamic.xyz/react-sdk/events/onauthflowclose).

### Extra text in the signup/login UI

*   Additional views section for extra text in the signup/login UI
    If you use [Programmable Views](/design-customizations/views), you can now insert extra text:

```js
views: [
  {
    type: 'login',
    sections: [
      { type: 'text', label: 'Intro Text', alignment: 'center' },
      ...
    ],
  },
],
```

<Frame>
  <img className="h-72" src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/views-text.png" />
</Frame>

### Multi-wallet prompts without userProfile widget

*   New [multiWalletPromptsWidget](https://docs.dynamic.xyz/react-sdk/components/dynamicmultiwalletpromptswidget)

This allows you to use the multi wallet prompts without needing the full user profile which in which these prompts are normally bundled.

<Frame>
  <img className="h-72" src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/multiWalletPromptsWidget-1.png" />
</Frame>

<Frame>
  <img className="h-72" src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/multiWalletPromptsWidget-2.png" />
</Frame>


# Upgrade from V2 to V3



## Changes in 3.0.0-alpha.53 -> latest

See [here](/react-sdk/upgrade/v3#2-x-x-to-3-0-0-alpha-52) for changes from 2.x.x to 3.0.0-alpha.52

### Wallet and WalletConnectors

#### No undesired prompting

No more undesired prompting to reconnect or switch wallets when not required!

Now a wallet can be made primary at any time without extra prompting, even if the wallet is not connected or unlocked.

Users will only be prompted to connect/unlock when sending/signing a transaction if the wallet is not connected or unlocked already.

#### Wallet object has changed

*   wallet.authenticated changed to wallet.isAuthenticated
*   wallet.connected is now an async method wallet.isConnected()
*   wallet.network is now an async method wallet.getNetwork()
*   wallet.chain valid values have changed

We used to use values like 'eip155' for wallet.chain. This is no longer the case. The new valid values for wallet.chain are the following:

```
'ALGO' | 'BTC' | 'COSMOS' | 'EVM' | 'FLOW' | 'SOL' | 'STARK'
```

The Wallet object has changed and now contains some methods that were previously on the WalletConnector object. Each chain specific wallet has its own methods. The methods specific to the main chains on the wallet object are:

*   EthereumWallet
    *   getPublicClient
    *   getWalletClient

*   SolanaWallet
    *   getConnection
    *   getSigner

*   BitcoinWallet
    *   sendBitcoin
    *   sendRawTransaction
    *   signMessageWithAddress
    *   signPsbt
    *   signPsbts

*   CosmosWallet
    *   getOfflineSigner
    *   getProvider

*   StarknetWallet
    *   getProvider
    *   getWalletAccount

*   AlgorandWallet
    *   getSigner

### Wallet type checking

You should always use our utilities to check that you're dealing with the correct wallet type before calling the wallet specific methods.

```tsx
import { isEthereumWallet } from '@dynamic-labs/ethereum';
import { isSolanaWallet } from '@dynamic-labs/solana';
import { isBitcoinWallet } from '@dynamic-labs/bitcoin';

const { primaryWallet } = useDynamicContext();

if (isEthereumWallet(primaryWallet)) {
  console.log('call any ethereum wallet method')
}

if (isSolanaWallet(primaryWallet)) {
  console.log('call any solana wallet method')
}

if (isBitcoinWallet(primaryWallet)) {
  console.log('call any bitcoin wallet method')
}
```

Learn more in the [Wallet Interactions section](/wallets/using-wallets/interacting-with-wallets).

#### Wallet connector object has changed

Since we have moved some methods from the connector to the wallet object, the wallet connector object is now slimmed down, you can find the methods and props available on both the connector and the wallet object in the [Wallet Interactions section](/wallets/using-wallets/interacting-with-wallets).

As with the wallet object, each chain specific wallet connector has its own methods so we recommend checking out the appropriate section for the chain you are working with.

<Tabs>
  <Tab title="Before">
    ```jsx
    import { useDynamicContext } from '@dynamic-labs/sdk-react-core'
    const { primaryWallet } = useDynamicContext()

    const fetchBalance = async () => {
      const balance = await primaryWallet?.connector.getBalance()
    }
    ```
  </Tab>

  <Tab title="After">
    ```jsx
    import { useDynamicContext } from '@dynamic-labs/sdk-react-core'
    const { primaryWallet } = useDynamicContext()

    const fetchBalance = async () => {
      const balance = await primaryWallet?.getBalance()
    }
    ```
  </Tab>
</Tabs>

### Return value type changes

In V2, you needed to type method return values on specific wallet methods like fetching the Viem public client, Viem wallet client, Solana connection etc. In V3, you no longer need to do this.

All you have to do is check for the wallet type (as shown in the previous section), and all the methods will come typed for you.

<Tabs>
  <Tab title="Before">
    ```tsx
    import { WalletClient, Transport, Chain, Account } from 'viem'

    import { useDynamicContext } from '@dynamic-labs/sdk-react-core'
    const { primaryWallet } = useDynamicContext()

    const walletClient = await primaryWallet.connector.getWalletClient<
        WalletClient<Transport, Chain, Account>
      >();
    ```
  </Tab>

  <Tab title="After">
    ```tsx
    import { useDynamicContext } from '@dynamic-labs/sdk-react-core'
    import { isEthereumWallet } from '@dynamic-labs/ethereum'
    const { primaryWallet } = useDynamicContext()

    if (!isEthereumWallet(primaryWallet)) {
      throw new Error('This wallet is not a Ethereum wallet');
    }

    const walletClient = await primaryWallet.getWalletClient()
    ```
  </Tab>
</Tabs>

### Smart Wallet connector updates

*   getEOAConnector has been removed

The `getEOAConnector` method is no longer available, you can use [the useSmartWallets hook](/react-sdk/hooks/usesmartwallets) to get the EOA wallet.

<Before>
  ```tsx
  import { isZeroDevConnector } from "@dynamic-labs/ethereum-aa";

  const Component = () => {
    const { primaryWallet } = useDynamicContext()

      const { connector } = primaryWallet

      if (!isZeroDevConnector(connector)) {
        return;
      }

      const signerConnector = connector.getEOAConnector()

      if (!signerConnector) {
        return;
      }

      // Do your thing!
  };
  ```
</Before>

<After>
  ```tsx
  import { useSmartWallets } from '@dynamic-labs/sdk-react-core'

  const Component = () => {
    const { getEOAWallet } = useSmartWallets()
    const { primaryWallet } = useDynamicContext()

    const signer = getEOAWallet(primaryWallet)

    // Do your thing!
  };
  ```
</After>

### Ethers support refactored

Instead of using the `EthersExtension` on the Context Provider, you now need to use the `ethers-v6` package directly, which exports all of the Ethers specific methods.

<Tabs>
  <Tab title="Before">
    ```jsx
    // App.tsx
    import { EthersExtension } from '@dynamic-labs/ethers-v6'

    <DynamicContextProvider
      settings={{
        environmentId: 'XXXXX',
        walletConnectorExtensions: [EthersExtension],
      }}
    >
    </DynamicContextProvider>

    // Main.tsx
    import { useDynamicContext } from '@dynamic-labs/sdk-react-core'

    const { primaryWallet } = useDynamicContext()
    const signer = primaryWallet?.connector.ethers?.getSigner();
    ```
  </Tab>

  <Tab title="After">
    ```jsx
    // App.tsx
     <DynamicContextProvider
      settings={{
        environmentId: 'XXXXX',
      }}
    >
    </DynamicContextProvider>

    // Main.tsx
    import { useDynamicContext } from '@dynamic-labs/sdk-react-core'
    import { getSigner } from '@dynamic-labs/ethers-v6'

    const { primaryWallet } = useDynamicContext()
    const signer = await getSigner(primaryWallet)
    ```
  </Tab>
</Tabs>

### Helper method removed

*   createWalletClientFromWallet has been removed

The createWalletClientFromWallet function is no longer available, you can use the `getWalletClient` method on the wallet object instead.

<CodeGroup>
  ```tsx Before
  import { createWalletClientFromWallet } from '@dynamic-labs/sdk-react-core';
  ```

  ```tsx After
  import { useDynamicContext } from '@dynamic-labs/sdk-react-core'
  const { primaryWallet } = useDynamicContext()

  const walletClient = await primaryWallet?.getWalletClient()
  ```
</CodeGroup>

### SignWithEmailWalletName has been removed

Instead please use the available email login flows

## 2.X.X to 3.0.0-alpha.52

### Packages restructuring

#### Solana Utils

@dynamic-labs/solana-utils has been renamed to @dynamic-labs/solana-core

#### Viem Utils

@dynamic-labs/viem-utils has been renamed to @dynamic-labs/ethereum-core

#### Ethereum RPC provider

@dynamic-labs/rpc-provider-ethereum has been removed. You can now use @dynamic-labs/ethereum-core to get the RPC provider.

#### Solana RPC provider

@dynamic-labs/rpc-provider-solana has been removed. You can now use @dynamic-labs/solana-core to get the RPC provider.

#### Starknet RPC provider

@dynamic-labs/rpc-provider-starknet has been removed. You can now use @dynamic-labs/starknet-core to get the RPC provider.

### UserProfile

Removed props that should be retrieved from the primaryWallet: `chain`, `ens`, `wallet`
Remove `isAuthenticatedWithAWallet` as a prop and added it as a helper method.
See [isAuthenticatedWithAWallet](/react-sdk/utilities/isauthenticatedwithawallet)

### Removed props from `useDynamicContext`

`hideEmbeddedWalletTransactionUIs` has been removed from useDynamicContext. This is now [a toggle in the dashboard](https://app.dynamic.xyz/dashboard/embedded-wallets/dynamic).

`setPrimaryWallet` has been removed. You should use [useSwitchWallet](/react-sdk/hooks/useswitchwallet#useswitchwallet) instead.

`isAuthenticated` has been removed. Please refer to [this page](/react-sdk/loading-login-states) for other ways to check the login state.

`walletConnector` has been removed. You can get access to it by getting the primary wallet prop and doing `primaryWallet?.connector`.

`walletConnectorOptions` has been removed. You can get access to the available wallet options from the [useWalletOptions hook](/react-sdk/hooks/usewalletoptions).

### Replaced props

*   `setDefaultTabIndex` replaced with `setSelectedTabIndex` in useDynamicContext

This function is used to create [wallet list views](/design-customizations/tutorials/wallet-list-views-guide).

<CodeGroup>
  ```tsx Before
  const { setDefaultTabIndex } = useDynamicContext()

  const onChangeTab = (index: number) => {
    setDefaultTabIndex(index)
  }
  ```

  ```tsx After
  const { setSelectedTabIndex } = useDynamicContext()

  const onChangeTab = (index: number) => {
    setSelectedTabIndex(index)
  }
  ```
</CodeGroup>

### Package restructuring

*   ethers-v5 no longer available

We no longer support ethers v5. If you are using ethers v5 and wish to keep using ethers with Dynamic,
please upgrade to ethers v6 by following [this guide](https://docs.ethers.org/v6/migrating/), and then switch
to using our `ethers-v6` package. They have the same usability.

*   rpcProviders no longer available in the sdk-react-core packages

The type DynamicRPCProviders that used to be exported from @dynamic-labs/sdk-react-core is no longer available and has been replaced with individual packages i.e. `@dynamic-labs/ethereum-core` & `@dynamic-labs/solana-core`:

<CodeGroup>
  ```tsx Before
  import { useDynamicContext } from '@dynamic-labs/sdk-react-core';

  const App = () => {
    const { rpcProviders } = useDynamicContext();

    console.log(rpcProviders.evmProviders);
    console.log(rpcProviders.solanaProviders);
  }
  ```

  ```tsx After
  import { useRpcProviders } from '@dynamic-labs/sdk-react-core';
  import { evmProvidersSelector } from '@dynamic-labs/ethereum-core';
  import { solanaProvidersSelector } from '@dynamic-labs/solana-core';

  const App = () => {
    const { providers: evmProviders } = useRpcProviders(evmProvidersSelector);
    const { providers: solanaProviders } = useRpcProviders(solanaProvidersSelector);

    console.log(evmProviders);
    console.log(solanaProviders);
  }
  ```
</CodeGroup>

*   ITurnkeySolanaSigner renamed to IEmbeddedWalletSolanaSigner and import path changed

<CodeGroup>
  ```tsx Before
  import { ITurnkeySolanaSigner } from '@dynamic-labs/turnkey';
  ```

  ```tsx After
  import { IEmbeddedWalletSolanaSigner } from '@dynamic-labs/solana'
  ```
</CodeGroup>

### Events arguments

*   [onAuthSuccess](/react-sdk/events/onauthsuccess) doesn't return an authToken anymore

*   onEmbeddedWalletCreated returns the user instead of the authToken

You can call [getAuthToken](/react-sdk/utilities/getauthtoken) to access the authToken at any time!

### Other

*   Polyfill for Solana Embedded Wallet users

You may need to polyfill the 'crypto' module.

webpack.config.js:

```typescript
config.resolve.fallback = {
  ...config.resolve.fallback,
  crypto: require.resolve('crypto-browserify'),
}
```

vite.config.ts

```typescript
import { nodePolyfills } from 'vite-plugin-node-polyfills';

plugins: [
      nodePolyfills({
        globals: {
          global: true,
        },
        include: [],
      }),
    ],
```

### Renamed Hooks

`useSelectWalletOption` has been renamed to `useWalletOptions`

### New Hooks

*   Re-initialize the SDK (useReinitialize)

```tsx
import {
  DynamicContextProvider,
  useReinitialize,
} from '@dynamic-labs/sdk-react-core';

const ReInitButton = () => {
  const reinitialize = useReinitialize();

  return (
    <button onClick={reinitialize}>Reinitialize</button>
  );
}

const App = () => {
  return (
    <DynamicContextProvider>
      <ReInitButton />
    </DynamicContextProvider>
  )
}
```

*   Trigger state refresh (useRefreshUser)

```tsx
import {
  DynamicContextProvider,
  useRefreshUser,
} from '@dynamic-labs/sdk-react-core';

const RefreshButton = () => {
  const refresh = useRefreshUser();

  return (
    <button onClick={refresh}>Reinitialize</button>
  );
}

const App = () => {
  return (
    <DynamicContextProvider>
      <RefreshButton />
    </DynamicContextProvider>
  )
}
```


# Upgrade from V3 to v4



### Breaking changes

#### Starknet V6

We upgraded to Starknet v6 See this guide for more details: [https://starknetjs.com/docs/guides/migrate/](https://starknetjs.com/docs/guides/migrate/) T

There are no code changes required on the Dynamic SDK side, however if you have installed the starknet package, you will need to upgrade to >= v6.11.0.

#### Walletbook methods

isWalletMethodSupported and isWalletEventSupported have been removed from wallet-book as part of a refactor to improve the way the SDK operates.


# Using Ethers



The SDK and above ships with Viem by default as it is much lighter than Ethers and is generally more performant.

We have also made viem a peerDependency to avoid bundling it multiple times, as other other packages such as wagmi also need it.

For the above reason, viem will be installed as a peerDependency when you install the SDK with npm, even if you want to use Ethers.

<Warning>
  If you are using yarn instead of npm, even if you want to use Ethers, you will need to install viem manually:

  ```bash
  yarn i viem
  ```
</Warning>

Dynamic only supports version v6+ of Ethers. In order to use it, simply import the ethers methods you need from `@dynamic-labs/ethers-v6` directly in the component where you are using them.

```bash
npm i @dynamic-labs/ethers-v6
```

```jsx
import { useDynamicContext } from '@dynamic-labs/sdk-react-core'
import { getWeb3Provider,getSigner, } from '@dynamic-labs/ethers-v6'

const { primaryWallet } = useDynamicContext()

const provider = await getWeb3Provider(primaryWallet)
const signer = await getSigner(primaryWallet)

// do your thing

```


# Using Wagmi



The default SDK implementation does not use Wagmi, but you can add full support for it by following the steps below.

#### Installing packages

First, you want to install the DynamicWagmiConnector package using `npm` or `yarn`

<CodeGroup>
  ```shell npm
  npm install viem wagmi @tanstack/react-query @dynamic-labs/wagmi-connector @dynamic-labs/sdk-react-core @dynamic-labs/ethereum
  ```

  ```shell yarn
  yarn add viem wagmi @tanstack/react-query @dynamic-labs/wagmi-connector @dynamic-labs/sdk-react-core @dynamic-labs/ethereum
  ```

  ```shell pnpm
  pnpm add viem wagmi @tanstack/react-query @dynamic-labs/wagmi-connector @dynamic-labs/sdk-react-core @dynamic-labs/ethereum
  ```
</CodeGroup>

#### Configure Wagmi and Dynamic

Here's a full code snippet with all the setup code required.

```TypeScript
import {
  DynamicContextProvider,
  DynamicWidget,
} from '@dynamic-labs/sdk-react-core';
import { EthereumWalletConnectors } from '@dynamic-labs/ethereum';
import { DynamicWagmiConnector } from '@dynamic-labs/wagmi-connector';
import {
  createConfig,
  WagmiProvider,
  useAccount,
} from 'wagmi';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { http } from 'viem';
import { mainnet } from 'viem/chains';

const config = createConfig({
  chains: [mainnet],
  multiInjectedProviderDiscovery: false,
  transports: {
    [mainnet.id]: http(),
  },
});

const queryClient = new QueryClient();

export default function App() {
  return (
    <DynamicContextProvider
      settings={{
        environmentId: 'ENV_ID',
        walletConnectors: [EthereumWalletConnectors],
      }}
    >
      <WagmiProvider config={config}>
        <QueryClientProvider client={queryClient}>
          <DynamicWagmiConnector>
            <DynamicWidget />
            <AccountInfo />
          </DynamicWagmiConnector>
        </QueryClientProvider>
      </WagmiProvider>
    </DynamicContextProvider>
  );
}

function AccountInfo() {
  const { address, isConnected, chain } = useAccount();

  return (
    <div>
      <p>
        wagmi connected: {isConnected ? 'true' : 'false'}
      </p>
      <p>wagmi address: {address}</p>
      <p>wagmi network: {chain?.id}</p>
    </div>
  );
};
```

Two things to note here:

1.  `multiInjectedProviderDiscovery` is set to `false` – this is because Dynamic implements the multi injected provider discovery protocol itself. If you'd like to keep this enabled on Wagmi, please do, but you might see some undefined behavior and we might not be able to debug.
2.  While this example configures mainnet as the only chain, you can pass in all your supported chains to the `chains` array. For more info, see the "Chain Configuration" section below.

Throughout your app, you can now use Wagmi hooks like `useAccount` and they will automatically sync to the wallet that you logged in with via Dynamic.

<Note>
  Make sure to call `createConfig` at the top-level of your app. If you call it
  inside of a component, it will be called during each render, which can lead to
  unexpected behavior.
</Note>

#### Chain Configuration

If you are upgrading from Dynamic + Wagmi v1, then this config will look new to you. Previously, Dynamic was automatically updating the Wagmi config with the chains that you configured in your Dynamic Dashboard.
This behavior has changed in v2 to give you more control over your Wagmi config and for a simpler integration. What this means is that you will need to pass to Wagmi all of the chains you have configured with Dynamic.

For example, if in Dynamic you have enabled Ethereum Mainnet, Optimism and Base, you will need to update your Wagmi config to look like this:

```TypeScript
import {
  createConfig,
  WagmiProvider,
} from 'wagmi';
import { http } from 'viem';
import { mainnet, optimism, base } from 'viem/chains';

const config = createConfig({
  chains: [mainnet, optimism, base],
  multiInjectedProviderDiscovery: false,
  transports: {
    [mainnet.id]: http(),
    [optimism.id]: http(),
    [base.id]: http(),
  },
});
```

### Adding Custom Networks

As you probably already know, you can add a custom EVM network through Dynamic using [the evmNetworks prop](/chains/evmNetwork). If you want to use these custom networks with Wagmi, you will need to declare it in your Wagmi config.

You can use our util function called \[getOrMapViemChain] to convert the EVM network object to a Viem chain object.

Here's an example of adding the Morph network to both Dynamic and to the Wagmi config:

```tsx
import {
  createConfig,
  WagmiProvider,
} from 'wagmi';

import { http } from 'viem';

import { getOrMapViemChain } from '@dynamic-labs/sdk-react-core';

const customEvmNetworks = [
  {
    blockExplorerUrls: ["https://explorer-holesky.morphl2.io/"],
    chainId: 2810,
    name: "Morph",
    rpcUrls: ["https://rpc-quicknode-holesky.morphl2.io"],
    iconUrls: ["https://avatars.githubusercontent.com/u/132543920?v=4"],
    nativeCurrency: {
      name: "Ethereum",
      symbol: "ETH",
      decimals: 18,
    },
    networkId: 2810,
  },
]

export const wagmiConfig = createConfig({
  chains: [
    mainnet,
    ...customEvmNetworks.map(getOrMapViemChain),
  ],
  client({ chain }) {
    return createClient({
      chain,
      transport: http(),
    });
  },
});
```

### Adding Private RPCs

Note that if you are using [a private RPC in Dynamic](/chains/rpc-urls#dashboard-configuration), you will also need to declare that in your Wagmi config. Luckily this is as simple as passing it into your http transport in Wagmi:

```tsx
import {
  createConfig,
  WagmiProvider,
} from 'wagmi';

export const wagmiConfig = createConfig({
  chains: [mainnet],
  multiInjectedProviderDiscovery: false,
  transports: {
    [mainnet.id]: http('your-private-rpc-url'),
  },
});
```

### Further Resources

For docs on `createConfig`, see: [https://wagmi.sh/react/api/createConfig](https://wagmi.sh/react/api/createConfig)

For more general information on what you can do with Wagmi, check out their getting started docs: [https://wagmi.sh/react/getting-started](https://wagmi.sh/react/getting-started)


# getAuthToken

This utility can be used to get a JWT token generated by Dynamic which is stored inside of localStorage.

```TypeScript
import { getAuthToken } from "@dynamic-labs/sdk-react-core";

const App = () => {
    const dynamicJwtToken = getAuthToken()
    ...
}
```


# getNetwork



### Summary

This utility function should be used to find the current network used by a wallet. It receives the wallet connector and returns a promise that will resolve with the network.

### Annotation

TypeScript

`const getNetwork: (walletConnector: WalletConnector | null) => Promise<number | undefined>
`

### Examples

##### Get primary wallet network

TypeScript

```TypeScript
import { useDynamicContext, getNetwork } from '@dynamic-labs/sdk-react-core';

const App = () => {
  const { primaryWallet } = useDynamicContext();

  useEffect(() => {
     primaryWallet.connector.getNetwork().then((network) => {...});
  }, [primaryWallet]);

  ...
}
```

##### Get a network for all wallets

TypeScript

```TypeScript
import { useUserWallets, getNetwork } from '@dynamic-labs/sdk-react-core';

const App = () => {
  const userWallets = useUserWallets();

  useEffect(() => {
    userWallets.forEach(async ({ connector }) => {
      const network = await getNetwork(connector);

      ...
    });
  }, [userWallets]);

  ...
}
```


# getOrMapViemChain



## Summary

<Warning>
  Only available in latest V3 alpha release.
</Warning>

This utility gets the Viem chain object for a given chain id, it is imported from `@dynamic-labs/ethereum-core`.

## Annotation

```TypeScript
export declare const getOrMapViemChain: (network: EvmNetwork) => Chain;
```

## Example

A common usage is when you declare your own [custom EVM networks](/chains/evmNetwork) for Dynamic, and you want to get the Viem chain objects for each so that you can pass them to your Wagmi config:

```tsx
import { DynamicContextProvider, mergeNetworks } from "@dynamic-labs/sdk-react-core";
import { getOrMapViemChain } from "@dynamic-labs/ethereum-core";
import { EthereumWalletConnectors } from "@dynamic-labs/ethereum";
import { DynamicWagmiConnector } from "@dynamic-labs/wagmi-connector";
import { createConfig, WagmiProvider } from "wagmi";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { http, createClient } from "viem";
import { mainnet } from "viem/chains";

import Main from "./Main";

const customEvmNetworks = [
  {
    blockExplorerUrls: ["https://explorer-holesky.morphl2.io/"],
    chainId: 2810,
    name: "Morph",
    rpcUrls: ["https://rpc-quicknode-holesky.morphl2.io"],
    iconUrls: ["https://avatars.githubusercontent.com/u/132543920?v=4"],
    nativeCurrency: {
      name: "Ethereum",
      symbol: "ETH",
      decimals: 18,
    },
    networkId: 2810,
  },
];

const config = createConfig({
  chains: [mainnet, ...customEvmNetworks.map(getOrMapViemChain)],
  multiInjectedProviderDiscovery: false,
  client({ chain }) {
    return createClient({
      chain,
      transport: http(),
    });
  },
});

const queryClient = new QueryClient();

export default function App() {
  return (
    <DynamicContextProvider
      settings={{
        environmentId: "2762a57b-faa4-41ce-9f16-abff9300e2c9",
        walletConnectors: [EthereumWalletConnectors],
        overrides: {
          evmNetworks: (networks) => mergeNetworks(customEvmNetworks, networks),
        }
      }}
    >
      <WagmiProvider config={config}>
        <QueryClientProvider client={queryClient}>
          <DynamicWagmiConnector>
           <Main />
          </DynamicWagmiConnector>
        </QueryClientProvider>
      </WagmiProvider>
    </DynamicContextProvider>
  );
}
```


# isZeroDevConnector



### Summary

Utility to determine if a wallet connector is a ZeroDev connector.
This is useful for interacting with ZeroDev and keep typesafety.

### Annotation

TypeScript

```ts
isZeroDevConnector(connector: WalletConnector): boolean
```

### Examples

Get the ZeroDev ECDSAProvider from the connector

```tsx
import { isZeroDevConnector } from '@dynamic-labs/ethereum-aa';

const App = () => {
  const { primaryWallet } = useDynamicContext();

  useEffect(() => {
    const { connector } = primaryWallet;

    if (isZeroDevConnector(connector)) {
      const ecdsaProvider = connector.getAccountAbstractionProvider();
    }
  }, [primaryWallet]);

  ...
}
```


# isAuthenticatedWithAWallet

This utility can be used check if the user has authenticated with a wallet.

```TypeScript
import { isAuthenticatedWithAWallet } from "@dynamic-labs/sdk-react-core";

const App = () => {
    const authenticatedWithAWallet = isAuthenticatedWithAWallet(user)
    ...
}
```


# isLedgerSolanaWallet

Determine if a given wallet is connected as a Ledger Solana wallet.

```TypeScript
import { useDynamicContext } from '@dynamic-labs/sdk-react-core'
import { isLedgerSolanaWallet } from "@dynamic-labs/solana-core";

const App = () => {
  const { primaryWallet } = useDynamicContext()

  if (primaryWallet && isLedgerSolanaWallet(primaryWallet)) {
    /**
     * This block will execute if the primaryWallet was connected
     * as a Ledger wallet on the Solana blockchain during the
     * Dynamic UI connection process.
     */
  }
}
```


# mergeNetworks



## Summary

This utility function merges two arrays of `EvmNetwork` objects, with the first array taking precedence in case of a conflict. It is imported from `@dynamic-labs/sdk-react-core`.

## Annotation

```TypeScript
export declare const mergeNetworks: (priorityNetworks: GenericNetwork[], secondaryNetworks: GenericNetwork[]) => GenericNetwork[];
```

## Example

It is commonly used when you declare [custom EVM networks](/chains/evmNetwork) for Dynamic, and you want to merge them with the networks you get from the dashboard configurations:

```TypeScript
import { mergeNetworks } from "@dynamic-labs/sdk-react-core";

const myEvmNetworks = [
    ...
]

const DynamicSettings = {
  overrides: {
    evmNetworks: (networks) => mergeNetworks(myEvmNetworks, networks),
  }
};
```


# Utilities Introduction



You can think of utilities as helper functions on top of the SDK. You could implement them yourself, but we've found that these are common tasks and so have done the legwork for you.

You can import them directly from the @dynamic-labs/sdk-react-core package, for example:

```jsx
import { getAuthToken } from "@dynamic-labs/sdk-react-core";
```

[**getAuthToken**](/react-sdk/utilities/getauthtoken): Used to get a JWT token generated by Dynamic which is stored inside of localStorage

[**getNetwork**](/react-sdk/utilities/getnetwork): Used to find the current network used by a wallet. It receives the wallet connector and returns a promise that will resolve with the network.

[**isZeroDevConnector**](/react-sdk/utilities/is-zero-dev-connector): Used to type check if a wallet connector is a ZeroDev connector

[**isAuthenticatedWithAWallet**](/react-sdk/utilities/isauthenticatedwithawallet): Used to check is the user has authenticated with a wallet

[**isLedgerSolanaWallet**](/react-sdk/utilities/isledgersolanawallet): Determine if a given wallet is connected as a Ledger Solana wallet


# Smart Wallets



Dynamic integrates with multiple account abstraction providers so you can choose your optimal setup to convert wallets into smart contract wallets, leveraging [EIP-4337](https://eips.ethereum.org/EIPS/eip-4337) (the official account abstraction specification).

## What can you do with a smart contract wallet?

Smart wallets offer a myriad of benefits. Four specific ones are:

1.  **Gas fee sponsorship** - you can choose to allow your users to conduct “gasless” transactions that will be sponsored by you.
2.  **Transaction bundling** - you can collect a single signature which is used for a bundle of transactions vs having to ask a user for signature on every independent action.
3.  **Sophisticated recovery and transfer options** - because the wallet is a smart contract, you can define sophisticated recovery methods for the wallet signer, as well as options to transfer ownership from one private key owner to another.
4.  **Session keys** - you can create session keys with permissions to send specific transactions for your users, so that you can execute transactions without asking your users to sign, and even execute transactions while they are offline.

## How does smart wallet creation work?

Once a user logs in with email and an embedded wallet is created for them, a key pair is generated for that wallet. The key pair is then used to generate a smart contract wallet that uses the private key as the owner. In essence, you use the private key from the embedded wallet to control a more sophisticated smart wallet.

The resulting wallet can be interacted with using the same standard interface as native wallets, with the ability to add features such as gas sponsorship, session keys and batched transactions.

## Getting started with Smart Wallets

To get started with smart wallets, you will need to choose an account abstraction provider. Dynamic currently supports the following providers:

<Tip>
  Note that ZeroDev is the only provider integrated natively into Dynamic.
</Tip>

*   [ZeroDev](/smart-wallets/smart-wallet-providers/zerodev)
*   [Alchemy](/smart-wallets/smart-wallet-providers/alchemy)
*   [Pimlico](/smart-wallets/smart-wallet-providers/pimlico)
*   [Biconomy](/smart-wallets/smart-wallet-providers/biconomy)


# Alchemy



This guide shows you how to use Dynamic as a signer for Alchemy Smart Accounts.

## Install the sdk

By default, the latest version of the Dynamic SDK ships with Viem. If you need to use Ethers, please refer to [this guide](/react-sdk/using-ethers).

<Tip>
  In this example, we are installing only the Ethereum connectors in order to
  keep bundle size light. If you need any others, you can find the references
  [here](react-sdk/providers/dynamiccontextprovider#walletconnectors).
</Tip>

```bash
npm i -s @dynamic-labs/sdk-react-core @dynamic-labs/ethereum
```

## Add Dynamic to your application

In order to use Dynamic, you should wrap your app with DynamicContextProvider at the highest possible level i.e.

```jsx
import { DynamicContextProvider } from '@dynamic-labs/sdk-react-core'
import { EthereumWalletConnectors } from '@dynamic-labs/ethereum'
import Home from './Home'

// Found in your Dynamic dashboard (https://app.dynamic.xyz/dashboard/developer/api)
const DYNAMIC_ENVIRONMENT_ID = 'XXXXX'

const App = () => {
  return (
    <div className="app">
      <DynamicContextProvider
        settings={{
          environmentId: DYNAMIC_ENVIRONMENT_ID,
          walletConnectors: [EthereumWalletConnectors],
        }}
      >
        <Home />
      </DynamicContextProvider>
    </div>
  )
}

export default App
```

## Create a SmartAccountSigner

Next, inside any component which is wrapped by the above DynamicContextProvider, use the useDynamicContext hook to fetch your provider, and create a SmartAccountSigner:

```tsx
import { WalletClientSigner, type SmartAccountSigner } from '@alchemy/aa-core'
import { useDynamicContext } from '@dynamic-labs/sdk-react-core'
import { isEthereumWallet } from '@dynamic-labs/ethereum'

// eslint-disable-next-line react-hooks/rules-of-hooks
const { primaryWallet } = useDynamicContext()

if (!isEthereumWallet(primaryWallet)) {
  throw new Error('This wallet is not a Ethereum wallet');
}

const dynamicProvider = await primaryWallet?.getWalletClient()

// a smart account signer you can use as an owner on ISmartContractAccount
export const dynamicSigner: SmartAccountSigner = new WalletClientSigner(
  dynamicProvider,
  'dynamic' // signer type
)
```

## Use it with Light Account

Let's see it in action with aa-alchemy and LightSmartContractAccount from aa-accounts:

<CodeGroup>
  ```tsx example.ts
  import { AlchemyProvider } from '@alchemy/aa-alchemy'
  import {
    LightSmartContractAccount,
    getDefaultLightAccountFactoryAddress,
  } from '@alchemy/aa-accounts'
  import { sepolia } from 'viem/chains'
  import { dynamicSigner } from './dynamic'

  const chain = sepolia

  const provider = new AlchemyProvider({
    apiKey: 'ALCHEMY_API_KEY',
    chain,
  }).connect(
    (rpcClient) =>
      new LightSmartContractAccount({
        chain,
        owner: dynamicSigner,
        factoryAddress: getDefaultLightAccountFactoryAddress(chain),
        rpcClient,
      })
  )
  ```

  ```tsx dynamic.ts
  import { WalletClientSigner, type SmartAccountSigner } from '@alchemy/aa-core'
  import { useDynamicContext } from '@dynamic-labs/sdk-react-core'
  import { isEthereumWallet } from '@dynamic-labs/ethereum'

  // eslint-disable-next-line react-hooks/rules-of-hooks
  const { primaryWallet } = useDynamicContext()

  if (!isEthereumWallet(primaryWallet)) {
    throw new Error('This wallet is not a Ethereum wallet');
  }

  const dynamicProvider = await primaryWallet?.getWalletClient()

  // a smart account signer you can use as an owner on ISmartContractAccount
  export const dynamicSigner: SmartAccountSigner = new WalletClientSigner(
    dynamicProvider,
    'dynamic' // signer type
  )
  ```
</CodeGroup>


# Biconomy



This guide will walk you through how to use Biconomy's smart accounts with Dynamic. You can find more info on how Biconomy works here: [https://docs.biconomy.io/account](https://docs.biconomy.io/account). We will use Ethereum Sepolia, so make sure your paymaster etc. is set up for that, and also make sure your user is on this network once logged in.

<Note>
  This guide and example is for V4 of the Biconomy packages and V3 of Dynamic.
</Note>

## Install Dynamic and Biconomy

In your app's repository, install `@dynamic-labs/sdk-react-core`, `@dynamic-labs/ethereum` and `@biconomy/{account, bundler}`:

```bash
yarn add @dynamic-labs/sdk-react-core @biconomy/account @biconomy/bundler
```

## Initialize & configure Dynamic

### Add the DynamicContextProvider

All you need to get started is the DynamicContextProvider - you will want to wrap your app with this component at the highest possible level, like so:

```jsx
import { DynamicContextProvider } from '@dynamic-labs/sdk-react-core'
import { EthereumWalletConnectors } from '@dynamic-labs/ethereum'

<DynamicContextProvider
  settings={{
    // Find your environment id at https://app.dynamic.xyz/dashboard/developer/api
    environmentId: 'REPLACE-WITH-YOUR-ENVIRONMENT-ID',
    walletConnectors: [EthereumWalletConnectors]
  }}
>
  {/* Your app's components */}
</DynamicContextProvider>
```

<Tip>
  To enable embedded wallets for your users, [toggle it
  on](https://app.dynamic.xyz/dashboard/embedded-wallets/dynamic) along with
  [email/social auth](https://app.dynamic.xyz/dashboard/log-in-user-profile) in
  the dashboard.
</Tip>

## Configure your Biconomy bundler and paymaster

Go to the Biconomy Dashboard and configure a Paymaster and a Bundler for your app. Make sure these correspond to the desired network for your user's smart accounts.

Make sure to set the paymaster URL as the value for REACT\_APP\_BICONOMY\_PAYMASTER\_URL in your env file, and do the same with your bundler URL for REACT\_APP\_BICONOMY\_BUNDLER\_URL.

### Create our Biconomy constants

Let's introduce a new `biconomy.js` file to house our Biconomy methods. Here we'll initialize the paymaster, the bundler, the validation module, and the method to create a smart account using createSmartAccountClient.

The validation module allows the user to authorize actions from their Biconomy smart account by signing messages with their Dynamic wallet.

```js biconomy.js
import { Bundler } from "@biconomy/bundler";
import { Paymaster, createSmartAccountClient, DEFAULT_ENTRYPOINT_ADDRESS, ECDSAOwnershipValidationModule, DEFAULT_ECDSA_OWNERSHIP_MODULE } from "@biconomy/account";
import { sepolia } from "viem/chains";

const bundler = new Bundler({
  bundlerUrl: process.env.REACT_APP_BICONOMY_BUNDLER_URL,
  chainId: sepolia.id, // Replace this with your desired network
  entryPointAddress: DEFAULT_ENTRYPOINT_ADDRESS, // This is a Biconomy constant
});

const paymaster = new Paymaster({
  paymasterUrl: process.env.REACT_APP_BICONOMY_PAYMASTER_URL,
});

const createValidationModule = async (signer) => {
  return await ECDSAOwnershipValidationModule.create({
    signer: signer,
    moduleAddress: DEFAULT_ECDSA_OWNERSHIP_MODULE, // This is a Biconomy constant
  });
};

export const createSmartAccount = async (walletClient) => {
  const validationModule = await createValidationModule(walletClient);
  console.log('creating')

  return await createSmartAccountClient({
    signer: walletClient,
    chainId: sepolia.id, // Replace this with your target network
    bundler: bundler, // Use the `bundler` we initialized above
    paymaster: paymaster, // Use the `paymaster` we initialized above
    entryPointAddress: DEFAULT_ENTRYPOINT_ADDRESS, // This is a Biconomy constant
    defaultValidationModule: validationModule, // Use the `validationModule` we initialized above
    activeValidationModule: validationModule, // Use the `validationModule` we initialized above
  });
};
```

### Create our custom hook

We'll create a custom hook to fetch the wallet client from Dynamic, and then initialize the Biconomy smart account using the methods we created above.

```jsx useBiconomyAccount.js
import { useState, useEffect, useCallback } from 'react';
import { useDynamicContext } from '@dynamic-labs/sdk-react-core';
import { createSmartAccount } from "./biconomy.js";

export function useBiconomyAccount() {
  const { primaryWallet } = useDynamicContext();
  const [smartAccount, setSmartAccount] = useState(null);

  const createAndSetSmartAccount = useCallback(async () => {
    if (!primaryWallet) {
      setSmartAccount(null);
      return;
    }

    if(!primaryWallet.connector.isEmbeddedWallet) {
      alert('No embedded wallet selected');
      return;
    }

    try {
      const walletClient = await primaryWallet.getWalletClient();
      if (walletClient && !smartAccount) {
        const newSmartAccount = await createSmartAccount(walletClient);
        setSmartAccount(newSmartAccount);
      }
    } catch (error) {
      console.error('Error fetching wallet clients or creating smart account:', error);
    }
  }, [primaryWallet, smartAccount]);

  useEffect(() => {
    createAndSetSmartAccount();
  }, [createAndSetSmartAccount]);

  return { smartAccount };
}
```

## Create Main component

Since we're calling dynamic hooks, we need to do this inside the component tree, above where the DynamicContextProvider wrapper lives. For our purposes, let's create a Main.js component, and this is also where we can place or signup and login UI (DynamicWidget):

```jsx Main.js

import { useEffect } from "react";
import { useBiconomyAccount } from "./useBiconomyAccount.js";

const Main = () => {
  const { smartAccount } = useBiconomyAccount();
  
  useEffect(() => {
    console.log('My Biconomy smart account', smartAccount)
  }, [smartAccount])

  return <></>;
};

export default Main;
```

As you can see, we're looking the now initialized smart account in Main, and you can then do whatever you want with it!

## End to end flow

You can find a complete example of the integration here: [https://github.com/dynamic-labs/dynamic-biconomy-example](https://github.com/dynamic-labs/dynamic-biconomy-example)


# Pimlico



## How to use a Dynamic signer with permissionless.js

Permissionless.js allows you to plug in custom signers to control the accounts that you create. It is possible to use Dynamic as a signer with permissionless.js, allowing you to use Dynamic to create and control smart accounts and sign transactions.

### Install the dependencies

```bash
npm i @dynamic-labs/sdk-react-core @dynamic-labs/wagmi-connector @dynamic-labs/ethereum permissionless viem wagmi
```

### Create the Dynamic provider

Following Dynamic's quickstart guide, set up the Dynamic provider in your app. Also integrate the DynamicWagmiConnector, which will allow you to use Dynamic as a signer with permissionless.js.

```jsx
import {
  DynamicContextProvider,
  DynamicWidget,
} from "@dynamic-labs/sdk-react-core";
import { DynamicWagmiConnector } from "@dynamic-labs/wagmi-connector";

import { EthereumWalletConnectors } from "@dynamic-labs/ethereum";

export const App = () => {
  return (
    <DynamicContextProvider
      settings={{
        // Find your environment id at https://app.dynamic.xyz/dashboard/developer/api
        environmentId: "REPLACE-WITH-YOUR-ENVIRONMENT-ID",
        walletConnectors: [EthereumWalletConnectors],
      }}
    >
      <DynamicWagmiConnector>
        <DynamicWidget />
      </DynamicWagmiConnector>
    </DynamicContextProvider>
  );
};
```

### Create the SmartAccountClient

Create the smart account client using the Dynamic signer. Note: DynamicWagmiConnector internally sets up the WagmiConfig, so there is no need to do it separately. This is where you would configure what smart account implementation (e.g. Safe, Kernel, Biconomy, SimpleAccount) and what paymaster logic you want to use.

```jsx
import {
  createSmartAccountClient,
  walletClientToCustomSigner,
} from "permissionless";
import { signerToSimpleSmartAccount } from "permissionless/accounts";
import { useWalletClient } from "wagmi";

const { data: walletClient } = useWalletClient();

const customSigner = walletClientToCustomSigner(walletClient);

const simpleSmartAccountClient = await signerToSimpleSmartAccount(
  publicClient,
  {
    entryPoint: "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
    signer: customSigner,
    factoryAddress: "0x9406Cc6185a346906296840746125a0E44976454",
  }
);

const smartAccountClient = createSmartAccountClient({
  account: simpleSmartAccountClient,
  chain: sepolia, // or whatever chain you are using
  transport: http("<bundler_endpoint>"),
  sponsorUserOperation: pimlicoPaymaster.sponsorUserOperation, // if using a paymaster
});
```

### Send a transaction

You can now send transactions as normal. The sponsorUserOperation function will be called before each transaction is signed and sent, applying the custom paymaster logic you have set.

```js
const txHash = await smartAccountClient.sendTransaction({
  to: zeroAddress,
  data: "0x",
  value: BigInt(0),
});
```


# Safe



Let's deploy a Safe account on top of any wallet through Dynamic! First we'll make sure signup/login are enabled, then we'll fetch the walletClient once logged in before deploying the Safe account.

## Enable signup/login

Once you've signed up for Dynamic, integrating it is as easy as either `npx create-dynamic-app`, or simply following the [Quickstart](/quickstart) guide.

When that's done you should have an app which is wrapped with the [DynamicContextProvider](react-sdk/providers/dynamiccontextprovider).

```jsx
import { DynamicContextProvider } from '@dynamic-labs/sdk-react-core';
import Main from './Main';

const App = () => {
  return (
    <DynamicContextProvider
        settings={{
            environmentId: 'YOUR_ENV_ID'
            ...
        }}
    >
      <Main />
    </DynamicContextProvider>
  );
}
```

Before we work on Main, let's declare a function that can help us deploy the Safe account, taking a walletClient and wallet address as arguments:

<Tip>
  We're using a few dependancies below, you might want to first install them:

  ```bash
  npm i viem permissionless @dynamic-labs/wallet-connector-core
  ```
</Tip>

```jsx
// src/utils/safe.ts
import { isEthereumWallet } from "@dynamic-labs/ethereum";
import { WalletClient, PublicClient, http } from "viem";
import { ENTRYPOINT_ADDRESS_V07, createSmartAccountClient, walletClientToSmartAccountSigner } from "permissionless";
import { createPimlicoBundlerClient, createPimlicoPaymasterClient } from "permissionless/clients/pimlico";
import { signerToSafeSmartAccount } from "permissionless/accounts";
import { Wallet } from '@dynamic-labs/wallet-connector-core';

const transportUrl = (chain: Chain) =>
  `https://api.pimlico.io/v2/${chain.id}/rpc?apikey=${process.env.NEXT_PUBLIC_PIMLICO_API_KEY}`;

export const pimlicoBundlerClient = (chain: Chain) =>
  createPimlicoBundlerClient({
    transport: http(transportUrl(chain)),
    entryPoint: ENTRYPOINT_ADDRESS_V07,
});

export const paymasterClient = (chain: Chain) =>
  createPimlicoPaymasterClient({
    transport: http(transportUrl(chain)),
    entryPoint: ENTRYPOINT_ADDRESS_V07,
});

export const getPimlicoSmartAccountClient = async (
  wallet: Wallet
) => {
  if (!isEthereumWallet(wallet)) return;

  const address = wallet.address as `0x${string}`;
  const publicClient = await wallet.getPublicClient() as PublicClient;
  const walletClient = wallet.getWalletClient() as WalletClient;
  const chain = walletClient.chain;

  const signer = walletClientToSmartAccountSigner(walletClient);

  const safeSmartAccountClient = await signerToSafeSmartAccount(publicClient(chain), {
    entryPoint: ENTRYPOINT_ADDRESS_V07,
    signer: signer,
    safeVersion: "1.4.1",
  });

  return createSmartAccountClient({
    account: safeSmartAccountClient,
    entryPoint: ENTRYPOINT_ADDRESS_V07,
    chain,
    bundlerTransport: http(transportUrl(chain)),
    middleware: {
      gasPrice: async () => (await pimlicoBundlerClient(chain).getUserOperationGasPrice()).fast, // use pimlico bundler to get gas prices
      sponsorUserOperation: paymasterClient(chain).sponsorUserOperation, // optional
    },
  });
};
```

Let's walk through what we just wrote step by step:

<Steps>
  <Step title="Transport URL">
    This is the RPC URL for the Pimlico API which we will use in multiple places, so better to extract it. It depends on your Pimlico API key so make sure you have that set under `NEXT_PUBLIC_PIMLICO_API_KEY` in your `.env` file.
  </Step>

  <Step title="pimlicoBundlerClient">
    The Bundler is the node that can help us batch our user operations and submit them as a single transaction to the blockchain. The client we are creating here is an interface to that Bundler, and it comes with a load of helpful methods related to those userops, such as Gas estimations. Specifically, we are going to need `getUserOperationGasPrice` later.

    You can see that as well as the transport (i.e. how it should communicate which in this case is via HTTP using the Transport URL we defined earlier), it needs something called the entrypoint address. This is imported as a static variable via `ENTRYPOINT_ADDRESS_V07` from the `permissionless` package. This is an address of a smart contract (deployed on every chain) that acts as a gateway for user operations. [This article](https://blog.smart-contracts-developer.com/entrypoint-v0-7-0-the-new-era-of-account-abstraction-854f9f82912e) helps describe what the V07 version is compared to previous.

    You can learn more about the Bundler Client [here](https://docs.pimlico.io/permissionless/reference/clients/pimlicoBundlerClient).
  </Step>

  <Step title="pimlicoPaymasterClient">
    This Paymaster is where we handle the sponsoring of user operations. It's optional, but we are going to use it in our middleware later.

    This client also needs the transport method and entrypoint address as we described in the previous step.

    You can learn more about the Paymaster Client [here](https://docs.pimlico.io/permissionless/reference/clients/pimlicoPaymasterClient).
  </Step>

  <Step title="Viem Clients & Chain">
    We are going to need to make some read actions and write actions onchain, and Viem provides a Public Client (for read actions) and Wallet Client (for write actions). We are going to need both of these, so we are going to extract them from the wallet object we get passed in as an argument.

    Dynamic makes it super easy to return these clients from the wallet object. We can use the `getPublicClient` and `getWalletClient` methods on the wallet connector respectively.

    You can learn more about interacting with Wallets (covers all kinds of actions) [here](/wallets/using-wallets/interacting-with-wallets).
  </Step>

  <Step title="walletClientToSmartAccountSigner">
    Something needs to sign the user operations we are going to send to the Bundler. This function takes the Wallet Client and returns a signer that can be used to sign those operations.

    In normal transactions, the walletClient is enough to sign with, and you can use methods like `signTransaction` or `signMessage` directly on the walletClient. But for Smart Accounts, we need a specific type of signer that can handle the extra complexity of Smart Accounts.

    You can learn more about this specific Signer type [here](https://docs.pimlico.io/permissionless/how-to/signers#signers-for-permissionlessjs).
  </Step>

  <Step title="signerToSafeSmartAccount">
    While we now have something that can sign for user operations, we don't have a Smart Account yet to actually use it with. That's the purpose of `signerToSafeSmartAccount`. It takes the Public Client, the signer we just created, and some other options, and returns a Safe Account.

    You can learn more about this method [here](https://docs.pimlico.io/permissionless/reference/accounts/signerToSafeSmartAccount).
  </Step>

  <Step title="createSmartAccountClient">
    With our signer and Safe account now in hand, the last step is to create an interface where we can start to take actions on the Safe Account. This is what `createSmartAccountClient` does.

    You can see we are defining some extra middleware here, such as the gas price and the sponsorUserOperation. These are optional but can be helpful in certain situations.

    In our case we're going to use the paymaster we created earlier to sponsor the user operations, and the bundler to get the gas prices.

    You can learn more about the signerToSafeSmartAccount method [here](https://docs.pimlico.io/permissionless/reference/accounts/signerToSafeSmartAccount).
  </Step>
</Steps>

In `Main.js`, we can now fetch the walletClient once logged in and then create that Safe account using the function we just created. To learn how to handle loading states, visit the [Loading States guide](/react-sdk/loading-login-states).

```jsx
import { useState } from 'react';
import { useDynamicContext, primaryWallet } from '@dynamic-labs/sdk-react-core';
import { getOrMapViemChain } from "@dynamic-labs/ethereum-core";
import { getPimlicoSmartAccountClient } from '../utils/safe';

const Main = () => {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  const [safeAddress, setSafeAddress] = useState(null);
  const [safeDeployed, setSafeDeployed] = useState(false);

  const { user } = useDynamicContext();

  const handleDeploySafe = async () => {
    setLoading(true);
    setError(null);

    try {
      if(!primaryWallet) {
        setError("No wallet found");
        return;
      };

      if (!process.env.NEXT_PUBLIC_PIMLICO_API_KEY) {
        console.error("Please set NEXT_PUBLIC_PIMLICO_API_KEY in .env file and restart");
        return;
      }

      const { account } = await getPimlicoSmartAccountClient(primaryWallet);
      setSafeAddress(account.address);
      setSafeDeployed(true);
    } catch (err) {
      setError("Failed to deploy Safe account.");
      console.error(err);
    } finally {
      setLoading(false);
    }
  };
  
  if (!user) {
    return <div>Loading...</div>;
  } else if(user && !safeDeployed) {
    return (
      <div>
        <h1>Welcome</h1>
        <button onClick={handleDeploySafe}>Deploy Safe</button>
      </div>
    );
  } else if (user && safeDeployed) {
    return (
      <div>
        <h1>Safe Account Deployed</h1>
        <p>Safe Address: {safeAddress}</p>
      </div>
    );
  }

}

export default Main;
```

That's it! You now have the ability to create a Safe account on top of any wallet used with Dynamic, including embedded wallets! To learn how to interact with the Safe account, check out our Hackathon starter kit which handles ERC20 token transfers and cross chain ERC20 token transfers.

*   [Hacker Starter Kit Overview Video](https://www.youtube.com/watch?v=dkONR9dJD2g)
*   [Hacker Starter Kit Codebase](https://github.com/dynamic-labs/hackathon-starter-kit/tree/main)


# ZeroDev



The first iteration of account abstraction from Dynamic uses [ZeroDev](https://zerodev.app/) and embedded wallets.

This guide will walk you through settting up ZeroDev and Dynamic so that you can sponsor transactions on Polygon Amoy.

#### Initial setup

<Steps>
  <Step title="ZeroDev Account">
    Sign up for a free account at [https://dashboard.zerodev.app/](https://dashboard.zerodev.app/) and create a project, configure your project name and network (please use Polygon Amoy for this example) and copy your new ZeroDev project ID.

    <Tip>
      Note that the network you select in your ZeroDev project will be the network
      that the smart contract wallet is deployed on and cannot be changed without
      creating a new project. For this guide, we will be using Polygon Amoy.
    </Tip>
  </Step>

  <Step title="Enable in Dynamic">
    In [the EVM section of your Dynamic Dashboard](https://app.dynamic.xyz/dashboard/chains-and-networks#evm), toggle on Polygon Amoy and click Save.

    Now, go to [the Account Abstraction section](https://app.dynamic.xyz/dashboard/configurations#accountabstraction), enable ZeroDev and paste in your ZeroDev project id.
  </Step>

  <Step title="Adding Multichain Providers (if desired)">
    <Tip>
      You must be using at least SDK version 3.3.0 or 4.0.0-alpha.8 to have access to this feature
    </Tip>

    Click on "Add another chain". You can input 1 ZeroDev project ID per chain you would like enabled.

    <Frame>
      <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/zerodev-add-provider.png" />
    </Frame>

    If you would like to remove a multichain ZeroDev provider, hover your mouse over the network icon and you should be able to click the "Trash icon" to remove.

    <Frame>
      <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/zerodev-remove-provider.png" />
    </Frame>

    Click "Yes" to save and add another provider if desired.
  </Step>

  <Step title="Choose who gets a SCW">
    On the same configuration page as the step above, you'll see there are two different types of configuration for issuing smart contract wallets (SCWs) in Dynamic - the wallet level and the user level.

    *   Wallet level

    Choose whether to issue SCWs to all wallets, or only to embedded wallets. Note that for the former, you will need to handle the UI and frontend yourself.

    *   User level

    Choose whether to issues SCWs to all your users (existing included next time they log in), or just new users.
  </Step>

  <Step title="Enable Dynamic-powered embedded wallets + Email">
    Back in your Dynamic Dashboard, go to [the Embedded Wallets section](https://app.dynamic.xyz/dashboard/embedded-wallets/dynamic) and enable Dynamic-powered embedded wallets.

    Lastly, in [the Log in & User Profile section](https://app.dynamic.xyz/dashboard/log-in-user-profile), enable Email sign up (optionally, enable social sign up and configure oauth)

    <Tip>
      Note that we currently only create smart wallets for embedded wallets. You
      will see native web3 wallets in your Dynamic widget, and can still use one to
      sign in, but only new embedded wallets will have a smart wallet.
    </Tip>
  </Step>

  <Step title="Render Dynamic">
    In your local React project, install the following packages:

    *   `@dynamic-labs/sdk-react-core`
    *   `@dynamic-labs/ethereum-aa`
    *   `@dynamic-labs/ethereum`

    Then import and add the `ZeroDevSmartWalletConnectors` and `EthereumWalletConnectors` to the `walletConnectors` prop on `DynamicContextProvider`.

    <Info>
      To use ZeroDev v5.2, use Dynamic SDK version `^2.0.5`
    </Info>

    ```jsx
      import { DynamicContextProvider, DynamicWidget } from "@dynamic-labs/sdk-react-core";
      import { EthereumWalletConnectors } from "@dynamic-labs/ethereum";
      import { ZeroDevSmartWalletConnectors } from "@dynamic-labs/ethereum-aa";

      const App = () => (
        <DynamicContextProvider
          settings={{
            environmentId: "YOUR_ENVIRONMENT_ID",
            walletConnectors: [
              EthereumWalletConnectors,
              ZeroDevSmartWalletConnectors
            ],
          }}
        >
          <DynamicWidget />
        </DynamicContextProvider>
      )

      export default App;
    ```

    <Tip>
      Make sure to grab your Dynamic environment id from from the Dynamic Dashboard
      under Developer > SDK & API Keys, and replace it in the `environmentID`
      setting.
    </Tip>
  </Step>

  <Step title="Set up a gas sponsorship rule">
    Now we will set up a basic gas sponsorship policy in the ZeroDev
    dashboard

    In the Gas Policies tab, click on the button labeled "New" in the Project Policies section

    <Frame>
      <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/zerodev-gas-policy-page.png" />
    </Frame>

    Select "Amount" as the Type, "10" as the value and "Day" as the interval. This is saying that we will sponsor up to 10 MATIC total per day

    <Frame>
      <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/zerodev-gas-policy-setup.png" />
    </Frame>

    Click Create Project Policy, and that's it! Now start your app, log in and try sending some MATIC out. The Dynamic SDK will check if a transaction meets your gas policies and will automatically hide the gas in the transaction confirmation step if the gas is sponsored, if not the gas will be displayed.

    <Info>
      Since you will be creating a brand new wallet when you log in, you will have
      to first send some MATIC to this address. You can access a free MATIC faucet
      here: [https://www.alchemy.com/faucets/polygon-amoy](https://www.alchemy.com/faucets/polygon-amoy)
    </Info>
  </Step>

  <Step title="Send a transaction">
    Run your app, and if you copied our snippet from earlier, you should see this
    basic page

    <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/basic-cra-landing.png" />

    Click Connect your wallet, enter your email and hit Continue. After pasting in your OTP, you are prompted to Set up a passkey.

    <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/passkey-prompt.png" />

    Go ahead and set up your passkey, and now you're fully logged in! Next, we're going to send a transaction. To do that, we will need some MATIC. Grab your wallet address by clicking on the Dynamic Widget, then click on the three dots next to your address and hit "Copy wallet address". You can paste your address into [free Polygon Amoy faucet](https://www.alchemy.com/faucets/polygon-amoy) which will deposit some free test MATIC into your account. After doing so, if you refresh your app, you should see your balance update

    <Tip>
      Optionally, set up fiat onramp by following our guide here:
      [https://docs.dynamic.xyz/fiat-onboarding/banxa](https://docs.dynamic.xyz/fiat-onboarding/banxa)
    </Tip>

    <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/updated-balance.png" />

    Now, send yourself some MATIC by clicking on the Send button in the Dynamic Widget. Enter 0.1 as the amount, and an addres of your choosing as the recipient, then hit Send now. You will see a screen like the following. Notice that there is no gas estimate field, because this transaction will be sponsored!

    <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/gasless-txn.png" />

    Hit confirm, sign for the transaction with your passkey. Congratulations, you just sent a gas-sponsored transaction! If you take your smart wallet address and paste it into the [Polygon Amoy scanner](https://amoy.polygonscan.com/), you will see your smart wallet and the transaction you just sent.

    <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/mumbai-scan.png" />
  </Step>
</Steps>

### Advanced Configuration

Now you are set up, you can utilize the full functionality of ZeroDev inside Dynamic - everything from session keys to gas policies. Learn more in the [ZeroDev Docs](https://docs.zerodev.app/).

#### Specifying a bundler/paymaster RPC

Use `ZeroDevSmartWalletConnectorsWithConfig` and pass in values for `bundlerRpc` and `paymasterRpc`:

```ts
import { ZeroDevSmartWalletConnectorsWithConfig } from "@dynamic-labs/ethereum-aa";

<DynamicContextProvider
  settings={{
    environmentId: "YOUR_ENV_ID",
    walletConnectors: [
      ZeroDevSmartWalletConnectorsWithConfig({
        bundlerRpc: "CUSTOM_BUNDLER_RPC",
        paymasterRpc: "CUSTOM_PAYMASTER_RPC",
      }),
    ],
  }}
>
  {/* ... your app */}
</DynamicContextProvider>;
```

For more info, see: [https://docs.zerodev.app/sdk/infra/pimlico#using-pimlico-paymaster](https://docs.zerodev.app/sdk/infra/pimlico#using-pimlico-paymaster)

#### Specifying a bundler

To specify a bundler, use `ZeroDevSmartWalletConnectorsWithConfig` and pass in a value for `bundlerProvider`:

```ts
import { ZeroDevSmartWalletConnectorsWithConfig } from "@dynamic-labs/ethereum-aa";

<DynamicContextProvider
  settings={{
    environmentId: "YOUR_ENV_ID",
    walletConnectors: [
      ZeroDevSmartWalletConnectorsWithConfig({ bundlerProvider: "STACKUP" }),
    ],
  }}
>
  {/* ... your app */}
</DynamicContextProvider>;
```

For more info, see: [https://docs.zerodev.app/meta-infra/rpcs#bundler--paymaster-rpcs](https://docs.zerodev.app/meta-infra/rpcs#bundler--paymaster-rpcs)

#### Retrieving the Kernel Client using `getAccountAbstractionProvider()`

```ts
import { isZeroDevConnector } from '@dynamic-labs/ethereum-aa';

const App = () => {
  const { primaryWallet } = useDynamicContext();

  useEffect(() => {
    const { connector } = primaryWallet;

    if (isZeroDevConnector(connector)) {
      const params = {
        // if you have gas sponsorship enabled, set `withSponsorship` to `true`, else omit
        withSponsorship: true
      };
      const kernelClient = connector.getAccountAbstractionProvider(params);
    }
  }, [primaryWallet]);

  ...
}
```

#### Using with Viem & Ethers

You can use viem or ethers with account abstraction to sign messages or send sponsored transaction with no extra configuration, it also works with our [wagmi integration](/react-sdk/providers/dynamicwagmiconnector).

## Going Further

Once you've tested things out and want to deploy to a live network, you will need to do the following:

1.  Add your credit card to ZeroDev under Account Settings > Billing
2.  Create a new ZeroDev project and select a live network
3.  Copy your new ZeroDev project id and paste it into your Dynamic Dashboard
    a. We recommend using your Dynamic Sandbox environment for testing your testnet setup, and using your Dynamic Live environment for production.

### Restricting Access to your ZeroDev Project

In order to restrict access to your ZeroDev project id to allow only dynamic to use it you can add dynamic's static IP address's to your projects IP allowlist.

Dynamic's IP addresses:

*   `52.204.85.87`
*   `54.145.74.8`
*   `107.20.170.238`
*   `52.206.26.56`
*   `3.232.2.67`
*   `44.213.187.169`

<img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/zerodev-accesscontrol.png" />

## Examples

### Get smart wallet address vs signer address

The wallet connector will return your smart wallet address, that address will be used in the Dynamic UI and is the main address you will interact with. But you can fetch the signer address by using the wallet connector getEOAConnector and then fetching the address there.

```tsx
import { useEffect, useState } from "useEffect";
import {
  useDynamicContext,
  DynamicContextProvider,
  DynamicWidget,
} from "@dynamic-labs/sdk-react-core";
import {
  isZeroDevConnector,
  ZeroDevSmartWalletConnectors,
} from "@dynamic-labs/ethereum-aa";
import { EthereumWalletConnectors } from "@dynamic-labs/ethereum";

const SignerAddress = () => {
  const { primaryWallet } = useDynamicContext();
  const [signerAddress, setSignerAddress] = useState();

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

    const {
      connector,
      address, // This is your smart wallet address
    } = primaryWallet;

    if (!isZeroDevConnector(connector)) {
      return;
    }

    const signerConnector = connector.getEOAConnector();

    if (!signerConnector) {
      return;
    }

    // This is the signer address
    const [address] = signerConnector.getAddress();

    setSignerAddress(address);
  }, [primaryWallet]);

  return <span>My Signer address: {signerAddress}</span>;
};

const App = () => (
  <DynamicContextProvider
    settings={{
      environmentId: "YOUR_ENVIRONMENT_ID",
      walletConnectors: [
        EthereumWalletConnectors,
        ZeroDevSmartWalletConnectors,
      ],
    }}
  >
    <DynamicWidget />

    <SignerAddress />
  </DynamicContextProvider>
);

export default App;
```

For more information about ZeroDev's AA features, go to ZeroDev's documentation: [https://docs.zerodev.app/](https://docs.zerodev.app/)

## FAQ

<AccordionGroup>
  <Accordion title="Can I use an existing wallet as a smart contract wallet with account abstraction?">
    Yes, but not today with Dynamic. We are working on developing new flows to make managing existing EOA wallets with SCWs a smooth transition.
  </Accordion>

  <Accordion title="What networks are supported for deploying smart contract wallets?">
    It depends which provider you choose. For example, with ZeroDev you have the following options:

    *   Arbirtrum One
    *   Avalanche
    *   Base
    *   Binance Smart Chain
    *   Eth
    *   Optimism
    *   Polygon
  </Accordion>

  <Accordion title="Can I change the network for a smart contract wallet after it's deployed?">
    Each provider will handle things differently, so it's always better to check
    directly with them. For example, with ZeroDev you can't change the network
    after deployment. With Alchemy, it might be possible in a roundabout way.
  </Accordion>

  <Accordion title="What happens if I don't want to use a provider i.e. ZeroDev any more?">
    With Dynamic, you will need to use either ZeroDev or Alchemy. If you have
    alternative AA providers, please reach out via our
    [slack](https://www.dynamic.xyz/slack).
  </Accordion>

  <Accordion title="What pricing package includes this feature?">
    This is [an advanced feature](https://www.dynamic.xyz/pricing), but there is
    no additional cost from Dynamic beyond the advanced tier itself. The providers
    do take a transaction fee, which you can see on their respective pricing
    pages.
  </Accordion>

  <Accordion title="What does private beta mean in this context, what should I expect?">
    We are adding customers one at a time for a few weeks, after which we’ll open
    it up to the general public as GA.
  </Accordion>

  <Accordion title="How are private keys securely managed and stored for smart contract wallets with account abstraction?">
    Private keys are managed by the EOA, not the SCW. Every SCW has a Signer, or
    Owner, which is the EOA.
  </Accordion>

  <Accordion title="What is the process for recovering a smart contract wallet in case of key loss or compromise?">
    The only way the SCW can be recovered is if the EOA is recovered. The SCW is a
    smart contract, and the EOA is the owner of the SCW. If the EOA is lost, the
    SCW is lost.
  </Accordion>

  <Accordion title="Are your smart contract wallets non-custodial?">
    It's a common misconception that AA wallets are inherently non-custodial. In fact, whether a wallet is AA or not has nothing to do with whether it's custodial. It's the signers that determine whether a wallet is custodial.

    That is, if you use a non-custodial signer such as local private keys to manage your AA wallet, then it's non-custodial. At the same time, if you use a custodial key provider such as Fireblocks to manage your AA wallet, then it's custodial.

    In any case, whoever has custody over the signers has custody over the wallet.
  </Accordion>

  <Accordion title="Why is it better to do this through Dynamic than integrating ZeroDev directly?">
    We have wrapped our providers so you get all the benefit, and more. For
    example, we handle the transaction hashing so it’s user friendly and are able
    to show the SCW in our widgets and connectors.
  </Accordion>
</AccordionGroup>


# Apple



## Important information

For every Dynamic `environmentId`, please ensure to use a unique set of Apple oauth app credentials.

Apple will only return a user's `email` on the first login across any site that uses the OAuth application's credentials.

## Creating a new App

Here are the step-by-step instructions for obtaining OAuth credentials for Apple:

1.  Open Dynamic dashboard, in either social account linking on the [Login Methods](https://app.dynamic.xyz/dashboard/configurations#informationcapture) or in [the Social Signup page](https://app.dynamic.xyz/dashboard/configurations#emailsocialsignin) depending on whether you want to do linking or signup:
    1.  Open the Apple section, copy the Redirect URL and paste it in the "Valid OAuth Redirect URIs" field in Apple.
2.  Go to the [Apple Developers](https://developer.apple.com/account) dashboard and sign in to your account.
3.  Navitate to [Membership details](https://developer.apple.com/account/#/membership)
4.  Find your Team ID.
5.  In Apple's dashboard, navigate to Identifiers under [Certificates, IDs, & Profiles](https://developer.apple.com/account/resources/identifiers/list/serviceId)
6.  Add a new Service ID.
7.  Click your newly created Services ID to enable it for Sign in with Apple. Then, click Configure.
8.  In Return URLs, you'll paste the Redirect URI listed on Dynamic Dashboard for your Apple provider. Once you've entered the correct information, you can save your Services ID by clicking Continue, then Save.
9.  Return to Dynamic Dashboard, you should have Team ID and Services ID populated by this step. Now you can paste your Key ID as well. Finally, you'll need to paste the contents of the Private Key file you downloaded from Apple's dashboard.

That's it! You now have OAuth credentials for your Apple application that you can use in Dynamic.


# Discord



## Creating a new App

Here are the step-by-step instructions for obtaining OAuth credentials for Discord:

1.  Go to the [Discord Developer Portal](https://discord.com/developers/applications) and sign in to your account.
2.  Once you're signed in, click on the "New Application" button in the top right corner of the screen.
3.  Enter a name for your application and click the "Create" button.
4.  In the left-hand menu, click on the "OAuth2" tab.
5.  Back in the Dynamic dashboard, in either social account linking on the [Info Capture page](https://app.dynamic.xyz/dashboard/configurations#informationcapture) or in [the Social Signup page](https://app.dynamic.xyz/dashboard/configurations#emailsocialsignin) depending on whether you want to do linking or signup.
    1.  Copy the "Client ID" and "Client secret" from the application you created in Discord and paste it into the modal in the Dynamic Dashboard.
    2.  We recommend you already copy the Redirect URL to your clipboard so you can use it in the next step.
    3.  Click Confirm.
6.  Return to your Discord OAuth2 page:
    1.  Under the "OAuth2 Redirects" section, click the "Add Redirect" button.
    2.  Copy & paste the Redirect URL provided in your Dynamic Dashboard in the prior step.
    3.  Copy the Redirect URL and paste it in the section on Discord page.
    4.  Click "Save changes" button.

That's it! You now have OAuth credentials for your Discord application that you can use in Dynamic.

If you need more help with configuring the Discord OAuth App check their docs [here](https://discord.com/developers/docs/topics/oauth2).


# Epic Games



## Creating a new App

Here's the step-by-step guide on how to set up a new account, organization, product, and application to set up OAuth2 with Epic Games:

Create an Epic Games Developer Account:

1.  [Go to Epic Games Developer Portal](https://dev.epicgames.com/) to create an account

    *   Once your account is created, log in to the Developer Portal.

2.  [Create an Organization](https://dev.epicgames.com/portal/en-US/profile/signup)

    *   Click Create Organization and fill in the required details.
    *   Click Submit to create the organization.

3.  Register a Product:

    *   In the Developer Portal, go to the Products section.
    *   Click on Create Product and fill in the product details.

4.  Create an Application:

    *   Go to the Applications section in the Developer Portal.
    *   Click Create Application.
    *   You'll need to validate your domain. It will ask you to add a TXT DNS record to your domain.
    *   Fill in the application details such as name, description, and redirect URI (the URL where users will be redirected after successful authentication).

5.  Create an Client:

    *   Go to Product Settings > Client. Click Add new Client.
    *   Fill in the application details such as name, description, and redirect URI (the URL where users will be redirected after successful authentication).

6.  Get Client ID and Client Secret:

    *   Go to Product Settings
    *   Copy the **Client ID** and **Client Secret** that are generated for your application.

7.  Turn on Epic Games in [Dynamic Dashboard](https://app.dynamic.xyz/dashboard/log-in-user-profile)

    *   Go to Log in & User Profile, switch on Epic Games and add **Client ID** and **Client Secret**

That’s it! You now have OAuth credentials for your Epic Games application that you can use in Dynamic.


# Facebook



## Creating a new App

Here are the step-by-step instructions for obtaining OAuth credentials for Facebook:

1.  Go to the [Facebook Developers](https://developers.facebook.com/apps/) website and sign in to your account.
2.  Once you're signed in, click on the "My Apps" dropdown menu in the top navigation bar and select "Create App."
3.  Choose the type of app you want to create.
4.  Enter a name for your app and click "Create App ID."
5.  Once your app is created under "Add a product" section click "Set up" under Facebook Login card.
6.  Select "Settings" under "Facebook Login" section in the left side bar.
7.  Back in the Dynamic dashboard, in either social account linking on the [Info Capture page](https://app.dynamic.xyz/dashboard/configurations#informationcapture) or in [the Social Signup page](https://app.dynamic.xyz/dashboard/configurations#emailsocialsignin) depending on whether you want to do linking or signup:
    1.  Open the Facebook section, copy the Redirect URL and paste it in the "Valid OAuth Redirect URIs" field in Facebook.
    2.  Click "Save changes" on the bottom of the screen.
8.  Select "Basic" under "Settings" section in the left side bar.
9.  Copy the "App ID" and "App secret" and paste it into "Set up Facebook Auth" Modal in the Dynamic Dashboard.

That's it! You now have OAuth credentials for your Facebook application that you can use in Dynamic.

If you need more help with configuring the Facebook OAuth App check their docs [here](https://developers.facebook.com/docs/development/create-an-app).


# Farcaster



## Configuration

<Frame>
  <iframe src="https://scribehow.com/embed/Set_Up_User_Sign-up_with_Email_and_Social_Providers__4A6KU9InTwWkASGkW5FSRg" width="100%" height="640" allowfullscreen frameborder="0" />
</Frame>

## User Values

We map the Farcaster values returned to the standard oauth user values (found in [verified Credentials](/react-sdk/objects/verified-credential)):

```typescript
 {
  id: string; // Unique ID (Dynamic)
  publicIdentifier: string; // Farcaster username
  oauthAccountId: string; // Farcaster ID
  oauthProvider: ProviderEnum; // "farcaster"
  oauthAccountPhotos?: []string; // Farcaster PFP URLs
  oauthDisplayName?: string; // name if provided
  oauthUsername?: string; // Farcaster username
};
```

To get this information, you can use the `user` object returned from the [`useDynamicContext` hook](/react-sdk/hooks/usedynamiccontext) i.e.

```typescript
import { useDynamicContext } from '@dynamic-labs/sdk-react-core';
const { user } = useDynamicContext();

if (user) {
  console.log(user.verifiedCredentials);
}
```

You can then use this information to create enhanced experiences within your app!


# Github



## Creating a new App

Here are the step-by-step instructions for obtaining OAuth credentials for GitHub:

1.  Go to the [GitHub Developer Settings](https://github.com/settings/developers) page and sign in to your account.

    1.  If you're on your profile page, click "Developer Settings" at the bottom of the left-hand menu.

2.  Click on the Oauth Apps menu and then "Register a new application".

3.  Enter a name for your application, the homepage URL, and the callback URL.
    1.  Enter whatever name and homepage URL you want. For the callback URL follow the steps below.
    2.  In the Dynamic dashboard, visit either social account linking on the [Info Capture page](https://app.dynamic.xyz/dashboard/configurations#informationcapture) or in [the Social Signup page](https://app.dynamic.xyz/dashboard/configurations#emailsocialsignin) depending on whether you want to do linking or signup.
    3.  Open the Github section of the configuration.
    4.  Copy the Redirect URL and paste it in the Callback URL section in Github.

4.  Click "Register application" button.

5.  Github will create an OAuth app for you and will redirect you to the details page.

6.  Click "Generate a new client secret" button

7.  Copy the "Client ID" and "Client secret" you've just created and paste it into "Set up Github Auth" Modal in the Dynamic Dashboard.

8.  Click "Confirm" button.

That's it! You now have OAuth credentials for your GitHub application that you can use in Dynamic.

If you need more help with configuring the Github OAuth App check their docs [here](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app).


# Google



## Creating a new App

Here are the step-by-step instructions for obtaining OAuth credentials for Google:

1.  Go to the [Google Cloud Console](https://console.cloud.google.com/) and sign in to your account.
2.  Once you're signed in, click on the project dropdown menu in the top navigation bar and select or create the project you want to use for your OAuth credentials.
3.  In the left-hand menu, click on the "APIs & Services" tab, then click on the "Credentials" submenu.
4.  Complete the "Configure Consent Screen" instructions if you haven't completed this before.
5.  Click on the "+ Create Credentials" in the horizontal navigation below the search bar, then select "OAuth client ID" from the dropdown menu.
6.  Select the "Web application" application type.
7.  Enter a name for your OAuth client ID, such as "My App OAuth Credentials."
8.  In the "Authorized JavaScript origins" field, enter the origin of your web application, for example, "[https://example.com"]().
9.  In the "Authorized redirect URIs" field, enter the URI that Google should redirect users to after they authorize your application. This will be provided by Dynamic.
    1.  You can find the "Authorized redirect URI" in either social account linking on the [Info Capture page](https://app.dynamic.xyz/dashboard/configurations#informationcapture) or in [the Social Signup page](https://app.dynamic.xyz/dashboard/configurations#emailsocialsignin) depending on whether you want to do linking or signup.
    2.  Open the the Google section of the configuration page you brought up in the previous step.
    3.  Copy the Redirect URL, paste it in the section on Google page and click "Save changes" button.
10. Click the "Create" button to create your OAuth client ID.
11. After creating your OAuth client ID, you should see a "Client ID" and "Client Secret" on the "Credentials" page. These are the credentials you'll need to copy and paste them into "Set up Google Auth" Modal in the Dynamic Dashboard.

That's it! You now have OAuth credentials for your Google application that you can use in Dynamic.

If you need more help with configuring the Google OAuth App check their docs [here](https://support.google.com/cloud/answer/6158849?hl=en).

## Submitting your app for review

By default, when you click "log in with Google", Google will display dynamicauth.com in the name of the pop up. To show your organization name and logo in the Google auth consent screen instead, you can apply to a Google "brand review". You can do this by following the instructions [here](https://support.google.com/cloud/answer/13461325?sjid=10558921481157129495-EU).


# LINE



## Creating a new App

Here are the step-by-step instructions for obtaining OAuth credentials for LINE

1.  Go to the [LINE Developer Portal](https://developers.line.biz/console) and sign in or create an account
2.  Click "Create" under the "Providers" section
3.  Specify a "Provider name"
4.  Click on "Create a LINE Login channel"
5.  Set the "Company or owner's country or region" for your channel, "Channel name", "Channel description"
6.  Under "App types", select "Web app"
7.  Review and agree to the terms and conditions and click "Create"
8.  Click on the newly created channel and select "LINE Login" tab
9.  Under "Callback URL"
    1.  In the Dynamic dashboard, visit either social account linking on the [Info Capture page](https://app.dynamic.xyz/dashboard/configurations#informationcapture) or in [the Social Signup page](https://app.dynamic.xyz/dashboard/configurations#emailsocialsignin) depending on whether you want to do linking or signup.
    2.  Open the LINE configuration section.
    3.  Copy the Redirect URL and paste it in the "Callback URL" field in LINE Developer Portal.
10. Select the "Basic settings" tab now
11. Copy the "Channel ID" and "Channel Secret" and paste it into the "Set up LINE Auth" Modal in the Dynamic Dashboard as "Client ID" and "Client Secret"
12. Make sure to publish your channel before testing out login
    That's it! You now have OAuth credentials for your LINE application that you can use in Dynamic.


# Social Providers Overview



This section relates to setting up oauth providers inside Dynamic. You would do this for two purposes:

1.  To allow [linking of social accounts](/users/social-linking) as part of their profile.
2.  To allow users to [sign up or log in with social accounts](/authentication-methods/email-social-sms).

In both cases you will need to configure whatever social providers you want to use. Dynamic supports the following providers:

*   [Apple](/social-providers/apple)
*   [Discord](/social-providers/discord)
*   [Epic Games](/social-providers/epicgames)
*   [Farcaster](/social-providers/farcaster)
*   [Facebook](/social-providers/facebook)
*   [GitHub](/social-providers/github)
*   [Google](/social-providers/google)
*   [LINE](/social-providers/line)
*   [Spotify](/social-providers/spotify)
*   [Telegram](/social-providers/telegram)
*   [TikTok](/social-providers/tiktok)
*   [Twitch](/social-providers/twitch)
*   [Twitter](/social-providers/twitter)

<Tip>
  If you need to get the auth token of a valid OAuth provider, use [this API
  request](/api-reference/endpoints/users/getUserOauthAccessToken).
</Tip>


# Spotify



## Creating a new App

Here are the step-by-step instructions for obtaining OAuth credentials for Spotify

1.  Go to the [Spotify Developer Portal](https://developer.spotify.com/dashboard) and sign in
2.  Click on "Create App" on the right of the screen
3.  Specify an "App Name", "App Description"
4.  Under "Redirect URIs"
    1.  In the Dynamic dashboard, visit either social account linking on the [Info Capture page](https://app.dynamic.xyz/dashboard/configurations#informationcapture) or in [the Social Signup page](https://app.dynamic.xyz/dashboard/configurations#emailsocialsignin) depending on whether you want to do linking or signup.
    2.  Open the Spotify configuration section.
    3.  Copy the Redirect URL and paste it in the "Redirect URI" field in Spotify Developer Portal.
5.  Under "Which API/SDKs are you planning to use?", check "Web API"
6.  Check the terms and conditions
7.  Click "Save" at the bottom of the form
8.  Once saved, click on the app and click "Settings"
9.  Copy the "Client ID" and "Client Secret" and paste it into the "Set up Spotify Auth" Modal in the Dynamic Dashboard

That's it! You now have OAuth credentials for your Spotify application that you can use in Dynamic.


# Telegram



<Warning>
  **Attention Required: Telegram Bot Secret Security**

  To use automatic wallet creation for Telegram, developers must create a Telegram bot configured with a bot secret, which serves as both the control mechanism for the bot and as a symmetric key for authentication. This key technically grants the developer an ability to sign authentication data and actions with a wallet. Any compromise of the bot secret internally or externally can expose users and their accounts to significant risk.
  Securing this symmetric key is critical to ensuring the integrity of all Telegram login sessions in your application.
</Warning>

## Summary

This feature allows developers to obtain authentication credentials with Telegram so users can log in and link their Telegram username to their account. The Telegram handle will be added as a verified credential in the user's profile.

## Creating a new App

Here are the step-by-step instructions for obtaining OAuth credentials for Telegram:

1.  Open Telegram and search for the "BotFather" bot, or click [here](https://t.me/botfather) to start a conversation with it.
2.  Start a conversation with BotFather by typing `/start`.
3.  Type `/newbot` to create a new bot.
4.  Follow the prompts to name your bot and create a unique username for it. The username must end with "bot" (e.g., `mycoolbot` or `my_cool_bot`).
5.  After creating your bot, you will receive a message with the bot's token. Copy this token as it will be needed for configuration.
6.  Navigate to the Dynamic Dashboard social section. ([https://app.dynamic.xyz/dashboard/log-in-user-profile](https://app.dynamic.xyz/dashboard/log-in-user-profile)).
7.  Open the Telegram configuration module.
8.  Paste the token you received from BotFather into the "Bot Token" field.
9.  Paste the bot's username in the "Bot Name" field.
10. You also need to set up a domain URL for your bot. In the Dynamic Dashboard, find the "Bot Domain" field in the Telegram configuration section.
11. Copy the URL provided by Dynamic.
12. Return to your conversation with BotFather and type `/setdomain`.
13. When prompted, paste the URL you copied in the previous step.

That's it! You now have OAuth credentials for your Telegram application that you can use in Dynamic.

If you need more help with configuring the Telegram OAuth App check their docs [here](https://core.telegram.org/bots#botfather).

## Frequently Asked Questions

### What is BotFather?

BotFather is an official bot on Telegram. It allows developers to create and manage their own Telegram bots and is required for obtaining the necessary credentials to interact with the Telegram API.

### What username should I use?

We suggest using your brand or application name to avoid confusing users when they authenticate. Example: `Dynamic_bot`.


# TikTok



<Info>
  Due to TikTok's requirement to verify URL ownership for all redirect URLs, TikTok cannot be used with Dynamic shared credentials in sandbox or production environments.
  You will need create an app with custom credentials. To go to production, you will need use to contact us for custom domains.
</Info>

## Creating a new sandbox application

<Tip>
  • When you are creating your app, make sure to specify "Configure for Web" for your app type (it will be treated as a web app in the context of OAuth)

  • Your OAuth client\_id is referred to as client\_key

  • As of September 2024, TikTok requires Terms of Service URL, Privacy Policy URL, your Desktop or web URL, Redirect URI and Webhooks Callback URL

  • TikTok docs and pre-requisites can be found [here](https://developers.tiktok.com/doc/login-kit-web?enter_method=left_navigation)
</Tip>

Here are the step-by-step instructions for obtaining OAuth credentials for TikTok:

1.  Go to the [TikTok Developer Portal](https://developers.tiktok.com/apps) and sign in to your account.
2.  Once you're signed in, click on "Connect an app"
3.  Fill out the "App name" and specify if this is for an Individual or an Organization, then click "Confirm"
4.  Make sure that "Sandbox" is selected. Sandbox applications do not require domain verification
5.  Fill out the "Category", "Description", "Terms of Service URL", "Privacy Policy URL"
6.  Select "Web" under "Platforms"
7.  Under "Products", select "Add Products" and click on "Login Kit"
8.  Under "App info" paste your "Redirect URL" (Redirect URL can be found in the Dynamic dashboard [https://app.dynamic.xyz/dashboard/log-in-user-profile#social-tiktok](https://app.dynamic.xyz/dashboard/log-in-user-profile#social-tiktok))
9.  Enter your "Website URL".
10. In order to allow test users to login, click "Add account" under "Target Users" and link an existing TikTok account
11. Click save under the form.
12. Copy and paste the "Client Key" and "Client Secret" and paste it into "Set up TikTok Auth" Modal in the Dynamic Dashboard.

That's it! You now have OAuth credentials for your TikTok application that you can use in Dynamic.

## Creating a production app

Contact us to setup a custom domain due to TikTok's callback URI verification requirements. This step will be improved in the near future.


# Twitch



## Creating a new App

Here are the step-by-step instructions for obtaining OAuth credentials for Twitch:

1.  Go to the [Twitch Developer Portal](https://dev.twitch.tv/console) and sign in to your account.
2.  Once you're signed in, click on the [Applications](https://dev.twitch.tv/console/apps) on the left nav bar
3.  Click on "Register Your Application", and enter the following information:
    1.  Name of your application
    2.  OAuth Redirect URLs
        1.  In the Dynamic dashboard, visit either social account linking on the [Info Capture page](https://app.dynamic.xyz/dashboard/configurations#informationcapture) or in [the Social Signup page](https://app.dynamic.xyz/dashboard/configurations#emailsocialsignin) depending on whether you want to do linking or signup.
        2.  Open the Twitch configuration section.
        3.  Copy & paste the Redirect URL provided in your Dynamic Dashboard to the text field in Twitch
    3.  Category (You can select any that is most appropriate)
4.  Click "Create"
5.  You should see a new row added to the "Developer Applications" table
6.  On the right side of the table in the app you just created, click on "Manage"
7.  Click on "New Secret", this will generate a secret key you will see only once
8.  Copy the "Client ID" and "Client secret" from the application you created in Twitch and paste it into the modal in the Dynamic Dashboard.

That's it! You now have OAuth credentials for your Twitch application that you can use in Dynamic.


# Twitter/X



## Creating a new App

Here are the step-by-step instructions for obtaining OAuth credentials for Twitter:

1.  Go to the [Twitter Developer Portal](https://developer.twitter.com/en/portal/dashboard) and sign in to your account.
2.  Once you're signed in, go to "Projects & Apps", choose the project you want to configure,and in the "Apps" section at the bottom of the page, click the gears icon on the right.
3.  At the bottom of the following page, under "User authentication settings" select "Set up"
4.  Select "App permissions" and "Type of App" you want.
5.  Under "App info" paste the "Redirect URL":
    1.  In the Dynamic dashboard, visit either social account linking on the [Info Capture page](https://app.dynamic.xyz/dashboard/configurations#informationcapture) or in [the Social Signup page](https://app.dynamic.xyz/dashboard/configurations#emailsocialsignin) depending on whether you want to do linking or signup.
    2.  Open the Twitter configuration sectione.
    3.  Copy the Redirect URL and paste it in the "Redirect URL" field in Twitter Developer Portal.
6.  Enter your "Website URL".
7.  Click save under the form.
8.  Copy the "Client ID" and "Client Secret" and paste it into "Set up Twitter Auth" Modal in the Dynamic Dashboard.

That's it! You now have OAuth credentials for your Twitter application that you can use in Dynamic.

If you need more help with configuring the Twitter OAuth App check their docs [here](https://developer.twitter.com/en/docs/authentication/guides).


# Dynamic Doctor



<Frame>
  <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/dynamic-doctor.png" />
</Frame>

Web3 moves fast, and this means that version management when it comes to packages is difficult. Often one library might require a specific version of Viem while another requires a different version. This is where Dynamic Doctor can help.

Use it if you've installed and initialized Dynamic but you're getting some errors. Often these can be related to the versions currently installed even though the errors themselves might not indicate it.

## Usage

In the root of your project, run the following command:

```bash
npx dynamic-doctor@latest run
```

## Output

You will see a list of issues found and a file `dynamic-doctor-report-<timestamp>.html` will be generated in your root directory.

Make sure that all of the dynamic packages have the same version, and if that doesn't fix things, you can review the generated file and as long as it doesn't contain any sensitive data, share this file with the Dynamic team to help diagnose the issue.


# Module not found Error (Framer-motion/browser.js)



<Note>
  ### 🚧 The issue has been resolved - we no longer have Framer Motion as a dependency. \[ >0.14.x]
</Note>

After installing the SDK you may hit an error when you try to build your React App:

```
ERROR in ./node_modules/@dynamic-labs/sdk-react/node_modules/framer-motion/dist/es/utils/process.mjs 5:17-24
Module not found: Error: Can't resolve 'process/browser' in 'node_modules/@dynamic-labs/sdk-react/node_modules/framer-motion/dist/es/utils'
Did you mean 'browser.js'?
BREAKING CHANGE: The request 'process/browser' failed to resolve only because it was resolved as fully specified
(probably because the origin is strict EcmaScript Module, e. g. a module with javascript mimetype, a '*.mjs' file, or a '*.js' file where the package.json contains '"type": "module"').
The extension in the request is mandatory for it to be fully specified.
Add the extension to the request.

ERROR in ./node_modules/@dynamic-labs/sdk-react/node_modules/framer-motion/dist/es/utils/process.mjs
Cannot read properties of undefined (reading 'module')
```

This is because by default Webpack requires all EcmaScript Modules to be fully specified when importing. This is likely an issue in a downstream third-party library such as framer-motion for which our SDK depends.

This is a simple fix by updating your Webpack config rules to disable [fullySpecified](https://webpack.js.org/configuration/module/#resolvefullyspecified):

```JavaScript
module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.m?js$/,
        resolve: {
          fullySpecified: false, // disable the behaviour
        },
      },
    ],
  },
};
```


# Hydration failed because initial UI does not match what was rendered on the server



This error happens when there is a mismatch between the React tree rendered during the first render in the browser (called hydration), and the React tree that was pre-rendered from the server.

Hydration is the process of React converting pre-rendered HTML into an interactive application by attaching event handlers.

In general these errors happen with components that should only be rendered in the browser (i.e. components that depend on state like the user being logged in or not, like the navigation).

To easily resolve this, you can wrap those components with our own `IsBrowser` component, exported from `sdk-react-core`.

```jsx
import { IsBrowser } from "@dynamic-labs/sdk-react-core";

const MyComponent = () => {
    return (
        <IsBrowser>
            <YourComponents>
        </IsBrowser>
    )
};
```


# FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed



You might run into the following error when building your application when using the `@dynamic-labs/sdk-react-core` package in a NextJS version 14.x project.

```
FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
```

## Solution

Update `next.config.js` to optimize `@dynamic-labs/sdk-react-core`:

```typescript
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    optimizePackageImports: ["@dynamic-labs/sdk-react-core"],
  },
};

export default nextConfig;
```


# Module not found: Can't resolve 'encoding'



If you ever see console warning like the following related to encoding, lokijs and pino-pretty:

```
./node_modules/node-fetch/lib/index.js
Module not found: Can't resolve 'encoding' in '/Users/xxx/xxx/node_modules/node-fetch/lib'
```

You can fix it by adding the following to your `next.config.js`:

```js
webpack: (config) => {
  config.externals.push("pino-pretty", "lokijs", "encoding");
  return config;
};
```

It would appear that it's coming from the following issue: [https://github.com/WalletConnect/walletconnect-monorepo/issues/1908#issuecomment-1487801131](https://github.com/WalletConnect/walletconnect-monorepo/issues/1908#issuecomment-1487801131)


# React Static Flag Error



<Note>
  ### 🚧 The issue has been resolved - we no longer have Framer Motion as a dependency. \[ >0.14.x]
</Note>

React version 18, you might see a warning like this in the console:

![](https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/react-static-flag-issue.png)

This is just noise and shouldn't be causing real issues. We've identified the source of the warning, and it's coming from our dependency on Framer Motion. We are working on removing as many dependencies as possible from the SDK, including Framer Motion. Once complete, this warning will no longer appear.

For more information on the warning, [see this issue in the React repo](https://github.com/facebook/react/issues/24391)


# Cannot convert a BigInt value to a number



If using `create-react-app`, without a specific browserlist, the compiler tries to compile for older browser versions.

[BigInts are not supported by all browser versions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#browser_compatibility), but are still used in many libraries

This means that you may see the following error when trying to run your react app: `Cannot convert a BigInt value to a number`

Here is one example in the wild: [https://github.com/0xs34n/starknet.js/issues/37#issuecomment-955797303](https://github.com/0xs34n/starknet.js/issues/37#issuecomment-955797303)

If you see the error, you can fix it by instructing your compiler to target only recent browser versions. You do this by adding the following to your package.json:

```
{
  “browserslist”: {
    “production”: [
    “chrome >= 67”,
    “edge >= 79",
    “firefox >= 68”,
    “opera >= 54",
    “safari >= 14”
    ],
    “development”: [
    “last 1 chrome version”,
    “last 1 firefox version”,
    “last 1 safari version”
    ]
  }
}

```

As a precaution, better to also `rm -rf node_modules/.cache` before building or running your application again.


# Cannot resolve rpc-websockets



If you are seeing an error like `Module not found: Can't resolve 'rpc-websockets/dist/lib/client.cjs'` when trying to use the SDK, you can fix this by adding the following to your `package.json` file:

<Warning>
  Note that even if you are not using Solana, you still need to include the `@solana/web3` line below. This will be fixed in the next major SDK release (V3).
</Warning>

<CodeGroup>
  ```json npm
  "overrides": {
    "rpc-websockets": "7.10.0",
    "@solana/web3.js": "1.91.6"
  },
  ```

  ```json yarn
  "resolutions": {
    "rpc-websockets": "7.10.0",
    "@solana/web3.js": "1.91.6"
  } 
  ```
</CodeGroup>


# Failed to parse source map warnings



If using create-react-app v5, you may see some warnings (although they appear like errors) when running/building your react application.

This has been reported as seen in this [Github discussion](https://github.com/facebook/create-react-app/discussions/11767).

Example Warning Message

```
WARNING in ./node_modules/json-rpc-engine/dist/mergeMiddleware.js
Module Warning (from ./node_modules/source-map-loader/dist/cjs.js):
Failed to parse source map from 'node_modules/json-rpc-engine/src/mergeMiddleware.ts' 
file: Error: ENOENT: no such file or directory, 
open 'node_modules/json-rpc-engine/src/mergeMiddleware.ts'
```

These warnings can be ignored, and do not affect your application. However, if you wish to suppress the warnings and disable source map loader altogether, you can set this environment variable:

```Shell
export GENERATE_SOURCEMAP=false
```


# Jest Error: SyntaxError: Cannot use import statement outside a module



When using Jest to test a component that renders DynamicContextProvider, you might run into an error like:

```
FAIL  app/page.spec.tsx
  ● Test suite failed to run

    Jest encountered an unexpected token

    Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.

    Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.

    By default "node_modules" folder is ignored by transformers.

    Here's what you can do:
     • If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/ecmascript-modules for how to enable it.
     • If you are trying to use TypeScript, see https://jestjs.io/docs/getting-started#using-typescript
     • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
     • If you need a custom transformation specify a "transform" option in your config.
     • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

    You'll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/configuration
    For information about custom transformations, see:
    https://jestjs.io/docs/code-transformation

    Details:

    /Users/testuser/code/test-jest-thing/node_modules/uint8arrays/esm/src/concat.js:1
    ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,jest){import { allocUnsafe } from './alloc.js';
                                                                                      ^^^^^^

    SyntaxError: Cannot use import statement outside a module

      10 |       some text
      11 |     </DynamicContextProvider>
    > 12 |   );
         |               ^
      13 | }
      14 |

      at Runtime.createScriptFromCode (node_modules/jest-runtime/build/index.js:1496:14)
      at Object.<anonymous> (app/page.tsx:12:19)
      at Object.<anonymous> (app/page.spec.tsx:7:54)
```

This can be easily fixed by mocking DynamicContextProvider:

```JavaScript
jest.mock("@dynamic-labs/sdk-react-core", () => ({
  DynamicContextProvider: ({ children }) => children,
}));
```


# NextJS with React 17 With Dynamic React SDK >=0.14.0



## Upgrading Dynamic SDK to >=0.14.0

If you are still running NextJS with React \<=17, installing our latest SDK will fail to load the ESM module.

This is because from 0.14.0+ we are now bundling a fully compliant hybrid ESM and CJS packages.

After installing Dynamic's package, you might see this error:

```Text
Error: Cannot find module 'nextjs-blog/node_modules/react/jsx-runtime' imported from nextjs-blog/node_modules/@dynamic-labs/sdk-react/index.js
Did you mean to import react/jsx-runtime.js?
```

This is because in React 18 they added an `exports` field to their package.json to handle mapping `react/jsx-runtime` to `react/jsx-runtime.js`

### NextJS >=13.1 Solution

NextJS introduced a configuration option to automatically transpile ESM packages.

```TypeScript
const nextConfig = {
  experimental: {
    transpilePackages: ['@dynamic-labs/sdk-react-core'],
  },
};
```

### NextJS \<13.1

Before 13.1, you need to `npm i next-transpile-modules` and update your next.config.js:

```TypeScript
const withTM = require('next-transpile-modules')(['@dynamic-labs/sdk-react-core']);

const nextConfig = withTM({
  reactStrictMode: true,
});

module.exports = nextConfig;

```


# Modal pop up isn't visible



Make sure that you set the body min-height to 100vh

`body {
  min-height: 100vh;
}
`


# Vite.js polyfills necessary for Dynamic SDK



## Vite.js requires global and process polyfills

If you're using Vite.js with react and using the Dynamic SDK you may get this error in your console:

```
util.js:109 Uncaught ReferenceError: process is not defined
```

This is because one of the many libraries that our SDK depends on uses the `process` module which is natively not in the browser environment nor is it automatically polyfilled by Vite.

#### Polyfill `process` using `vite.config.js`

To fix this we must modify the `vite.config.js` file to modify `esbuild` to polyfill this for us. Here is a minimal example config:

```TypeScript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { NodeGlobalsPolyfillPlugin } from "@esbuild-plugins/node-globals-polyfill";
import { NodeModulesPolyfillPlugin } from "@esbuild-plugins/node-modules-polyfill";

export default defineConfig({
  optimizeDeps: {
    esbuildOptions: {
      // Enable esbuild polyfill plugins
      plugins: [
        NodeGlobalsPolyfillPlugin({
          process: true,
        }),
        NodeModulesPolyfillPlugin(),
      ],
    },
  },
  plugins: [react()],
});
```

#### Warning: You may still see a console error `React Static Flag Error`

After applying the polyfill, you may still see an error for `React Static Flag Error`. This is currently expected. To learn more, checkout the troubleshooting page [React Static Flag Error](/troubleshooting/solved/react-static-flag-error). At this time, you can ignore this error in local development environment.


# TypeScript Compilation Errors



We support TypeScript v5.x. If you have a compilation error and are on TypeScript 4 or lower, either upgrade to latest, or add an override to your package.json:

```
"dependencies": {
  ...
},
"overrides": {
  "typescript": "5.2.0"
},
```


# Wallet is redirecting to the App Store



If a user is loading the site on on iOS, and is being redirected to the mobile app store instead of the wallet, then a user may need to uninstall and reinstall the wallet.

This is happening because the deeplinks "breaks" (ie, a user is getting redirected to the app store). The scenario specifically arises after a user does the following:

1.  The user is in incognito mode
2.  The user will get a pop up to Cancel/Open the wallet
3.  The user clicks cancel.

This will cause the deeplinks in iOS for that wallet to break across any site. The only way to reset the deeplinks for the impacted wallet is to delete and re-install the wallet.


# Working with environments that have both v1 and v2 embedded wallets



This article is for developers who are working with environments that have both v1 and v2 embedded wallets. You can check the version of the wallet by checking the version parameter in the verified credential of the wallet.

## Overview

### Key difference between V1 and v2 embedded wallets

v1 embedded wallets require transaction MFA. This means that before a transaction can be signed by the end user, they must
perform an additional MFA step. Currently we support Passkeys and One Time Codes for transaction MFA.

v2 embedded wallets allow transaction MFA to be configured as optional. This means that after an end user logs in/ signs up
and obtains a valid JWT, they will be able to perform actions with their embedded wallet without any additional MFA steps.

Currently adding transaction MFA is disabled for v2 embedded wallets. Configuring this will be possible in a future update.

### Example of signing a transaction using v1 embedded wallets

<Tabs>
  <Tab title="One Time Codes">
    ```javascript
    import { useEmbeddedWallet, useDynamicContext } from "@dynamic-labs/sdk-react-core"
    import { isEthereumWallet } from "@dynamic-labs/ethereum"

    // component declaration and all other logic you might need
    const {
        createOrRestoreSession,
        isSessionActive,
        sendOneTimeCode,
        userHasEmbeddedWallet
    } = useEmbeddedWallet();
    const { primaryWallet } = useDynamicContext();


    const oneTimeCodeSent = useRef(false);

    useEffect(() => {
       const startSession = () => {
            try {
                if (isSessionActive) return;
                createOrRestoreSession();
            } catch (err) {
            return;
            }
        };

        startSession();
    }, []);

    const onSendOneTimeCodeHandler = async () => {
        if(!isSessionActive) {
            try {
                await sendOneTimeCode();
                oneTimeCodeSent.current = true;
                return;
            } catch(e) {
                console.error(e)
            }
        }
        else return;
    }

    const onCreateSessionHandler: FormEventHandler<HTMLFormElement> = async (event) => {
        try {
        event.stopPropagation();
        event.preventDefault();

        if (!primaryWallet || !userHasEmbeddedWallet()) return;

        const otc = event.currentTarget.otc.value;

        await createOrRestoreSession({oneTimeCode: otc})
            .then((result) => setResult(result))
            .catch((error) => setResult(JSON.stringify(error, null, 2)));
        } catch (err) {
            logger.error(err);
        }
    };

    const handleTransaction = async () => {
        if(!isEthereumWallet(primaryWallet)) return;

        const provider = await primaryWallet.getWalletClient();

        const transaction = {
          account: primaryWallet.address as Hex,
          chain: getChain(await provider.getChainId()),
          to: address as Hex,
          value: amount ? parseEther(amount) : undefined,
        };

        const hash = await provider.sendTransaction(transaction);

        const client =
          await primaryWallet.getPublicClient();

        const { transactionHash } = await client.getTransactionReceipt({
          hash,
        });
        console.log('Transaction: ' + transactionHash);
    }

    return (
    <>
        {!isSessionActive && (
            <div>
                {!oneTimeCodeSent.current && <button onClick={onSendOneTimeCodeHandler}>Start session</button>}
                {oneTimeCodeSent.current && (
                <form onSubmit={onCreateSessionHandler} className='create-session-method'>
                <p>
                    Enter one-time code sent to email to create a session
                </p>

                <input required name='otc' type='text' placeholder='One-time code' />
                <br />
                <button type='submit'>Create session</button>

                </form>
                )}
            <div>
        )}
        {isSessionActive && (
            <div>
                <button type='submit' onClick={handleTransaction}>Send transaction</button>
            <div>
        )}
    </>
    )
    ```
  </Tab>

  <Tab title="Passkeys">
    ```javascript
    import { useEmbeddedWallet, useDynamicContext } from “@dynamic-labs/sdk-react-core”

    // component declaration and all other logic you might need
    const { createEmbeddedWallet,
        getPasskeys,
        userHasEmbeddedWallet } = useEmbeddedWallet();
    const { primaryWallet } = useDynamicContext();
    const [passkeys, setPasskeys] = useState([]);
    useEffect(() => {
        const fetchPasskeys = async () => {
            const passkeys = await getPasskeys();
            setPasskeys(passkeys);
        };
        fetchPasskeys();
    }, []);

    const handleTransaction = async () => {
        if (!isEthereumWallet(primaryWallet)) return;

        const provider = await primaryWallet.getWalletClient();

        const transaction = {
          account: primaryWallet.address as Hex,
          chain: getChain(await provider.getChainId()),
          to: address as Hex,
          value: amount ? parseEther(amount) : undefined,
        };

        // The dynamic SDK will intercept this viem call to prompt the user for their passkey at this point.
        const hash = await provider.sendTransaction(transaction);

        const client =
          await primaryWallet.getPublicClient();

        const { transactionHash } = await client.getTransactionReceipt({
          hash,
        });
        console.log('Transaction: ' + transactionHash);
    }

    return (
    <>
        {passkeys.length > 0 && (
        <button type='submit' onClick={handleTransaction}>Send transaction</button>
        )}
    </>
    )
    ```
  </Tab>
</Tabs>

### Example of signing a transaction using v2 embedded wallets

```javascript
    import { useEmbeddedWallet, useDynamicContext } from “@dynamic-labs/sdk-react-core”

    // component declaration and all other logic you might need
    const { primaryWallet } = useDynamicContext();

    const handleTransaction = async () => {
        if (!isEthereumWallet(primaryWallet)) return;

        const provider = await primaryWallet.getWalletClient();

        const transaction = {
          account: primaryWallet.address as Hex,
          chain: getChain(await provider.getChainId()),
          to: address as Hex,
          value: amount ? parseEther(amount) : undefined,
        };

        const hash = await provider.sendTransaction(transaction);

        const client =
          await primaryWallet.getPublicClient();

        const { transactionHash } = await client.getTransactionReceipt({
          hash,
        });
        console.log('Transaction: ' + transactionHash);
    }

    return (
        <>
            <button type='submit' onClick={handleTransaction}>Send transaction</button>
        </>
    );
```

### Example of signing a transaction if your environment has both v1 and v2 embedded wallets

`Note: getWalletVersion is only available in 3.0.0-alpha.30 and above`

```javascript
 import { useEmbeddedWallet, useDynamicContext } from “@dynamic-labs/sdk-react-core”

    // component declaration and all other logic you might need
    const {
            createOrRestoreSession,
            isSessionActive,
            sendOneTimeCode,
            userHasEmbeddedWallet,
            getWalletVersion,
        } = useEmbeddedWallet();
    const { primaryWallet } = useDynamicContext();
    const version = getWalletVersion();


    const oneTimeCodeSent = useRef(false);

    useEffect(() => {
        if (version === EmbeddedWalletVersion.V1) {
           const startSession = () => {
                try {
                    if (isSessionActive) return;
                    createOrRestoreSession();
                } catch (err) {
                return;
                }
            };

            startSession();
        }
    }, [version]);

    const onSendOneTimeCodeHandler = async () => {
        if(!isSessionActive) {
            try {
                await sendOneTimeCode();
                oneTimeCodeSent.current = true;
                return;
            } catch(e) {
                console.error(e)
            }
        }
        else return;
    }

    const onCreateSessionHandler: FormEventHandler<HTMLFormElement> = async (event) => {
        try {
        event.stopPropagation();
        event.preventDefault();

        if (!primaryWallet || !userHasEmbeddedWallet()) return;

        const otc = event.currentTarget.otc.value;

        await createOrRestoreSession({oneTimeCode: otc})
            .then((result) => setResult(result))
            .catch((error) => setResult(JSON.stringify(error, null, 2)));
        } catch (err) {
            logger.error(err);
        }
    };

    const handleTransaction = async () => {
        if (!isEthereumWallet(primaryWallet)) return;

        const provider = await primaryWallet.getWalletClient();

        const transaction = {
          account: primaryWallet.address as Hex,
          chain: getChain(await provider.getChainId()),
          to: address as Hex,
          value: amount ? parseEther(amount) : undefined,
        };

        const hash = await provider.sendTransaction(transaction);

        const client =
          await primaryWallet.getPublicClient();

        const { transactionHash } = await client.getTransactionReceipt({
          hash,
        });
        console.log('Transaction: ' + transactionHash);
    }
    return (
        <>
            {!isSessionActive && version === EmbeddedWalletVersion.V1 && (
                <div>
                    {!oneTimeCodeSent.current && <button onClick={onSendOneTimeCodeHandler}>Start session</button>}
                    {oneTimeCodeSent.current && (
                    <form onSubmit={onCreateSessionHandler} className='create-session-method'>
                    <p>
                        Enter one-time code sent to email to create a session
                    </p>

                    <input required name='otc' type='text' placeholder='One-time code' />
                    <br />
                    <button type='submit'>Create session</button>
                    </form>
                    )}
                <div>
            )}
            {isSessionActive || version === EmbeddedWalletVersion.V2  (
                <div>
                    <button type='submit' onClick={handleTransaction}>Send transaction</button>
                <div>
            )}
        </>
    );
```


# WalletConnect Unsupported Chain



<img width="700px" src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/walletconnect-unsupported-chain.png" alt="walletconnect-unsupported-chain" />

A common error encountered when using WalletConnect is `Unsupported chain`. This error occurs when the chain ID of the network you are trying to connect to is different than the chain specified by the WalletConnect provider.

```
UnknownRPCError: An unknown RPC error occurred.

Details: Unsupported chain
```

To resolve the above error, the `DynamicContextProvider` can be configured to dynamically change the desired WalletConnect chain ID of the network you are trying to connect to using the `walletConnectPreferredChains` property. [See here](/react-sdk/providers/dynamiccontextprovider#settings)

```jsx
<DynamicContextProvider
walletConnectPreferredChains={['eip155:1']}
... // additional parameters
/>
```


# Accessing Users



## Four ways to access a user

### Overview

When a new user signs up, Dynamic creates a new user record in the database including their general information, and verified credentials.

There are four main ways you can programmatically access this new user record as soon as it's been created:

1.  **handleAuthenticatedUser** handler: You can access the user record in the `handleAuthenticatedUser` handler before authentication even completes.
2.  **User Created Webook**: You can listen for the `user.created` event and access the user record from the event payload.
3.  **User Created Callback**: You can use the onAuthSuccess callback and access the user record from the returned values.
4.  **useDynamicContext hook** (React only): You can access the user record from the `user` object in the context.

### handleAuthenticatedUser Handler

Dynamic has a concept of ["handlers"](/react-sdk/overview#handlers), which is a particular kind of callback to allow custom code to run as part of a process (i.e. signup).

One such handler is called handleAuthenticatedUser, and it allows for custom code to receive the user object and do something with it, before the Dynamic SDK finishes the authentication flow and closes the UI.

<Tip>The args returned by handleAuthenticatedUser is a [UserProfile](/react-sdk/objects/userprofile).</Tip>

```jsx
<DynamicContextProvider
  settings={{
    handlers: {
      handleAuthenticatedUser: async (args) => {
        console.log("handleBeforeAuth was called", args);

        await customUserObjectProcess(args.user);
      },
    },
  }}
>
  {/* ... rest of your app ... */}
</DynamicContextProvider>
```

## User Created Webhook

One of the valid webhook events you can listen to is called `user.created` (learn more about event types [here](http://localhost:3001/developer-dashboard/eventTypes)).

All you will need to get started is a server that can receive events, and then you can plug in that URL in the dashboard. [Learn more about setting up the webhook step by step here](/developer-dashboard/webhooks#setting-up-webhooks).

## onAuthSuccess Event

The Dynamic SDK has a concept called [Events](/react-sdk/events/events-introduction) which allows you to hook into lifycycle events of the SDK. One such event is called \[]`onAuthSuccess`]\(/react-sdk/events/onauthsuccess), which is called when the user has successfully authenticated.

This event returns a user object that you can access and do something with.

```jsx
<DynamicContextProvider
  settings={{
    events: {
      onAuthSuccess: (args) => {
        console.log('user', args.user);
      }
    }
  }}
>
 {/* ... rest of your app ... */}
</DynamicContextProvider>
```

## useDynamicContext

Once placed anywhere inside the component tree that is wrapped by the `DynamicContextProvider`, [the `useDynamicContext` hook](/react-sdk/hooks/usedynamiccontext) will give you access to the `user` object.

```jsx
import { useDynamicContext } from '@dynamic/sdk-react-core';

function MyComponent() {
  const { user } = useDynamicContext();

  console.log('user', user);

  return <div>My Component</div>;
}
```

## User Format

This user record follows a specific format, which you can read all about in the [User](/react-sdk/objects/userprofile) reference.

One important array inside that user object will be called `verified_credentials`. This is where you'll find information about any and every associated "credential" for the user i.e. email, social, blockchain, passkey.

<Card title="What next?" href="/users/verified-credential" icon="link" color="#4779FE">
  Click here to learn more about Verified Credentials
</Card>


# Email and Phone Verification



**Summary**

This page expands on the information capture described [here](/users/information-capture) but for email, with added support for Uniqueness and Verification options.

**OTP Verification** verifies the validity of an email or phone provided by a customer by sending a One Time Password (OTP to ensure it is a functional and active email/phone. This helps prevent errors in customer communication and ensures that customer data is accurate.

**Uniqueness** ensures that each customer has a unique email address in each project environment to prevent duplicates.

**Usage**

To enable Email and Phone OTP Verification and/or Uniqueness, follow these steps:

1.  Navigate to the [Log in & User Profile](https://app.dynamic.xyz/dashboard/log-in-user-profile) page of the developer dashboard.
2.  In the Email & Phone Number Selection, toggle both on and click the gear icon to the right of each one.
3.  Check the boxes for either or both OTP Verification and/or Uniqueness to enable them.
4.  Click the 'Save Changes' button to save your changes.

Note: If Email or Phone Verification is enabled, Email Uniqueness will also be mandatory.

<Warning>
  ### 🚧 Caution when toggling verification/uniqueness on Live mode

  When enabling/disabling Email Verification and/or Uniqueness on live sites, be aware that such actions can result in data inconsistencies. If testing is necessary, it is recommended to conduct the tests on a Sandbox environment and to avoid frequently changing the fields on a live environment.
</Warning>

## Twilio Credentials for enabling additional countries

Specifically for your phone number, Dynamic by default uses its own Twilio credentials and has the United States and Canada on by default. If you wish to enable additional countries, then you will need to add your own Twilio credentials. To do so:

1.  Click on the configure button and turn off the `Use Dynamic's credentials` Toggle
2.  Enter your Twilio Credentials
3.  Add the additional countries you would like to support.
    a. It's important you <b>also</b> enable the same countries in your Twilio account. We do not have access to see what countries you have enabled so you must ensure they are both configured the same.


# Get the user's profile

Here is an example where you can use the user profile to display in a HTML table.

```TypeScript
import { useDynamicContext } from '@dynamic-labs/sdk-react-core';

const UserProfileTable = () => {
  const { user } = useDynamicContext();

  return (
    <table>
      <thead>
        <tr>
          <th>Property</th>
          <th>Value</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>Email</td>
          <td>{user?.email}</td>
        </tr>

        <tr>
          <td>First name</td>
          <td>{user?.firstName}</td>
        </tr>

        <tr>
          <td>Last name</td>
          <td>{user?.lastName}</td>
        </tr>

        <tr>
          <td>Alias</td>
          <td>{user?.alias}</td>
        </tr>

        <tr>
          <td>Job title</td>
          <td>{user?.jobTitle}</td>
        </tr>

        <tr>
          <td>Country</td>
          <td>{user?.country}</td>
        </tr>
      </tbody>
    </table>
  );
};
```


# Collecting Extra Information (Information Capture)



<img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/info-capture.gif" />

## Summary

You may want to capture information in your onboarding flow, whether it's a customer's email for communication purposes, alias, first name, or other fields.

We have a few fields pre-made for you. All the fields but 3 are are standard input fields, but these 3 have custom attributes:

3.  **Username** this field is a string that is unique for each project, and has the following validations:
    1.  Letters A-Z, a-z, numbers 0-9, or symbols \$ ! # % ?
    2.  Must be length 5 to 20
    3.  Cannot contain consecutive symbols
4.  **T-shirt size** - this field is a dropdown with the standard options available (XS, S, M, L, XL, XXL).

## Usage

### Enabling

Scroll to "Additional User Information" in [the Login/User Profile section of the dashboard](https://app.dynamic.xyz/dashboard/log-in-user-profile).

<Frame>
  <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/info-capture-configuration.png" />
</Frame>

Here you specify which fields you want to collect during onboarding. For each field, you can select whether it is optional or mandatory.

Simply save the settings and on the next log in, a user be prompted to enter the information in onboarding.

### Manually Triggering

If fields are mandatory, they cannot be skipped during onboarding and the user will be prompted to enter the information. However, if you have only optional fields, you can skip them during onboarding and prompt for them at a time of your choosing.

First, scroll to Design Settings in [the Login/User Profile section of the dashboard](https://app.dynamic.xyz/dashboard/log-in-user-profile) and toggle on "Skip Optional fields during onboarding".

Next, you can trigger the information capture modal by calling the `updateuserwithmodal` method from the [`useuserupdaterequest` hook](/react-sdk/hooks/useuserupdaterequest#function-updateuserwithmodal). This will trigger the UI for info capture.

## FAQ

Q. What happens with your current users if you change the settings?

A. No big deal. If you make certain fields required, then the next time a user connects to your application, we'll ask them to enter the field that you just made mandatory. That way, your users will stay up to date with the fields that you need.


# Social Linking



Dynamic supports linking social accounts from Apple, Discord, Facebook, Farcaster, Github, Google, Telegram, Twitch and Twitter!

You can do this by going to the Information Capture section of the dashboard and toggling on Social Account Linking.

It will then prompt you you to configure a social provider, and you can find the guides for each one in [the social providers section.](/social-providers/overview)


# Verified Credentials



## Introduction

In the physical world, we as people have numerous ways of identifying ourselves (a passport, driving license or birth certificate for example). They are each used in different contexts, and can have different fields of information but they are share one defining characteristic, they are accepted as proof of identity and are called "physical credentials".

We have exactly the same need in the digital world and for this reason, the concept of ["verified credentials"](https://en.wikipedia.org/wiki/Verifiable_credentials) were created. A Verified Credential (VC) is a way to represent a digital identity in a way that can be shared and verified by others. They can be digital representations of physical credentials (like a passport) or they can be entirely digital (like an email address).

Specific to Dynamic, verified credentials can map to things like oauth (i.e. Facebook and Google), email, blockchain wallets, passkeys and more (these are called "formats"). Each format has its own set of fields and information that can be shared. They also always have a "provider" which is the service that issued the credential (i.e. Google, Apple, Dynamic).

## Accessing Verified Credentials

As you probably already read in the [accessing users](/users/accessing-users) section, you can access verified credentials as part of the user profile, so once you have that it is as simple as accessing the `verifiedCredentials` array.

```javascript
import { useDynamicContext } from '@dynamic-labs/sdk-react-core';

const { user } = useDynamicContext();

const verifiedCredentialsArray = user?.verifiedCredentials;
```

## Verified Credential Structure

### Fields Applicable to All VCs

| Field                                   | Description                                                                                                                                                |
| --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id                                      | Internal ID for this VC                                                                                                                                    |
| format: JwtVerifiedCredentialFormatEnum | \[Optional] - Type of VC, e.g., email. See [JwtVerifiedCredentialFormatEnum](/users/verified-credential#jwtverifiedcredentialformatenum) for more details. |
| publicIdentifier?                       | \[Optional] - Common way to reference the VC, e.g., email address for email VCs or the name of a user for Google VC.                                       |
| email?                                  | \[Optional] - Email associated with the VC.                                                                                                                |

### Fields Specific to OAuth

| Field               | Description                                                                                                                                      |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| oauthProvider?      | \[Optional] - Social provider associated with the VC, e.g., Apple. See [ProviderEnum](/users/verified-credential#providerenum) for more details. |
| oauthAccountId?     | \[Optional] - ID associated with the social provider for the VC.                                                                                 |
| oauthDisplayName?   | \[Optional] - Display name associated with the social provider for the VC.                                                                       |
| oauthUsername?      | \[Optional] - Username associated with the social provider for the VC.                                                                           |
| oauthEmails?        | \[Optional] - Emails associated with the social provider for the VC.                                                                             |
| oauthAccountPhotos? | \[Optional] - Photos associated with the social provider for the VC.                                                                             |
| refId?              | \[Optional] - If using OAuth, this is the embedded wallet ID associated with the social account.                                                 |
| previousUsers?      | \[Optional] - User IDs who were previously associated with the VC.                                                                               |

### Fields Specific to Wallets

| Field             | Description                                                                                                                                                                     |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| walletName?       | \[Optional] - Wallet name associated with the VC.                                                                                                                               |
| address?          | \[Optional] - Wallet address associated with the VC.                                                                                                                            |
| chain?            | \[Optional] - Chain associated with the VC.                                                                                                                                     |
| walletProvider?   | \[Optional] - Type of wallet. See [WalletProviderEnum](/users/verified-credential#walletproviderenum) for more details.                                                         |
| walletProperties? | \[Optional] - Generally only used for embedded wallets with Turnkey. See [WalletProperties](/users/verified-credential#walletproperties) for more details.                      |
| embeddedWalletId? | \[Optional] - ID of the embedded wallet associated with the VC.                                                                                                                 |
| nameService?      | \[Optional] - ENS data name and avatar URL, if one is associated with the wallet in the VC. See [NameServiceData](/users/verified-credential#nameservicedata) for more details. |
| signerRefId?      | \[Optional] - For smart contract wallets, this is the wallet ID of the signer for the SCW.                                                                                      |

### NameServiceData

| Field           | Description                                  |
| --------------- | -------------------------------------------- |
| avatar?: string | \[Optional] - avatar associated with the ens |
| name?: string   | \[Optional] - name associated with the ens   |

### WalletProviderEnum

'browserExtension' || 'custodialService' || 'walletConnect' || 'qrCode' || 'deepLink' || 'embeddedWallet' || 'smartContractWallet'

### WalletProperties

| Field                             | Description                                                        |
| --------------------------------- | ------------------------------------------------------------------ |
| turnkeySubOrganizationId?: string | \[Optional] - turnkey sub organization ID associated with wallet   |
| turnkeyHDWalletId?: string        | \[Optional] - turnkey HD wallet ID associated with the the wallet  |
| isAuthenticatorAttached?: boolean | \[Optional] - is authenticator i.e. passkey attached to the wallet |

### JwtVerifiedCredentialFormatEnum

'blockchain' || 'email' || 'oauth' || 'passkey'

### ProviderEnum

'emailOnly' || 'magicLink' || 'apple' || 'bitbucket' || 'discord' || 'facebook' || 'github' || 'gitlab' || 'google' || 'instagram' || 'linkedin' || 'microsoft' || 'twitch' || 'twitter' || 'blocto' || 'banxa' || 'dynamic' || 'alchemy' || 'zerodev' || 'turnkey'


# Integrate your Chain



We're excited about the possibility of supporting your chain on Dynamic! We're always looking to expand our ecosystem and provide developers with more options. If you'd like us to consider adding support for your chain, please submit your information using the form below. Our team will review your submission and reach out to learn more about your chain and explore potential integration.

<iframe src="https://interfaces.zapier.com/embed/page/clyt3pohu0062shutwmoxrodj?" height="1500px" width="100%" />


# Integrate your Wallet



## Overview

If you're a wallet looking to integrate with Dynamic, you're in the right place. We support multiple wallet standards, and if you implement one of these standards, we can quickly add you to our list of supported wallets.

After implementing one of these standards below, reach out via the form below to get allowlisted.

## EVM

### WalletConnect Protocol

[WalletConnect Protocol](https://walletconnect.com/) is an open protocol that enables secure communication between wallets and dApps. It supports a wide range of wallets across multiple chains, including MetaMask, Rainbow, and other EVM-compatible wallets.

### ERC-6963

[ERC-6963](https://eips.ethereum.org/EIPS/eip-6963) is a standard for wallet discovery and connection for browser extension wallets. It aims to improve the user experience by allowing dApps to detect and interact with multiple installed wallets at the same time. MetaMask, Coinbase Wallet and Rainbow are some of the wallets that support this standard.

### Mobile Wallet Protocol

The Mobile Wallet Protocol is designed for seamless integration between mobile wallets and dApps and is led by Coinbase. You can find more information on the protocol [here](https://github.com/MobileWalletProtocol/wallet-mobile-sdk).

### Smart Wallets (ERC-1271 and ERC-6492)

Smart wallets are contract-based wallets that offer advanced features like account abstraction. If you're a smart wallet looking to integrate with Dynamic, note that we support both [ERC-1271](https://eips.ethereum.org/EIPS/eip-1271) (Standard Signature Validation Method for Contracts) and [ERC-6492](https://eips.ethereum.org/EIPS/eip-6492) (Signature Validation for Predeploy Contracts).

## Solana

### Solana Wallet Standard

The [Solana Wallet Standard](https://github.com/solana-labs/wallet-standard) is a specification for Solana wallets to interact with dApps. It supports wallets such as Phantom, Solflare, and other Solana-compatible wallets.

### WalletConnect Protocol

WalletConnect Protocol also supports Solana wallets, enabling cross-chain compatibility.

## Bitcoin

### Bitcoin Wallet Standard

The [Bitcoin Wallet Standard](https://github.com/ExodusMovement/bitcoin-wallet-standard) is an emerging specification for Bitcoin wallets to interact with web applications. Wallets such as Exudus, Phantom and Magic Eden support this protocol.

### Sats Connect

[Sats Connect](https://docs.xverse.app/sats-connect) is a protocol for connecting Bitcoin wallets to web applications. Xverse, Hiro, and other Bitcoin wallets implement this standard

## Custom Wallet Connectors

Dynamic now offers developers the ability to add their own custom wallet connectors to the Dynamic SDK. This feature allows for greater flexibility and customization in integrating wallets that may not follow standard protocols or have unique requirements.

To create a custom wallet connector:

1.  Visit the [Dynamic Public Wallet Connectors repository](https://github.com/dynamic-labs/public-wallet-connectors) on GitHub.
2.  Follow the guidelines in the [CONTRIBUTING.md](https://github.com/dynamic-labs/public-wallet-connectors/blob/main/CONTRIBUTING.md) file to understand the process of creating and submitting a custom connector.
3.  For an example of a custom connector implementation, you can refer to the [Safe connector](https://github.com/dynamic-labs/public-wallet-connectors/blob/main/packages/@dynamic-labs-connectors/safe-evm/README.md).

By creating a custom wallet connector, you can ensure that your wallet integrates seamlessly with the Dynamic SDK, providing a smooth experience for users of your wallet and applications using Dynamic.

## Submit your wallet

Ready to add your wallet to Dynamic or need us to make changes to a current wallet's name or logo? Fill out our form below and we'll get back to you shortly.

<iframe src="https://interfaces.zapier.com/embed/page/cm1bxw87g000p6qtqxv1u260u?" height="1000px" width="100%" />


# Branded Wallets Overview



Branded wallets are a way to let users sign up and log in to your application using their own wallet, such as MetaMask. This is opposed to embedded wallets, where the wallet is created and managed by Dynamic on signup or after.

<Tip>
  Branded wallets and embedded wallets are completely compatible, a user can start with an embedded wallet and also link their branded wallet, or vice versa.
</Tip>

We cover enabling signup/login with branded wallets in the [authentication methods section](/authentication-methods/branded-wallets), and assume if you're reading this section that you've already enabled the appropriate chains and configured RPC URLs.

This section details the more advanced areas of branded wallets, such as:

*   [Connect vs Authenticate modes](/wallets/advanced-wallets/connected-vs-authenticated)
*   [Allowing multiple wallet connections](/wallets/advanced-wallets/multi-wallet)
*   [Recommending certain wallets at signup](/wallets/advanced-wallets/recommend-wallets)
*   [Blocking risky wallet addresses with Chainalysis](/wallets/advanced-wallets/chainalysis)


# Block Addresses With Chainalysis



#### Summary

The Chainalysis integration allows for blocking certain wallet addresses based on their association with sanctioned lists or risky categories. Today, you can enable this integration with a simple toggle in your dashboard.

<Note>
  ### 📘 Let us know if you want the Address Screening API

  Today, we have an integration with the free sanctions API. If you want us to add the address screening API, let us know through [slack or email](/introduction/welcome#support-and-feedback).
</Note>

#### Usage

1.  Enable the Chainalysis integration in your developer dashboard by going to the [Configurations tab](https://app.dynamic.xyz/dashboard/configurations).
2.  Use the free sanctions API to block wallet addresses associated with sanctioned lists or use the paid address screening API for additional risky category screening.
3.  View the results of the screening for any given user, go to the Users table and click on their row. Here you will see a "Pass" or "Fail" status.
4.  If a wallet address fails the screening, the user will see the "sad path" message reflected in the SDK, with a header "This address is not approved for access", secondary text "This wallet has been correlated to illicit activity and cannot be used to access this site."


# Coinbase Smart Wallet



### Introduction

Coinbase Smart Wallets lets your users easily onboard and create wallet accounts without the need for a browser extension or mobile app. You can see a walkthrough of the feature and use with Dynamic below:

<iframe width="100%" height="315" src="https://www.youtube.com/embed/URPSJbKafXI?si=Tyn9NdR9tVwwXZNc" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen />

<Tip>
  You can see the Coinbase Smart wallet in action in our <a href="https://demo.dynamic.xyz">demo environment</a> by clicking on Coinbase.
</Tip>

### Integration Steps

#### Initial Setup

To set up Coinbase Smart Wallet, simply upgrade to our latest SDK (v2.2+), ensure [you have wallet login set up](/authentication-methods/branded-wallets), and [a network compatible with Coinbase Smart Wallet enabled](/chains/enabling-chains#enabling-a-chain-network), then we'll do the rest!

<Accordion title="What networks will be supported at launch?">
  *   Base
  *   Arbitrum
  *   Optimism
  *   Zora
  *   Polygon
  *   BNB
  *   Avalanche
  *   ETH mainnet (not preferred for use, due to gas cost)
</Accordion>

#### Recommend Coinbase Smart Wallet

Dynamic gives you the ability to [recommend wallets](/wallets/advanced-wallets/recommend-wallets) by making them stick to the top of the wallet list, whether or not they are installed.

You can easily recommend Coinbase Smart Wallet by adding the following to your DynamicContext settings:

```Typescript
<DynamicContextProvider
   settings={{
      ...,
      recommendedWallets: [
         { walletKey: "coinbase" },
      ],
   }}>
   <HomePage />
</DynamicContextProvider>
```

<Frame>
  ![](https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/coinbase-recommended.png)
</Frame>

#### Coinbase SW + New To Web3

The Dynamic provided UI components include a [New to Web3 help section](/wallets/advanced-wallets/recommend-wallets#new-to-web3-section) as part of the DynamicWidget, where we show a common wallet for users to get started for each chain.

You can easily recommend Coinbase Smart Wallet by adding the following to your DynamicContext settings:

```Typescript
<DynamicContextProvider
   settings={{
      ...,
      newToWeb3WalletChainMap: {
         primary_chain: 'evm',
         wallets: {
           evm: 'coinbase'
         },
      },
   }}>
   <HomePage />
</DynamicContextProvider>
```

<Frame>
  <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/coinbase-new-to-web3.png" />
</Frame>

### How it works

### Overview

Each wallet is created as a smart contract wallet (ERC-4337), and is secured by a user added Passkey, which is tied to the domain "keys.coinbase.com".

Because the passkey is scoped to Coinbase's domain, the wallets will work securely across any site that integrates with Coinbase Smart Wallet.

This is a huge unlock for the ecosystem, as it allows users to have a seamless experience across multiple sites, without any extra lift.

Since the wallet is actually an ERC-4337 compliant smart contract, you can also make use of batch transactions and app-defined paymasters which are supported by the Coinbase Smart Wallet.

Lastly, the wallet is integrated with [Magic Spend](https://www.smartwallet.dev/why#magic-spend) by default, allowing for seamless onramping of funds into the newly created wallet.

### Signing

The user signs with their passkey, and a function named `isValidSignature` is subsequently called on their Smart Wallet in order to verify that signature ([based on EIP-1271](https://eips.ethereum.org/EIPS/eip-1271)).

Since the user's passkey is an owner on the Smart Wallet's contract, it would return that the signature is valid and the transaction can be processed from there. However, what happens if the smart acccount has not yet been deployed?

A Smart Wallet is not deployed on a chain until the first transaction on that chain, so there must be a method of handling the signing of an initial transaction offchain. This is done by adopting the solutions outlined in [EIP-6492](https://eips.ethereum.org/EIPS/eip-6492).

### What's Next

We're excited to support the move of Coinbase Smart Wallet to mainnet in the coming weeks. With incredible features such as [Magic Spend](https://www.smartwallet.dev/guides/magic-spend) and [Batch Transactions](https://www.smartwallet.dev/guides/batch-transactions), we see a future of even simpler new user onboarding. <strong>Learn more in our deeper blog post on [Coinbase Smart Wallet](https://www.dynamic.xyz/blog/test-drive-coinbases-smart-wallet-today).</strong>

### Get up to \$20k in paymaster credits

Utilizing Coinbase Smart Wallets or AA accounts on @base with Dynamic?

We teamed up with @coinbasewallet to kick it up a notch 💙

You may be eligible for up to \$20K in gas credits: \$15K from the Smart Wallet Gas Program + \$5K just for being a Dynamic customer.

Fill out this form to indicate that you are building with Dynamic and get credits: [apply now](https://docs.google.com/forms/d/1yPnBFW0bVUNLUN_w3ctCqYM9sjdIQO3Typ53KXlsS5g/viewform?edit_requested=true)


# Configure Connect Only Mode



### Setup

By default, the SDK will authenticate all users which means users will need to
sign when logging in. This page describes how you can remove the authentication
step and allow users to just connect their wallets to log into your site.

### Usage

#### initialAuthenticationMode (optional)

If you want to skip signing a message and just connect, then set the
`initialAuthenticationMode` to `connect-only`.

```js TypeScript
import { useDynamicContext } from "@dynamic-labs/sdk-react-core";

<DynamicContextProvider
  settings={{
    initialAuthenticationMode: 'connect-only',
    ...
  }}
>
  {...}
</DynamicContextProvider>
```

#### enableVisitTrackingOnConnectOnly (optional)

If you are using the `connect-only` mode, then you also have the option to
disable visitor tracking by setting `enableVisitTrackingOnConnectOnly` to false.

If you set this to `false` then we will not track visitors. Note that you will
not be able to see visitor information in our visitors table or analytics page.

```js TypeScript
import { useDynamicContext } from "@dynamic-labs/sdk-react-core";

<DynamicContextProvider
  settings={{
    initialAuthenticationMode: 'connect-only',
    enableVisitTrackingOnConnectOnly: false,
    ...
  }}
>
  {...}
</DynamicContextProvider>
```

### useIsLoggedIn

The [useIsLoggedIn](/react-sdk/hooks/useisloggedin#useisloggedin) hook determines whether a user is logged in or not by returning a boolean value you can use to determine if the user has connected their wallets. The hook needs to be initialized within a child of [DynamicContextProvider](/react-sdk/providers/dynamiccontextprovider).

#### User Management & Analytics

Any user who connect, but does not authenticate, will appear in your dashboard
as a `Visitor`. You'll be able to see them in your
[user management table](https://app.dynamic.xyz/dashboard/users/visitors) or in
[analytics](https://app.dynamic.xyz/dashboard/analytics) under as `Visitors`.


# Configure Authenticate Mode



### Setup

By default, we will authenticate users which means users will need to sign on
every connection. This means, that you don't need to set any prop to
automatically authenticate users.

That being said, you can explicitly control the settings using the
`initialAuthenticationMode` prop and setting the value to `connect-and-sign`.

#### initialAuthenticationMode (optional)

```js TypeScript
import { useDynamicContext } from "@dynamic-labs/sdk-react-core";

<DynamicContextProvider
  settings={{
    initialAuthenticationMode: 'connect-and-sign', // this is the default option
    ...
  }}
>
  {...}
</DynamicContextProvider>
```

### Usage

#### useAuthenticateConnectedUser

If you elect to avoid authentication initially by using `connect-only`, but at
some point you want to authenticate your connected users (ie, prove ownership of
the wallet), then you can call `authenticateUser` to trigger a sign request.

You can check the boolean `isAuthenticating` to check on the status of a user
that is authenticating which will be either `true` or `false`.

```ts TypeScript
import { useAuthenticateConnectedUser, useDynamicContext } from "@dynamic-labs/sdk-react-core";

const Element = () => {
  const { user } = useDynamicContext();
  const { authenticateUser, isAuthenticating } = useAuthenticateConnectedUser();

  if (!user) {
    return (
      <button onClick={authenticateUser} disabled={isAuthenticating}>
        Authenticate user
      </button>;
    )
  }

  return <div>User is authenticated!</div>;
};
```

### Example

This video shows our [demo site](https://demo.dynamic.xyz). Here we start with a connected user who
has not yet authenticated. We call the authenticateUser method and signature
request appears. Once the user signs, then Dynamic verifies the signature and
returns a JWT.

<Frame>
  <img src="https://res.cloudinary.com/mintlify/image/upload/v1678924082/Customers/Eqtble/connect_authenticate_2_weowyq.gif" />
</Frame>


# Connected vs Authenticated



## Definitions

When using Dynamic, one of the first things you'll need to decide is whether you want to have users sign to prove ownership of their wallets or if you just want them to connect their wallets.

As we talk about these two options, you'll regularly see that we either discuss a `visitor` or an `authenticated user` in our documents and dashboard. In short, here are there definitions:

*   `Visitor` is a user that connects their wallet to your dApp but has not yet signed to prove ownership of that wallet.
    *   You can identify a visitor as having a `primaryWallet` but user is not defined.
*   `Authenticated User` is a user that connects their wallet and has signed to prove ownership.
    *   You can identify an authenticated user since the `user` object will be defined.
    *   A JWT is returned for an authenticated user

For example, here we log in a user whether they are connected or authenticated. We check for either a primary wallet or a user being defined.

```typescript
import { useDynamicContext } from "@dynamic-labs/sdk-react-core"; //later you will read about dynamicContext

const HomePage = () => {
  const { primaryWallet, user } = useDynamicContext();

  if (primaryWallet !== null || user) {
    return (
      <div className={styles.logged_in}>
        <WalletKitActions />
      </div>
    );
  }

  return <LoginView />;
};
```

## An analogy to the rescue

The difference can be explained with a simple analogy to confirming your phone number in a sign up flow.

If you enter a phone number as part of a website registration flow, the website doesn’t actually know if you own that phone number. You can just as easily enter someone else’s phone number and register on their behalf.

Hence, websites text you to confirm that you have access to your phone, making sure you are who you say you are. You have to enter a code and prove you have access to your phone number.

The same holds true for wallets. Connecting is similar to entering a phone number. Signing is similar to entering the confirmation code you received on that number.

In the sign in case, the way to do it is by generating a cryptographic nonce for you to sign with your private key. That signature proves without a doubt that you are indeed the owner of your wallet.

## The difference, in practice

At Dynamic, we're building deep authentication and authorization features that include access lists, information capture, NFT gating, and a lot more. For some of these features, a signed user **is required**. The reason being, we need to know that this user has confirmed ownership of the wallet and we can therefore trust that this is not a spoofed account.

While we are aiming to limit the feature limitations between the `Visitor` and the `Authenticated user`, there are indeed some limitations and we hope the table below provides the appropriate context:

| Feature                         | Connected Wallet | Signed User | Explanation                                                                                                                                                                  |
| :------------------------------ | :--------------- | :---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Multi-chain Authentication Flow | ✅                | ✅           |                                                                                                                                                                              |
| JWT Token                       |                  | ✅           | JWT tokens are reserved for `authenticated users` who have proved ownership. `visitors` will get an API response with the wallet object                                      |
| Users Table                     |                  | ✅           | Until a wallet is signed to prove ownership, a user\_id is not generated in our DB                                                                                           |
| Designs                         | ✅                | ✅           |                                                                                                                                                                              |
| Information Capture             |                  | ✅           | Until a wallet is signed to prove ownership, a user\_id is not generated in our DB so information cannot be associated to a user                                             |
| Access Lists                    |                  | ✅           | Access lists will not work for connected only account. In your site, you'll want to ensure that only authenticated users can access your gated site or sections of the site. |
| NFT Gating                      |                  | ✅           | NFT Gates will not work for connected only account. In your site, you'll want to ensure that only authenticated users can access your gated site or sections of the site.    |
| Chainalysis                     | ✅                | ✅           |                                                                                                                                                                              |
| Multi-wallet                    |                  | ✅           | We currently require that a user be authenticated to be able to access multiwallet functionality                                                                             |
| Analytics                       | ✅                | ✅           | We display both authenticated users and visitors and segment our analytics accordingly                                                                                       |


# Custom Sign Message



We currently send the following message when asking a user to sign their wallet
when authenticating:

> Welcome to \{\{your app name}}. Signing is the only way we can truly know
> that you are the owner of the wallet you are connecting. Signing is a safe,
> gas-less transaction that does not in any way give \{\{your app name}}
> permission to perform any transactions with your wallet.

<Note>Note: We base the default message on the standard SIWE format.</Note>

If you would like to update this message and customize it for your users, you
simply need to update this message by passing in the prop `siweStatement` and
your custom message:

```tsx
<DynamicContextProvider
  settings={{
    environmentId: "XXXXX",
    siweStatement: "Your custom message goes here....",
    ...
  }}
>
```


# Multiple Wallet Connections (Multi-Wallet)



### Summary

Lots of users have multiple wallets but managing multiple wallets and multiple
accounts for each wallet is challenging. You have to manage event listeners,
various connection states, wallets shared across accounts, and much much more.
If you want to learn more about our multi-wallet solution, check out our
[blog post](https://www.dynamic.xyz/blog/multi-wallet-announcement).

Our multiwallet widget allows your users to easily:

1.  Add multiple wallets
2.  Add multiple accounts from the same wallet
3.  Transfer wallets between accounts
4.  It is mobile friendly
5.  Keep track of the active account in a wallet
6.  Swap primary wallets
7.  and so much more.

To simplify management of these scenarios and the resulting edge cases, we've
released a widget that does it for you. You can see it in action on our
[demo site](https://demo.dynamic.xyz).

### Setup

To enable multiwallet, simply visit [the Log in & User Profile section of the dashboard](https://app.dynamic.xyz/dashboard/log-in-user-profile) and toggle it on in the "Branded Wallets" section.

### Headless Multi-Wallet

Multi-wallet functionality comes bundled as part of the user profile, and we have a guide to achieve every aspect of this in a headless manner [here](/headless/headless-user-profile).


# Recommend Wallets at Signup



There are two different kinds of wallet recommentdations that you can configure. The first is for a user who might already have a wallet, but you want to recommend a specific wallet to them, to learn about this, continue to [the General Wallet List section](/wallets/advanced-wallets/recommend-wallets#general-wallet-list). The second is for a user who is new to Web3 and doesn't have a wallet at all, to learn about this you can skip to [the New to Web3 section](/wallets/advanced-wallets/recommend-wallets#new-to-web3-section).

## General Wallet List

<Frame>
  ![](https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/RecommendedTagsExample.png)
</Frame>

You can now specify recommended wallets that stick to the top of the wallet list, whether or not they are installed.

To do so, we added a new prop `recommendedWallet` to the DynamicContext settings which is an array of:

```JSON
{
   "walletKey": "phantomevm",
   // label: freeform string input for the tag label. If not specified, defaults to "Recommended"
   "label": "Popular"
}
```

<Tip>
  You can find the valid wallet keys in the chain configuration section of the
  dashoard. For example,
  [here](https://app.dynamic.xyz/dashboard/chains-and-networks#evm) is the EVM
  section that lists all the wallets and their keys.
</Tip>

```Typescript
<DynamicContextProvider
   settings={{
      ...,
      recommendedWallets: [
         { walletKey: "phantomevm", label: "Popular" },
         { walletKey: "okxwallet" }
      ],
   }}>
   <HomePage />
</DynamicContextProvider>
```

If you would like to customize the background and text color for the recommended tag, please refer to the [CSS variable guide](/design-customizations/css/css-variables):

```
--dynamic-badge-primary-background: #4779FF;
--dynamic-badge-primary-color: #fff;
```

***

## New To Web3 Section

<Frame>
  <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/new-to-web3-sdk-view.png" />
</Frame>

In our New to Web3 help section in our SDK, we show a common wallet for users to get started for each chain. For now, if you have enabled more than 1 chain, then we will show only 1 chain and 1 wallet.

To change the default wallet shown in this this section, you can pass a prop `newToWeb3WallChainMap` to specify the primary chain (this is to support multi-chain) and then specify the wallet with the corresponding chain.

```Typescript
<DynamicContextProvider
   settings={{
      ...,
      newToWeb3WalletChainMap: {
         primary_chain: 'flow', // <-- Here you specify the primary chain which will select the wallet to show
         wallets: { // <-- With "wallets" you specify the wallets you want to show for each chain
           flow: 'blocto',
           solana: 'glow'
         },
      },
   }}>
   <HomePage />
</DynamicContextProvider>
```

For customers supporting multiple chains, you can also specify which chain you prefer to be primary which will then recommend the wallet aligned with that chain. You **don't** need to specify a wallet unless you want to override the current default wallet for that chain.

```TypeScript
<DynamicContextProvider
   settings={{
     ...,
     newToWeb3WalletChainMap: {
          primary_chain: 'flow',
          wallets: {}
       },
     },
   }}>
   <HomePage />
</DynamicContextProvider>
```


# Sort and Filter Wallets



### Summary

There are a few helper methods you can leverage to sort, filter, remove, or filter and sort wallets. You can call the methods from within the `walletsFilter` prop.

The helper methods are:

1.  FilterWallets
2.  RemoveWallets
3.  SortWallets
4.  FilterAndSortWallets
5.  BYOF (Bring your own function)

### Wallet Type

You can see the fields of the wallet that you'll be able to access when building your filter function in the [WalletOption](/react-sdk/objects/wallet-option) object.

Note that if you need to check if the wallet is installed on the browser, you can use the `isInstalledOnBrowser` field, while if you want to check if it's the most recently used wallet, you should check if a value for the key `dynamic_last_used_wallet` is present in local storage, and then match against the key field of the wallets.

### Setup

#### walletsFilter (optional)

To filter and sort wallets, you'll want to use the `walletsFilter` prop under settings. You will then use either the one of the helper methods or BYOF to organize your wallet list.

```TypeScript
import { DynamicContextProvider, DynamicWidget, FilterWallets } from '@dynamic-labs/sdk-react-core';
....
 <DynamicContextProvider
  settings={{
    ...
    walletsFilter: //select the function you want to use to filter and/or sort.
  }}
>
```

### Usage

For all examples we mention here, make sure you have the chains enabled in [the dashboard](https://app.dynamic.xyz/dashboard/chains-and-networks) and that you have used [the appropriate walletConnectors](https://docs.dynamic.xyz/react-sdk/wallet-connectors) in the DynamicContextProvider.

#### FilterWallets

If you would like to filter out all wallets except for a predefined list, then call the FilterWallets method and include the wallets that you want your users to use:

```TypeScript
import { DynamicContextProvider, DynamicWidget, FilterWallets } from '@dynamic-labs/sdk-react-core';

 <DynamicContextProvider
  settings={{
    ...
    walletsFilter: FilterWallets(['wallet1', 'wallet2', 'wallet3', 'wallet4']);
  }}
>
```

#### SortWallets

The Dynamic SDK has a predefined sorted list of wallets. If you want to change the wallet list to a different ordered list, add a sorted array of the wallets that you want to appear first.
The rest of the wallets will keep their current order.

```jsx
import { DynamicContextProvider, DynamicWidget, SortWallets } from '@dynamic-labs/sdk-react-core';

 <DynamicContextProvider
  settings={{
    ...
    walletsFilter: SortWallets(['wallet1', 'wallet2', 'wallet3', 'wallet4'])
  }}
>
```

#### FilterAndSortWallets

If you would like to combine the previous two options, then you can use the FilterAndSortWallets method to define the wallets that you want to appear in a specific order.

```jsx
import { DynamicContextProvider, DynamicWidget, FilterAndSortWallets } from '@dynamic-labs/sdk-react-core';
....
 <DynamicContextProvider
  settings={{
    ...
    walletsFilter: FilterAndSortWallets(['wallet1', 'wallet2', 'wallet3', 'wallet4']);
  }}
>
```

#### RemoveWallets

If you want to remove specific wallets from the available list of wallets, you can call the RemoveWallets and specify which wallets you don't want your users to use:

```jsx
import { DynamicContextProvider, DynamicWidget, RemoveWallets } from '@dynamic-labs/sdk-react-core';
....
 <DynamicContextProvider
  settings={{
    ...
    walletsFilter: RemoveWallets(['wallet1', 'wallet2', 'wallet3', 'wallet4']);
  }}
>
```

#### FilterChain

If you want to filter out wallets based on whether they support a chain, then you can call FilterChain and specify the chain you want the wallets to support.

```TypeScript
import { DynamicContextProvider, FilterChain } from '@dynamic-labs/sdk-react-core';

 <DynamicContextProvider
  settings={{
    ...
    walletsFilter: FilterChain('EVM'), // Will only display EVM compatible wallets
  }}
>
```

#### BYOF (Bring Your Own Function)

If the above helper methods are not sufficient, you can design your own function and pass it to walletFilter.

```jsx
<DynamicContextProvider
  settings={{
    ...
    walletsFilter: (wallets) =>  {
      return wallets.filter((wallet) => {
        return ['wallet1', 'wallet2'].includes(wallet.key)
      });
    },
  }}
>
```

#### Combine multiple filters

You can use the `pipe` function to combine multiple operations to create your wallets filter.

For example, filters by chain, remove wallets and sort.

```TypeScript
import { DynamicContextProvider, FilterChain } from '@dynamic-labs/sdk-react-core';
import { pipe } from '@dynamic-labs/utils';

 <DynamicContextProvider
  settings={{
    ...
    walletsFilter: pipe(FilterChain('EVM'))
      .pipe(RemoveWallets(['wallet1']))
      .pipe(SortWallets(['wallet2', 'wallet3'])),
  }}
>
```

#### Fetch standardized wallet options keys

To find the key of all the wallets options you can output all the keys using the following syntax:

```jsx
import { useWalletOptions } from ‘@dynamic-labs/sdk-react-core’;

const { walletOptions } = useWalletOptions();
console.log(walletOptions.map((wallet) => wallet.key));
```

#### Wallet list tabs

To configure multiple wallet filters, you can use the [wallet list tabs feature](/react-sdk/objects/views#wallet-list) to define different tabs for users to select.


# TOS & Privacy Policy



## Overview

Displaying your Terms of Service and Privacy Policy are an important step of customer onboarding. We give you the flexibility to display those two documents in our SDK or you can hide the links and display them somehwere else in your site.

<Frame>
  <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/tos-privacy-policy.png" />
</Frame>

## Display as a sentence

#### privacyPolicyUrl (optional) & termsOfServiceUrl (optional)

We currently display the Terms of Service and Privacy Policy during the connecting step of the SDK.

If you want to update the links, then you can do so by updating the props in settings.

```TypeScript
<DynamicContextProvider
  settings={{
    environmentId: "1234abcd-1234-abcd-1234-abcd12343abc",
    appName: "My Cool App",
    appLogoUrl: "..."
    apiBaseUrl: "https://app.dynamic.xyz/api/v0",
    privacyPolicyUrl: "https://example.com/policies/privacy-policy",
    termsOfServiceUrl: "https://example.com/policies/terms-of-service",
    ...
  }}
>
```

#### Hiding the Terms of Service and Privacy Policy Copy

If you don't want to display this text, then you can instead hide the sentence with:

```TypeScript
<DynamicContextProvider
  settings={{
    environmentId: "1234abcd-1234-abcd-1234-abcd12343abc",
    appName: "My Cool App",
    appLogoUrl: "..."
    apiBaseUrl: "https://app.dynamic.xyz/api/v0",
    displaySiweStatement: false,
    ...
  }}
>
```

## Display as a checkbox

As an alternative to displaying the Terms of Service and Privacy Policy as a sentence, you can display a checkbox that the user must check in order to proceed. To do so, turn on TOS and PP in the login methods section under information capture. After that, you can display the checkbox component like so:

Note: this will display the TOS and PP in a page post login as part of the user's onboarding flow.

```TypeScript
<DynamicContextProvider
  settings={{
    environmentId: '2995b456-45c1-4326-af24-5f9fca2214cf',
    walletConnectors: [ EthereumWalletConnectors ],
    policiesConsentInnerComponent: (
      <div>
        <p>
          By checking this box, you agree to our{' '}
          <a href="https://www.dynamic.xyz/terms-of-service" target="_blank">
            Terms of Service
          </a>{' '}
          and{' '}
          <a href="https://www.dynamic.xyz/privacy-policy" target="_blank">
            Privacy Policy
          </a>
          .
        </p>
      </div>
    )
  }}
>
  {children}
</DynamicContextProvider>
```


# Architecture & Security



## Authentication and JWT

End users must be logged in and issued a JWT by Dynamic's backend API to create and use embedded wallets. This JWT acts as an authentication token allowing the browser to identify a logged in user to the backend. The JWT is signed by a private key that is stored encrypted using AWS KMS. A developer could validate Dynamic's JWT using a standard JWKS endpoint to retrieve the public key. Unauthenticated requests or requests with an invalid JWT for creating or accessing an embedded wallet will be rejected by the Dynamic API.

## Wallet private key storage

All private keys are encrypted, and never stored in raw form. End user private keys are not accessible to Dynamic, the developer, or the key management service. All decryption functions to use private keys must be user-initiated and done so in secure enclaves (i.e Trusted Execution Environments).

Dynamic does not sign transactions on a end-user’s behalf, and end-users have to take explicit actions to sign transactions. Dynamic also does not offer APIs to sign transactions for end-users.

## Iframes

For data that must never be exposed except to the end-user i.e. private keys, it is sent encrypted through an API call to an iframe in the end-users browser, and only decrypted on the client device. This ensures that at all times, no-one but the end-user will ever be able to access that data, and only on their own device.

## Recovery of Wallets

Recovering a wallet is tied to the authentication method enabled by the application developer. For example, if an application developer allows login with email, the user can always recover their wallet by having access to their email. They can use standard email recovery procedures from their email client to support regaining access.
Adding additional recovery options through other authentication methods.

Dynamic allows users to link other verified credentials to their account such as phone number, Google or other Social Media login methods. If users have multiple credentials assigned, they can be used to recover their wallet if a single method is lost.

Retrieving Wallet Key if Dynamic or Application Developer are down. (Coming soon)
Dynamic will be adding additional fall back support in case an application developer or Dynamic services are down. A custom URL will be available so end-users can log in and export their private key.

## Trusted Execution Environments (AWS Nitro Enclaves)

A core component of Dynamic-powered embedded wallets is the isolation of any sensitive key material or credentials. All sensitive operations that involve an embedded wallet’s private key occur within secure AWS nitro enclaves.

Additionally, token private keys never exist outside of the AWS secure enclave unencrypted. There is no avenue of unilateral access to the secure enclave from any employee of Dynamic, nor AWS KMS.

## Sessions, Signatures, Transactions

Developers can configure Dynamic-powered embedded wallets to require an optional second level of authentication by the end user before performing an action. This is referred to as transactional MFA. There are two transaction MFA methods that we currently support: passkeys and email one time codes.

If transaction MFA is not enabled, Dynamic allows the end user to use session keys to authenticate and interact with their wallets.

Session keys are implemented by creating api keys in the developer website and registering those as valid authentication methods within a secure enclave. Those session keys are retained by the end user on the developer website and are never sent to Dynamic. Session key registration requires a valid Dynamic JWT, which is obtained via a successful user authentication. Once the session keys expire, they can no longer be used to authenticate into a Dynamic-powered embedded wallet.

Transactions requests are signed by the end user’s wallet authenticator and validated by AWS KMS. Upon successful validation, the end user’s private keys are used to sign the actual transaction within AWS KMS’s secure enclave.


# Creating Embedded Wallets



No matter how your user signs up, whether it's using social login i.e. Telegram, email/phone login or even with a branded wallet i.e. Metamask, you can create an embedded wallet for them using Dynamic. Simply decide when you want this wallet created and follow the guides below.

## During Signup (automatic)

By default, wallets are created automatically for users when they sign up for your application. All you have to do is check that the "Create on Sign up" toggle is turned on in [the Embedded Wallet configuration page](https://app.dynamic.xyz/dashboard/embedded-wallets/dynamic).

<Frame>
  <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/wallets-on-signup.png" />
</Frame>

<Note>
  Automatic embedded wallet creation only creates a single wallet for a user on
  each chain you have selected. If you want to create multiple wallets per chain
  you will need to use the
  [createEmbeddedWalletAccount](/react-sdk/hooks/useembeddedwallet#create-embedded-wallet-account)
  method from the `useEmbeddedWallet` hook. See [creating additional wallets](/wallets/embedded-wallets/create-wallets/overview#creating-additional-wallets)
</Note>

## Before Signup (pre-generated)

Pre-generated wallets allow you to create a wallet for a user prior to their first interaction with your application using either an email, phone number or social identifier (i.e. Farcaster or Twitter).

Simply provide an identifier to our API and receive the new wallet address in the response. The user can receive funds and assets into their wallet!

```javascript
const options = {
  method: "POST",
  headers: {
    Authorization: "Bearer <token>",
    "Content-Type": "application/json",
  },
  body: '{"identifier":"me@myemail.com","type":"email", "chain":"ETH", ,"socialProvider":"emailOnly"}',
};

fetch(
  "https://app.dynamicauth.com/api/v0/environments/YOUR_ENVIRONMENT_ID/embeddedWallets",
  options
)
  .then((response) => response.json())
  .then((response) => console.log(response))
  .catch((err) => console.error(err));
```

<Tip>
  Check out the [createEmbeddedWallet
  API](/api-reference/endpoints/wallets/createEmbeddedWallet) to see all the
  available identifier types.
</Tip>

## Creating wallets on additional chains

For users with an existing embedded wallet on a single chain, you can create an embedded wallet on an additional chain using the [createEmbeddedWalletAccount](/react-sdk/hooks/useembeddedwallet#create-embedded-wallet-account) method exported from the `useEmbeddedWallet` hook.

## Custom Logic (manual)

### Creating wallets any time

If you do not want to create wallets for users automatically when they sign up, you can create wallets for users using custom logic, calling the `createEmbeddedWallet` method from the `useEmbeddedWallet` hook when you want to create a wallet for a user.

```javascript
import { useEmbeddedWallet } from "@dynamic-labs/sdk-react-core";

// component declaration and all other logic you might need

const { createEmbeddedWallet } = useEmbeddedWallet();

const onCreateWalletHandler = async () => {
  try {
    await createEmbeddedWallet();
  } catch (e) {
    console.error(e);
  }
};
```

### Creating additional wallets

For users with an existing embedded wallet, you can create additional embedded wallets using the [createEmbeddedWalletAccount](/react-sdk/hooks/useembeddedwallet#create-embedded-wallet-account) method exported from the `useEmbeddedWallet` hook.

Using the `createEmbeddedWalletAccount` method to create additional wallets on the same chain derives wallets from the user's existing secret recovery phrase. This means that the user can access all their wallets using the same exported recovery phrase for each chain. The private keys for each wallet are derived from the recovery phrase and are unique to each wallet.

<Warning>
  Embedded wallets created in Live environments cannot be deleted or removed. Ensure that you only create the desired quantity of wallets for each user.
</Warning>

The limitations for creating additional wallets are as follows:

*   Maximum of 5 wallets per chain
*   Maximum of 15 wallets per user
*   If Smart Wallets are enabled, each generated signer wallet will have its own AA enabled wallet

### Creating Embedded Wallet alongside Branded Wallet

Keep the "Create on Sign up" toggle in the [Embedded Wallet configuration page](https://app.dynamic.xyz/dashboard/embedded-wallets/dynamic) toggled on, and then use the `createEmbeddedWallet` method from the `useEmbeddedWallet` hook to create a wallet for a user who has signed up using a branded wallet.

You can tell if you need to create that wallet post signup by checking `userHasEmbeddedWallet` from the `useEmbeddedWallet` hook.

```javascript
import { useEmbeddedWallet } from "@dynamic-labs/sdk-react-core";

// component declaration and all other logic you might need

const { createEmbeddedWallet, userHasEmbeddedWallet } = useEmbeddedWallet();

const onCreateWalletHandler = async () => {
  if (!userHasEmbeddedWallet) {
    try {
      await createEmbeddedWallet();
    } catch (e) {
      console.error(e);
    }
  }
};
```

## Notes

### Content Security Policy (CSP)

Embedded wallets use iframes to provide one more security layer to the wallet. If you enforce CSP on your website, you will need to add the following to your `frame-src` directive:

*   [https://auth.turnkey.com](https://auth.turnkey.com) - used for establishing sessions with one-time codes
*   [https://recovery.turnkey.com](https://recovery.turnkey.com) - used for creating a new passkey for an existing wallet
*   [https://export.turnkey.com](https://export.turnkey.com) - used for exporting a wallet's private key or seed phrase

## What Next?

*   Learn how to use/interact with the wallet in [the Using Wallets section](/wallets/using-wallets)
*   Learn how to customize the transaction flow in the [Embedded Transaction UI guide](/wallets/embedded-wallets/embedded-transaction-ui)


# Creating Transactions



Since you can use embedded wallets like any other wallet, you can learn how to access and interact with the wallet in the [Using Wallets](/wallets/using-wallets) section.

The Using Wallets section also provides examples of wallet interactions for both [EVM](/wallets/using-wallets/evm) and [Solana](/wallets/using-wallets/solana), some of these examples are outlined below:

## EVM

*   [Sign a message](/wallets/using-wallets/evm/sign-a-message)
*   [Sign typed data](/wallets/using-wallets/evm/sign-typed-data)
*   [Send a transaction](/wallets/using-wallets/evm/send-balance)
*   [Sign a transaction with Wagmi](/wallets/using-wallets/evm/send-a-transaction-wagmi)
*   [Fetch wallet balance](/wallets/using-wallets/evm/get-wallet-balance)

## Solana

*   [Send legacy transaction](/wallets/using-wallets/solana/send-legacy-solana-transaction)
*   [Send versioned transaction](/wallets/using-wallets/solana/send-versioned-solana-transaction)


# Embedded Wallets Overview



## Overview

You can think of an embedded wallet like a powerful web-account. An embedded wallet is a programmable web3 crypto wallet that can be issued invisibly to customers on your website or app. Customers with an embedded wallet can immediately receive digital tokens and make on-chain interactions without needing to go through the complexities of understanding the intricacies of typical EOA wallets like Metamask or Phantom, or needing to download anything to start their journey.

Dynamic-powered embedded wallets can be used in a range of scenarios - from ways to ease onboarding on your websites to working as the base for building your own full stack wallet.

## Non Custodial

**Dynamic-powered embedded wallets are non-custodial, meaning they are always end-user owned and controlled. Only the end-user has ownership and access to their wallet private keys.**

Dynamic leverages a combination of internal and third party services including trusted execution environments (TEEs, specifically AWS Nitro Enclaves), secure key management, advanced policy engines and iframes to limit potential security threats and ensure end user self-custody. All wallet private keys are encrypted and isolated such that neither Dynamic nor the Developer have access to the end user’s wallet private keys. End-user decryption activities are all performed in trusted execution environments and only run upon end-user activity requests.

In addition, all end users of Dynamic-powered embedded wallets can always export their wallet private key to take their assets into a different wallet provider or alternative storage location. Dynamic is also working to add additional features to limit potential connections between the end-user and Dynamic or Developer services. As an example, these may include:

*   Fallback back up support if Dynamic or your site is ever down
*   Allowing users to disassociate their embedded wallet data with the service.

Dynamic is SOC2 Type 2 compliant and hires an independent third parties to regularly conduct audits of our code, processes and systems. Dynamic also runs evergreen bug bounty programs.

## Multi-chain

Dynamic offers embedded wallets on EVM compatible networks and Solana. If you enable both, they will both be created at once and whichever you have marked as "primary" will be shown as the primary address in their profile upon sign in.

<Tip title="Enabling Chains">
  To enable embedded wallets for EVM or Solana networks the respective chains
  must also be enabled. You can find more information about enabling chains and
  networks [here](/chains/enabling-chains).
</Tip>

## Seamless Signing

## Flexible Signing Flows

Dynamic-powered embedded wallets let you define different security profiles for your users. You can choose between optional and mandatory MFA when signing transactions and messages. If you do enable transaction MFA, you can choose between one-time codes or passkeys.

## Widgets or Headless SDK

You can build your embedded wallet experience using [our UI components](/react-sdk/components/components-introduction#ui-components)(default) or [go headless with our SDK](/headless).

You also have the choice to either keep our default transaction confirmation modals or use your own, depending on your application's needs.

<Frame>
  <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/transaction-confirmation-toggle.png" />
</Frame>

## Smart Wallets

You can turn these embedded wallets into smart contract wallets using our [smart wallet](/smart-wallets/add-smart-wallets) feature. By doing so, you can sponsor your end-users' fees, add complex approval logic, and much more.


# Customizing Transaction UI



By default, when prompted to sign a transaction with an embedded wallet, a user will see a screen similar to this one:

<Frame>
  <img className="h-80" src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/embedded-wallet-transaction-ui.png" />
</Frame>

## Disable Confirmation Screen

You can enable/disable this transaction confirmation screen with a single toggle in the [Embedded Wallet configuration page](https://app.dynamic.xyz/dashboard/embedded-wallets/dynamic). If you want to keep the confirmation UI but customize it, read on!

## Customize Confirmation Screen

### Modal title

By default this reads "Confirm transaction" as you can see in the image above, but you can customize it to say whatever you want. To do this, use [Translations](/design-customizations/customizing-copy-translations) and set the following:

```tsx
const locale = {
  en: {
    dyn_send_transaction: {
      confirmation: {
        title: "My New Title",
      },
    },
  }
};

<DynamicContextProvider settings={settings} locale={locale}>
  <MyApp />
</DynamicContextProvider>
```

### Logo/Icon

In the [general page under Settings](https://app.dynamic.xyz/dashboard/settings/general) of your Dynamic Developer Dashboard, you can set your app logo URL. This logo will be displayed in the transaction confirmation screen in place of where you currently see the Dynamic logo in the image above.

### App Name

In the same [general page under Settings](https://app.dynamic.xyz/dashboard/settings/general) as above, you can also set your app name. This name will be displayed in the transaction confirmation screen in place of where you currently see "Dynamic" in the image above.

### Origin URL

This parameter is set by Dynamic and cannot be customized for obvious security reasons. It is the URL of the website that is requesting the transaction.

## Bring your own UI

You can also build your own flows for transactions and simply use the SDK hooks to trigger the actions you need, check out [the headless section](/headless) for that!


# Wallet Reveal & Unlinking



This page details the Dynamic-powered embedded wallet reveal and export process, which enables end users to retrieve and backup their wallet credentials, as well as delete them from the Dynamic-powered embedded wallet.

## Revealing a Private Key/Seed Phrase

<Note>
  When implementing Dynamic in headless mode, please ensure you are surfacing this flow to your end users, so that they always maintain control of their wallet.
</Note>

Revealing a wallet private key/seed phrase allows end users to retrieve their wallet credentials for backup or migration to another wallet or service. This is critical to ensure a user maintains control of their wallet, in case a user loses access to the provider service, needs to recover their wallet, or wants to use a different wallet provider with their existing wallet.

## End User Experience

<Info>
  The Dynamic export flow reveals the private key/seed phrase in an secure isolated iframe to limit the ability of Dynamic, the developer, or anyone in the flow to view the exported key. For more details on the security of the iframe and cryptographic flows go to our FAQs.
</Info>

<Frame>
  <video width="100%" autoPlay muted loop playsInline src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/reveal.mp4" />
</Frame>

## Programmatically Triggering Wallet Export Flow

<Note>
  To ensure a user maintains control of their wallet, in case a user loses access to the provider service, we recommend using our hook to place the export flow immediately after creating the account.
</Note>

To open the export wallet flow on behalf of your users, you can call the `initExportProcess` method from the [`useEmbeddedReveal`](/react-sdk/hooks/useembeddedreveal) hook. This will open the flow described above. Only the end-user will be able to see the private key or seed phrase.

```jsx
import { useEmbeddedReveal } from "@dynamic-labs/sdk-react-core";


const { initExportProcess } = useEmbeddedReveal();


<button onClick={() => initExportProcess()}>Export Wallet</button>;
```

You can see the experience by going to [demo.dynamic.xyz](https://demo.dynamic.xyz) and creating an embedded wallet.

## Unlinking customer’s embedded wallet from your app

<Info>
  This feature is in development. If you’re interested in learning more, [reach out to us in slack](https://dynamic.xyz/slack).
</Info>

Once an end-user has revealed their private key/seed phrase, they can choose to unlink it from your site, maintaining full control over the interfaces on which their private key exists. To do this, Dynamic provides a checkbox option and confirmation screen to complete this flow.

If an end user chooses to complete this flow, their embedded wallet will no longer work within your application. They will be issued a new embedded wallet if they log in again.

## Risks and Considerations

<Warning>
  You should always provide your end users with a path to reveal and replicate their keys from their embedded wallet. When using the headless embedded wallet flow, please ensure you add a path for users to complete this step using the programmatic option described above.
</Warning>

End users should be aware that replicating their wallet credentials can expose their wallet to risk if the credentials are not stored securely. Users are advised to store their credentials in a secure location and not share them with anyone. When implementing Dynamic in headless mode, we recommend communicating these warnings to users.

## FAQs

Q. What is the security of the iFrame and how do you ensure only the end-user can access these credentials?
A. We leverage joint architecture with Turnkey to secure this flow, and you can dive into the cryptographic flows here: [https://docs.turnkey.com/features/export-wallets#cryptographic-details](https://docs.turnkey.com/features/export-wallets#cryptographic-details)

Q. What happens if user reveals and replicates their private key and loses it?
A. Users can return to use the application service. If they unlinked their account from the application service during export and then lost their private key, they would not be able to retrieve that wallet.


# Transactional MFA



Dynamic offers multiple options for security at the transaction level when using the Dynamic-powered embedded wallets. These options include no MFA (Multi-factor authentication), Passkey MFA, and One-Time Codes MFA. It's important to note that transactional MFA is distinct from authentication MFA, which is used when the user logs in. This means a user can be protected with MFA during login and also required to confirm actions when signing messages or transactions.

## No Transactional MFA

<Info>
  No transactional MFA is currently in closed beta. Please [reach out to us](https://www.dynamic.xyz/talk-to-us) for early access if you are interested in testing this feature. No transactional MFA provides less user protection and should be used carefully (for instance, we advise against using this feature along-side phone login, unless authentication MFA is enabled)
</Info>

In this mode, transactions for logged-in users are processed without any additional verification steps. While this offers a seamless user experience, it also increases the risk of an end-user accidentially approving a transaction. It is recommended to use this mode only in low-risk scenarios or when heightened security is not a primary concern (e.g. signing messages or trading free NFTs).

## One-Time Email Codes MFA

One-Time Email Codes MFA provides an additional layer of security by requiring the user to enter a one-time code when performing a transaction. This code is typically sent to the user’s registered email.

**How it Works:**
When a transaction is initiated, a one-time code is sent to the user’s registered email. The user must enter this code to proceed with the transaction. This ensures that even if an unauthorized party gains access to the user’s device, they would still need the one-time code to complete a transaction.

**Configuration:**

*   You can enable One-Time Codes MFA in the Wallet Transactions section under [the Embedded Wallets tab](https://app.dynamic.xyz/dashboard/embedded-wallets/dynamic)
*   You can adjust the length of time before a session with OTC expires by clicking the gear to the right of the feature section once you've toggled it on.

## Passkey MFA

<Info>
  Passkey MFA provides the most secure transactional MFA option. We strongly recommend this option for applications that handle money or trading.
</Info>

Passkey MFA enhances security by requiring the user to authenticate using a passkey when performing a transaction. This method leverages the user's device-based authentication mechanisms (e.g., biometrics, device PIN) to confirm their identity.

**How it Works:**
When a transaction is initiated, the user is prompted to authenticate using their device’s passkey mechanism. This could be a fingerprint scan, facial recognition, or entering a device PIN. Once authenticated, the transaction proceeds as normal.

**Configuration:**

*   You can enable Passkey MFA in the Wallet Transactions section under [the Embedded Wallets tab](https://app.dynamic.xyz/dashboard/embedded-wallets/dynamic)
*   You can control whether a Passkey is required at signup or not by clicking the gear to the right of the feature section once you've toggled it on.
*   You can control whether a user can add more than one Passkey in the same configuration section as mentioned above.

**Important Notes**

<AccordionGroup>
  <Accordion title="Domains">
    Passkeys are scoped to top level domains - This means that they will work on
    app.example.com, example.com, and market.example.com, but will require a new passkey if interacting on on
    app.example.xyz or notexample.com.  If a customer interacts with your application on a different domain, the user's account and wallet will appear as expected. Dynamic will detect that they are attempting to interact with a wallet on a new domain and prompt them to add a new passkey or use a one-time (if enabled) to complete the transaction.

    If you decide to migrate your domain, your
    end users will need to generate new passkeys. We can walk you through that
    process as you implement.
  </Accordion>

  <Accordion title="Browsers">
    Passkeys are supported on the following browsers:

    Android: Chrome, Brave

    MacOS v13+  Safari, Chrome, Brave

    iOs v16+:  Safari, Chrome, Brave

    Windows:  Chrome, Edge

    **Not Supported:**

    Chromebook: Not supported

    Opera Mini

    For customers that are on an unsupported browser, we recommend they update to the a recent version
  </Accordion>

  <Accordion title="1Password">
    (Requirement: 1password installed on both desktop and phone)

    Delayed syncing across mobile: Passkey may not sync to the mobile device when it is created first on desktop. User may have to adjust their 1pass password settings in IOS to allow 1password to auto-fill browsers.
    Not pairing: Even if the 1password is installed on both devices, iOS sometimes does not check 1password for passkeys. This prevents a user from accessing their passkey on their mobile device. To remediate, open password options under settings on your iOS device. Make sure that 1Password is selected to auto-fill for passwords and passkeys. If its already selected, deselect and re-enable in order to refresh it.
  </Accordion>

  <Accordion title="Multi-Chain">
    Passkeys are assigned at wallet creation, meaning the same passkey can be used for all embedded wallets created at the time of passkey creation. Adding additional embedded wallet chains will require users to use their passkey. Support for adding an embedded wallet on a new chain for existing embedded wallet users will be availalbe in the coming weeks.
  </Accordion>
</AccordionGroup>

## Transaction and Message Signing UI

Dynamic offers a customizable transaction and message signing UI, enhancing the user experience by providing clear and intuitive confirmation prompts. These prompts ensure users are aware of and can confirm actions before they are executed, adding an extra layer of transparency and security.

**Features of Transaction and Message Signing UI:**

*   Provides clear, user-friendly confirmation prompts for transactions and message signing.
*   Helps prevent accidental or unauthorized actions by requiring user confirmation.
*   Can be customized to match the look and feel of your application.

**Turning Off Signing Confirmation UI:**
You can disable the transaction and message signing UI via [an option in the Dynamic dashboard](https://app.dynamic.xyz/dashboard/embedded-wallets/dynamic). This can be useful in scenarios where speed is a priority, and the risk of unauthorized actions is minimal, or when you control your own UI to show a similar confirmation screen.


# Multi Asset UI



## Overview

From SDK version v2.2.0-alpha.0, the Dynamic Widget now supports multi-asset UI by default.

This means you can present your end users with the individual balances of each asset they hold in their wallet.

Balance and pricing information is refreshed every 20 min. If you need minute by minute refresh and different asset support, contact sales.

The list of tokens reflect the top 1000 tokens by DEX volume. We leverage Coingecko for pricing.

You can also choose to show the Fiat balance of those assets in whatever their primary currency is.

<Tip>
  Multi-asset is only supported on the following networks: Ethereum, Optimism, Polygon, Arbitrum, and Base.
</Tip>

## Configuration

Enabling multi-asset (turned on by default) is as simple as a toggle in the Dynamic Dashboard within [the Log in & User Profile page](https://app.dynamic.xyz/dashboard/log-in-user-profile), under the "Design Settings" section.

## UI Layout

The multi-asset UI is designed to be collapsible, so that it doesn't take up too much space on your page. It will display balances in either the native token or the fiat equivalent, depending on your configuration.

It will display in the user profile section of [the DynamicWidget](/react-sdk/components/dynamicwidget), or if you are implementing individual UI components, you can use [the UserProfile](/react-sdk/components/dynamicuserprofile) component.

<Tabs>
  <Tab title="With Fiat">
    <Frame>
      <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/multi-asset-collapsed-fiat.png" />
    </Frame>

    <Frame>
      <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/multi-asset-open-fiat.png" />
    </Frame>
  </Tab>

  <Tab title="Without Fiat">
    <Frame>
      <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/multi-asset-collapsed.png" />
    </Frame>

    <Frame>
      <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/multi-asset-open.png" />
    </Frame>
  </Tab>
</Tabs>

## Build it yourself

You can also build your own multi-asset UI using the hooks provided by the Dynamic SDK, you can use [the useTokenBalances hook](/react-sdk/hooks/usetokenbalances) to do so.


# Multi Wallet



## Managing multiple wallets

Your end users can of course connect one wallet during signup/login, but they can also connect multiple! Lots of users have multiple wallets but managing multiple wallets and multiple
accounts for each wallet is challenging. You have to manage event listeners, various connection states, wallets shared across accounts, and much much more. That's why we introduced [multi-wallet](https://www.dynamic.xyz/blog/multi-wallet-announcement). You can see it in action on our
[demo site](https://demo.dynamic.xyz).

Our multiwallet widget allows your users to easily:

1.  Add multiple wallets
2.  Add multiple accounts from the same wallet
3.  Transfer wallets between your Dynamic accounts
4.  It is mobile friendly
5.  Keep track of the active account in a wallet
6.  Swap primary wallets
7.  and so much more.

To enable multiwallet, simply visit [the Log in & User Profile section of the dashboard](https://app.dynamic.xyz/dashboard/log-in-user-profile) and under "Branded Wallets", toggle MultiWallet on!


# Send Assets



## Summary

Dynamic provides a simple and easy-to-use interface for users to send tokens to any address on EVM and Solana (available on the latest V3 of the SDK).

This allows your users to easily move funds from their wallets without leaving the dapp, while removing the burden of creating send flows and gas calculations across all EVM networks and Solana for you as a developer. Note that for Solana, we also include a priority fee to improve success rates in case of congestion on the network.

You have two options to surface this UI to your end users: the first is within the Dynamic Widget, and the second is programmatically opening the UI from your own site. We cover both options here.

## Within Dynamic Widget/Embedded Widget

### Add the widget

Simply add the [Dynamic Widget](/react-sdk/components/dynamicwidget) or [Dynamic Embedded Widget](/react-sdk/components/dynamicembeddedwidget) to your dapp and the Send Token UI will be available to your users.

```jsx
import { DynamicWidget, DynamicEmbeddedWidget } from "@dynamic-labs/sdk-react-core";

// COMPONENT INSIDE TREE WHERE DYNAMIC CONTEXT IS AVAILABLE
const Main => {
    return (
        <div>
            // ONLY USE ONE OR THE OTHER
            <DynamicWidget />
            <DynamicEmbeddedWidget />
        </div>
    )
}
```

### End user experience

Once logged in, on the Wallets screen of the widget, you will notice a "Send" button. Clicking on this button will take you to the Send Token UI.

<Frame>
  <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/widget-wallets-screen.png" />
</Frame>

You can then choose from any available token in your wallet, add an amount and recipient address, then preview the transaction before confirming.

<Frame>
  <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/send-token-list.png" />
</Frame>

<Frame>
  <img src="https://mintlify.s3-us-west-1.amazonaws.com/dynamic-docs/images/preview-transaction.png" />
</Frame>

## Standalone UI (useSendBalance)

You can use our useSendBalance hook to trigger the Send Balance UI and optionally, prepopulate the form for the user.

### How it works

The useSendBalance hook depends on the DynamicContextProvider, so it has to be used as a child of the Dynamic context. When you use the hook, you will get a function named `open`. That method accepts the follow options:

| Parameter        | Type             | Description                    |
| :--------------- | :--------------- | :----------------------------- |
| recipientAddress | String           | The initial recipient address  |
| value            | ethers.BigNumber | The initial transaction amount |

The `open` function will return a promise; If successful, the promise will be resolved with the transaction; if not successful, the promise will be rejected accompanied by the error collected from the provider.

Here is an example of a custom Send button

```typescript
import { useSendBalance } from "@dynamic-labs/sdk-react-core";

const MySendButton = () => {
  const { open } = useSendBalance();

  return <button onClick={() => open()}>Send</button>;
};
```

From this example, when the user clicks the button, they will be prompted to fill the amount and recipient fields, review the transaction, and they will see a confirmation at the end of the flow.

For the second example, we will pre-populate the recipient and amount fields for the user.

```typescript
import { useSendBalance } from "@dynamic-labs/sdk-react-core";
import { ethers } from "ethers";

const MySendButton = () => {
  const { open } = useSendBalance();

  const onCickSend = async () => {
    try {
      const tx = await open({
        recipientAddress: "<address>",
        value: ethers.utils.parseEther("1"),
      });
    } catch (err) {
      // Handle the promise rejection
    }
  };

  return <button onClick={onCickSend}>Send</button>;
};
```

Here, when the user clicks the button, they will be prompted with the same UI, but now it will be pre-populated with the recipient address and amount.


# Accessing Wallets

Learn about primaryWallet, useUserWallets, onEmbeddedWalletCreated, and handleConnectedWallet

## Introduction

It's not neccessary for a wallet to be associated with a user when they sign up, but the likelyhood is that if a user is interacting with Web3, one will be connected at some point, whether embedded or external (branded).

### Primary Wallet

Normally, if one or more wallets are connected, a `primaryWallet` object will be available on the Dynanmic Context which you can access with [the `useDynamicContext` hook](/react-sdk/hooks/usedynamiccontext). This is a quick and easy way to access the main wallet associated with that user.

```jsx
const { primaryWallet } = useDynamicContext();
```

### useUserWallets

Going beyond the primary wallet, you might want to get every wallet associated with the given user. The best option for this is to use [the `useUserWallets` hook](/react-sdk/hooks/useuserwallets).

```jsx
import { FC } from 'react'
import { useUserWallets } from '@dynamic-labs/sdk-react-core'

export const ListConnectedWallets: FC = () => {
  const userWallets = useUserWallets()

  return (
    <div>
      <h1>Wallets</h1>

      {userWallets.map((wallet) => (
        <p key={wallet.id}>
          {wallet.address}
        </p>
      ))}
    </div>
  )
}
```

### onEmbeddedWalletCreated

If you're using embedded wallets, you can listen for the `onEmbeddedWalletCreated` event to know when a wallet has been created for a user. This is useful for when you want to know when a user has a wallet created for them, but you don't want to have to poll the API to check.

```tsx
<DynamicContextProvider
  settings={{
    events: {
      onEmbeddedWalletCreated: (args) => {
        console.log('onEmbeddedWalletCreated was called', args);
      }
    }
  }}
>
 {/* ... rest of your app ... */}
</DynamicContextProvider>
```

## Add extra logic during any wallet connection (handleConnectedWallet)

Sometimes you may want to inject some of your own logic during the process of a wallet becoming connected. Dynamic has a concept of ["handlers"](/react-sdk/overview#handlers), which is a particular kind of callback to allow custom code to run as part of a process (i.e. signup).

`handleConnectedWallet` is a handler that runs before we establish the wallet connection. You can use this callback to run your logic and reject (by returning boolean false). For example, running a fraud check before establishing the connection.

<Tip>The args returned by handleConnectedWallet is a [Wallet](/wallets/using-wallets/accessing-wallets#wallet-interface) but without the `connected` boolean.</Tip>

```jsx
<DynamicContextProvider
  settings={{
    handlers: {
      handleConnectedWallet: (args) => {
        console.log("handleConnectedWallet was called", args);
        // if runYourOwnLogic return true, the connection will be established, otherwise it will not
        return runYourOwnLogic();
      },
    },
  }}
>
  {/* ... rest of your app ... */}
</DynamicContextProvider>
```

## What next?

<Card title="What next?" href="/wallets/using-wallets/interacting-with-wallets" icon="link" color="#4779FE">
  Click here to learn how to interact with wallets.
</Card>


# Bitcoin Wallets



We covered what a wallet in general looks like in the [Accessing Wallets](/wallets/using-wallets/accessing-wallets) section and then what a wallet connector looks like in the [Interacting With Wallets section](/wallets/using-wallets/interacting-with-wallets). However, the wallet connector for Bitcoin wallets is a bit different from the EVM wallets, so we will cover that seperately here.

This is because Bitcoin wallets have different capabilities and requirements. For example, Bitcoin wallets can have multiple addresses associated with them, like payment and ordinal addresses.

## Checking if a wallet is a Bitcoin wallet

You can use the `isBitcoinWallet` helper method to check if a wallet is a Bitcoin wallet. That way, TypeScript will know which methods are available to you.

```ts
import { isBitcoinWallet } from '@dynamic-labs/bitcoin';

if (!isBitcoinWallet(wallet)) {
  throw new Error('This wallet is not a Bitcoin wallet');
}

const result = wallet.sendBitcoin({
  amount: 100000000n,
  recipientAddress: 'SOME-ADDRESS',
});

```

## Bitcoin Wallet

| Method                                                                                                        | Description                                                                                                    |
| ------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| sendRawTransaction(rawTransactionHex: string): Promise\<string \| undefined>                                  | This method submits a raw transaction to the bitcoin blockchain and returns the transaction ID as the response |
| sendBitcoin(transaction: BitcoinTransaction): Promise\<string \| undefined>                                   | A method to send an amount of satoshis to a recipient bitcoin address                                          |
| signMessageWithAddress(messageToSign: string, addressType: WalletAddressType): Promise\<string\ \| undefined> | Signs a message with a specific bitcoin address type (ordinals or payment)                                     |
| signPsbt(request: BitcoinSignPsbtRequest): Promise\<BitcoinSignPsbtResponse \| undefined>                     | Signs a PSBT and returns an object with the signed PSBT                                                        |
| signPsbts(request: BitcoinSignPsbtRequest\[]): Promise\<string\[] \| undefined>                               | Signs a list of PSBTs and returns an array of signed PSBTs in base64                                           |

## Type Definitions

```ts
type BitcoinSignPsbtRequest = {
  allowedSighash: number[];
  unsignedPsbtBase64: string;
  signature?: {
    address: string;
    signingIndexes: number[] | undefined;
    disableAddressValidation?: boolean; // helpful for multi-sig
  }[];
};

type BitcoinTransaction = {
  amount: bigint;
  recipientAddress: string;
}

type WalletAddressType = 'payment' | 'ordinal';

type WalletAdditionalAddress {
  address: string;
  publicKey?: string;
  type: WalletAddressType;
}

type BitcoinSignPsbtResponse = {
  signedPsbt: Psbt; // see reference below for what a PSBT is
};
```

## What is a satoshi?

This smallest unit allows for transactions involving very small amounts of bitcoin,
facilitating microtransactions and improving the granularity of payments in the Bitcoin network.

```
1 Bitcoin = 100,000,000 Satoshis
```

## What is a PSBT?

A partially signed bitcoin transaction (PSBT) is a standard for transactions that have not fully signed.
This allows different participants with different keys/signers to sign a transaction without revealing their private keys to others.
Multi-sig wallets utilize these. This allows for a multi-step transaction process which is both safer and more efficient.

## Examples

We’ve included a few examples of how to use the Bitcoin wallet connector in this section:

*   [Send a transaction](/wallets/using-wallets/bitcoin/send-a-transaction)
*   [Send a raw transaction](/wallets/using-wallets/bitcoin/send-raw-transaction)
*   [Sign a message](/wallets/using-wallets/bitcoin/sign-a-message)


# Send Bitcoin



In this example, we are going to send bitcoin using the wallet connector.

```JavaScript
import { useDynamicContext } from '@dynamic-labs/sdk-react-core';
import { isBitcoinWallet } from '@dynamic-labs/bitcoin';

const SendBitcoinButton = () => {
  const { primaryWallet } = useDynamicContext();

  const sendBitcoin = async () => {
    if (!primaryWallet || !isBitcoinWallet(primaryWallet)) return;

    // The first argument is the address you are sending to, the second argument is the amount of BTC in satoshis
    const transactionId = await primaryWallet.sendBitcoin('<bitcoin payment address>', 1);

    console.log('transactionId', transactionId);
  };

  return <button onClick={sendBitcoin}>Send Bitcoin</button>;
};
```


# Send a raw transaction



In this example, we are going to send a raw transaction as a hex value to the bitcoin network.

For information on how to construct the transactionHexString, please refer to [this example](https://medium.com/@claudio_69833/how-to-create-bitcoin-transactions-with-javascript-b3b43f53ca0c).

```JavaScript
import { useDynamicContext } from '@dynamic-labs/sdk-react-core';
import { isBitcoinWallet } from '@dynamic-labs/bitcoin';

const SendRawTransaction = () => {
  const { primaryWallet } = useDynamicContext();

  const signMessage = async () => {
    if (!primaryWallet || !isBitcoinWallet(primaryWallet)) return;

    const transactionId = await primaryWallet.sendRawTransaction('transactionHexString');

    console.log('transactionId', transactionId);
  };

  return <button onClick={SendRawTransaction}>Send Raw Transaction</button>;
};
```


# Sign a message



In this example, we are going to sign a message for Bitcoin.

```JavaScript
import { useDynamicContext } from '@dynamic-labs/sdk-react-core';
import { isBitcoinWallet } from '@dynamic-labs/bitcoin';

const SignMessageButton = () => {
  const { primaryWallet } = useDynamicContext();

  const signMessage = async () => {
    if (!primaryWallet || !isBitcoinWallet(primaryWallet)) return;

    const signature = await primaryWallet.signMessage('example');

    console.log('signature', signature);
  };

  return <button onClick={signMessage}>Sign message</button>;
};
```

You can also sign a message with a specific address type (payment or ordinal) using the `signMessageWithAddress` method.

```JavaScript
import { useDynamicContext } from '@dynamic-labs/sdk-react-core';
import { isBitcoinWallet } from '@dynamic-labs/bitcoin';

const SignMessageButton = () => {
  const { primaryWallet } = useDynamicContext();

  const signMessageWithOrdinalsAddress = async () => {
    if (!primaryWallet || !isBitcoinWallet(primaryWallet)) return;

    const signature = await primaryWallet.signMessageWithAddress('example', 'ordinals');

    console.log('signature', signature);
  };

  const signMessageWithPaymentAddress = async () => {
    if (!primaryWallet || !isBitcoinWallet(primaryWallet)) return;

    const signature = await primaryWallet.signMessageWithAddress('example', 'payment');

    console.log('signature', signature);
  };

  return <>
    <button onClick={signMessageWithOrdinalsAddress}>Sign message with ordinals address</button>
    <button onClick={signMessageWithPaymentAddress}>Sign message with payment address</button>
  </>;
};
```


# Sign a Partially Signed Bitcoin Transaction (PSBT)



# Guide to Creating a Partially Signed Bitcoin Transaction (PSBT) for Developers

This guide will walk you through the steps to create and sign a Partially Signed Bitcoin Transaction (PSBT) using a combination of JavaScript libraries and React components. The provided code example is comprehensive, but we'll break it down step-by-step to ensure you understand each part.

## Table of Contents

1.  [Understanding Partially Signed Bitcoin Transactions (PSBT)](#1-understanding-partially-signed-bitcoin-transactions-psbt)
2.  [Step-by-Step Guide](#2-step-by-step-guide)
    *   [Step 1: Import Necessary Libraries and Functions](#step-1-import-necessary-libraries-and-functions)
    *   [Step 2: Initialize the ECC Library](#step-2-initialize-the-ecc-library)
    *   [Step 3: Define Constants and Helper Functions](#step-3-define-constants-and-helper-functions)
    *   [Step 4: Generate the PSBT](#step-4-generate-the-psbt)
    *   [Step 5: Create the SignMessageViaTransaction Component](#step-5-create-the-signmessageviatransaction-component)
    *   [Step 6: Create the Main Component](#step-6-create-the-main-component)
3.  [Full Code Example](#3-full-code-example)

***

## 1. Understanding Partially Signed Bitcoin Transactions (PSBT)

A Partially Signed Bitcoin Transaction (PSBT) is a standard format for handling Bitcoin transactions that require multiple signatures or other complex signing workflows. PSBTs enable the separation of transaction creation and signing, allowing for more flexible and secure transaction management.

### Key Features of PSBT

*   **Interoperability**: PSBTs can be used across different wallets and software.
*   **Flexibility**: Allows for multiple parties to contribute signatures to a single transaction.
*   **Security**: Enhances security by enabling hardware wallets and other secure devices to sign transactions without exposing private keys.

***

## 2. Step-by-Step Guide to Creating a PSBT

This section provides a detailed step-by-step guide to creating a PSBT using the `bitcoinjs-lib` library in JavaScript. We'll break down each part of the provided code example and explain what it does.

### Step 1: Import Necessary Libraries and Functions

First, we import the necessary libraries and helper functions for working with Bitcoin transactions, data conversions, hashing, elliptic curve cryptography, and React components.

*   **bitcoinjs-lib**: A popular JavaScript library for working with Bitcoin transactions.
*   **@stacks/common**: Provides utility functions for handling data conversions.
*   **@noble/hashes/sha256**: Implements the SHA-256 hashing algorithm.
*   **@bitcoinerlab/secp256k1**: Provides elliptic curve cryptography functions for Bitcoin.
*   **@dynamic-labs/sdk-react-core**: Provides context for accessing wallet information.
*   **@dynamic-labs/wallet-connector-core**: Checks if the wallet connector is a Bitcoin connector.
*   **useState**: A React hook for managing state in a functional component.

```javascript
import { DynamicWidget, useDynamicContext } from "@dynamic-labs/sdk-react-core";
import { isBitcoinWallet } from '@dynamic-labs/bitcoin';

import * as bitcoin from 'bitcoinjs-lib';
import { hexToBytes, utf8ToBytes } from '@stacks/common';
import { sha256 } from '@noble/hashes/sha256';
import ecc from '@bitcoinerlab/secp256k1';
import { useState } from "react";
```

### Step 2: Initialize the ECC Library

Before we can work with elliptic curve cryptography (ECC) functions in Bitcoin transactions, we need to initialize the ECC library. This step is crucial for enabling cryptographic operations such as signing and verifying transactions.

**bitcoin.initEccLib(ecc);**

*   **Purpose**: The `bitcoinjs-lib` library relies on an ECC library to perform cryptographic operations. By default, `bitcoinjs-lib` does not include an ECC library to keep the core library lightweight and modular. We need to explicitly provide an ECC implementation.
*   **Library Used**: In this example, we use the `@bitcoinerlab/secp256k1` library, which is an implementation of the secp256k1 elliptic curve, commonly used in Bitcoin.
*   **Initialization**: We pass the `ecc` object from the `@bitcoinerlab/secp256k1` library to the `bitcoin.initEccLib` function to initialize the ECC library. This allows `bitcoinjs-lib` to use the ECC functions provided by the `@bitcoinerlab/secp256k1` library.

```javascript
bitcoin.initEccLib(ecc);
```

### Step 3: Define Constants and Helper Functions

Next, we define some constants and helper functions that will be used throughout the process. These include a message tag for BIP-322, a hashed version of this tag, and values for a dummy transaction input.

#### BIP-322 Message Tag and Hash

**BIP-322** is a Bitcoin Improvement Proposal that defines a standard way to sign and verify arbitrary messages using Bitcoin keys. To ensure the message being signed is unique and recognizable as a BIP-322 message, a specific tag (`bip322MessageTag`) is used. This tag is hashed twice using SHA-256 to create a unique identifier (`messageTagHash`).

```javascript
const bip322MessageTag = 'BIP0322-signed-message';

const messageTagHash = Uint8Array.from([
  ...sha256(utf8ToBytes(bip322MessageTag)),
  ...sha256(utf8ToBytes(bip322MessageTag)),
]);
```

#### Dummy Transaction Input Values

The `bip322TransactionToSignValues` object contains placeholder values for a previous transaction output. These values are used to construct a virtual transaction that is not actually spending real Bitcoins but is required to prepare the PSBT.

*   **prevoutHash**: A 32-byte array of zeros, representing the hash of a non-existent previous transaction.
*   **prevoutIndex**: Set to `0xffffffff`, a special value indicating that this is not a real transaction output.
*   **sequence**: Set to `0`, indicating the sequence number of the transaction input.

```javascript
export const bip322TransactionToSignValues = {
  prevoutHash: hexToBytes('0000000000000000000000000000000000000000000000000000000000000000'),
  prevoutIndex: 0xffffffff,
  sequence: 0,
};
```

#### Hashing the BIP-322 Message

The `hashBip322Message` function takes a message (as a `Uint8Array` or a string) and hashes it using the SHA-256 algorithm, along with the `messageTagHash`. This ensures that the message is uniquely identified as a BIP-322 message.

```javascript
export function hashBip322Message(message) {
  return sha256(
    Uint8Array.from([...messageTagHash, ...(isString(message) ? utf8ToBytes(message) : message)])
  );
}

function isString(value) {
  return typeof value === 'string';
}
```

### Step 4: Generate the PSBT

The `generatePsbt` function creates a Partially Signed Bitcoin Transaction (PSBT) using the provided address and message.

#### Step-by-Step Explanation:

1.  **Generate Script**: Convert the Bitcoin address into a scriptPubKey. This script is used to determine the conditions under which the Bitcoin can be spent.
2.  **Hash the Message**: Hash the message using the BIP-322 message hash function. This ensures the message is uniquely identified and securely hashed.
3.  **Create ScriptSig**: Create the scriptSig with the hashed message. The scriptSig is part of the transaction input and contains the conditions required to spend the UTXO.
4.  **Virtual Transaction**: Create a virtual transaction to spend, setting the necessary inputs and outputs. This virtual transaction simulates spending a UTXO without actually spending real Bitcoins.
5.  **Create PSBT**: Create a PSBT object and add the necessary inputs and outputs. This PSBT will be signed by the wallet.

```javascript
const generatePsbt = (address, message) => {
  const { prevoutHash, prevoutIndex, sequence } = bip322TransactionToSignValues;

  // Generate the script for the given address
  const script = bitcoin.address.toOutputScript(
    address,
    bitcoin.networks.bitcoin
  );

  // Hash the message
  const hash = hashBip322Message(message);

  // Create the scriptSig with the hashed message
  const commands = [0, Buffer.from(hash)];
  const scriptSig = bitcoin.script.compile(commands);

  // Create a virtual transaction to spend
  const virtualToSpend = new bitcoin.Transaction();
  virtualToSpend.version = 0;
  virtualToSpend.addInput(Buffer.from(prevoutHash), prevoutIndex, sequence, scriptSig);
  virtualToSpend.addOutput(script, 0);

  // Create the PSBT
  const virtualToSign = new bitcoin.Psbt({ network: bitcoin.networks.bitcoin });
  virtualToSign.setVersion(0);
  const prevTxHash = virtualToSpend.getHash();
  const prevOutIndex = 0;
  const toSignScriptSig = bitcoin.script.compile([bitcoin.script.OPS.OP_RETURN]);
  try {
    virtualToSign.addInput({
      hash: prevTxHash,
      index: prevOutIndex,
      sequence: 0,
      witnessUtxo: { script, value: 0 },
    });
  } catch (e) {
    console.log(e);
    throw e;
  }

 

 virtualToSign.addOutput({ script: toSignScriptSig, value: 0 });
  return virtualToSign.toBase64();
}
```

### Step 5: Create the SignMessageViaTransaction Component

The `SignMessageViaTransaction` component handles the interaction with the wallet to sign the PSBT.

#### Step-by-Step Explanation:

1.  **useDynamicContext**: Hook to access the primary wallet from the context. This provides information about the connected wallet.
2.  **useState**: React hook to manage the message state. This allows us to keep track of the message that we want to sign.
3.  **Check Wallet Connector**: Ensure the wallet connector supports Bitcoin. If the wallet does not support Bitcoin, we return `null` and do not render the component.
4.  **signMessageViaTransaction**: Function to generate the PSBT, request the wallet to sign it, and handle the signed PSBT. This function includes:
    *   **Get Address**: Get the Bitcoin address from the wallet.
    *   **Generate PSBT**: Call the `generatePsbt` function to create the PSBT.
    *   **Sign PSBT**: Request the wallet to sign the PSBT using the provided parameters.
    *   **Handle Signed PSBT**: Log the signed PSBT or handle any errors that occur during signing.

```javascript
const SignMessageViaTransaction = () => {
  const { primaryWallet } = useDynamicContext();
  const [message, setMessage] = useState('Hello World');

  if (!isBitcoinWallet(primaryWallet)) return null;

  const signMessageViaTransaction = async () => {
    // Get the Bitcoin address from the wallet
    const address = await primaryWallet.getAddress();
    
    // Generate the PSBT
    const psbt = generatePsbt(address, message);
    
    // Define the parameters for signing the PSBT
    const params = {
      allowedSighash: [1], // Only allow SIGHASH_ALL
      unsignedPsbtBase64: psbt, // The unsigned PSBT in Base64 format
      signature: [{
        address, // The address that is signing
        signingIndexes: [0] // The index of the input being signed
      }]
    };

    try {
      // Request the wallet to sign the PSBT
      const signedPsbt = await primaryWallet.signPsbt(params);
      console.log(signedPsbt); // Log the signed PSBT
    } catch (e) {
      console.log(e); // Handle any errors that occur during signing
    }
  }

  return (
    <div>
      <button onClick={signMessageViaTransaction}>Sign Transaction Via Message</button>
    </div>
  )
}
```

### Step 6: Create the Main Component

The `Main` component renders the `DynamicWidget` for wallet connection and the `SignMessageViaTransaction` component.

#### Step-by-Step Explanation:

1.  **DynamicWidget**: This component handles the wallet connection UI, allowing users to connect their wallets.
2.  **SignMessageViaTransaction**: This component handles the signing of the PSBT via the connected wallet.

```javascript
const Main = () => {
  return (
    <div className="min-h-screen bg-gradient-to-b from-gray-900 to-black flex flex-col items-center justify-center text-white">
      <DynamicWidget />
      <SignMessageViaTransaction />
    </div>
  );
}

export default Main;
```

***

## Full Code Example

Here is the full code example that incorporates all the steps explained above:

```javascript
import { DynamicWidget, useDynamicContext } from "@dynamic-labs/sdk-react-core";
import { isBitcoinWallet } from '@dynamic-labs/bitcoin';

import * as bitcoin from 'bitcoinjs-lib';
import { hexToBytes, utf8ToBytes } from '@stacks/common';
import { sha256 } from '@noble/hashes/sha256';
import ecc from '@bitcoinerlab/secp256k1';
import { useState } from "react";

bitcoin.initEccLib(ecc);

function isString(value) {
  return typeof value === 'string';
}

const bip322MessageTag = 'BIP0322-signed-message';

const messageTagHash = Uint8Array.from([
  ...sha256(utf8ToBytes(bip322MessageTag)),
  ...sha256(utf8ToBytes(bip322MessageTag)),
]);

export const bip322TransactionToSignValues = {
  prevoutHash: hexToBytes('0000000000000000000000000000000000000000000000000000000000000000'),
  prevoutIndex: 0xffffffff,
  sequence: 0,
};

export function hashBip322Message(message) {
  return sha256(
    Uint8Array.from([...messageTagHash, ...(isString(message) ? utf8ToBytes(message) : message)])
  );
}

const generatePsbt = (address, message) => {
  const { prevoutHash, prevoutIndex, sequence } = bip322TransactionToSignValues;

  // Generate the script for the given address
  const script = bitcoin.address.toOutputScript(
    address,
    bitcoin.networks.bitcoin
  );

  // Hash the message
  const hash = hashBip322Message(message);

  // Create the scriptSig with the hashed message
  const commands = [0, Buffer.from(hash)];
  const scriptSig = bitcoin.script.compile(commands);

  // Create a virtual transaction to spend
  const virtualToSpend = new bitcoin.Transaction();
  virtualToSpend.version = 0;
  virtualToSpend.addInput(Buffer.from(prevoutHash), prevoutIndex, sequence, scriptSig);
  virtualToSpend.addOutput(script, 0);

  // Create the PSBT
  const virtualToSign = new bitcoin.Psbt({ network: bitcoin.networks.bitcoin });
  virtualToSign.setVersion(0);
  const prevTxHash = virtualToSpend.getHash();
  const prevOutIndex = 0;
  const toSignScriptSig = bitcoin.script.compile([bitcoin.script.OPS.OP_RETURN]);

  try {
    virtualToSign.addInput({
      hash: prevTxHash,
      index: prevOutIndex,
      sequence: 0,
      witnessUtxo: { script, value: 0 },
    });
  } catch (e) {
    console.log(e);
    throw e;
  }

  virtualToSign.addOutput({ script: toSignScriptSig, value: 0 });
  return virtualToSign.toBase64();
}
  
const SignMessageViaTransaction = () => {
  const { primaryWallet } = useDynamicContext();
  const [message, setMessage] = useState('Hello World');

  if (!isBitcoinWallet(primaryWallet)) {
    return null;
  }

  const signMessageViaTransaction = async () => {
    // Get the Bitcoin address from the wallet
    const address = await primaryWallet.address;
    
    // Generate the PSBT
    const psbt = generatePsbt(address, message);
    
    // Define the parameters for signing the PSBT
    const params = {
      allowedSighash: [1], // Only allow SIGHASH_ALL
      unsignedPsbtBase64: psbt, // The unsigned PSBT in Base64 format
      signature: [{
        address, // The address that is signing
        signingIndexes: [0] // The index of the input being signed
      }]
    };

    try {
      // Request the wallet to sign the PSBT
      const signedPsbt = await primaryWallet.signPsbt(params);
      console.log(signedPsbt); // Log the signed PSBT
    } catch (e) {
      console.log(e); // Handle any errors that occur during signing
    }
  }

  return (
    <div>
      <button onClick={signMessageViaTransaction}>Sign Transaction Via Message</button>
    </div>
  )
}

const Main = () => {
  return (
    <div className="min-h-screen bg-gradient-to-b from-gray-900 to-black flex flex-col items-center justify-center text-white">
      <DynamicWidget />
      <SignMessageViaTransaction />
    </div>
  );
}

export default Main;
```


# EVM Wallets



There are methods in the Ethereum wallet specific to EVM which we list below, but in general all the methods you need are present on the generic Wallet [described here](/wallets/using-wallets/interacting-with-wallets).

## Check if a wallet is an Ethereum wallet

You can use the `isEthereumWallet` helper method to check if a wallet is a Ethereum wallet. That way, TypeScript will know which methods are available to you.

```ts
import { isEthereumWallet } from '@dynamic-labs/ethereum';

if (!isEthereumWallet(wallet)) {
  throw new Error('This wallet is not a Ethereum wallet');
}

const client = await primaryWallet.getWalletClient();

```

## Ethereum Wallet Methods

| Method                                                                                | Description                  |
| ------------------------------------------------------------------------------------- | ---------------------------- |
| getPublicClient(): Promise\<PublicClient\<Transport, Chain>>                          | Retrieves the public client. |
| getWalletClient(chainId?: string): Promise\<WalletClient\<Transport, Chain, Account>> | Retrieves the wallet client. |

### Read only actions

If you want to read data from the blockchain, you will want either a ["Public Client"](https://viem.sh/docs/clients/public.html) (Viem terminology), or a ["Provider"](https://docs.ethers.org/v5/getting-started/#getting-started--glossary) (Ethers terminology). Both allow you read only access to the blockchain.

<Tabs>
  <Tab title="Viem">
    ```jsx
    import { useDynamicContext } from '@dynamic-labs/sdk-react-core';

    const { primaryWallet } = useDynamicContext();

    const getEnsName = async () => {
      const publicClient = await primaryWallet?.getPublicClient()

      // Now you can use the public client to read data from the blockchain
      const ens = await publicClient?.getEnsName({ address: primaryWallet.address })
      return ens
    }
    ```
  </Tab>

  <Tab title="Ethers">
    ```jsx
    import { getWeb3Provider } from '@dynamic-labs/ethers-v6';
    import { useDynamicContext } from '@dynamic-labs/sdk-react-core';

    const { primaryWallet } = useDynamicContext();

    const getBalance = async () => {
      const provider = await getWeb3Provider(primaryWallet);

      // Now you can use the provider to read data from the blockchain
      const balance = await provider?.getBalance(primaryWallet.address);
      return balance
    }
    ```
  </Tab>
</Tabs>

### Write actions

If you want to write data to the blockchain, you will need a ["Wallet Client"](https://viem.sh/docs/clients/wallet.html) (Viem terminology), or a ["Signer"](https://docs.ethers.io/v5/api/signer/) (Ethers terminology). Both allow you to sign transactions with the private key.

<Tabs>
  <Tab title="Viem">
    ```jsx
    import { useDynamicContext } from '@dynamic-labs/sdk-react-core';
    import { isEthereumWallet } from '@dynamic-labs/ethereum';
    const { primaryWallet } = useDynamicContext();

    const sendTransaction = async () => {
      if(!primaryWallet || !isEthereumWallet(primaryWallet)) {
        return;
      }

      const walletClient = await primaryWallet.getWalletClient();

      // Now you can use the wallet client to write data to the blockchain
      const tx = await walletClient?.sendTransaction({
        to: '0x1234567890abcdef',
        value: '1000000000000000000'
      });
      return tx
    }
    ```
  </Tab>

  <Tab title="Ethers">
    ```jsx
    import { getSigner } from '@dynamic-labs/ethers-v6';
    import { useDynamicContext } from '@dynamic-labs/sdk-react-core';

    const { primaryWallet } = useDynamicContext();

    const sendTransaction = async () => {
      const signer = await getSigner(primaryWallet);

      // Now you can use the signer to write data to the blockchain
      const tx = await signer?.sendTransaction({
        to: '0x1234567890abcdef',
        value: '1000000000000000000'
      });
      return tx
    }
    ```
  </Tab>
</Tabs>

## Examples

We've included a few examples of how to use the EVM wallet connector in this section:

*   [Get balance for all connected wallets](/wallets/using-wallets/evm/get-balance-for-all-wallets)
*   [Get balance for a single wallet](/wallets/using-wallets/evm/get-wallet-balance)
*   [Send a transaction](/wallets/using-wallets/evm/send-a-transaction)
*   [Send a transaction with Wagmi](/wallets/using-wallets/evm/send-a-transaction-wagmi)
*   [Send balance using embedded wallet](/wallets/using-wallets/evm/send-balance)
*   [Sign a message](/wallets/using-wallets/evm/sign-a-message)


# Get balance for all wallets

In this example, we will get the balance for each connected wallet.

For each wallet, we will get the provider with [useRpcProviders](/react-sdk/hooks/userpcproviders), to fetch the
balance by calling the `getBalance` with the wallet address.

```JavaScript
import { useUserWallets } from '@dynamic-labs/sdk-react-core';
import { useRpcProviders } from '@dynamic-labs/sdk-react-core'
import { evmProvidersSelector } from '@dynamic-labs/ethereum-core'


const App = () => {
  const userWallets = useUserWallets();
  const { defaultProvider } = useRpcProviders(evmProvidersSelector)

  useEffect(() => {
    userWallets.forEach(async (wallet) => {
      if (!wallet) return;

      // Get the EVM Mainnet provider
     const provider = defaultProvider?.provider;

      if (!provider) return;

      // Fetch the wallet balance
      const balance = await provider.getBalance({ address: wallet.address });

      console.log('balance', balance.toString());
    });
  }, [userWallets, defaultProvider]);

  ...
}

```


# Send a transaction on Ethereum/EVM



```tsx
import { FC, FormEventHandler, useState } from "react";

import { parseEther } from "viem";

import { useDynamicContext } from "@dynamic-labs/sdk-react-core";
import { isEthereumWallet } from "@dynamic-labs/ethereum";

export const SendTransactionSection: FC = () => {
  const { primaryWallet } = useDynamicContext();

  const [txnHash, setTxnHash] = useState("");

  if (!primaryWallet || !isEthereumWallet(primaryWallet)) return null;

  const onSubmit: FormEventHandler<HTMLFormElement> = async (event) => {
    event.preventDefault();

    const formData = new FormData(event.currentTarget);

    const address = formData.get("address") as string;
    const amount = formData.get("amount") as string;

    const publicClient = await primaryWallet.getPublicClient();
    const walletClient = await primaryWallet.getWalletClient();

    const transaction = {
      to: address,
      value: amount ? parseEther(amount) : undefined,
    };

    const hash = await walletClient.sendTransaction(transaction);
    setTxnHash(hash);

    const receipt = await publicClient.getTransactionReceipt({
      hash,
    });

    console.log(receipt);
  };

  return (
    <form onSubmit={onSubmit}>
      <p>Send to ETH address</p>
      <input name="address" type="text" required placeholder="Address" />
      <input name="amount" type="text" required placeholder="0.05" />
      <button type="submit">Send</button>
      <span data-testid="transaction-section-result-hash">{txnHash}</span>
    </form>
  );
};
```


# Mint an NFT on EVM with Wagmi



```jsx
import { FC } from 'react';

import {
  useContractWrite,
  usePrepareContractWrite,
  useWaitForTransaction,
} from 'wagmi';

export const ContractWriteSection: FC = () => {
  const { config } = usePrepareContractWrite({
    abi: [
      {
        inputs: [],
        name: 'mint',
        outputs: [],
        stateMutability: 'nonpayable',
        type: 'function',
      },
    ] as const,
    address: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
    functionName: 'mint',
  });

  const { data, write } = useContractWrite(config);

  const { isLoading, isSuccess } = useWaitForTransaction({
    hash: data?.hash,
  });

  return (
    <div>
      <p>Mint NFT</p>
      <button disabled={!write || isLoading} onClick={() => write?.()}>
        {isLoading ? 'Minting...' : 'Mint'}
      </button>
      {isSuccess && (
        <div>
          Successfully minted your NFT!
          <div>
            <a href={`https://etherscan.io/tx/${data?.hash}`}>Etherscan</a>
          </div>
        </div>
      )}
    </div>
  );
};
```


# Sign a message in Ethereum/EVM

In this example, we are creating a button to sign a message and log the signature to the console.

```JavaScript
import { useDynamicContext } from '@dynamic-labs/sdk-react-core';

const SignMessageButton = () => {
  const { primaryWallet } = useDynamicContext();

  const signMessage = async () => {
    if (!primaryWallet) return;

    const signature = await primaryWallet.signMessage('example');

    console.log('signature', signature);
  };

  return <button onClick={signMessage}>Sign message</button>;
};
```


# Fetch all token balances for a wallet



<Tip>The Dynamic UI components (DynamicWidget and UserProfile) have a built in UI for displaying multi asset balances, we strongly recommend using those components if you can! Learn more in [the Multi-Asset UI section](/wallets/multi-asset).</Tip>

## Hook

You can fetch token balances for a user's wallet using the `useTokenBalances` hook:

```jsx
import { useTokenBalances } from "@dynamic-labs/sdk-react-core";

const { tokenBalances, isLoading, isError, error } = useTokenBalances();
```

<Warning>The hook currently only supports the Ethereum, Optimism, Polygon, Arbitrum, and Base networks.</Warning>

## Return Value

This hook returns an object for `tokenBalances` with the following properties:

| Property   | Type    | Description                                                            |
| ---------- | ------- | ---------------------------------------------------------------------- |
| networkId  | integer | The network ID of the token i.e. 1 for Ethereum, 137 for Polygon, etc. |
| address    | string  | The address of the token.                                              |
| name       | string  | The name of the token.                                                 |
| symbol     | string  | The symbol of the token.                                               |
| decimals   | integer | The number of decimals the token has.                                  |
| logoURI    | string  | The URI of the token's logo image.                                     |
| balance    | float   | The balance of the token in the user's wallet.                         |
| rawBalance | integer | The raw balance of the token in the user's wallet.                     |

## Reference

You can find the full reference for the `useTokenBalances` hook [here](/react-sdk/hooks/usetokenbalances).

## Full Example

```jsx
import { useDynamicContext, useTokenBalances } from "@dynamic-labs/sdk-react-core";

const Balances = () => {
  const { primaryWallet } = useDynamicContext();
  const { tokenBalances, isLoading, isError, error } = useTokenBalances();

  if(!primaryWallet) return <div>No wallet connected</div>;

  if (isLoading) return <div>Loading...</div>;
  if (isError) return <div>Error: {error.message}</div>;

  return (
    <div>
      {tokenBalances.map((token) => (
        <div key={token.address}>
          <img src={token.logoURI} alt={token.symbol} />
          <div>{token.name}</div>
          <div>{token.symbol}</div>
          <div>{token.balance}</div>
        </div>
      ))}
    </div>
  );
};
```


# Switch user's primary wallet



The [useSwitchWallet hook](/react-sdk/hooks/useswitchwallet) gives you the ability to switch the user's primary wallet.

All it needs is a wallet ID as a parameter, and it resolve upon success. You can find the ID on any wallet object in Dynamic i.e. on the primaryWallet or those returned by [useUserWallets](/react-sdk/hooks/useuserwallets):

```jsx
import React from 'react';
import { useSwitchWallet, useUserWallets } from '@dynamic-labs/sdk-react-core';

const WalletSwitcher = () => {
  const switchWallet = useSwitchWallet();
  const userWallets = useUserWallets();

  return (
    <div>
      {userWallets.map(wallet => (
        <button
          key={wallet.id}
          onClick={() => switchWallet(wallet.id)}
        >
          {wallet.address}
        </button>
      ))}
    </div>
  );
};
```


# Interacting with wallets



There are two aspects of the wallet object that are useful to know about:

1.  The wallet object itself
2.  The connector object

### Wallet Interface

The wallet is akin to a single account on your Wallet provider. For example, in Metamask, you can have multiple accounts, each with their own public address.

You can also have multiple chains associated with a single account in your wallet provider, for example in Magic Eden, you can have "Account 1", which has a public address for Solana, another for Bitcoin, etc. Each of these chain addresses are mapped to a different wallet object in Dynamic.

You'll find props and methods for a wallet object are specific to interacting with a single account/sub-account (i.e. for a single chain) on your wallet provider.

For example, getBalance, or signMessage interact with a single account/subaccount.

Below we outline the generic props and methods for a wallet object no matter what chain the account belongs to.

In each chain specific section, we also outline the props and methods for a wallet object for that chain.

<Accordion title="Wallet props">
  | Field                                           | Description                                                                                             |
  | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
  | additionalAddresses: WalletAdditionalAddress\[] | Additional addresses associated to the wallet (e.g. ordinals and payment addresses for bitcoin wallets) |
  | address: string                                 | Public address of the connected wallet                                                                  |
  | chain: string                                   | Current BlockChain name (e.g: 'ETH', 'SOL', 'BTC', etc)                                                 |
  | connector: WalletConnector                      | The wallet connector object                                                                             |
  | id: string                                      | The wallet's unique id (matches thw wallet verified credential on connect-anf-sign)                     |
  | isAuthenticated: boolean                        | True if the user is authenticated, otherwise it's false                                                 |
  | key: string                                     | The wallet key (e.g. metamask, phantom, etc)                                                            |
</Accordion>

<Accordion title="Wallet methods">
  | Method                                                                | Description                                                                                                          |
  | --------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
  | getBalance(): Promise\<string \| undefined>                           | Retrieves the balance of the wallet.                                                                                 |
  | getNameService(): Promise\<NameServiceData \| undefined>              | Retrieves the name service data associated with the wallet.                                                          |
  | getNetwork(): Promise\<string \| number \| undefined>                 | Retrieves the network value. (e.g: 1, 137, 'mainnet')                                                                |
  | isConnected(): Promise\<boolean>                                      | If the wallet is connected.                                                                                          |
  | proveOwnership(messageToSign: string): Promise\<string\ \| undefined> | Proves ownership of the wallet by signing a message.                                                                 |
  | signMessage(messageToSign: string): Promise\<string\ \| undefined>    | Signs a message using the wallet.                                                                                    |
  | switchNetwork(networkChainId: number \| string): Promise\<void>       | Switches the network that the wallet is connected to.                                                                |
  | sync(): Promise\<void>                                                | Prompts the user to reconnect the wallet if not connected and only resolves when the wallet is connected and active. |
</Accordion>

### WalletConnector Interface

The connector is akin to the wallet provider itself i.e. Metamask, Phantom, etc. It is abstracted away from any single account. Therefore you'll find methods and props that are relevant to the wallet provider itself like isInstalledOnBrowser, or getDeepLink.

Below we outline the props and methods for a wallet connector object no matter what chain the account belongs to. In each chain specific section, we also outline the props and methods for a wallet connector object for that chain.

<Accordion title="Connector Interface">
  | Field                                                                              | Description                                                                                    |
  | ---------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
  | canConnectViaCustodialService: boolean                                             | If the wallet needs to be connected via a custodial service such as Blocto, this will be true. |
  | canConnectViaQrCode: boolean                                                       | If the wallet is not installed, and can be connected via a QR code, this will be true.         |
  | canConnectViaSocial: boolean                                                       | Whether this connector can be connected via social login.                                      |
  | chainRpcProviders: typeof ChainRpcProviders \| undefined                           | The RPC providers for the chain the wallet is connected to                                     |
  | connectedChain: Chain                                                              | The chain this wallet is connected to                                                          |
  | endSession(): Promise\<void>                                                       | Close the wallet connection                                                                    |
  | getAddress(opts?: GetAddressOpts): Promise\<string \| undefined>                   | Get the public address of the wallet                                                           |
  | getAdditionalAddresses(mainAddress?: string): Promise\<WalletAdditionalAddress\[]> | Get the additional addresses of the wallet, given the main address                             |
  | getConnectedAccounts(): Promise\<string\[]>                                        | Get the address silently                                                                       |
  | getDeepLink(): string \| undefined                                                 | Get the deep link of the wallet                                                                |
  | getMobileOrInstalledWallet(): WalletConnector                                      | Whether the wallet connector should fall back to a different wallet connector                  |
  | getNetwork(): Promise\<string \| \| number \| undefined>                           | Get the current network of the wallet                                                          |
  | getSession(): unknown \| Promise\<T>                                               | Get the session for the wallet                                                                 |
  | isAvailable: boolean                                                               | If the wallet provider is available (i.e. Metamask is not installed)                           |
  | isEmbeddedWallet: boolean                                                          | If the wallet generated by a valid embedded wallet provider                                    |
  | isInitialized: boolean                                                             | Whether the connector has been initialized                                                     |
  | isInstalledOnBrowser(): boolean                                                    | Check if the wallet is installed on the browser                                                |
  | isWalletConnect: boolean                                                           | Flag if it is wallet Connect                                                                   |
  | key: string                                                                        | Override key or the normalized wallet name if needed                                           |
  | proveOwnership(messageToSign: string): Promise\<string \| undefined>               | In most cases this is an alias for `signMessage`                                               |
  | providerResources: string\[] \| undefined                                          | Additional resources to add to the message to be signed                                        |
  | supportsNetworkSwitching():boolean                                                 | Whether the wallet supports network switching                                                  |
  | switchNetworkOnlyFromWallet: boolean \| undefined                                  | Requires switching network in the wallet itself                                                |
</Accordion>

## Chain specific interfaces

*   [EVM Interactions](/wallets/using-wallets/evm/evm-wallets)
*   [Bitcoin Interactions](/wallets/using-wallets/bitcoin/bitcoin-wallets)
*   [Solana Interactions](/wallets/using-wallets/solana/solana-wallets)


# Send Solana Legacy Transaction



```tsx
import { ISolana, isSolanaWalet } from '@dynamic-labs/solana-core';

import { Connection } from '@solana/web3.js';

const { primaryWallet } = useDynamicContext();

if(!isSolanaWalet(primaryWallet)) {
  return;
}

const connection: Connection = await primaryWallet.getConnection();

const fromKey = new PublicKey(primaryWallet.address);
const toKey = new PublicKey(address);

const amountInLamports = Number(amount) * 1000000000;
const transferTransaction = new Transaction().add(
  SystemProgram.transfer({
    fromPubkey: fromKey,
    lamports: amountInLamports,
    toPubkey: toKey,
  }),
);
const blockhash = await connection.getLatestBlockhash();
transferTransaction.recentBlockhash = blockhash.blockhash;
transferTransaction.feePayer = fromKey;

const signer: ISolana = await primaryWallet.getSigner();

await signer
  .signAndSendTransaction(transferTransaction)
  .then((res: any) => {
    console.log(
      `Transaction successful: https://solscan.io/tx/${res.signature}?cluster=devnet`,
    );
  })
  .catch((reason: any) => {
    console.error(reason);
  });
```


# Send a Versioned Solana Transaction



```tsx
import { ISolana, isSolanaWalet } from '@dynamic-labs/solana';

import { Connection } from "@solana/web3.js";

const { primaryWallet } = useDynamicContext();

if(!isSolanaWalet(primaryWallet)) {
  return;
}

const connection: Connection = await primaryWallet.getConnection();

const fromKey = new PublicKey(primaryWallet.address);
const toKey = new PublicKey(address);
const amountInLamports = Number(amount) * 1000000000;

const instructions = [
  SystemProgram.transfer({
    fromPubkey: fromKey,
    lamports: amountInLamports,
    toPubkey: toKey,
  }),
];

const blockhash = await connection.getLatestBlockhash();

// create v0 compatible message
const messageV0 = new TransactionMessage({
  instructions,
  payerKey: fromKey,
  recentBlockhash: blockhash.blockhash,
}).compileToV0Message();

const transferTransaction = new VersionedTransaction(messageV0);

const signer: ISolana = await primaryWallet.getSigner();

await signer
  .signAndSendTransaction(transferTransaction)
  .then((res: any) => {
    console.log(
      `Transaction successful: https://solscan.io/tx/${res.signature}?cluster=devnet`,
    );
  })
  .catch((reason: any) => {
    console.error(reason);
  });
```


# Tips for transactions with Solana embedded wallets



Navigating transactions on the Solana  can be challenging due to network congestion. This guide is designed to equip you with the essential strategies to ensure your transactions are processed smoothly and efficiently when using Dynamic-powered Solana embedded wallets. With the high demand on Solana, it's crucial to have a well-thought-out approach, including selecting the right RPC (Remote Procedure Call) endpoint and understanding how to set priority fees. By carefully choosing an RPC, you can access less congested nodes, which can significantly speed up your transaction times. Additionally, setting appropriate priority fees ensures that your transactions are given the necessary attention by validators. This guide will walk you through these steps, providing practical tips and insights and achieve successful transactions with greater ease and reliability.

## Which Solana RPC do I use?

*   We found that Helius, [Helius.dev](http://Helius.dev) was the most reliable RPC.
*   Ironforge, [Ironforge.cloud](http://Ironforge.cloud) can be used to improve transaction landing rates by sending transactions to multiple RPCs at a time.

**How do I add a priority fee to my transactions?**

Validators on the Solana network are incentivized by priority fees. Higher fees can attract validators to include a transaction in the next block, thus ensuring faster confirmation times for users willing to pay more.

*   Set a priority fee dynamically based on the priority fees sent in recent blocks, can be found easily from the “Helius Priority Fee API”
    *   [https://docs.helius.dev/solana-rpc-nodes/priority-fee-api](https://docs.helius.dev/solana-rpc-nodes/priority-fee-api)
    *   [https://solana.com/docs/rpc/http/getrecentprioritizationfees](https://solana.com/docs/rpc/http/getrecentprioritizationfees)
*   If your transaction is being broadcast to the network, but it is not actually being confirmed, then you may need to increase your priority fee. You do not need to increase the priority fees drastically above the recent priority fees, as it is not necessary.

```jsx
  // Priority fee
  const addPriorityFee = ComputeBudgetProgram.setComputeUnitPrice({
    microLamports: 1,
  });
  
   // Total fee will be 5,001 Lamports for 1M CU
  const transaction = new Transaction()
    .add(addPriorityFee)
    .add(
      SystemProgram.transfer({
        fromPubkey: payer.publicKey,
        toPubkey: toAccount,
        lamports: 10000000,
      }),
    );
```

## What is the compute unit limit and why should I modify it?

*   Solana programs have a compute limitations: **Max Compute per block**: 48 million CU
*   Therefore, if you decrease/optimize the compute unit limit for your transaction, it is more likely to land since it is more likely to fit in the block. E.g, only 48 transactions with 1 million CU can fit in one block, but 96 transactions with 0.5 million CU can fit in a block.

```jsx
 // Compute unit budget
  const modifyComputeUnits = ComputeBudgetProgram.setComputeUnitLimit({
    units: 1000000,
  });
 
 
  // Total fee will be 5,001 Lamports for 1M CU
  const transaction = new Transaction()
    .add(modifyComputeUnits)
    .add(
      SystemProgram.transfer({
        fromPubkey: payer.publicKey,
        toPubkey: toAccount,
        lamports: 10000000,
      }),
    );
 
```

## General Tips

*   Refresh the blockhash in your transaction before sending it, as a transaction will fail if the blockhash it references is 150 blocks old.

    ```jsx

     const connection = await primaryWallet.getConnection();
     
     const signer = await primaryWallet.getSigner();
        
        const { blockhash } = await connection.getLatestBlockhash();

        transaction.message.recentBlockhash = blockhash;

        await signer
          .signAndSendTransaction(transaction)
    ```

*   implement your own manual method to rebroadcast transactions. After a transaction is signed, you can keep sending the transaction to the RPC for about another minute before it expires.

## More links

[https://www.helius.dev/blog/how-to-land-transactions-on-solana](https://www.helius.dev/blog/how-to-land-transactions-on-solana)

[https://solana.com/developers/cookbook/transactions/add-priority-fees](https://solana.com/developers/cookbook/transactions/add-priority-fees)


# Solana Wallets



## Introduction

The Solana wallet connectors will generally have a couple of extra methods. Specifically, in order to interact using public actions with Solana.

## Checking if a wallet is a Solana wallet

You can use the `isSolanaWallet` helper method to check if a wallet is a Solana wallet. That way, TypeScript will know which methods are available to you.

```ts
import { isSolanaWallet } from '@dynamic-labs/solana';

if (!isSolanaWallet(wallet)) {
  throw new Error('This wallet is not a Solana wallet');
}

const connection = await primaryWallet.getConnection();

```

## Solana Wallet

| Method                                | Description                                 |
| ------------------------------------- | ------------------------------------------- |
| getConnection(): Promise\<Connection> | Retrieves the RPC connection                |
| getSigner(): Promise\<ISolana>        | Retrieves the solana signer for the wallet. |

## Examples

You can find examples of how to interact with Solana wallets in the examples section:

*   [Send a Legacy Solana Transaction](/wallets/using-wallets/solana/send-legacy-solana-transaction) (only available for Solana embedded wallets).
*   [Send a Versioned Solana Transaction](/wallets/using-wallets/solana/send-versioned-solana-transaction) (only available for Solana embedded wallets).