@meso-network/meso-js
v0.2.0
Published
A TypeScript SDK to embed the Meso on/off-ramp into a dapp.
Downloads
2,594
Readme
@meso-network/meso-js
meso-js
is a browser SDK that allows you to integrate Meso's fiat-to-crypto
on-ramp directly into your decentralized application (dApp).
Meso handles user onboarding, including KYC checks and
identity verification. It also debits funds from users' bank accounts and then
purchases and transfers crypto to the specified wallet address.
⚡️ Looking for a quickstart? Jump to usage. ⚡️
The SDK is written in TypeScript and ships with type definitions, but can be used in a vanilla JavaScript application as well.
📓 Currently, the SDK is in private beta. To request access, contact [email protected].
- @meso-network/meso-js
Requirements
Account setup
To use the meso-js
SDK, you must have a Meso partner
account. You can reach out to
[email protected] to sign up. During the
onboarding process, you will need to specify the
origin of your dApp
or web application to ensure meso-js
operates seamlessly on your site. Meso
will then provide you with a partnerId
for use with the SDK.
Content Security Policy
meso-js
can be used in most web browsers and application stacks. However, you
will need to ensure your Content Security Policy (CSP)
will allow Meso's iframe and network calls from your page.
Your CSP should enable at least frame-src and connect-src with the correct values per-environment.
Sandbox:
Content-Security-Policy: frame-src https://api.sandbox.meso.network/; connect-src https://api.sandbox.meso.network/;
Production:
Content-Security-Policy: frame-src https://api.meso.network/; connect-src https://api.meso.network/;
Usage
⚠️ Currently, the SDK is in private beta. To request access, contact [email protected].
Installation
meso-js
is available via npm and ships with TypeScript definitions.
# npm
npm i @meso-network/meso-js
# yarn
yarn add @meso-network/meso-js
# pnpm
pnpm i @meso-network/meso-js
Initialize a transfer
To initiate a transfer for a user, use the transfer
function from
the SDK. Upon activation, Meso will display an iframe covering the entire
screen, guiding the user through the onboarding and crypto transfer process.
📙 View the reference to learn more about how to set up your integration.
meso-js
can be used in vanilla JavaScript or TypeScript applications as well
as within popular frameworks such a React.
Vanilla JavaScript/TypeScript
You can launch the Meso experience at any time, but usually it is in response to
a user action such as clicking a button. The following example assumes you have
a page with a Buy Crypto
button.
import { transfer, Environment } from "@meso-network/meso-js";
import type {
EventKind,
MesoEvent,
SigningMessage,
} from "@meso-network/meso-js";
const buyCrypto = document.querySelector("button#buy-crypto");
buyCrypto.addEventListener("click", () => {
transfer({
partnerId: "<PARTNER_ID>", // Your unique Meso partner ID
environment: Environment.SANDBOX, // SANDBOX | PRODUCTION
sourceAmount: "100", // The amount (in USD) the user will spend
destinationAsset: Asset.ETH, // The token the user will receive ("ETH" | "SOL" | "USDC")
network: Network.ETHEREUM_MAINNET, // The network to use for the transfer
walletAddress: "<WALLET_ADDRESS>", // The user's wallet address obtained at runtime by your application
// A callback to handle events throughout the integration lifecycle
onEvent({ kind, payload }: MesoEvent) {
switch (kind) {
// The iframe/window is ready
case EventKind.READY:
break;
// The transfer has been approved and will go through, however funds have not yet moved.
case EventKind.TRANSFER_APPROVED:
// The transfer has been finalized and the assets have been transferred.
case EventKind.TRANSFER_COMPLETE:
console.log(payload.transfer);
break;
// There was an issue with the provided configuration
case EventKind.CONFIGURATION_ERROR:
console.error(payload.error.message);
break;
// The `network` provided in the configuration is not supported
case EventKind.UNSUPPORTED_NETWORK_ERROR:
console.error(payload.error.message);
break;
// The `destinationAsset` provided in the configuration is not supported
case EventKind.UNSUPPORTED_ASSET_ERROR:
console.error(payload.error.message);
break;
// A general error has occurred
case EventKind.ERROR:
console.error(payload.error.message);
break;
case EventKind.CLOSE:
console.log("Meso experience closed.");
}
},
// A callback to handle having the user verify their wallet ownership by signing a message
async onSignMessageRequest(message: SigningMessage) {
// Have the user sign a message via their wallet and return the result.
return await signMessage(message);
},
});
});
React
You can also use meso-js
in a React application out of the box.
import { useCallback, useState, useEffect } from "react";
import { transfer, Environment } from "@meso-network/meso-js";
import type {
EventKind,
Event,
SigningMessage,
TransferInstance,
} from "@meso-network/meso-js";
export const BuyCrypto = () => {
const [mesoTransfer, setMesoTransfer] = useState<TransferInstance>();
// Make sure to clean up the integration when your component unmounts
useEffect(() => {
return () => {
mesoTransfer.destroy();
};
}, [mesoTransfer]);
const launchMeso = useCallback(() => {
const transfer = transfer({
partnerId: "<PARTNER_ID>", // Your unique Meso partner ID
environment: Environment.SANDBOX, // SANDBOX | PRODUCTION
sourceAmount: "100", // The amount (in USD) the user will spend
destinationAsset: Asset.ETH, // The token the user will receive ("ETH" | "SOL" | "USDC")
network: Network.ETHEREUM_MAINNET, // The network to use for the transfer
walletAddress: "<WALLET_ADDRESS>", // The user's wallet address obtained at runtime by your application
// A callback to handle events throughout the integration lifecycle
onEvent({ kind, payload }: MesoEvent) {
switch (kind) {
// The iframe/window is ready
case EventKind.READY:
break;
// The transfer has been approved and will go through, however funds have not yet moved.
case EventKind.TRANSFER_APPROVED:
// The transfer has been finalized and the assets have been transferred.
case EventKind.TRANSFER_COMPLETE:
console.log(payload.transfer);
break;
// There was an issue with the provided configuration
case EventKind.CONFIGURATION_ERROR:
console.error(payload.error.message);
break;
// The `network` provided in the configuration is not supported
case EventKind.UNSUPPORTED_NETWORK_ERROR:
console.error(payload.error.message);
break;
// The `destinationAsset` provided in the configuration is not supported
case EventKind.UNSUPPORTED_ASSET_ERROR:
console.error(payload.error.message);
break;
// A general error has occurred
case EventKind.ERROR:
console.error(payload.error.message);
break;
case EventKind.CLOSE:
console.log("Meso experience closed.");
}
},
// A callback to handle having the user verify their wallet ownership by signing a message
async onSignMessageRequest(message: SigningMessage) {
// Have the user sign a message via their wallet and return the result.
return await signMessage(message);
},
});
setMesoTransfer(transfer);
}, []);
return (
<div>
<button onClick={launchMeso}>Buy Crypto</button>
</div>
);
};
Integration lifecycle
| Lifecycle | | -------------------------------------------------------------------------------------------------------------------------- | | |
When your user is ready, initiate the on-ramp by calling
transfer()
. During setup, subscribe to the onEvent
callback to receive updates on transfer statuses, errors, and user interactions.
When the Meso interface appears, your onSignMessageRequest
callback will prompt a message signing request. You'll then retrieve a signed
message from the user's wallet and pass it to the SDK.
After the transfer receives an APPROVED
status, await its progression to
COMPLETE
. The Meso UI will be removed, leaving the iframe in place. You'll
receive a notification in your onEvent callback once the transfer finalizes.
ℹ Note: Funds are not moved until the transfer is
COMPLETE
.
While awaiting transfer completion, you can display a loading state for the user. Upon completion, the SDK will provide the transfer details for display to the user.
Please note,if the Meso experience is closed (via destroy
),
there's no way to check the transfer's status. It's best to wait for the
COMPLETE
event.
Once the transfer is complete, the onEvent
callback will notify you, and the
Meso iframe will be unmounted. You can then navigate the user to another view.
Meso will also send the user an email detailing the transfer.
Reference
transfer
To initialize the transfer
experience, provide the following configuration.
type TransferConfiguration = {
partnerId: string; // Your unique Meso partner ID
environment: Environment; // SANDBOX | PRODUCTION
sourceAmount?: string; // The amount (in USD) the user will spend
destinationAmount?: string; // The amount (in destinationAsset token) the user will receive. If both sourceAmount and destinationAmount are specified, destinationAmount will take precedence
destinationAsset: Asset; // The token the user will receive ("ETH" | "SOL" | "USDC")
network: Network; // The network to use for the transfer
walletAddress: string; // The user's wallet address obtained at runtime by your application
layout?: Layout; // Configuration to customize how the Meso experience is launched and presented
authenticationStrategy?: AuthenticationStrategy; // Determines the authentication mechanism for users to perform a transfer. Defaults to `WALLET_SIGNATURE`
onSignMessageRequest: (message: string) => Promise<SignedMessageResult>; // A callback that is fired when you need to collect the user's signature via their wallet
onEvent?: (event: MesoEvent) => void; // An optional handler to notify you when an event or error occurs. This is useful for tracking the state of the user through the experience
};
enum Network {
ETHEREUM_MAINNET = "eip155:1"
SOLANA_MAINNET = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"
POLYGON_MAINNET = "eip155:137",
OP_MAINNET = "eip155:10",
ARBITRUM_MAINNET = "eip155:42161",
BASE_MAINNET = "eip155:8453"
}
enum Asset {
ETH = "ETH"
SOL = "SOL"
USDC = "USDC"
POL = "POL"
}
enum Environment {
SANDBOX = "SANDBOX"
// Uses mainnet(s) and transfers fiat currency.
PRODUCTION = "PRODUCTION"
} as const
enum Position {
TOP_RIGHT = "top-right",
BOTTOM_RIGHT = "bottom-right",
BOTTOM_LEFT = "bottom-left",
TOP_LEFT = "top-left",
CENTER = "center",
}
/**
* A stringified positive integer (excluding units) representing a number of pixels.
*/
export type PixelValue = `${number}`;
type Layout = {
position?: Position;
offset?:
| PixelValue
| { horizontal: PixelValue, vertical?: PixelValue}
| { horizontal?: PixelValue, vertical: PixelValue};
};
enum AuthenticationStrategy {
WALLET_VERIFICATION = "wallet_verification",
HEADLESS_WALLET_VERIFICATION = "headless_wallet_verification",
BYPASS_WALLET_VERIFICATION = "bypass_wallet_verification",
}
The transfer
call returns a TransferInstance
with a destroy()
method. You
can use this to tear down the integration at any time. Calling this will clean
up the Meso iframe and you can safely re-initialize if needed.
Type:
type TransferInstance = {
destroy: () => void;
};
Example:
const { destroy } = transfer({ ... });
destroy(); // The meso iframe is unmounted. No more events/callbacks will fire.
Authentication Strategies
Depending on your integration, you may have different requirements for users authenticating with Meso. In all scenarios, the user will still be required to perform two-factor authentication (2FA) and, in some cases provide email/password.
Wallet verification
By default, users are prompted to sign a message with their wallet to prove ownership.
{
// ...
authenticationStrategy: AuthenticationStrategy.WALLET_VERIFICATION,
}
Headless wallet verification
In cases such as embedded wallets, message signing may need be transparent to the user. In these cases, passing will allow you to perform message signing yourself in the background.
{
// ...
authenticationStrategy: AuthenticationStrategy.HEADLESS_WALLET_VERIFICATION,
}
Bypass wallet verification
In the case where pre-deployment smart contract wallets are being used and wallet verification cannot be performed, you can skip wallet verification altogether and rely on Meso's other authentication mechanisms (such as two-factor auth).
{
// ...
authenticationStrategy: AuthenticationStrategy.BYPASS_WALLET_VERIFICATION,
}
Customizing the layout
The Meso experience renders as a full-viewport modal. However, you can provide configuration to set the positioning of the rendered UI. This is helpful in cases where you want the application to render in a more precise location.
You can customize the Position
of the rendered UI and the offset (from the
edge of the viewport).
Position
When initializing the Meso experience, you can provide a position
value of:
TOP_RIGHT
(default)BOTTOM_RIGHT
BOTTOM_LEFT
TOP_LEFT
CENTER
Example:
transfer({
// ... other params
layout: {
position: Position.BOTTOM_RIGHT,
},
});
Offset
You can provide a horizontal and vertical offset to the rendered UI to add more padding from the edge of the viewport. These values are provided as stringified non-negative integers. You do not need to provide units.
Example:
transfer({
// ... other params
layout: {
position: Position.TOP_RIGHT,
offset: {
horizontal: "50", // An extra 50px of right-padding will be applied
vertical: "100", // An extra 100px of top-padding will be applied
},
},
});
The horizontal and vertical offsets will be applied based on the
layout.position
value you provided.
| Position | Horizontal offset | Vertical offset |
| -------------- | --------------------- | --------------------- |
| TOP_RIGHT
| Right padding | Top padding |
| BOTTOM_RIGHT
| Right padding | Bottom padding |
| BOTTOM_LEFT
| Left padding | Bottom padding |
| TOP_LEFT
| Left padding | Top padding |
| CENTER
| No padding is applied | No padding is applied |
If you would like to use the same value for horizontal and vertical offset, you can provide a string value:
transfer({
// ... other params
layout: {
position: Position.TOP_RIGHT,
offset: "50", // An extra 50px of right-padding and 50px of top-padding will be applied
},
});
Handling errors
Configuration errors
If there is an error in the configuration provided to transfer
, a
CONFIGURATION_ERROR
will be provided to your onEvent
callback.
This error may occur if you provide an invalid wallet address or mismatched
networks and assets.
Type:
// An error surfaced from the `meso-js` integration.
type MesoError = {
// A client-friendly error message.
message: string;
};
type ConfigurationErrorPayload = { error: MesoError };
Example:
transfer({
// ...
onEvent: (event: MesoEvent) => {
if (event.kind === EventKind.CONFIGURATION_ERROR) {
// handle configuration error
console.log(event.payload.error.message); // "some error message"
}
},
});
If you do not provide the onEvent
callback, meso-js
will throw
an exception.
Unsupported Network errors
If the network
that you provided in the configuration isn't currently
supported, an UNSUPPORTED_NETWORK_ERROR
will be provided to your
onEvent
callback. This error may occur for networks we're in the
process of adding support for or if a previously supported network is not
currently supported.
Type:
// An error surfaced from the `meso-js` integration.
type MesoError = {
// A client-friendly error message.
message: string;
};
type UnsupportedNetworkErrorPayload = { error: MesoError };
Example:
transfer({
// ...
onEvent: (event: MesoEvent) => {
if (event.kind === EventKind.UNSUPPORTED_NETWORK_ERROR) {
// handle unsupported network error
console.log(event.payload.error.message); // "some error message"
}
},
});
Unsupported Asset errors
If the destinationAsset
that you provided in the configuration isn't
currently supported, an UNSUPPORTED_ASSET_ERROR
will be provided to your
onEvent
callback. This error may occur for assets we're in the
process of adding support for or if a previously supported asset is not
currently supported.
Type:
// An error surfaced from the `meso-js` integration.
type MesoError = {
// A client-friendly error message.
message: string;
};
type UnsupportedAssetErrorPayload = { error: MesoError };
Example:
transfer({
// ...
onEvent: (event: MesoEvent) => {
if (event.kind === EventKind.UNSUPPORTED_ASSET_ERROR) {
// handle unsupported asset error
console.log(event.payload.error.message); // "some error message"
}
},
});
Other errors
Other errors dispatched during the integration will also be surfaced via the
onEvent
callback.
Type:
// An error surfaced from the `meso-js` integration.
type MesoError = {
// A client-friendly error message.
message: string;
};
Example:
transfer({
// ...
onEvent: (event: MesoEvent) => {
if (event.kind === EventKind.ERROR) {
// handle general error
console.log(event.payload.error.message); // "some error message"
}
},
});
export type ErrorPayload = { error: MesoError };
Events
The onEvent
callback you provide during initialization will be
called with structured events that are dispatched throughout the integration
lifecycle.
Each event has a kind
and a payload
(which may be null
in some cases).
Example usage:
onEvent({kind, payload}: MesoEvent) {
switch (kind) {
case EventKind.READY:
// Handle iframe ready if needed.
break;
case EventKind.TRANSFER_APPROVED:
console.log(payload); // { transfer: { ... }}
break;
case EventKind.TRANSFER_COMPLETE:
console.log(payload); // { transfer: { ... }}
break;
case EventKind.CONFIGURATION_ERROR:
console.log(payload); // { message: "an error message" }
break;
case EventKind.UNSUPPORTED_NETWORK_ERROR:
console.log(payload); // { message: "an error message" }
break;
case EventKind.UNSUPPORTED_ASSET_ERROR:
console.log(payload); // { message: "an error message" }
break;
case EventKind.ERROR:
console.log(payload); // { message: "an error message" }
break;
case EventKind.CLOSE:
console.log(payload); // null
break;
}
}
READY
This event is fired when the iframe is loaded and ready. This can be useful in cases where you want to delay showing the Meso experience or negotiate animations.
TRANSFER_APPROVED
This event is fired when the user successfully completes a transfer and it has been approved. At this point, the transfer is not yet complete and funds have not been moved.
Internally, MesoJS uses the transfer.id
in this payload to poll for the status
of the transfer and emit further events as it changes.
Example payload:
{
kind: "TRANSFER_APPROVED",
payload: {
transfer: {
id: "a_meso_transfer_id",
status: "APPROVED",
updatedAt: "2023-10-24T15:59:29.562Z",
networkTransactionId: "a_network_transaction_id" // If available
}
}
}
TRANSFER_COMPLETE
This event is dispatched when the user's transfer is complete and funds have been moved.
Example payload:
{
kind: "TRANSFER_COMPLETE",
payload: {
transfer: {
id: "a_meso_transfer_id",
status: "COMPLETED",
updatedAt: "2023-10-24T15:59:29.562Z",
networkTransactionId: "a_network_transaction_id" // If available
}
}
}
ERROR
This event is fired when an error occurs in the Meso experience.
Example payload:
{
kind: "ERROR",
payload: {
message: "Unable to complete transfer."
}
}
CONFIGURATION_ERROR
This event is fired when there is a validation error when calling transfer
.
See configuration errors for more.
Example payload:
{
kind: "CONFIGURATION_ERROR",
payload: {
message: "Invalid ETH wallet address."
}
}
UNSUPPORTED_NETWORK_ERROR
This event is fired when the network
provided in the transfer
configuration
is not supported.
See unsupported network errors for more.
Example payload:
{
kind: "UNSUPPORTED_NETWORK_ERROR",
payload: {
message: "\"network\" must be a supported network: eip155:1,solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp,eip155:137."
}
}
UNSUPPORTED_ASSET_ERROR
This event is fired when the destinationAsset
provided in the transfer
configuration is not supported.
See unsupported asset errors for more.
Example payload:
{
kind: "UNSUPPORTED_ASSET_ERROR",
payload: {
message: "\"destinationAsset\" must be a supported asset: ETH,SOL,USDC,MATIC,POL.",
}
}
CLOSE
This event is called when the Meso experience is closed and the iframe is removed.
This event will fire in the following scenarios:
- The transfer is
COMPLETE
- The user dismisses the iframe manually
- You call
destroy
on the Meso instance
Example payload:
{
kind: "CLOSE";
payload: null;
}
Environments
Meso provides two environments:
SANDBOX
– In this environment, no crypto assets are transferred and no fiat assets are moved.PRODUCTION
– In this environment, production networks will be used to transfer real crypto assets. Fiat assets are moved.
You can specify the network when calling transfer.
Testing
In sandbox, you can use the following values for testing:
transfer
configurationsourceAmount
"666.66"
will cause onboarding to fail due to risk checks and the user will be frozen"666.06"
will fail the transfer with the payment being declined
- 2FA (SMS)
000000
will succeed
- Onboarding values
- Debit Card
- Number:
5305484748800098
- CVV:
435
- Expiration Date:
12/2025
- Number:
- Taxpayer ID (last 4 digits of SSN)
0000
will require the user enter a fully valid SSN (you can use123345432
).- Any other 4-digit combination will pass
- Debit Card
Supported browsers/runtimes
The Meso experience will work in first-party web applications. Currently, mobile support (such as webviews) is limited and not supported.
Meso supports the latest versions of:
- Chrome (Desktop, iOS, Android)
- Safari (macOS)
- Safari (iOS)
- Brave, Arc, and other Chrome-like browsers
- Firefox
If you have issues with specific browsers/environments, contact [email protected].
Caveats
- Next.js and SSR –
meso-js
will not work in a server-side environment. Ensure you are deferring initialization until your client-side code is ready in the browser.