SphereOneManager

The SphereOneManager is how you will interact with the SDK. Its a singleton MonoBehavior that can be accessed anywhere in your code, and will stay instantiated across scenes with DontDestroyOnLoad(). It should be placed in your starting scene.

Configuration

Once you have successfully installed the SDK and added the SphereOneManager to your unity scene you will be required to fill in the following information:

  1. API Key - This is the API key provided by SphereOne which can be accessed in the Settings tab on the SphereOne Dashboard (https://dashboard.sphereone.xyz/). (Required for all Platforms)
  2. Client ID - This is the ID provided by SphereOne which can be accessed after creating an OAUTH client on the SphereOne Dashboard, a guide to the creation of the OAUTH client can be found on the Self-Service OIDC Configuration page. (Required for all Platforms)
  3. Scheme - This is the Redirect Scheme the SDK will use to authenticate users on Android. (Required for Android)
  4. Redirect URL - This is the URL your game is hosted at when testing your build locally it will be hosted at http://localhost:<port> with the <port> attribute being dependant on how your unity has been set up.
  • Windows / Mac / IOS

๐Ÿ‘

Able to be tested directly in the Unity Editor

  • Android

๐Ÿšง

Running this in the Unity Editor will produce a error, SphereOneSDK: Login Failed: Only 'http' and 'https' schemes are supported . It is nessacary to build and run it on a AndroidDevice for testing

  • How to setup a custom Redirect Scheme on Android:
  • A custom Redirect Scheme can be used to differentiate your app from others installed on the user's device. The default is sphereone. Please be sure to use all lowercase.
  • The Redirect Scheme can be configured directly on the SphereOneManager component, it is important to follow the next steps when using a custom Scheme:
    • Open the AndroidManifest File (Located at Assets/Plugins/Android/AndroidManifest.xml)
    • Locate the Android:scheme attribute and update to be the same as the Scheme you have entered on the SphereOneManager Component.
  • WebGL

๐Ÿ‘

Able to be tested directly in the Unity Editor

Login Mode

You will need to choose a login mode for you game. Currently we support 2 modes: Slideout and Popup.

  • Slideout Mode
    • This mode will embed the SphereOne Wallet (https://wallet.sphereone.xyz) into an iframe that slides out on top of your game. This gives your players a convenient way to quickly access their wallet without leaving the game.
  • Popup Mode
    • This mode will launch an auth popup when SphereOneManager.Instance.Login() is called. Once the player logs in, it will redirect them back to your game and load their credentials.
    • Once authenticated, the SDK will encrypt the player's credentials and store in the browser's local storage.

iOS/Mac

  • The Redirect Scheme is always UnitySafariViewControllerScheme instead of sphereone. The complete Redirect Url is UnitySafariViewControllerScheme://auth.
  • If building for iOS, please be sure to use the latest Unity version. There is sometimes an incompatibility between the Xcode project generated by Unity and the current Xcode on one's MacBook. Please consult help from the Unity community.

Windows

  • The Redirect Scheme isn't enabled for users to input their own custom scheme. Instead, currently, it uses the default http://localhost:8080/win-standalone/oauth2 as its Redirect Scheme/Url.

Environments

  • Editor
    • For local testing in the editor (play mode), mock data can be used to simulate data coming from the SphereOne API. This mode uses mock data from multiple json files located in SphereOne / Scripts / MockData.
    • You must switch to production before building.
  • Production
    • This environment will connect to the production SphereOne API, and use real data.

Accessing the SDK

using SphereOne;

...

SphereOneManager.Instance

Accessing Data

User Data (eg. Email, UID, Name)

To access the currently signed-in user's data, the SDK has public User member.

SphereOneManager.Instance.User;

Example Data:

{
  "data": {
    "uid": "google-oauth2|106336942023214914849",
    "signedUp": true,
    "email": "[email protected]",
    "currencyISO": "USD",
    "countryFlag": "๐Ÿ‡บ๐Ÿ‡ธ",
    "countryCode": "US",
    "name": "[email protected]",
    "username": "[email protected]",
    "isMerchant": false
  },
  "error": null
}


Users Wallets

To access the currently signed-in user's wallets, the SDK has public Wallets member.

// it is an array of Wallet
SphereOneManager.Instance.Wallets;

Example Data:

{
  "data": [
    {
      "address": "0x05EBEACD2296b979E72966420914E3409079355e",
      "chains": ["ETHEREUM", "GNOSIS", "IMMUTABLE", "FANTOM", "EOSEVM"],
      "isImported": false,
      "publicKey": "0x05EBEACD2296b979E72966420914E3409079355e",
      "type": "EOA",
      "uid": "0x05EBEACD2296b979E72966420914E3409079355e",
      "label": ""
    },
    {
      "address": "0x16fBF98a7dDc3957168Ae4670d63582DEF3D67C7",
      "chains": [
        "ETHEREUM",
        "POLYGON",
        "AVALANCHE",
        "OPTIMISM",
        "GNOSIS",
        "ARBITRUM",
        "BINANCE",
        "FANTOM"
      ],
      "isImported": true,
      "publicKey": "0x16fBF98a7dDc3957168Ae4670d63582DEF3D67C7",
      "type": "EOA",
      "uid": "0x16fBF98a7dDc3957168Ae4670d63582DEF3D67C7",
      "label": ""
    },
    {
      "address": "0x41cC1be3efa8304dD0c74d156e95E11a9eEF836f",
      "chains": ["POLYGON", "AVALANCHE", "BINANCE", "ARBITRUM", "OPTIMISM"],
      "isImported": false,
      "publicKey": "0x41cC1be3efa8304dD0c74d156e95E11a9eEF836f",
      "type": "SmartWallet",
      "uid": "0x41cC1be3efa8304dD0c74d156e95E11a9eEF836f",
      "label": ""
    },
    {
      "address": "AsBZ4wwTGXejbZhsBLXefgfyDpJQmdtt4dK1Xs6Myk3L",
      "chains": ["SOLANA"],
      "isImported": false,
      "publicKey": "AsBZ4wwTGXejbZhsBLXefgfyDpJQmdtt4dK1Xs6Myk3L",
      "type": "EOA",
      "uid": "AsBZ4wwTGXejbZhsBLXefgfyDpJQmdtt4dK1Xs6Myk3L",
      "label": ""
    }
  ],
  "error": null
}


Users Wallet Balance
To access the currently signed-in user's balances, the SDK has public Balances and TotalBalance members.

// This contains an array of `Balance` objects, which contain information about individual tokens
// on different chains and their amounts and prices
SphereOneManager.Instance.Balances;

// This is the total computed balance in USD based on the `Balances` member
SphereOneManager.Instance.TotalBalance;

Example Data:

{
  "error": null,
  "data": {
    "total": "7793036",
    "balances": [
      {
        "address": "0x16fbf98a7ddc3957168ae4670d63582def3d67c7",
        "amount": "120110461830924",
        "price": "219287",
        "chain": "ETHEREUM",
        "tokenMetadata": {
          "chain": "ETHEREUM",
          "address": "0x0000000000000000000000000000000000000000",
          "logoURI": "https://tokens.1inch.io/0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee.png",
          "decimals": 18,
          "name": "Ethereum",
          "symbol": "ETH"
        }
      },
      {
        "address": "0x16fbf98a7ddc3957168ae4670d63582def3d67c7",
        "amount": "988223705304313064",
        "price": "657238",
        "chain": "POLYGON",
        "tokenMetadata": {
          "chain": "POLYGON",
          "address": "0x0000000000000000000000000000000000000000",
          "logoURI": "https://tokens.1inch.io/0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0.png",
          "decimals": 18,
          "name": "Matic",
          "symbol": "MATIC"
        }
      },
      {
        "address": "0x16fBF98a7dDc3957168Ae4670d63582DEF3D67C7",
        "amount": "27316894744052",
        "price": "49908",
        "chain": "OPTIMISM",
        "tokenMetadata": {
          "chain": "OPTIMISM",
          "address": "0x0000000000000000000000000000000000000000",
          "logoURI": "https://tokens.1inch.io/0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee.png",
          "decimals": 18,
          "name": "Ethereum",
          "symbol": "ETH"
        }
      },
      {
        "address": "0x16fbf98a7ddc3957168ae4670d63582def3d67c7",
        "amount": "1024209000000000",
        "price": "247262",
        "chain": "BINANCE",
        "tokenMetadata": {
          "chain": "BINANCE",
          "address": "0x0000000000000000000000000000000000000000",
          "logoURI": "https://tokens.1inch.io/0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c_1.png",
          "decimals": 18,
          "name": "BNB",
          "symbol": "BNB"
        }
      },
      {
        "address": "0x16fbf98a7ddc3957168ae4670d63582def3d67c7",
        "amount": "66215800000000",
        "price": "120955",
        "chain": "ARBITRUM",
        "tokenMetadata": {
          "chain": "ARBITRUM",
          "address": "0x0000000000000000000000000000000000000000",
          "logoURI": "https://tokens.1inch.io/0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c_1.png",
          "decimals": 18,
          "name": "Ethereum",
          "symbol": "ETH"
        }
      },
      {
        "chain": "ETHEREUM",
        "address": "0x16fBF98a7dDc3957168Ae4670d63582DEF3D67C7",
        "tokenMetadata": {
          "symbol": "IMX",
          "chain": "ETHEREUM",
          "address": "0xF57e7e7C23978C3cAEC3C3548E3D615c346e79fF",
          "decimals": 18,
          "name": "Immutable X",
          "logoURI": "https://tokens.1inch.io/0xf57e7e7c23978c3caec3c3548e3d615c346e79ff.png"
        },
        "amount": "5972090000000000000",
        "price": "4367574"
      },
      {
        "chain": "POLYGON",
        "address": "0x16fBF98a7dDc3957168Ae4670d63582DEF3D67C7",
        "tokenMetadata": {
          "symbol": "IXT",
          "chain": "POLYGON",
          "address": "0xE06Bd4F5aAc8D0aA337D13eC88dB6defC6eAEefE",
          "decimals": 18,
          "name": "PlanetIX",
          "logoURI": "https://tokens.1inch.io/0xe06bd4f5aac8d0aa337d13ec88db6defc6eaeefe.png"
        },
        "amount": "2061916140165942948",
        "price": "511965"
      },
      {
        "chain": "POLYGON",
        "address": "0x16fBF98a7dDc3957168Ae4670d63582DEF3D67C7",
        "tokenMetadata": {
          "symbol": "USDC",
          "chain": "POLYGON",
          "address": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
          "decimals": 6,
          "name": "USD Coin (PoS)",
          "logoURI": "https://tokens.1inch.io/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48.png"
        },
        "amount": "687727",
        "price": "687727"
      },
      {
        "chain": "OPTIMISM",
        "address": "0x16fBF98a7dDc3957168Ae4670d63582DEF3D67C7",
        "tokenMetadata": {
          "symbol": "USDC",
          "chain": "OPTIMISM",
          "address": "0x7F5c764cBc14f9669B88837ca1490cCa17c31607",
          "decimals": 6,
          "name": "USD Coin",
          "logoURI": "https://tokens.1inch.io/0x7f5c764cbc14f9669b88837ca1490cca17c31607.png"
        },
        "amount": "80000",
        "price": "80000"
      },
      {
        "chain": "AVALANCHE",
        "address": "0x16fBF98a7dDc3957168Ae4670d63582DEF3D67C7",
        "tokenMetadata": {
          "symbol": "USDt",
          "chain": "AVALANCHE",
          "address": "0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7",
          "decimals": 6,
          "name": "TetherToken (USDt)",
          "logoURI": "https://tokens.1inch.io/0x9702230a8ea53601f5cd2dc00fdbc13d4df4a8c7.png"
        },
        "amount": "100000",
        "price": "99897"
      },
      {
        "chain": "AVALANCHE",
        "address": "0x16fBF98a7dDc3957168Ae4670d63582DEF3D67C7",
        "tokenMetadata": {
          "symbol": "USDC",
          "chain": "AVALANCHE",
          "address": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
          "decimals": 6,
          "name": "USD Coin (USDC)",
          "logoURI": "https://tokens.1inch.io/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48.png"
        },
        "amount": "238298",
        "price": "238298"
      },
      {
        "chain": "ARBITRUM",
        "address": "0x16fBF98a7dDc3957168Ae4670d63582DEF3D67C7",
        "tokenMetadata": {
          "symbol": "USDC.e",
          "chain": "ARBITRUM",
          "address": "0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8",
          "decimals": 6,
          "name": "Bridged USDC",
          "logoURI": "https://tokens.1inch.io/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48.png"
        },
        "amount": "82711",
        "price": "82711"
      },
      {
        "chain": "ARBITRUM",
        "address": "0x16fBF98a7dDc3957168Ae4670d63582DEF3D67C7",
        "tokenMetadata": {
          "symbol": "USDT",
          "chain": "ARBITRUM",
          "address": "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9",
          "decimals": 6,
          "name": "Tether USD",
          "logoURI": "https://tokens.1inch.io/0xdac17f958d2ee523a2206206994597c13d831ec7.png"
        },
        "amount": "430600",
        "price": "430214"
      }
    ]
  }
}

Users NFTs

To access the currently signed-in user's NFTs, the SDK has public NFTs member.

SphereOneManager.Instance.NFTs

Example Data:

{
  "error": null,
  "data": [
    {
      "img": "https://usdclive.org/usdc.png",
      "name": "$1500 USDC",
      "address": "0xAbD94eb1D695760aDbc3101d15da96418F1f8Fb6",
      "tokenType": "ERC721"
    },
    {
      "img": "https://usdtevent.com/usdt.png",
      "name": "$2000 USDT",
      "address": "0xc561Cda5aEAdD18392D4B10Df694d112B2f559E4",
      "tokenType": "ERC721"
    },
    {
      "img": "https://ethercbase.com/image.png",
      "name": "$ETH NFT EVENT",
      "address": "0x778e66407B5E613eb9bf5cad52c88f1094cFCB84",
      "tokenType": "ERC721"
    },
    {
      "img": "https://bafybeihxfge3p26j5zddpu4hafvcbippcqt6eed7anbjc3xy7tvmqx7hsy.ipfs.w3s.link/398.png",
      "name": "Immutable Airdrop #398",
      "address": "0x44D380E142213b77E586e1709996f5BbA6CE8099",
      "tokenType": "ERC721"
    }
  ]
}


Events

The SDK provides delegate events that your scrips can observe.

These events trigger when new data is fetched from the Sphere One API. This happens during the awake() method of SphereOneManager, or when you manually call to fetch new data.

void OnEnable()
{
  SphereOneManager.onUserLoaded += UserLoaded;
  SphereOneManager.onUserLogout += ClearAll;
  SphereOneManager.onUserWalletsLoaded += WalletsLoaded;
  SphereOneManager.onUserBalancesLoaded += AccountsLoaded;
  SphereOneManager.onUserNftsLoaded += NftsLoaded;
}

void OnDisable()
{
  SphereOneManager.onUserLoaded -= UserLoaded;
  SphereOneManager.onUserLogout -= ClearAll;
  SphereOneManager.onUserWalletsLoaded -= WalletsLoaded;
  SphereOneManager.onUserBalancesLoaded -= AccountsLoaded;
  SphereOneManager.onUserNftsLoaded -= NftsLoaded;
}

void UserLoaded(User user)
{
  // do work
}

void WalletsLoaded(List<Wallet> wallets)
{
  // do work
}

...

Create a charge

To create a charge, please follow the proper version since there are two different implementations, based on the version of the unitypackage.

For v1.5 of the SphereOne Unity SDK, you can find example implementation here: SphereOne Unity v1.5 -> TestSphereOneManager#CreateCharge .

var chargeItems = new List<ChargeItem>
{
  new ChargeItem
  {
      name = "Your Item",
      image = "https://your-image-url.somewhere.com",
      amount = 0.9,
      quantity = 1,
   }
};

var chargeRequest = new ChargeReqBody
{
    chain = SupportedChains.SOLANA,
    symbol = "SOL",
    amount = 0.9,
    tokenAddress = "So11111111111111111111111111111111111111112",
    items = chargeItems,
    successUrl = "https://your-website.com/success",
    cancelUrl = "https://your-website.com/cancel",
};

var isTest = false;
var charge = await SphereOneManager.Instance.CreateCharge(chargeRequest, isTest);

if (charge == null) {
  // Handle the error
  return;
}

Debug.Log(charge.ToString());

For v1.6 (latest) of the SphereOne Unity SDK, you can find reference implementation here: SphereOne Unity v1.6 -> TestSphereOneManager#CreateCharge.

// DO REMEMBER TO TRY-CATCH.
var chargeItems = new List<ChargeItem>
{
  new ChargeItem
  {
      name = "Your Item",
      image = "https://your-image-url.somewhere.com",
      amount = 0.9,
      quantity = 1,
   }
};

var chargeRequest = new ChargeReqBody
{
    chain = SupportedChains.SOLANA,
    symbol = "SOL",
    amount = 0.9,
    tokenAddress = "So11111111111111111111111111111111111111112",
    items = chargeItems,
    successUrl = "https://your-website.com/success",
    cancelUrl = "https://your-website.com/cancel",
};

// for test charges with test money. Please use `Test API Key` instead of `Prod API Key`
var isTest = false;
// Always False. But please see https://docs.sphereone.xyz/docs/making-payments-with-an-external-wallet#create-charge.
var isDirectTransfer = false;

// for normal charges
var charge = await SphereOneManager.Instance.CreateCharge(chargeRequest, isTest, isDirectTransfer);

// =========== example for smart contract calls ===========
// More info here: https://docs.sphereone.xyz/docs/whitelist-contract
// var functionParams = new List<string>();
// functionParams.Add("900000000"); // 0.9*10^9 (0.9 SOL)
// 
// var smartContractPropsData = new CallSmartContractProps
// {
//		alias = "test", //<-- needs to be registered beforehand in SphereOne Merchant Dashboard
//    nativeValue = "0", //<-- SOL is already the native currency for SOLANA, so no need.
//    functionParams = functionParams.ToArray()
// };
// var charge = await SphereOneManager.Instance.CreateCharge(chargeRequest, isTest, isDirectTransfer, smartContractPropsData);
// =========== example for smart contract calls ===========

/* the response from `charge`(for both scenarios above) has this response:
  {
  	paymentUrl: string, //<-- payment url for self-hosted checkout page
    chargeId: string //<-- chargeId to be used for route estimation and payment
  }
*/

// just printing the response for now, but you should technically want to store this somewhere
Debug.Log(charge.ToString());

Pay a charge

There are two ways to pay for a Charge.

(1) To pay in SphereOne's Self-hosted Checkout Page. Please use the paymentUrl that's returned from the charge response. Once inside SphereOne's Self-hosted Checkout Page, all payments and onramps are handled by SphereOne. No further implementation needed.

// Create a charge

var chargeItems = new List<ChargeItem>
{
  new ChargeItem
  {
      name = "Your Item",
      image = "https://your-image-url.somewhere.com",
      amount = 0.9,
      quantity = 1,
   }
};

var chargeRequest = new ChargeReqBody
{
    chain = SupportedChains.SOLANA,
    symbol = "SOL",
    amount = 0.9,
    tokenAddress = "So11111111111111111111111111111111111111112",
    items = chargeItems,
    successUrl = "https://your-website.com/success",
    cancelUrl = "https://your-website.com/cancel",
};

var isTest = false;
var isDirectTransfer = false;

var charge = await SphereOneManager.Instance.CreateCharge(chargeRequest, isTest, isDirectTransfer);

/* the response from `charge`(for both scenarios above) has this response:
  {
  	paymentUrl: string, //<-- payment url for self-hosted checkout page
    chargeId: string //<-- chargeId to be used for route estimation and payment
  }
*/

// Open the PaymentUrl in the user's default web browser
Application.OpenURL(charge.paymentUrl);

(2) Paying for the Charge can be done within the SDK itself. Please follow the exact steps below.

In v1.5, this is the basics of paying for a charge. But Caution: This is deprecated and won't work with current system. Please upgrade to v1.6 if you wish to make payments within the SDK.

You can find example implementation here: SphereOne Unity v1.5 -> TestSphereOneManager#PayCharge .

var payment = await SphereOneManager.Instance.PayCharge(charge.chargeId);

if (payment == null)
{
  // Handle the error
  return;
}

Debug.Log(payment.ToString());

// Payment has been submitted, it will take a few minutes to process

// At this point, your game server should listen to the webhook from sphereone (setup in the merchant dashboard)
// Sphereone will trigger the webhook when the payment completes successfully or fails

In v.1.6 (latest), there's been a major update. Please follow the exact steps below.

To pay for a charge after creating it, the sdk would need to call GetRouteEstimation. This will construct an estimated route for payment and determine if the user has enough to cover for the amount to be paid, and additional amount to cover for gas used in swaps and bridges.

You can find example implementation here: SphereOne Unity -> TestSphereOneManager#GetRouteEstimation

if (_chargeId == null)
  return;

try
{
  // pass in the `chargeId` generated from `Create a Charge` example
  var routeEstimation = await SphereOneManager.Instance.GetRouteEstimation(chargeId);
  // for now, just printing out the result
  Debug.Log(routeEstimation.ToString());
}
catch (RouteEstimateError e)
{
  // If user don't have money or have enough to cover the full transaction, an error will be thrown
  // which requires user to onramp with fiat.
  
  // Thus, the error response has a provided link to onramp.
  string onRampLink = e.onrampLink;
  // Open the onRampLink in the user's default web browser
  Application.OpenURL(onRampLink);
  return;
}
catch (Exception e)
{
  // If there's an internal server error or bad request, it's a generic error.
  // For now, just printing out the error message.
  Debug.LogError($"An error occurred while getting the route estimation: {e.Message}");
}

If GetRouteEstimation is successful, the sdk can now prompt user for their PIN.

You can find example implementation here: SphereOne Unity v1.6 -> TestSphereOneManager#OpenPinCode .

if (_chargeId == null)
  return;

// pass in the `chargeId` generated from `Create a Charge` example
SphereOneManager.Instance.OpenPinCode(chargeId);
// This will open a Window Popup that will prompt user for their PIN. And once PIN is successful,
// the Window Popup should closed itself and a `DEK` should be returned to the SDK.

Once this is done, the sdk can now prompt for user to pay the charge.

You can find example implementation here: SphereOne Unity v1.6 -> TestSphereOneManager#PayCharge .

if (_chargeId == null)
  return;

try
{
  // pass in the `chargeId` generated from `Create a Charge` example
  var payment = await SphereOneManager.Instance.PayCharge(chargeId);
  // just printing out the result for now
  Debug.Log(payment.ToString()); // if successful, result should say that, Payment has officially kicked off.
  
  // But once Payment has been submitted and kicked off, it will take a few minutes to process.
  // At this point, your game server should listen to the webhook from sphereone (setup in the merchant dashboard)
  // SphereOne will trigger the webhook when the payment completes successfully or fails
} catch (PayError e)
{
  // Just like `GetRouteEstimation`, if for some reason, user don't have enough money to
  // cover for paying the charge, it will throw an error.
  
  // As such, user would need to onramp, if they want to pay for the charge.
  string onRampLink = e.onrampLink;
  // Open the onRampLink in the user's default web browser
  Application.OpenURL(onRampLink);
  return;
} catch (Exception e)
{
  // Otherwise, if there's an internal server error or bad request, it's a generic error.
  Debug.LogError($"An error occurred while paying the charge: {e.Message}");
  return;
}

NFT Transfer

Caution: This feature is currently only available in SphereOne Unity SDK v1.6. It is not available in v1.5.

To transfer an NFT that the signed-user owns, the SDK would first need to prompt for the user's PIN. This is necessary because a transaction is going to be made from's the user's NFT collection. So, they need to give approval before SphereOne can proceed.

You can find example implementation here: SphereOne Unity v1.6 -> TestSphereOneManager#OpenPinCodeForNftTransfer .

// This will open a Window Popup that will prompt user for their PIN. And once PIN is successful,
// the Window Popup should closed itself and a `DEK` should be returned to the SDK.

// Unlike for `charges`, the `target` in this case is a string value `SEND_NFT`.
SphereOneManager.Instance.OpenPinCode(PincodeTargets.SendNft);

This will do something similar to, when paying for a charge and PIN is required. Now, the SDK should have DEK and can perform NFT Transfer.

Next, to perform NFT Transfer, the SDK needs to call TransferNft.

Please see example implementation here: SphereOne Unity v1.6 -> TestSphereOneManager#TransferNft.

Caution: The example implementation is forcefully filtering out the first NFT on a specific chain and that it is an ERC721. What should be done, is trigger this when a user selects a specific NFT.

Caution: NFT Transfer on SphereOne only works for NFTs that are ERC-721 on EVM-compatible chains and Solana NFTs. ERC-1155 on EVM-compatible and BEP-20 on Binance Smart Chain are not supported at the moment.

try 
{
  // for now, imagine user has already selected an Nft from their  NFT collection to be transferred

  // Nft dataType can be found here: https://github.com/SphereGlobal/unity-sdk/blob/d384c21e3d089a5a808b29bbf36138cf5ca96f3f/Assets/SphereOne/Scripts/ApiTypes.cs#L153
  // NftDataParams can be found here: https://github.com/SphereGlobal/unity-sdk/blob/d384c21e3d089a5a808b29bbf36138cf5ca96f3f/Assets/SphereOne/Scripts/ApiTypes.cs#L789
  var nftToTransfer = new NftDataParams
  {
    chain = nft.chain,
    fromAddress = nft.walletAddress, // wallet address that the NFT belongs to
    toAddress = receiver, // the receiver wallet address
    nftTokenAddress = nft.address,
    tokenId = nft.tokenId,
    reason = "Testing NFT transfer", // a `reason` is needed for the transfer
  };
  var nftTransferResult = await SphereOneManager.Instance.TransferNft(nftToTransfer);
  
  // the result should be a `transaction hash` string that can be used to find in a Blockchain Explorer
  Debug.Log("NFT transferred successfully: " + nftTransferResult.ToString());
}
catch (Exception e)
{
  // just printing out the error message
  Debug.LogError($"An error occurred while transferring the NFT: {e.Message}");
  return;
}