Getting Started with BSMT.fun in Unity

This tutorial will guide you through integrating your Unity game with the Basement.fun platform using web APIs.

In the future, we will provide a B3 SDK to be added to your game. Stay tuned and watch out for announcements on X.

Prerequisites
- Unity 2020.3 or later
- Basic knowledge of C# scripting in Unity
- Familiarity with RESTful APIs

Introduction

In each section there will be two code snippets per request. The first code snippet will contain models and are used to ensure the data conforms to a specific structure and type making it resuable throughout your game. The second code snippet will contain the api requests. Once you have decided which api requests you will use, you can put all the requests in one doc and paste `private string baseURL = "https://api.basement.fun";` at the top.

To properly format all your post requests, you will need this helper class.

/// <summary>
/// Posts a request to B3's API.
/// </summary>
/// <param name="url">Url path to post to.</param>
/// <param name="method">The service method to call, null calls without xservicemethod.</param>
/// <param name="jsonBody">The json data being sent, null calls without body.</param>
/// <param name="bearer ">Use bearer token or not.</param>
/// <returns></returns>
private async Task<string> PostRequest(string url, [CanBeNull] string method, [CanBeNull] string jsonBody, bool bearer = true)
{
    var request = new UnityWebRequest(url, "POST");
    if (jsonBody != null)
    {
        var bodyRaw = Encoding.UTF8.GetBytes(jsonBody);
        request.uploadHandler = new UploadHandlerRaw(bodyRaw);
    }
    request.downloadHandler = new DownloadHandlerBuffer();
    request.SetRequestHeader("Content-Type", "application/json");
    if (method != null)
    {
        request.SetRequestHeader("X-Service-Method", method);
    }

    if (bearer)
    {
        request.SetRequestHeader("Authorization", $"Bearer {secret}");
    }

    await request.SendWebRequest();

    if (request.result == UnityWebRequest.Result.Success)
    {
        Debug.Log("Notification sent successfully!");
    }
    else
    {
        Debug.LogError($"Error sending notification: {request.error}");
    }

    return request.downloadHandler.text

To handle responses for all your requests, you will need this helper class.

/// <summary>
/// Get request helper with generic type.
/// </summary>
/// <param name="url">Get url.</param>
/// <typeparam name="T">Class to return.</typeparam>
/// <returns>Specified class.</returns>
private async Task<T> GetRequest<T>(string url)
{
    using UnityWebRequest webRequest = UnityWebRequest.Get(url);
    await webRequest.SendWebRequest();

    if (webRequest.result != UnityWebRequest.Result.Success)
    {
        Debug.LogError(webRequest.error);
    }
    else
    {
        string jsonResponse = webRequest.downloadHandler.text;
        var apiResponse = JsonConvert.DeserializeObject<T>(jsonResponse);
        return apiResponse;
    }
    return default

As we step through the functions you 'll notice that each one has an accompanying model, this is needed by unity in order to deserialize the json responses correctly. Please make sure you have these somewhere in your project so that it can read the API responses as intended.

You'll also notice there are request (POST) and response functions, these can be dropped into your project and used as needed.

Now that we've covered the basics, let's get started.

Get Channel status

This function will return the channel status, it can be used to tell whether or not a user is active in a session.

Get Channel Status Model

namespace B3Models
{
    /// <summary>
    /// Channel status payloads.
    /// </summary>
    public class ChannelStatus
    {
        /// <summary>
        /// B3 channel status data.
        /// </summary>
        public class Data
        {
            public string launcherJwt { get; set; }
        }

        /// <summary>
        /// B3 channel status response.
        /// </summary>
        public class Response
        {
            public bool exists { get; set; }
            public string error { get; set; }
            public bool? present { get; set; }
            public string wallet { get; set; }
            public long? openedAt { get; set; }
            public long? closedAt { get; set

Get Channel Status Request

/// <summary>
/// Gets channel status.
/// </summary>
/// <param name="jwtToken">Token from launcher.</param>
private async void ChannelStatus(string jwtToken)
{
    Debug.Log("Sending Channel Status");
    var url = $"{baseURL}/launcher";
    var jsonBody = JsonConvert.SerializeObject(new ChannelStatus.Data
    {
        launcherJwt = jwtToken
    });
    var request = await PostRequest(url, "channelStatus", jsonBody);
    var response = JsonConvert.DeserializeObject<ChannelStatus.Response>(request);
    Debug.Log(response.exists
        ? $"Channel Status Sent {response.wallet} Present {response.present.Value}"
        : "Channel Status response doesn't exist."

Send Notifications

Allows you to send a notification to the user, a popup will appear on their screen when called. Useful for keeping to user up to date with game information as needed.

Send Notifications Model

namespace B3Models
{
    /// <summary>
    /// B3 notification data.
    /// </summary>
    public class NotificationData
    {
        public string message { get; set; }
        public string type { get; set; }
        public string launcherJwt { get; set

Send Notifications Request

/// <summary>
/// Sends a notification to the launcher.
/// </summary>
/// <param name="message">Notification message.</param>
/// <param name="type">Success or error</param>
/// <param name="jwtToken">Token from launcher.</param>
private async void SendNotification(string message, string type, string jwtToken)
{
    Debug.Log("Sending Launcher Notification");
    var url = $"{baseURL}/launcher";
    var jsonBody = JsonConvert.SerializeObject(new NotificationData
    {
        message = message,
        type = type,
        launcherJwt = jwtToken
    });
    await PostRequest(url, "sendNotification", jsonBody);
    Debug.Log("Launcher Notification Sent"

Get Activities

Gets user or game activites set by sendActivites, useful for things like in-game events or custom leaderboards.

Get Activities Model

using System.Collections.Generic;

namespace B3Models
{
    /// <summary>
    /// Get activities payloads.
    /// </summary>
    public class GetActivities
    {
        /// <summary>
        /// B3 get activities data.
        /// </summary>
        public class Data
        {
            public string gameId { get; set; }
        }

        /// <summary>
        /// B3 activities response.
        /// </summary>
        public class Response
        {
            public bool success { get; set; }
            public Result result { get; set; }
        }

        public class Result
        {
            public int pageNumber { get; set; }
            public int pageSize { get; set; }
            public List<ActivityData> data { get; set; }
        }

        public class ActivityData
        {
            public string type { get; set; }
            public string eventId { get; set; }
            public string gameName { get; set; }
            public string gameSlug { get; set; }
            public User user { get; set; }
            public long timestamp { get; set; }
            public string displayText { get; set; }
        }

        public class User
        {
            public string id { get; set; }
            public string address { get; set

Get Game Activities Request

/// <summary>
/// Gets activities.
/// </summary>
/// <param name="gameId">Game's ID.</param>
/// <param name="type">string specifying on-chain or off-chain.</param>
private async void GetActivities(string gameId, string type)
{
    var type = "off-chain"; // or "on-chain"
    Debug.Log("Getting Activities");
    var url = $"{baseURL}/activities?gameId={gameId}&type={type}";
    var response = await GetRequest<GetActivities.Response>(url);
    activitiesResponseCache = response;
    Debug.Log("Getting Activities Complete"

Get User Activities Request

/// <summary>
/// Gets user activities.
/// </summary>
/// <param name="wallet">Notification message.</param>
/// <param name="type">Success or error.</param>
/// <param name="pageSize">Size to call.</param>
/// <param name="pageNumber">Page number to call.</param>
private async void GetUserActivities(string wallet, string type, int pageSize, int pageNumber)
{
    var type = "off-chain"; // or "on-chain"
    Debug.Log("Getting Activities");
    var url = $"{baseURL}/activities?pageSize={pageSize}&pageNumber={pageNumber}&walletAddress={wallet}&type={type}";
    var response = await GetRequest<GetActivities.Response>(url);
    activitiesResponseCache = response;
    Debug.Log("Getting Activities Complete"

Send Activities

Sends custom activites to the backend, can be used with getActivies.

Send Activities Model

namespace B3Models
{
    /// <summary>
    /// Send activities payloads.
    /// </summary>
    public class SendActivities
    {
        /// <summary>
        /// B3 send activities data.
        /// </summary>
        public class Data
        {
            public string label { get; set; }
            public string eventId { get; set; }
            public string launcherJwt { get; set; }
        }

        /// <summary>
        /// B3 send activities response.
        /// </summary>
        public class Response
        {
            public bool success { get; set; }
            public string error { get; set; }
            public Activity activity { get; set; }
        }

        public class Activity
        {
            public string _id { get; set; }
            public string label { get; set; }
            public string normalizedAddress { get; set; }
            public long timestamp { get; set; }
            public string eventId { get; set; }
            public string gameId { get; set

Send Activities Request

/// <summary>
/// Sends activities.
/// </summary>
/// <param name="label">Label to assign.</param>
/// <param name="eventId">Event id to assign.</param>
/// <param name="jwtToken">Token from launcher.</param>
private async void SendActivity(string label, string eventId, string jwtToken)
{
    Debug.Log("Sending Activity");
    var url = $"{baseURL}/launcher";
    var jsonBody = JsonConvert.SerializeObject(new SendActivities.Data
    {
        label = label,
        eventId = eventId,
        launcherJwt = jwtToken
    });
    var request = await PostRequest(url, "sendCustomActivity", jsonBody);
    var response = JsonConvert.DeserializeObject<SendActivities.Response>(request);
    Debug.Log($"Activity {response.activity.eventId} Sent");
    Debug.Log("Sending Activity Complete"

Get State

Gets custom state data based on the label chosen, useful for things like game saves and items/currency.

Get State Model

using System.Collections.Generic;
using Newtonsoft.Json;

namespace B3Models
{
    /// <summary>
    /// Get state payloads.
    /// </summary>
    public class GetState
    {
        /// <summary>
        /// B3 get states data.
        /// </summary>
        public class Data
        {
            public int limit { get; set; }
            public int skip { get; set; }
            [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
            public string label { get; set; }
            public string launcherJwt { get; set; }
        }

        /// <summary>
        /// B3 get state response.
        /// </summary>
        public class Response
        {
            public bool success { get; set; }
            public string error { get; set; }
            public List<States> states { get; set; }
        }

        public class States
        {
            public string id { get; set; }
            public string gameId { get; set; }
            public string normalizedAddress { get; set; }
            public long updatedAt { get; set; }
            public string label { get; set; }
            public string ipfsHash { get; set; }
            public State state { get; set; }
        }

        public class State
        {
            public StateData data { get; set; }
        }

        /// <summary>
        /// State data.
        /// </summary>
        public class StateData
        {
            public float questValue { get; set; }
            // 0 - points, 1 - shot speed, 2 - move speed, 0 or 1 etc
            public List<int> itemUnlocks { get; set

Get State Request

/// <summary>
/// Gets state.
/// </summary>
/// <param name="limit">Limit of items to call.</param>
/// <param name="skip">Amount to skip.</param>
/// <param name="label">Label to assign.</param>
/// <param name="jwtToken">Token from launcher.</param>
private async void GetState(int limit, int skip, string label, string jwtToken)
{
    Debug.Log("Getting State Data");
    var url = $"{baseURL}/launcher";
    var jsonBody = JsonConvert.SerializeObject(new GetState.Data
    {
        limit = limit,
        skip = skip,
        label = label,
        launcherJwt = jwtToken
    });
    var request = await PostRequest(url, "getStates", jsonBody);
    var response = JsonConvert.DeserializeObject<GetState.Response>(request);
    Debug.Log($"State data {response.states[0].state.data.questValue}");
    Debug.Log($"State data {response.states[0].state.data.itemUnlocks[0]}");
    Debug.Log("Getting State Data Complete"

Set State

Sets custom state data based on the label chosen, useful for things like game saves and items/currency.

Set State Model

namespace B3Models
{
    /// <summary>
    /// Set state payloads.
    /// </summary>
    public class SetState
    {
        /// <summary>
        /// B3 set state data.
        /// </summary>
        public class Data
        {
            public string state { get; set; }
            public string label { get; set; }
            public string launcherJwt { get; set; }
        }

        /// <summary>
        /// B3 set state response.
        /// </summary>
        public class Response
        {
            public bool success { get; set; }
            public string error { get; set; }
            public NewState newState { get; set; }
        }

        /// <summary>
        /// State data.
        /// </summary>
        public class NewState
        {
            public string id { get; set; }
            public string gameId { get; set; }
            public string normalizedAddress { get; set; }
            public long updatedAt { get; set; }
            public string label { get; set; }
            public string ipfsHash { get; set

Set State Requests

/// <summary>
/// Sets state.
/// </summary>
/// <param name="jsonString">State json string of your choice.</param>
/// <param name="label">Label to assign.</param>
/// <param name="jwtToken">Token from launcher.</param>
private async void SetState(string jsonString, string label, string jwtToken)
{
    Debug.Log("Setting State Data");
    var url = $"{baseURL}/launcher";
    var jsonBody = JsonConvert.SerializeObject(new SetState.Data
    {
        state = jsonString,
        label = label,
        launcherJwt = jwtToken
    });
    var request = await PostRequest(url, "setState", jsonBody);
    var response = JsonConvert.DeserializeObject<SetState.Response>(request);
    if (response.success)
    {
        Debug.Log($"{JsonConvert.SerializeObject(response.newState)}");
        Debug.Log("Setting State Data Complete");
    }
    else
    {
        Debug.Log("Error setting state data"

Get Leaderboards

Gets game leaderboard data.

Get Leaderboards Model

using System.Collections.Generic;

namespace B3Models
{
    public class GetLeaderboards
    {
        /// <summary>
        /// B3 get leaderboard data.
        /// </summary>
        public class Data
        {
            public string gameId { get; set; }
            public int limit { get; set; }
            public int skip { get; set; }
        }

        /// <summary>
        /// B3 leaderboard response.
        /// </summary>
        public class Response
        {
            public bool success { get; set; }
            public string error { get; set; }
            public List<Leaderboard> leaderboard { get; set; }
        }

        public class Leaderboard
        {
            public string id { get; set; }
            public string nonce { get; set; }
            public string gameId { get; set; }
            public string normalizedAddress { get; set; }
            public double score { get; set; }
            public long updatedAt { get; set; }
            public string username { get; set; }
            public string avatar { get; set

Get Leaderboards Request

/// <summary>
/// Gets leaderboards.
/// </summary>
/// <param name="gameId">Game's ID.</param>
/// <param name="limit">Limit of items to call.</param>
/// <param name="skip">Amount to skip.</param>
private async Task<GetLeaderboards.Response> GetLeaderboards(string gameId, int limit, int skip)
{
    Debug.Log("Getting Leaderboards");
    var url = $"{baseURL}/scores";
    var jsonBody = JsonConvert.SerializeObject(new GetLeaderboards.Data
    {
        gameId = gameId,
        limit = limit,
        skip = skip
    });
    var request = await PostRequest(url, "getGameScoresLeaderboard", jsonBody, false);
    var response = JsonConvert.DeserializeObject<GetLeaderboards.Response>(request);
    Debug.Log("Getting Leaderboards Complete");
    return response

Get User Scores

Gets a users scores that have been saved to the backend.

Get User Scores Model

using System.Collections.Generic;
using Newtonsoft.Json;

namespace B3Models
{
    /// <summary>
    /// Get score payloads.
    /// </summary>
    public class GetScore
    {
        /// <summary>
        /// B3 get score data.
        /// </summary>
        public class Data
        {
            public string launcherJwt { get; set; }
            public int limit { get; set; }
            public int skip { get; set; }

            [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
            public string nonce { get; set; }
        }

        /// <summary>
        /// B3 get score response.
        /// </summary>
        public class Response
        {
            public bool success { get; set; }
            public string error { get; set; }
            public List<Scores> scores { get; set; }

            public Response()
            {
                scores = new List<Scores>();
            }
        }

        /// <summary>
        /// Holds score data for score response.
        /// </summary>
        public class Scores
        {
            public string nonce { get; set; }
            public double score { get; set; }
            public long updatedAt { get; set

Get User Scores Request

/// <summary>
/// Gets user scores.
/// </summary>
/// <param name="limit">Limit of items to call.</param>
/// <param name="skip">Amount to skip.</param>
/// <param name="jwtToken">Token from launcher.</param>
/// <param name="nonce">Specific nonce to call, gets all if null.</param>
private async Task<GetScore.Response> GetScores(int limit, int skip, string jwtToken, string nonce = null)
{
    Debug.Log("Getting User Scores");
    var url = $"{baseURL}/launcher";
    var jsonBody = JsonConvert.SerializeObject(new GetScore.Data
    {
        launcherJwt = jwtToken,
        limit = limit,
        skip = skip,
        nonce = nonce // optional
    });
    var request = await PostRequest(url, "getUserScores", jsonBody);
    var response = JsonConvert.DeserializeObject<GetScore.Response>(request);
    Debug.Log("Getting User Scores Complete");
    return response

Set User Scores

Sets a users score and allows it to be called via the API with get user scores.

Set User Scores Model

using System.Collections.Generic;
using Newtonsoft.Json;

namespace B3Models
{
    /// <summary>
    /// Set score payloads.
    /// </summary>
    public class SetScore
    {
        /// <summary>
        /// B3 set score data.
        /// </summary>
        public class Data
        {
            public string nonce { get; set; }
            public float score { get; set; }
            public string launcherJwt { get; set; }
        }

        /// <summary>
        /// B3 set score response.
        /// </summary>
        public class Response
        {
            public bool success { get; set; }
            public string error { get; set; }
            public NewScore newScore { get; set; }
        }

        public class NewScore
        {
            public string _id { get; set; }
            public string nonce { get; set; }
            public string gameId { get; set; }
            public string normalizedAddress { get; set; }
            public double score { get; set; }
            public long updatedAt { get; set; }
        }

        /// <summary>
        /// B3 get score data.
        /// </summary>
        public class GetScoreData
        {
            public string launcherJwt { get; set; }
            public int limit { get; set; }
            public int skip { get; set; }

            [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
            public string nonce { get; set; }
        }

        /// <summary>
        /// B3 get score response.
        /// </summary>
        public class GetScoreResponse
        {
            public bool success { get; set; }
            public string error { get; set; }
            public List<Scores> scores { get; set; }

            public GetScoreResponse()
            {
                scores = new List<Scores>();
            }
        }

        /// <summary>
        /// Holds score data for score response.
        /// </summary>
        public class Scores
        {
            public string nonce { get; set; }
            public double score { get; set; }
            public long updatedAt { get; set

Set User Scores Request

/// <summary>
/// Sets a users score.
/// </summary>
/// <param name="score">Score to set.</param>
/// <param nonce="score">Nonce to set.</param>
/// <param jwtToken="score">jwt token from launcher.</param>
private async void SetScore(string score, string nonce, string jwtToken)
{
    Debug.Log("Setting Score");
    var url = $"{baseURL}/launcher";
    var jsonBody = JsonConvert.SerializeObject(new SetScore.Data
    {
        nonce = nonce,
        score = float.Parse(score),
        launcherJwt = jwtToken
    });
    var request = await PostRequest(url, "setUserScore", jsonBody);
    var response = JsonConvert.DeserializeObject<SetScore.Response>(request);
    Debug.Log($"Score Set: {response.newScore.score}");
    Debug.Log("Setting Score Complete"

Create Unverified Channel

Creates an unverified channel and starts the verification process. Useful for games being hosted outside of B3s launcher that need to obtain or refresh a JWT.

Create Unverified Channel Model

namespace B3Models
{
    /// <summary>
    /// Creates Unverified Channel.
    /// </summary>
    public class CreateUnverifiedChannel
    {
        /// <summary>
        /// B3 create unverified channel data.
        /// </summary>
        public class Data
        {
            public string wallet { get; set; }
        }
        
        /// <summary>
        /// B3 create unverified channel response.
        /// </summary>
        public class Response
        {
            public bool success { get; set; }
            public string signRequest { get; set; }
            public string channelId { get; set

Create Unverified Channel Request

/// <summary>
/// Create unverified channel.
/// </summary>
/// <returns>Response for users to sign.</returns>
private async void CreateUnverifiedChannel()
{
    Debug.Log("Creating Unverified Channel");
    var url = $"{baseURL}/launcher";
    var jsonBody = JsonConvert.SerializeObject(new CreateUnverifiedChannel.Data
    {
        wallet = Web3Accessor.Web3.Signer.PublicAddress
    });
    var request = await PostRequest(url, "createUnverifiedChannel", jsonBody);
    var response = JsonConvert.DeserializeObject<CreateUnverifiedChannel.Response>(request);
    if (response.success)
    {
        Debug.Log("Unverified Channel Created Success");
        SignCreateUnverifiedChannelRequest(response.channelId, response.signRequest);
    }
    else
    {
        Debug.Log("Unverified Channel Created Failed"

Sign Create Unverified Channel Request

Signs the channel data from the previous step.

/// <summary>
/// Signs unverified channel request, calls verify.
/// </summary>
/// <param name="channelId">Channel id from CreateUnverifiedChannel call.</param>
/// <param name="signatureToSign">Signature to sign from CreateUnverifiedChannel call.</param>
private async void SignCreateUnverifiedChannelRequest(string channelId, string signatureToSign)
{
    Debug.Log("Sign Create Unverified Channel Request Incoming");
    var signedMessage = await Evm.SignMessage(Web3Accessor.Web3, signatureToSign);
    Debug.Log($"Message To Sign: {signatureToSign}");
    Debug.Log("Sign Create Unverified Channel Request Complete");
    VerifyUnverifiedChannel(channelId, signedMessage

Verify Unverified Channel

Verifies the signed channel data from the previous step and generates a JWT.

Verify Unverified Channel Model

namespace B3Models
{
    /// <summary>
    /// Verifies Unverified Channel.
    /// </summary>
    public class VerifyUnverifiedChannel
    {
        /// <summary>
        /// B3 verify unverified channel data.
        /// </summary>
        public class Data
        {
            public string channelId { get; set; }
            public string signature { get; set; }
        }
        
        /// <summary>
        /// B3 verify unverified channel response.
        /// </summary>
        public class Response
        {
            public bool success { get; set; }
            public string Jwt { get; set

Verify Unverified Channel Request

/// <summary>
/// Verifies unverified channel.
/// </summary>
/// <param name="channelId">Channel Id to verify.</param>
/// <param name="signature">Signed signature.</param>
private async void VerifyUnverifiedChannel(string channelId, string signature)
{
    Debug.Log($"channelId: {channelId}");
    Debug.Log($"signedMessage: {signature}");
    Debug.Log("Verifying Unverified Channel");
    var url = $"{baseURL}/launcher";
    var jsonBody = JsonConvert.SerializeObject(new VerifyUnverifiedChannel.Data
    {
        channelId = channelId,
        signature = signature
    });
    Debug.Log($"Request: {jsonBody}");
    var request = await PostRequest(url, "verifyUnverifiedChannel", jsonBody);
    var response = JsonConvert.DeserializeObject<VerifyUnverifiedChannel.Response>(request);
    if (response.success)
    {
        Debug.Log("Verify Unverified Channel Success");
        Debug.Log("JWT updated");
        jwtToken = response.Jwt;
        // This is a timer that fires a heartbeat request to renew the jwt as it expires.
        StartCoroutine(WaitForXMinutes(30f));
    }
    else
    {
        Debug.Log("Verify Unverified Channel Failed"

Get Channel Heartbeat Signature Request

Used to refresh a JWT when the previous one has expired.

Get Channel Heartbeat Signature Request Model

namespace B3Models
{
    /// <summary>
    /// Verifies Unverified Channel.
    /// </summary>
    public class GetHeartbeatSignatureRequest
    {
        /// <summary>
        /// B3 verify unverified channel data.
        /// </summary>
        public class Data
        {
            public string gameId { get; set; }
            public string license { get; set; }
        }
        
        /// <summary>
        /// B3 verify unverified channel response.
        /// </summary>
        public class Response
        {
            public bool success { get; set; }
            public string signRequest { get; set

Get Channel Heartbeat Signature Request Request

/// <summary>
/// Every 30 minutes or so get heartbeat request to refresh the JWT.
/// </summary>
private async void GetChannelHeartbeatRequest()
{
    Debug.Log("Get Channel Heartbeat Signature Request Incoming");
    var url = $"{baseURL}/launcher";
    var jsonBody = JsonConvert.SerializeObject(new GetHeartbeatSignatureRequest.Data
    {
        gameId = gameId,
        license = secret
    });
    var request = await PostRequest(url, "getChannelHeartbeatSignatureRequest", jsonBody);
    var response = JsonConvert.DeserializeObject<GetHeartbeatSignatureRequest.Response>(request);
    if (response.success)
    {
        Debug.Log("Get Channel Heartbeat Signature Request Success");
        SignChannelHeartbeatRequest(response.signRequest);
    }
    else
    {
        Debug.Log("Get Channel Heartbeat Signature Request Failed"

Sign Channel Heartbeat Request

Signs the channel heartbeat request data from the previous step.

/// <summary>
/// Signs heartbeat request, calls verify.
/// </summary>
/// <param name="signatureToSign">Signature for user to sign.</param>
private async void SignChannelHeartbeatRequest(string signatureToSign)
{
    Debug.Log("Sign Channel Heartbeat Signature Request Incoming");
    var signedMessage = await Evm.SignMessage(Web3Accessor.Web3, signatureToSign);
    VerifyHeartbeatSignature(signedMessage);
    Debug.Log("Sign Channel Heartbeat Signature Request Complete"

Verify Heartbeat Signature

Verifies the heartbeat signature from the previous step and generates a JWT.

Verify Heartbeat Signature Model

namespace B3Models
{
    /// <summary>
    /// Verifies Heartbeat Signature.
    /// </summary>
    public class VerifyHeartbeatSignature
    {
        /// <summary>
        /// B3 verify heartbeat signature data.
        /// </summary>
        public class Data
        {
            public string launcherJwt { get; set; }
            public string signature { get; set; }
        }
        
        /// <summary>
        /// B3 verify heartbeat signature response.
        /// </summary>
        public class Response
        {
            public bool success { get; set

Verify Heartbeat Signature Request

/// <summary>
/// Verifies heartbeat with signature.
/// </summary>
/// <param name="signature">Signed signature from heartbeat request.</param>
private async void VerifyHeartbeatSignature(string signature)
{
    Debug.Log("Verifying Channel Heartbeat Signature");
    var url = $"{baseURL}/launcher";
    var jsonBody = JsonConvert.SerializeObject(new VerifyHeartbeatSignature.Data
    {
        launcherJwt = jwtToken,
        signature = signature
    });
    var request = await PostRequest(url, "channelHeartbeat", jsonBody);
    var response = JsonConvert.DeserializeObject<VerifyHeartbeatSignature.Response>(request);
    if (response.success)
    {
        Debug.Log("Verify Channel Heartbeat Success");
        // This is a timer that fires a heartbeat request to renew the jwt as it expires.
        StartCoroutine(WaitForXMinutes(30f));
    }
    else
    {
        Debug.Log("Verify Channel Heartbeat Failed"

Trigger Rules Engine

Used to trigger custom events set in the backends config by the game's developers.

Trigger Rules Engine Model

using Newtonsoft.Json;

namespace B3Models
{
    /// <summary>
    /// Triggers rules engine.
    /// </summary>
    public class TriggerRulesEngine
    {
        /// <summary>
        /// B3 trigger rule engine data.
        /// </summary>
        public class Data
        {
            public string trigger { get; set; }
            public string token { get; set; }
            [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
            public string nonce { get; set; }
            public object value { get; set; }
        }
        
        /// <summary>
        /// B3 trigger rule engine  response.
        /// </summary>
        public class Response
        {
            public bool success { get; set

Trigger Rules Engine Request

/// <summary>
/// Triggers events configured the backend.
/// </summary>
private async void TriggerRulesEngine(string trigger, string token, string nonce, object value)
{
    Debug.Log("Triggering Rules Engine");
    var url = $"{baseURL}/launcher";
    var jsonBody = JsonConvert.SerializeObject(new TriggerRulesEngine.Data
    {
        trigger = trigger,
        token = token,
        nonce = nonce,
        value = value
    });
    var request = await PostRequest(url, "triggerRulesEngine", jsonBody);
    var response = JsonConvert.DeserializeObject<TriggerRulesEngine.Response>(request);
    if (response.success)
    {
        Debug.Log("Triggering Rules Engine Success");
    }
    else
    {
        Debug.Log("Triggering Rules Engine Failed"

Helper Functions

These are helper functions that you may see throughout the calls above.

Generate Random String: Used in Nonce Generation

/// <summary>
/// Generates a random string for nonces.
/// </summary>
/// <param name="length">Output string length.</param>
/// <returns>A randomized string.</returns>
private string GenerateRandomString(int length)
{
    string characters = "abcdefghijklmnopqrstuvwxyz0123456789";
    char[] stringChars = new char[length];
    System.Random random = new System.Random();

    for (int i = 0; i < length; i++)
    {
        stringChars[i] = characters[random.Next(characters.Length)];
    }

    return new string(stringChars

Wait For X Minutes: Used In Hearbeat Refresh

/// <summary>
/// Waits for x minutes and gets heartbeat request.
/// </summary>
/// <param name="minutes">Minutes to wait for.</param>
/// <returns></returns>
private IEnumerator WaitForXMinutes(float minutes)
{
    Debug.Log("Checking Heartbeat System");
    // Convert minutes input to seconds
    float waitTime = minutes * 60f;
    yield return new WaitForSeconds(waitTime);
    Debug.Log($"{minutes} Minutes Have Passed");
    GetChannelHeartbeatRequest

© 2024 Player1 Foundation

Discover B3 - the Based L3

© 2024 Player1 Foundation

Discover B3 - the Based L3

© 2024 Player1 Foundation

Discover B3 - the Based L3