Skip to content

Instantly share code, notes, and snippets.

@nasdf
Last active March 4, 2022 19:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nasdf/d8b7fb4e570e563e990c52501bdfdfa7 to your computer and use it in GitHub Desktop.
Save nasdf/d8b7fb4e570e563e990c52501bdfdfa7 to your computer and use it in GitHub Desktop.
Unity3D NFT License
using Nethereum.Signer;
using System;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using UnityEngine;
public class NFTLicense : MonoBehaviour
{
[DllImport("__Internal")]
private static extern void Web3Connect();
[DllImport("__Internal")]
private static extern string ConnectAccount();
[DllImport("__Internal")]
private static extern void SetConnectAccount(string value);
private static string PREFS_SIGNATURE = "license-signature";
private static string PREFS_EXPIRATION = "license-expiration";
private static string MESSAGE_SALT = "replace-with-your-salt"; // TODO
public string chain = "polygon";
public string network = "mainnet";
public string contract = "0xb85ed41d49Eba25aE6186921Ea63b6055903e810"; // TODO
public string tokenId = "<INSERT YOUR LICENSE ID HERE>"; // TODO
private string address;
private string signature;
private string expiration;
public async void OnLogin()
{
// restore from preferences if previously logged in
signature = PlayerPrefs.GetString(PREFS_SIGNATURE, "");
expiration = PlayerPrefs.GetString(PREFS_EXPIRATION, "");
// initialize web3 provider
Web3Connect();
// connect an account
address = ConnectAccount();
while (address == "")
{
await new WaitForSeconds(1f);
address = ConnectAccount();
}
// reset login message
SetConnectAccount("");
// verify NFT license ownership
var authorized = await IsAuthorized();
while (!authorized)
{
await new WaitForSeconds(1f);
authorized = await SignIn();
}
Debug.Log("SUCCESS");
}
private async Task<bool> IsAuthorized()
{
// make sure the signature expiration is still valid
var now = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
if (!long.TryParse(expiration, out var result) || now > result)
{
Debug.Log("token expired");
return false;
}
// recover the signer address from the original message
var signer = new EthereumMessageSigner();
var message = MESSAGE_SALT + expiration;
var recovered = signer.EncodeUTF8AndEcRecover(message, signature);
// make sure the signer address matches the current address
if (!String.Equals(recovered, address, StringComparison.OrdinalIgnoreCase))
{
Debug.Log("address mismatch");
Debug.Log(recovered);
Debug.Log(address);
return false;
}
// ensure the address has at least one license
BigInteger balanceOf = await ERC1155.BalanceOf(chain, network, contract, address, tokenId);
Debug.Log(balanceOf);
return balanceOf > 0;
}
private async Task<bool> SignIn()
{
// sign out the previous account
SignOut();
// create an expiration timestamp 12 hours from now
var timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
expiration = $"{timestamp + (60 * 60 * 12)}";
// sign the expiration so we can prove ownership
var message = MESSAGE_SALT + expiration;
signature = await Web3GL.Sign(message);
// check if the user is authorized
var authorized = await IsAuthorized();
if (!authorized)
{
return false;
}
// save the signature and expiration so we can verify later
PlayerPrefs.SetString(PREFS_SIGNATURE, signature);
PlayerPrefs.SetString(PREFS_EXPIRATION, expiration);
PlayerPrefs.Save();
return true;
}
private void SignOut()
{
PlayerPrefs.DeleteKey(PREFS_SIGNATURE);
PlayerPrefs.DeleteKey(PREFS_EXPIRATION);
PlayerPrefs.Save();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment