Skip to main content

Sending Wallet Requests

Some in-game actions need the player to sign a transaction with their own wallet — transferring an item they own, listing loot on the marketplace, or burning a consumable. The Enjin Platform lets you send these transaction requests directly to your users' Enjin Wallet App: the request pops up in their app, their wallet signs it when they approve, and the platform broadcasts it on-chain — no third-party tooling involved.

Here's how the typical flow works:

  1. Set up your developer profile: A one-time step. Your name, image, and description are shown to users in the Enjin Wallet whenever they link or receive a request from you.
  2. Link the user's wallet: Generate a linking code, show the user its QR code, and they approve the connection by scanning it with their Enjin Wallet app. This happens once per user.
  3. Send transaction requests: Create any transaction with the linked wallet as the signer. The user receives it in their Enjin Wallet app and approves or rejects it.
  4. Track the request: Poll the transaction's state to react once the user approves and the transaction finalizes on-chain.

Linking also doubles as proof of wallet ownership — if all you need is a verified wallet address for each user, you can stop after step 2. See Verifying wallet ownership.

Onboarding with Managed Wallets first?

Many games onboard new players with Managed Wallets so they can play without installing anything. When a player is ready for self-custody, sweep their managed wallet to their own Enjin Wallet and link it using the flow on this page — from then on, you send transaction requests to their app instead of signing on their behalf.

What you'll need:
  • An Enjin Platform Account.
  • A user with the Enjin Wallet app installed.
  • For the example request on this page: a Token held by the user's wallet, and some Enjin Coin in that wallet to pay for Transaction Fees.

Setting Up Your Developer Profile

Before you can send linking requests, you must complete your Developer Profile — the identity users see in their Enjin Wallet when they're asked to link their wallet or approve a transaction. On the Enjin Platform, open Settings and fill in the Developer Profile section:

  • Image — a square JPG or PNG, at least 512×512 and up to 1 MB.
  • Name — your game or application's name.
  • Description — a short description that tells wallet users who you are.

Developer Profile settings on the Enjin Platform

Until your developer profile is completed, linking-related requests are rejected — for example, CreateLinkingCode returns an error instead of a code.

Linking a User's Wallet

To send requests to a user's Enjin Wallet app, the user must first link their wallet to your Enjin Platform account. Linking is consent-based: you generate a short-lived linking code, the user scans its QR code with their Enjin Wallet app, picks the account they want to link, and approves the connection. The link persists until the user disconnects your application from their wallet app, so this step happens only once per user.

Step 1: Create a linking code

Run the CreateLinkingCode mutation. You can pass an idempotencyKey of your choosing, or omit it and the platform generates one for you — either way, it's the key you'll use afterwards to look up which wallet address the user linked, so store it against the user's record in your database. Each key identifies one linking code and can never be reused: if a code expires before the user scans it, create the next code with a fresh key (for example, your player ID plus an attempt counter or timestamp).

mutation CreateLinkingCode($idempotencyKey: String) {
CreateLinkingCode(idempotencyKey: $idempotencyKey) {
idempotencyKey
qr
url
expires
}
}

Variables:

{
"idempotencyKey": "player-123"
}

Response:

{
"data": {
"CreateLinkingCode": {
"idempotencyKey": "player-123",
"qr": "https://platform.enjin.io/qrcode/aHR0cHM6Ly9wbGF0Zm9ybS5lbmppbi5pby9saW5rLzMyNzIzMTky",
"url": "https://platform.enjin.io/link/32723192",
"expires": "2026-08-24T15:36:12Z"
}
}
}

Present the response to the user in whichever form fits your application:

  • qr — a ready-to-display QR code image. Show it in your game/app for the user to scan with their Enjin Wallet app. This is the most common option for desktop and console.
  • url — a link that opens the linking flow directly. Useful when your application runs on the same mobile device as the Enjin Wallet app.

Linking codes are short-lived (see the expires field). If a code expires before the user completes the flow, simply create a new one with a fresh idempotencyKey.

Step 2: The user approves in their Enjin Wallet app

When the user scans the QR code with their Enjin Wallet app, they see your developer profile — the image, name, and description you configured earlier — along with a prompt to pick which account to link. Once they approve, their wallet is linked to your Enjin Platform account.

The user stays in control: they can disconnect your application from their Enjin Wallet app at any time, which removes the link and stops any further requests from reaching them.

Run the GetLinkedWallet query with the same idempotencyKey you used when creating the linking code. Once the user has approved, it returns the linked wallet; until then (or if the user has disconnected), it returns null. To catch the approval the moment it happens instead of checking repeatedly, subscribe to the WalletLinked WebSocket event — it fires with this same idempotencyKey and the linked wallet's publicKey.

query GetLinkedWallet($idempotencyKey: String) {
GetLinkedWallet(idempotencyKey: $idempotencyKey) {
publicKey
idempotencyKey
}
}

Variables:

{
"idempotencyKey": "player-123"
}

Response — user has linked their wallet:

