Skip to main content

Using Managed Wallets

Managed wallets provide a seamless experience for your users by allowing you to create and manage blockchain wallets on their behalf. Users don't need to download any special apps or take extra steps, making it easier for them to get started with your application.

This approach offers transparency, allowing users to easily track their digital assets. It simplifies user onboarding and provides clear, immutable on-chain data for tracking tokens, enhancing trust and engagement within your platform.

Here's how the typical flow works:

  1. Managed Wallet Creation: When a new user starts using your application, you initiate the creation of a managed wallet for them, and assign the user's internal ID from your database to the newly created managed wallet. This ensures a consistent link between the user and their wallet.
  2. Asset Management: As the user earns or deposits digital assets within your application, these assets are automatically stored in their managed wallet.
  3. In-App Transactions: Any actions the user performs within your application that involve their wallet (e.g., spending tokens, participating in in-game economies) are executed directly on their managed wallet.
  4. Graduating to Self-Custody (Optional): If a user decides they want to take full control of their assets, a single sweep moves every token and all their ENJ from their managed wallet to their own self-custodial wallet.

This process ensures that users can interact with blockchain assets seamlessly within your application without needing to understand the underlying complexities of blockchain technology or operating his own crypto wallet.

Building a real-time game?

A managed wallet can serve as a player's on-chain cold inventory. For fast-paced games where some items must be used the instant a player taps them (drinking a potion mid-fight), you can pair it with an off-chain hot inventory so consumption never waits on the chain. See Hot & Cold Inventories to understand the pattern.

What you'll need:
  • Some Enjin Coin on Enjin Matrixchain to pay for Transaction Fees. You can obtain cENJ (Canary ENJ) for testing from the built-in Canary faucet in the Platform UI.
  • An Enjin Platform Account.
  • A Collection and a Token to add to the wallet.

C++ SDK coming soon

The C++ examples on this page target an older version of the Enjin Platform and won't work against the current API. An updated C++ SDK is on the way — for now, use the C# SDK or the GraphQL examples.

Creating a Managed Wallet

To create a managed wallet, run the CreateManagedWallet mutation with a unique ID as a parameter.

Choose a unique externalId for each player/user that can be cross-referenced later. This unique identifier should be something already associated with the player in your database, such as a player ID or username. By doing so, you will be able to consistently link the Managed Wallet to the respective player.

mutation CreateManagedWallet {
CreateManagedWallet(externalId: "docs-example-player") #Replace this with a unique ID
}
Lost database data?

Recreate the managed wallets by running the CreateManagedWallet mutation again for each of the externalIds. Make sure to use the same Daemon wallet seed and password used to create the managed wallets prior, as managed wallets are derived with the following derivation path: walletSeed/externalId///password

Finding a Managed Wallet

Once a managed wallet is created, use the GetManagedWallet query to look it up by its externalId and retrieve its on-chain publicKey.

You'll need this public key to mint tokens to the wallet, and it can also be used to identify the wallet when signing transactions on its behalf.

Query:

query GetManagedWallet {
GetManagedWallet(network: CANARY, chain: MATRIX, externalId: "docs-example-player") { #Specify the managed wallet's unique ID
publicKey
externalId
}
}

Response:

{
"data": {
"GetManagedWallet": {
"publicKey": "0xded3c8f0296f5ee023f07aa5617fc261bd5991c4474ee775a16ec35c1d1a1e3a",
"externalId": "docs-example-player"
}
}
}

Minting tokens to a Managed Wallet

With the managed wallet's public key in hand, you can mint tokens directly to it by setting it as the recipient.

mutation MintToManagedWallet {
CreateTransaction(
network: CANARY
chain: MATRIX
transaction: {
mintToken: {
recipient: "0xded3c8f0296f5ee023f07aa5617fc261bd5991c4474ee775a16ec35c1d1a1e3a" #The managed wallet's public key, from the GetManagedWallet query
collectionId: 36105 #Specify the collection ID
tokenId: 1 #Token ID to mint
amount: 1 #Amount to mint
}
}
) {
uuid
action
state
}
}

