From 8d0fbedfca59b9380796fa4973a18b6a66f1764f Mon Sep 17 00:00:00 2001 From: Etay Matzliah Date: Sun, 5 Jul 2026 09:59:36 +0300 Subject: [PATCH 1/3] Document SweepManagedWallet mutation Add a "Sweeping a Managed Wallet" section to the managed-wallets guide covering the onboarding-to-self-custody flow, and backlink to it from the hot-cold inventories guide, the transferring-tokens guide, and the wallets API reference. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../05-transferring-tokens.md | 2 + .../03-using-managed-wallets.md | 105 +++++++++++++++++- .../07-hot-cold-inventories.md | 4 +- .../02-mutations/04-wallets-mutations.md | 32 +++++- 4 files changed, 140 insertions(+), 3 deletions(-) diff --git a/docs/02-guides/01-platform/01-managing-tokens/05-transferring-tokens.md b/docs/02-guides/01-platform/01-managing-tokens/05-transferring-tokens.md index f51519c..67f6b5b 100644 --- a/docs/02-guides/01-platform/01-managing-tokens/05-transferring-tokens.md +++ b/docs/02-guides/01-platform/01-managing-tokens/05-transferring-tokens.md @@ -686,4 +686,6 @@ Once a transfer transaction reaches `FINALIZED`, a `MultiTokens.Transferred` eve For a comprehensive view of all available arguments for queries and mutations, please refer to our [API Reference](/03-api-reference/03-api-reference.md). This resource will guide you on how to use the GraphiQL Playground to explore the full structure and functionality of our API. To sign with a managed wallet instead of the Wallet Daemon, set `signerAccount` on `CreateTransaction` / `CreateBatchTransaction`. + +To move a managed wallet's **entire** balance out in one call — every token and all its ENJ, e.g. when migrating a player to a self-custodial wallet — use [`SweepManagedWallet`](/02-guides/01-platform/02-managing-users/03-using-managed-wallets.md#sweeping-a-managed-wallet) instead of assembling the transfers yourself. ::: diff --git a/docs/02-guides/01-platform/02-managing-users/03-using-managed-wallets.md b/docs/02-guides/01-platform/02-managing-users/03-using-managed-wallets.md index 172daf9..3385521 100644 --- a/docs/02-guides/01-platform/02-managing-users/03-using-managed-wallets.md +++ b/docs/02-guides/01-platform/02-managing-users/03-using-managed-wallets.md @@ -17,7 +17,7 @@ Here's how the typical flow works: 1. **[Managed Wallet Creation](#creating-a-managed-wallet):** 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](#minting-tokens-to-a-managed-wallet):** As the user earns or deposits digital assets within your application, these assets are automatically stored in their managed wallet. 3. **[In-App Transactions](#signing-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. **[User Withdrawal (Optional)](#transferring-tokens-from-managed-wallets):** If a user decides they want to take full control of their assets, you can facilitate the transfer of all funds from their managed wallet to their own self-custodial wallet. +4. **[Graduating to Self-Custody (Optional)](#sweeping-a-managed-wallet):** If a user decides they want to take full control of their assets, a single [sweep](#sweeping-a-managed-wallet) 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. @@ -905,6 +905,109 @@ Make sure `signerExternalId` (or `signerAccount`) is set to the managed wallet t 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](/05-enjin-platform/03-working-with-events.md) for how to read them. +## Sweeping a Managed Wallet {#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 `signerAccount` for its public key) and the player's self-custodial wallet as the `recipient`: + + + +```graphql +mutation SweepManagedWallet { + SweepManagedWallet( + network: CANARY + chain: MATRIX + signerExternalId: "docs-example-player" #The managed wallet to empty + recipient: "cxLf6yvvtscKrHRfKDphnzsT3eoRY45VbJvqXKub5pmj5mdbQ" #The player's self-custodial wallet + ) +} +``` + + +``` +curl --location 'https://platform.beta.enjin.io/graphql' \ +-H 'Content-Type: application/json' \ +-H 'Authorization: Bearer enjin_api_key' \ +-d '{"query":"mutation SweepManagedWallet {\r\n SweepManagedWallet(\r\n network: CANARY\r\n chain: MATRIX\r\n signerExternalId: \"docs-example-player\"\r\n recipient: \"cxLf6yvvtscKrHRfKDphnzsT3eoRY45VbJvqXKub5pmj5mdbQ\"\r\n )\r\n}","variables":{}}' +``` + + +```javascript +fetch('https://platform.beta.enjin.io/graphql', { + method: 'POST', + headers: {'Content-Type': 'application/json','Authorization': 'Your_Platform_Token_Here'}, + body: JSON.stringify({ + query: ` + mutation SweepManagedWallet { + SweepManagedWallet( + network: CANARY + chain: MATRIX + signerExternalId: "docs-example-player" #The managed wallet to empty + recipient: "cxLf6yvvtscKrHRfKDphnzsT3eoRY45VbJvqXKub5pmj5mdbQ" #The player's self-custodial wallet + ) + } + ` + }), +}) +.then(response => response.json()) +.then(data => console.log(data)); +``` + + +```javascript +const axios = require('axios'); + +axios.post('https://platform.beta.enjin.io/graphql', { + query: ` + mutation SweepManagedWallet { + SweepManagedWallet( + network: CANARY + chain: MATRIX + signerExternalId: "docs-example-player" #The managed wallet to empty + recipient: "cxLf6yvvtscKrHRfKDphnzsT3eoRY45VbJvqXKub5pmj5mdbQ" #The player's self-custodial wallet + ) + } + ` +}, { + headers: {'Content-Type': 'application/json','Authorization': 'Bearer Your_Platform_Token_Here'} +}) +.then(response => console.log(response.data)) +.catch(error => console.error(error)); +``` + + +```python +import requests + +query = ''' +mutation SweepManagedWallet { + SweepManagedWallet( + network: CANARY + chain: MATRIX + signerExternalId: "docs-example-player" + recipient: "cxLf6yvvtscKrHRfKDphnzsT3eoRY45VbJvqXKub5pmj5mdbQ" + ) +} +''' + +response = requests.post('https://platform.beta.enjin.io/graphql', + json={'query': query}, + headers={'Content-Type': 'application/json', 'Authorization': 'Bearer Your_Platform_Token_Here'} +) +print(response.json()) +``` + + + +A successful call returns `true`. + +:::note 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](/05-enjin-platform/03-working-with-events.md). +::: + :::info Explore More Arguments For a comprehensive view of all available arguments for queries and mutations, please refer to our [API Reference](/03-api-reference/03-api-reference.md). This resource will guide you on how to use the GraphiQL Playground to explore the full structure and functionality of our API. ::: \ No newline at end of file diff --git a/docs/02-guides/01-platform/03-advanced-mechanics/07-hot-cold-inventories.md b/docs/02-guides/01-platform/03-advanced-mechanics/07-hot-cold-inventories.md index b90240d..0bedcec 100644 --- a/docs/02-guides/01-platform/03-advanced-mechanics/07-hot-cold-inventories.md +++ b/docs/02-guides/01-platform/03-advanced-mechanics/07-hot-cold-inventories.md @@ -152,7 +152,9 @@ mutation MoveToCold { ### Move several items at once -If a player moves several different item types between inventories at once — a "withdraw everything to my wallet" button, say — bundle the actions into a single [`CreateBatchTransaction`](/03-api-reference/02-mutations/01-transaction-mutations.md) so they settle atomically with one fee instead of many. +If a player moves several different item types between inventories at once — bringing a whole loadout to hot at the start of a session, say — bundle the actions into a single [`CreateBatchTransaction`](/03-api-reference/02-mutations/01-transaction-mutations.md) so they settle atomically with one fee instead of many. + +To empty a managed wallet completely — for instance when a player graduates their cold inventory to a self-custodial wallet — you don't need a batch at all: a single [`SweepManagedWallet`](/02-guides/01-platform/02-managing-users/03-using-managed-wallets.md#sweeping-a-managed-wallet) transfers every token and all their ENJ out in one call. ### Cover the fees diff --git a/docs/03-api-reference/02-mutations/04-wallets-mutations.md b/docs/03-api-reference/02-mutations/04-wallets-mutations.md index bfc8816..78e4a9b 100644 --- a/docs/03-api-reference/02-mutations/04-wallets-mutations.md +++ b/docs/03-api-reference/02-mutations/04-wallets-mutations.md @@ -11,7 +11,7 @@ import TabItem from '@theme/TabItem'; `https://platform.beta.enjin.io/graphql` ::: -The Enjin Platform has one wallet-related mutation: `CreateManagedWallet`. Managed wallets are keypairs derived from the Wallet Daemon's seed plus an `externalId` you control — they let your application create signing accounts on demand without managing private keys. +The Enjin Platform has two wallet-related mutations: `CreateManagedWallet` and `SweepManagedWallet`. Managed wallets are keypairs derived from the Wallet Daemon's seed plus an `externalId` you control — they let your application create signing accounts on demand without managing private keys. For end-user wallets connected via WalletConnect, there's no mutation here — the user's wallet already exists on chain. To submit transactions signed by an end-user wallet, see `CreateTransaction(..., signerAddress: )` followed by `SignTransaction(uuid:, signedExtrinsic:)` on the [Transactions](/03-api-reference/02-mutations/01-transaction-mutations.md) page. @@ -42,6 +42,36 @@ mutation CreateManagedWallet { +## SweepManagedWallet + +Transfers **all** transferable tokens and ENJ out of a managed wallet to a single `recipient` in one call — the simplest way to migrate a player to a self-custodial wallet. Identify the wallet to empty with `signerExternalId` (or `signerAccount` for its public key); the work runs asynchronously and is rate-limited to once per hour per wallet. Returns `true` once the sweep is accepted. + +See [Sweeping a Managed Wallet](/02-guides/01-platform/02-managing-users/03-using-managed-wallets.md#sweeping-a-managed-wallet) for the full walkthrough. + + + +```graphql +mutation SweepManagedWallet { + SweepManagedWallet( + network: ENJIN + chain: MATRIX + signerExternalId: "e73f9f38-6832-4822-922b-b9225245ba24" + recipient: "cxLf6yvvtscKrHRfKDphnzsT3eoRY45VbJvqXKub5pmj5mdbQ" + ) +} +``` + + +```json +{ + "data": { + "SweepManagedWallet": true + } +} +``` + + + :::tip Looking for the signing flow? - For Wallet Daemon-signed transactions, see [`CreateTransaction`](/03-api-reference/02-mutations/01-transaction-mutations.md#createtransaction). - For Managed Wallet-signed transactions, pass `signerAddress` or `signerExternalId` on `CreateTransaction`. From 0fd5ca030d34b2f0511396cb319359c0e59f6275 Mon Sep 17 00:00:00 2001 From: Etay Matzliah Date: Mon, 6 Jul 2026 12:50:33 +0300 Subject: [PATCH 2/3] Parameterize SweepManagedWallet snippets; add fee-coverage note Convert the SweepManagedWallet examples (guide + API reference) to use GraphQL variables, and add CLAUDE.md documenting the "use GraphQL variables in snippets" convention for future contributors. Add a note that sweeping needs the managed wallet to hold ENJ or be covered by a fuel tank, linking to the Require Signature setup. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../03-using-managed-wallets.md | 85 +++++++++++++------ .../02-mutations/04-wallets-mutations.md | 21 +++-- 2 files changed, 77 insertions(+), 29 deletions(-) diff --git a/docs/02-guides/01-platform/02-managing-users/03-using-managed-wallets.md b/docs/02-guides/01-platform/02-managing-users/03-using-managed-wallets.md index 3385521..6f38f68 100644 --- a/docs/02-guides/01-platform/02-managing-users/03-using-managed-wallets.md +++ b/docs/02-guides/01-platform/02-managing-users/03-using-managed-wallets.md @@ -916,14 +916,25 @@ Set the managed wallet as the signer (with `signerExternalId`, or `signerAccount ```graphql -mutation SweepManagedWallet { +mutation SweepManagedWallet($network: Network!, $chain: Chain!, $signerExternalId: String, $recipient: String!) { SweepManagedWallet( - network: CANARY - chain: MATRIX - signerExternalId: "docs-example-player" #The managed wallet to empty - recipient: "cxLf6yvvtscKrHRfKDphnzsT3eoRY45VbJvqXKub5pmj5mdbQ" #The player's self-custodial wallet + network: $network + chain: $chain + signerExternalId: $signerExternalId + recipient: $recipient ) } +``` + +Variables: + +```json +{ + "network": "CANARY", + "chain": "MATRIX", + "signerExternalId": "docs-example-player", + "recipient": "cxLf6yvvtscKrHRfKDphnzsT3eoRY45VbJvqXKub5pmj5mdbQ" +} ``` @@ -931,7 +942,7 @@ mutation SweepManagedWallet { curl --location 'https://platform.beta.enjin.io/graphql' \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer enjin_api_key' \ --d '{"query":"mutation SweepManagedWallet {\r\n SweepManagedWallet(\r\n network: CANARY\r\n chain: MATRIX\r\n signerExternalId: \"docs-example-player\"\r\n recipient: \"cxLf6yvvtscKrHRfKDphnzsT3eoRY45VbJvqXKub5pmj5mdbQ\"\r\n )\r\n}","variables":{}}' +-d '{"query":"mutation SweepManagedWallet($network: Network!, $chain: Chain!, $signerExternalId: String, $recipient: String!) {\r\n SweepManagedWallet(network: $network, chain: $chain, signerExternalId: $signerExternalId, recipient: $recipient)\r\n}","variables":{"network":"CANARY","chain":"MATRIX","signerExternalId":"docs-example-player","recipient":"cxLf6yvvtscKrHRfKDphnzsT3eoRY45VbJvqXKub5pmj5mdbQ"}}' ``` @@ -941,15 +952,21 @@ fetch('https://platform.beta.enjin.io/graphql', { headers: {'Content-Type': 'application/json','Authorization': 'Your_Platform_Token_Here'}, body: JSON.stringify({ query: ` - mutation SweepManagedWallet { + mutation SweepManagedWallet($network: Network!, $chain: Chain!, $signerExternalId: String, $recipient: String!) { SweepManagedWallet( - network: CANARY - chain: MATRIX - signerExternalId: "docs-example-player" #The managed wallet to empty - recipient: "cxLf6yvvtscKrHRfKDphnzsT3eoRY45VbJvqXKub5pmj5mdbQ" #The player's self-custodial wallet + network: $network + chain: $chain + signerExternalId: $signerExternalId + recipient: $recipient ) } - ` + `, + variables: { + network: "CANARY", + chain: "MATRIX", + signerExternalId: "docs-example-player", // the managed wallet to empty + recipient: "cxLf6yvvtscKrHRfKDphnzsT3eoRY45VbJvqXKub5pmj5mdbQ" // the player's self-custodial wallet + } }), }) .then(response => response.json()) @@ -962,15 +979,21 @@ const axios = require('axios'); axios.post('https://platform.beta.enjin.io/graphql', { query: ` - mutation SweepManagedWallet { + mutation SweepManagedWallet($network: Network!, $chain: Chain!, $signerExternalId: String, $recipient: String!) { SweepManagedWallet( - network: CANARY - chain: MATRIX - signerExternalId: "docs-example-player" #The managed wallet to empty - recipient: "cxLf6yvvtscKrHRfKDphnzsT3eoRY45VbJvqXKub5pmj5mdbQ" #The player's self-custodial wallet + network: $network + chain: $chain + signerExternalId: $signerExternalId + recipient: $recipient ) } - ` + `, + variables: { + network: "CANARY", + chain: "MATRIX", + signerExternalId: "docs-example-player", // the managed wallet to empty + recipient: "cxLf6yvvtscKrHRfKDphnzsT3eoRY45VbJvqXKub5pmj5mdbQ" // the player's self-custodial wallet + } }, { headers: {'Content-Type': 'application/json','Authorization': 'Bearer Your_Platform_Token_Here'} }) @@ -983,18 +1006,25 @@ axios.post('https://platform.beta.enjin.io/graphql', { import requests query = ''' -mutation SweepManagedWallet { +mutation SweepManagedWallet($network: Network!, $chain: Chain!, $signerExternalId: String, $recipient: String!) { SweepManagedWallet( - network: CANARY - chain: MATRIX - signerExternalId: "docs-example-player" - recipient: "cxLf6yvvtscKrHRfKDphnzsT3eoRY45VbJvqXKub5pmj5mdbQ" + network: $network + chain: $chain + signerExternalId: $signerExternalId + recipient: $recipient ) } ''' +variables = { + 'network': 'CANARY', + 'chain': 'MATRIX', + 'signerExternalId': 'docs-example-player', + 'recipient': 'cxLf6yvvtscKrHRfKDphnzsT3eoRY45VbJvqXKub5pmj5mdbQ' +} + response = requests.post('https://platform.beta.enjin.io/graphql', - json={'query': query}, + json={'query': query, 'variables': variables}, headers={'Content-Type': 'application/json', 'Authorization': 'Bearer Your_Platform_Token_Here'} ) print(response.json()) @@ -1004,6 +1034,13 @@ print(response.json()) A successful call returns `true`. +:::warning 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](/02-guides/01-platform/02-managing-users/04-using-fuel-tanks.md#recommended-setup). Managed wallets are signed by the Wallet Daemon, so such a tank covers their transactions. +::: + :::note 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](/05-enjin-platform/03-working-with-events.md). ::: diff --git a/docs/03-api-reference/02-mutations/04-wallets-mutations.md b/docs/03-api-reference/02-mutations/04-wallets-mutations.md index 78e4a9b..65c2203 100644 --- a/docs/03-api-reference/02-mutations/04-wallets-mutations.md +++ b/docs/03-api-reference/02-mutations/04-wallets-mutations.md @@ -51,14 +51,25 @@ See [Sweeping a Managed Wallet](/02-guides/01-platform/02-managing-users/03-usin ```graphql -mutation SweepManagedWallet { +mutation SweepManagedWallet($network: Network!, $chain: Chain!, $signerExternalId: String, $recipient: String!) { SweepManagedWallet( - network: ENJIN - chain: MATRIX - signerExternalId: "e73f9f38-6832-4822-922b-b9225245ba24" - recipient: "cxLf6yvvtscKrHRfKDphnzsT3eoRY45VbJvqXKub5pmj5mdbQ" + network: $network + chain: $chain + signerExternalId: $signerExternalId + recipient: $recipient ) } +``` + +Variables: + +```json +{ + "network": "ENJIN", + "chain": "MATRIX", + "signerExternalId": "e73f9f38-6832-4822-922b-b9225245ba24", + "recipient": "cxLf6yvvtscKrHRfKDphnzsT3eoRY45VbJvqXKub5pmj5mdbQ" +} ``` From feeb2ced7f550713d2ad538a66e86ada0ab30f38 Mon Sep 17 00:00:00 2001 From: Etay Matzliah Date: Mon, 6 Jul 2026 13:09:40 +0300 Subject: [PATCH 3/3] Use signerAddress (not signerAccount) in sweep docs signerAccount is not a valid CreateTransaction/SweepManagedWallet argument; the schema exposes signerAddress. (General-page occurrences are fixed on the fuel-tank branch.) Co-Authored-By: Claude Opus 4.8 (1M context) --- .../01-platform/02-managing-users/03-using-managed-wallets.md | 2 +- docs/03-api-reference/02-mutations/04-wallets-mutations.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/02-guides/01-platform/02-managing-users/03-using-managed-wallets.md b/docs/02-guides/01-platform/02-managing-users/03-using-managed-wallets.md index 6f38f68..c0224bf 100644 --- a/docs/02-guides/01-platform/02-managing-users/03-using-managed-wallets.md +++ b/docs/02-guides/01-platform/02-managing-users/03-using-managed-wallets.md @@ -911,7 +911,7 @@ Managed wallets make onboarding effortless: a player can start earning tokens an 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 `signerAccount` for its public key) and the player's self-custodial wallet as the `recipient`: +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`: diff --git a/docs/03-api-reference/02-mutations/04-wallets-mutations.md b/docs/03-api-reference/02-mutations/04-wallets-mutations.md index 65c2203..0a7d5fd 100644 --- a/docs/03-api-reference/02-mutations/04-wallets-mutations.md +++ b/docs/03-api-reference/02-mutations/04-wallets-mutations.md @@ -44,7 +44,7 @@ mutation CreateManagedWallet { ## SweepManagedWallet -Transfers **all** transferable tokens and ENJ out of a managed wallet to a single `recipient` in one call — the simplest way to migrate a player to a self-custodial wallet. Identify the wallet to empty with `signerExternalId` (or `signerAccount` for its public key); the work runs asynchronously and is rate-limited to once per hour per wallet. Returns `true` once the sweep is accepted. +Transfers **all** transferable tokens and ENJ out of a managed wallet to a single `recipient` in one call — the simplest way to migrate a player to a self-custodial wallet. Identify the wallet to empty with `signerExternalId` (or `signerAddress` for its public key); the work runs asynchronously and is rate-limited to once per hour per wallet. Returns `true` once the sweep is accepted. See [Sweeping a Managed Wallet](/02-guides/01-platform/02-managing-users/03-using-managed-wallets.md#sweeping-a-managed-wallet) for the full walkthrough.