{
"data": {
"GetLinkedWallet": {
"publicKey": "0x5a6aae294416f3e875d9a8975658905002cfd3e5e64105d76296c4b0adbfd77e",
"idempotencyKey": "player-123"
}
}
}

Response — user hasn't linked yet (or has disconnected):

{
"data": {
"GetLinkedWallet": null
}
}

Store the returned publicKey against the user's record in your database — it identifies the wallet the user linked, and it's the account you'll target with transaction requests. The hex public key and the SS58-encoded address (ef...) are two representations of the same account, and address arguments like signerAddress accept either form. You can also call GetLinkedWallet with an address argument instead of idempotencyKey to check whether a specific wallet address is linked to your account.

Watching the link state

If you poll rather than subscribe to WalletLinked, query GetLinkedWallet every few seconds while your "link your wallet" screen is open. Either way, there's no event for the user later disconnecting your application from their wallet app, so a null GetLinkedWallet response after a successful link means exactly that — treat the wallet as unlinked and offer to link again.

Verifying Wallet Ownership

Even if you never send a transaction request, the linking flow is the simplest way to learn a user's wallet address and prove they own it. Only the holder of the wallet can approve the linking request in their Enjin Wallet app — so when GetLinkedWallet returns a publicKey for the idempotencyKey you issued to that user, you can be certain the user controls that address.

From there you can associate the address with the user's account and read their wallet — check ENJ balance, list the tokens they hold, gate content based on ownership — without ever sending them a request.

Sending a Transaction Request

Once a user's wallet is linked, set the signerAddress argument to their linked address on any transaction you create. Instead of being signed by your Wallet Daemon, the request is delivered to the user's Enjin Wallet app, where they can review and approve it. Approving signs the transaction and hands it back to the platform, which broadcasts it on-chain — you don't need to handle signatures or broadcasting yourself.

The example below asks the user to transfer an NFT out of their wallet — for instance, handing an item to another player or back to the game:

mutation RequestTokenTransfer(
$signerAddress: String
$recipient: String!
$collectionId: BigInt!
$tokenId: BigInt!
$amount: BigInt!
) {
CreateTransaction(
network: ENJIN # or CANARY for testnet
chain: MATRIX
signerAddress: $signerAddress
transaction: {
transferToken: {
recipient: $recipient
collectionId: $collectionId
tokenId: $tokenId
amount: $amount
}
}
) {
uuid
state
}
}

Variables:

{
"signerAddress": "efRC9jw5LeZFqmaWBBDxZRTyaLP9dLAqixy32tSnqW9wCsb6y",
"recipient": "efRP7f5aFWWobNiNxcWGNxhY1RdRXZ4kScvwuFdD4bsBHEUZW",
"collectionId": 36105,
"tokenId": 1,
"amount": 1
}

Response:

{
"data": {
"CreateTransaction": {
"uuid": "6a1c7cb6-4b06-4a5c-9c3f-d3e1a4b2f8d0",
"state": "PENDING"
}
}
}

The user now receives the request in their Enjin Wallet app, where they can review exactly what they're being asked to sign — presented under your developer profile — and approve or reject it.

Anything you can sign, they can sign

Any transaction you can normally sign from the platform can be sent to a linked wallet instead — just set signerAddress. That includes every action available through CreateTransaction (transfers, burns, marketplace listings, ENJ infusions, nomination-pool bonds, and more), and all of its companion arguments work as usual:

  • Batches — use CreateBatchTransaction to have the user approve several actions with a single signature.
  • Fuel Tanks — add the fuelTank argument so a Fuel Tank pays the transaction fees. The user approves the transaction without spending any of their own ENJ — a big onboarding win.
  • Proxies — add proxyAddress to have the user sign a call that executes on behalf of a proxied account.

Tracking the Request

The transaction stays in the PENDING state while it waits for the user. Poll the GetTransaction query with the uuid you received to follow it:

query GetTransaction($uuid: String) {
GetTransaction(
network: ENJIN # or CANARY for testnet
chain: MATRIX
uuid: $uuid
) {
uuid
state
}
}

Variables:

{
"uuid": "6a1c7cb6-4b06-4a5c-9c3f-d3e1a4b2f8d0"
}

Response:

{
"data": {
"GetTransaction": {
"uuid": "6a1c7cb6-4b06-4a5c-9c3f-d3e1a4b2f8d0",
"state": "FINALIZED"
}
}
}

The state moves through:

  • PENDING — waiting for the user to approve in their Enjin Wallet app.
  • BROADCAST — the user approved; the signed transaction has been submitted to the blockchain.
  • FINALIZED — the transaction is included in a finalized block. Read its on-chain events to confirm the outcome and react in your application — see Working with Events.
  • FAILED — the transaction was broadcast but failed on-chain (check the error field).

If the user rejects the request or never responds, the transaction won't proceed. You can also withdraw a request that's still PENDING at any time with the CancelTransaction(uuid:) mutation — for example, when the in-game offer that triggered it expires — which marks the transaction ABANDONED.

Watching the transaction state

Subscribe to the TransactionStateChanged WebSocket event to be notified of each state change in real time, or poll GetTransaction while a request is outstanding.

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.