Signing transactions as a Managed Wallet

By default, every transaction the Enjin Platform creates is signed by your Wallet Daemon. To act on a user's behalf instead, you can have the platform sign a transaction with that user's managed wallet.

Both CreateTransaction and CreateBatchTransaction accept two optional arguments for this:

  • signerExternalId — the managed wallet's externalId. This is the most direct option, as you don't need to look the wallet's public key up first.
  • signerAccount — the managed wallet's public key, as returned by GetManagedWallet.

Set either one, and the platform signs that transaction with the corresponding managed wallet instead of the Wallet Daemon. Because these arguments live on CreateTransaction itself, any on-chain action — a transfer, a listing, an attribute change, anything you set on the transaction input — can be performed as a managed wallet.

Transferring tokens from a Managed Wallet

If you followed along with the previous snippets of code, you should have a managed wallet with a token in it. To transfer it out to another wallet, set signerExternalId to the managed wallet and use the transferToken action.

mutation TransferFromManagedWallet {
CreateTransaction(
network: CANARY
chain: MATRIX
signerExternalId: "docs-example-player" #The managed wallet that will sign this transaction
transaction: {
transferToken: {
recipient: "cxLf6yvvtscKrHRfKDphnzsT3eoRY45VbJvqXKub5pmj5mdbQ" #The recipient of the transfer
collectionId: 36105 #Specify the collection ID
tokenId: 1 #Token ID to transfer
amount: 1 #Amount to transfer
}
}
) {
uuid
action
state
}
}

Make sure signerExternalId (or signerAccount) is set to the managed wallet that holds the token being transferred.

Whether you're minting into a managed wallet or transferring out of one, the on-chain transaction emits the usual events on FINALIZED (e.g. MultiTokens.Minted, MultiTokens.Transferred) — with the managed wallet's address as the signer. See Working with Events for how to read them.

Sweeping a Managed Wallet

Managed wallets make onboarding effortless: a player can start earning tokens and ENJ from their very first session, without ever setting up a wallet or learning anything about crypto. When that player later decides they're ready to take full ownership of what they've earned and move to a self-custodial wallet, you don't have to shuttle each asset out by hand.

The SweepManagedWallet mutation empties a managed wallet in a single call, sending all of its transferable tokens and its ENJ to a recipient address. That turns "graduating" to self-custody into a one-click step for the player.

Set the managed wallet as the signer (with signerExternalId, or signerAddress for its public key) and the player's self-custodial wallet as the recipient:

mutation SweepManagedWallet($network: Network!, $chain: Chain!, $signerExternalId: String, $recipient: String!) {
SweepManagedWallet(
network: $network
chain: $chain
signerExternalId: $signerExternalId
recipient: $recipient
)
}

Variables:

{
"network": "CANARY",
"chain": "MATRIX",
"signerExternalId": "docs-example-player",
"recipient": "cxLf6yvvtscKrHRfKDphnzsT3eoRY45VbJvqXKub5pmj5mdbQ"
}

A successful call returns true.

The wallet's fees must be covered

Sweeping submits on-chain transfers signed by the managed wallet, so those transaction fees have to be paid somehow. Make sure one of the following holds, or the sweep will fail with no way to pay for gas:

  • The managed wallet holds enough ENJ to cover the sweep's fees, or
  • its fees are covered by a fuel tank that permits the managed wallet to dispatch — for example a Require Signature tank pointed at your Wallet Daemon address. Managed wallets are signed by the Wallet Daemon, so such a tank covers their transactions.
Runs in the background

The sweep is processed asynchronously and is rate-limited to once per hour, per wallet. The returned true confirms the sweep was accepted — not that every asset has arrived yet. Watch for the usual on-chain events (MultiTokens.Transferred, Balances.Transfer) to know when each transfer finalizes. See Working with Events.

Explore More Arguments

For a comprehensive view of all available arguments for queries and mutations, please refer to our API Reference. This resource will guide you on how to use the GraphiQL Playground to explore the full structure and functionality of our API.