Unity SDK reference
Overview
The MPC Unity SDK provides a collection of MPC features, giving players of your Unity game the ability to create wallets and transfer assets between addresses without leaving the game environment.
The SDK offers two ways to handle asynchronous operations—using await
with an async
method, or using Action
with a callback function. The following example demonstrates both ways:
public static async Task<(return_type)> FuncName(params, Action<(return_type)> onComplete = null) {...}
private async void FuncAsync()
{
var result = await FuncName(...);
// Continue to do something
}
private void FuncNormal()
{
FuncName(..., onComplete = (return_type result) => {
// Continue to do something
}
);
}
For simplicity, all methods mentioned in the documentation omit the return type and the onComplete
action. All main methods are static and are under the MPC
class.
Requirements
- Unity 2020.3 or later
- Json.NET
- Google.Protobuf
- System.Runtime.CompilerServices.Unsafe
Installation
Download the Unity package from the GitHub release page and import it into your project.
Initialization
Task<(bool success, MPCError error)> MPC.Initialize(accessToken : string, env : Environment);
Parameters:
Name | Type | Description |
---|---|---|
accessToken | String | Valid access token returned from an OAuth 2.0 provider. |
env | Environment | Defines the environment. Use Staging for the staging or testing environment. Use Production when the game is running in a live, production environment, serving real users. The default value is Staging . |
Examples:
await MPC.Initialize(CreateAccessToken(userId), Environment.Staging);
await MPC.Initialize(CreateAccessToken(userId), Environment.Production);
If the user's access token changes during the game session, call the UpdateAccessToken
method to update it.
MPC.UpdateAccessToken(AccessToken);
Methods
Main methods:
Method | Description |
---|---|
MPC.Initialize | Initializes the MPC SDK with the specified access token and environment. All other SDK methods must be called after this one. |
MPC.Keygen | Generates an MPC private key, encrypts it, and automatically backs it up to the Sky Mavis backup server. |
MPC.EncryptData | Encrypts the user's private key shard. |
MPC.DecryptData | Decrypts the user's private key shard. |
MPC.BackupKey | Creates a backup copy of the user's encrypted private key shard on the Sky Mavis backup server. |
MPC.GetBackupKey | Pulls the backup copy of the user's encrypted private key shard from the Sky Mavis backup server. |
MPC.SignMessage | Signs a message using the encrypted private key and the provided password. |
MPC.TransferNativeToken | Transfers a native token, such as RON, to a specified address. |
MPC.TransferERC20Token | Transfers an ERC20 token, such as AXS or SLP, to a specified address. |
MPC.ApproveERC20Token | Approves an ERC20 token for a specific contract address and amount. |
MPC.CallContract | Calls a contract asynchronously. |
Utilities:
Method | Description |
---|---|
MPC.WalletAddress | Retrieves the user's public wallet address from the server. |
MPC.HasBackupKey | Verifies if the user has a backup of the private key shard. |
MPC.HasPrivateKey | Verifies if the user already has an MPC private key. |
MPC.Logout | Logs the user out of the current session and deletes all local data. |
MPC.GetRONBalance | Retrieves the RON token balance for a given token address. |
MPC.GetERC20Balance | Retrieves an ERC20 token balance for a given token address. |
MPC.UpdateAccessToken | Used to update the user's access token if it changes during the game session. |
MPC.EstimateTransferNativeTokenGas | Estimates the gas required for transferring native tokens, such as RON. |
[MPC.EstimateERC20TokenGas ](#estimate-gas-for-erc20-token-transfer | Estimates the gas required for transferring ERC20 tokens, such as AXS or SLP. |
MPC.ConvertAmount | Convert an amount of tokens to HexString to use in a transaction's input data. |
Generate MPC key
Task<(bool success, MPCError error)> MPC.Keygen(string password);
Parameters:
Name | Type | Description |
---|---|---|
password | String | Recovery password set by the user to protect privateKeyShard . Only the user with the correct password can access the private key. |
Example:
string password = $"{RECOVERY_PASSWORD}";
var privateKeyShard = await MPC.Keygen(password);
if (privateKeyShard.error != null) {
Debug.LogError($"Error when generating a key: {privateKeyShard.error.message}");
} else {
Debug.Log($"Key generation result: {privateKeyShard}");
}
Encrypt private key shard
Task<(string, MPCError)> MPC.EncryptData(string privateKeyShard, string password);
Parameters:
Name | Type | Description |
---|---|---|
privateKeyShard | String (HexString) | Private key generated after completing the key generation process, exclusively owned by the user. This key participates in all transaction signing processes along with the key stored in the Sky Mavis backend. |
password | String | Recovery password set by the user to protect privateKeyShard . Only the user with the correct password can access the private key. |
Example:
string privateKeyShard = $"{PRIVATE_KEY}";
string password = $"{RECOVERY_PASSWORD}";
var (encryptedKey, error) = await MPC.EncryptData(privateKeyShard, password);
if (error != null)
{
Debug.LogError($"Error when getting encrypted data: {error.message}");
}
else
{
Debug.Log($"Encrypted private key shard: {encryptedKey}");
}
Decrypt private key shard
Task<(string, MPCError)> MPC.DecryptData(string encryptedKey, string password);
Parameters:
Name | Type | Description |
---|---|---|
encryptedKey | String | Private key shard encrypted using the user's password. |
password | String | Recovery password set by the user to protect privateKeyShard . Only the user with the correct password can access the private key. |
Example:
string encryptedKey = $"{ENCRYPTED_KEY}";
string password = $"{RECOVERY_PASSWORD}";
var (privateKeyShard, error) = await MPC.DecryptData(encryptedKey, password);
if (error != null) {
Debug.LogError($"Error when getting decrypted data: {error.message}");
}
else {
Debug.Log($"Decrypted private key shard: {privateKeyShard}");
}
Back up private key shard
Task<(bool success, MPCError error)> MPC.BackupKey(string password)
Parameters:
Name | Type | Description |
---|---|---|
password | String | Recovery password set by the user to protect privateKeyShard . Only the user with the correct password can access the private key. |
Example:
var (result, error) = await MPC.BackupKey(password);
if (error != null)
{
Debug.LogError($"Error when creating backup of private key shard: {error.message}");
}
else
{
Debug.Log($"Key backup saved to server: {result}");
}
Get backup of private key shard
Task<(string, MPCError)> MPC.GetBackupKey()
Example:
var (encryptedKey, error) = await MPC.GetBackupKey();
if (error != null) {
Debug.LogError($"Error when getting backup of private key shard: {error.message}");
}
else {
Debug.Log($"Key backup retrieved from server: {encryptedKey}");
}
Transfer native token
Task<(string txHash, MPCError error)> MPC.TransferNativeToken(string toAddress, string amount, string password);
Parameters:
Name | Type | Description |
---|---|---|
toAddress | String | Recipient's address. |
amount | String (HexString) | HexString representation of the amount to transfer. |
password | String | Recovery password set by the user to protect privateKeyShard . Only the user with the correct password can access the private key. |
Example:
string toAddress = "0xedb40e7abaa613a0b06d86260dd55c7eb2df2447";
string amount = "0xde0b6b3a7640000"; // 1 RON
string password = $"{RECOVERY_PASSWORD}";
await MPC.Initialize(CreateAccessToken(userId));
var (txHash, error) = await MPC.TransferNativeToken(toAddress, amount, password);
if (error != null)
{
Debug.LogError($"Error transferring native token: {error.message}");
}
else
{
Debug.Log($"Transaction hash of transferring 1 RON: {txHash}");
}
Transfer ERC20 token
Task<(string txHash, MPCError error)> MPC.TransferERC20Token(string tokenAddress, string toAddress, string amount, string password);
Parameters:
Name | Type | Description |
---|---|---|
tokenAddress | String | Address of the ERC20 token's contract. |
toAddress | String | Recipient's address. |
amount | String (HexString) | HexString representation of the amount to transfer. |
password | String | Recovery password set by the user to protect privateKeyShard . Only the user with the correct password can access the private key. |
Example:
string tokenAddress = "0x3c4e17b9056272ce1b49f6900d8cfd6171a1869d"; // AXS contract
string toAddress = "0xedb40e7abaa613a0b06d86260dd55c7eb2df2447";
string amount = "0xde0b6b3a7640000"; // 1 AXS
string password = $"{RECOVERY_PASSWORD}";
var (txHash, error) = await MPC.TransferERC20Token(tokenAddress, toAddress, amount, password);
if (error != null)
{
Debug.LogError($"Error when transferring ERC20: {JsonConvert.SerializeObject(error)}");
}
else
{
Debug.Log($"Transaction hash of transferring 1 AXS: {txHash}");
}
Approve ERC20 token
Task<(string txHash, MPCError error)> MPC.ApproveERC20Token(string spenderAddress, string tokenAddress, string amount, string password);
Parameters:
Name | Type | Description |
---|---|---|
spenderAddress | String | Spender address, to which you allow to spend the specified token. |
tokenAddress | String | Address of the ERC20 token's contract. |
amount | String (HexString) | HexString representation of the amount to spend. |
password | String | Recovery password set by the user to protect privateKeyShard . Only the user with the correct password can access the private key. |
Example:
string spenderAddress = "0xedb40e7abaa613a0b06d86260dd55c7eb2df2447";
string tokenAddress = "0x3c4e17b9056272ce1b49f6900d8cfd6171a1869d"; // AXS contract
string amount = "0xde0b6b3a7640000"; // 1 AXS
string password = $"{RECOVERY_PASSWORD}";
var (txHash, error) = await MPC.ApproveERC20Token(spenderAddress, tokenAddress, amount, password);
if (error != null)
{
Debug.LogError($"Error when approving ERC20: {JsonConvert.SerializeObject(error)}");
}
else
{
Debug.Log($"Transaction hash of approving 1 AXS: {txHash}");
}
Sign message
Task<(string signedMessage, MPCError error)> MPC.SignMessage(string message, string password);
Parameters:
Name | Type | Description |
---|---|---|
message | String | Message to sign. |
password | String | Recovery password set by the user to protect privateKeyShard . Only the user with the correct password can access the private key. |
Example:
string message = "Sign this message to breed Axie #223495 with Axie #123232";
string password = $"{RECOVERY_PASSWORD}";
var (data, error) = await MPC.SignMessage(message, password);
if (error != null)
{
Debug.LogError($"Error when signing message: {JsonConvert.SerializeObject(error)}");
}
else
{
Debug.Log($"Signature: {JsonConvert.SerializeObject(data)}");
}
Call contract
Task<(string txHash, MPCError error)> MPC.CallContract(string from, string to, string value, string input, string password);
Parameters:
Name | Type | Description |
---|---|---|
from | String | User's wallet address. |
to | String | Contract address. |
value | String (HexString) | HexString representation of the token amount. |
input | String | Transaction's input data. |
password | String | Recovery password set by the user to protect privateKeyShard . Only the user with the correct password can access the private key. |
Example:
string from = "0xB980D30Dc0c318BccFf6C760eDf465Fa9D2950Bd";
string to = "0xDa44546C0715ae78D454fE8B84f0235081584Fe0";
string value = "0xde0b6b3a7640000"; // 1 RON
string password = $"{RECOVERY_PASSWORD}";
string input = "0x7da5cd66000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000002082d1fedd8c2d2408c22869c49000f5e375ddbb000000000000000000000000000000000000000000000000000000006593f08b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000a959726154953bae111746e265e6d754f48570e60000000000000000000000003c4e17b9056272ce1b49f6900d8cfd6171a1869d";
var (txHash, error) = await MPC.CallContract(from, to, value, input, password);
if (error != null)
{
Debug.LogError($"Error when calling contract: {JsonConvert.SerializeObject(error)}");
}
else
{
Debug.Log($"Transaction hash of calling contract to swap 1 RON to AXS: {txHash}");
}
Get wallet address
// Initialize the MPC environment
await MPC.Initialize(CreateAccessToken(userId), Environment.Staging);
Debug.Log($"Wallet address: {MPC.WalletAddress}");
Verify if user has backup of private key shard
bool hasBackupKey = MPC.HasBackupKey;
Debug.Log($"User has backup key: {hasBackupKey}");
Verify if user has MPC key
bool hasPrivateKey = MPC.HasPrivateKey;
Debug.Log($"Is new wallet: {hasPrivateKey}");
Log user out
MPC.Logout();
Get native token balance
Task<(string balance, MPCError error)> MPC.GetRONBalance();
Example:
var result = await MPC.GetRONBalance();
if (result.error != null)
{
Debug.LogError($"Error when getting RON balance: {result.error}");
}
else
{
Debug.Log($"RON balance: {result.balance.HexToBigInt()}");
}
Get ERC20 token balance
Task<(string balance, MPCError error)> MPC.GetERC20Balance(string tokenAddress);
Parameters:
Name | Type | Description |
---|---|---|
tokenAddress | String | Address of the ERC20 token's contract. |
Example:
string tokenAddress = "0x3c4e17b9056272ce1b49f6900d8cfd6171a1869d"; // AXS contract
var result = await MPC.GetERC20Balance(axsAddress);
if (result.error != null) {
Debug.LogError($"Error when getting ERC20 balance: {result.error}");
}
else {
Debug.Log($"ERC20 balance: {result.balance.HexToBigInt()}");
}
Update access token
void MPC.UpdateAccessToken(string accessToken);
Parameters:
Name | Type | Description |
---|---|---|
accessToken | String | Valid access token returned from an OAuth 2.0 provider. |
Estimate gas for native token transfer
Task<(EstimateGas data, MPCError error)> MPC.EstimateTransferNativeTokenGas(string toAddress, string amount);
Parameters:
Name | Type | Description |
---|---|---|
toAddress | String | Recipient's address. |
amount | String (hexString) | HexString representation of the amount to transfer. |
Example:
string toAddress = "0xedb40e7abaa613a0b06d86260dd55c7eb2df2447";
string amount = "0xde0b6b3a7640000"; // 1 RON
var (data, error) = await MPC.EstimateTransferNativeTokenGas(toAddress, amount);
if (error != null) {
Debug.LogError($"Error estimating gas: {error}");
} else {
Debug.Log($"Gas: {data.gas} - {data.gas.HexToBigInt()}");
Debug.Log($"Gas price: {data.gasPrice} - {data.gasPrice.HexToBigInt()}");
}
Estimate gas for ERC20 token transfer
Task<(EstimateGas data, MPCError error)> MPC.EstimateERC20TokenGas(string signature, string tokenAddress, string toAddress, string amount);
Parameters:
Name | Type | Description |
---|---|---|
signature | String | Signature of the smart contract's function. |
tokenAddress | String | Address of the ERC20 token's contract. |
toAddress | String | Recipient's address. |
amount | String (HexString) | HexString representation of the amount to transfer. |
Example:
string tokenAddress = "0x3c4e17b9056272ce1b49f6900d8cfd6171a1869d"; // AXS contract
string toAddress = "0xedb40e7abaa613a0b06d86260dd55c7eb2df2447";
string amount = "0xde0b6b3a7640000"; // 1 AXS
var (data, error) = await MPC.EstimateERC20TokenGas(MPCConstants.kSignatureApprove, tokenAddress, toAddress, amount);
if (error != null) {
Debug.LogError($"Error estimating gas: {error}");
}
else {
Debug.Log($"Gas: {data.gas} - {data.gas.HexToBigInt()}");
Debug.Log($"Gas price: {data.gasPrice} - {data.gasPrice.HexToBigInt()}");
}
Convert amount
BigInteger MPCUtils.ConvertAmount(string amount, int power);
Example:
- Convert the amount from a string to a BigInteger:
var amount = MPCUtils.ConvertAmount("10", 18);
Debug.Log($"BigInteger amount: {amount}");
- Transform BigInt to HexString for transaction usage:
var amount = MPCUtils.ConvertAmount("10", 18);
Debug.Log($"Hex amount: {amount.BigIntToHex()}");
Error handling
When making requests to our APIs, you may encounter two types of errors: SDK errors and server errors.
- SDK errors are static, client-side errors that typically arise from issues in the syntax or semantics of your code using the MPC SDK.
- Server errors are dynamic errors that occur on the MPC server itself.
When a user encounters an error, the system's response may include an SDK error code and a server error code. SDK errors occur before the user interacts with the Sky Mavis server, while server errors indicate issues with the server itself. The error response may also contain a 4xx HTTP status code, which points to a problem with the request sent by your game to Sky Mavis, such as an invalid format or missing information.
Error response syntax
{
"code": <CODE>,
"errorMessage": <MESSAGE>,
"serverErrorCode": <SERVER_ERROR_CODE>,
"closedReason": <SOCKET_CLOSED_REASON>
}
Property | Type | Description |
---|---|---|
code | Number | Error code triggered by the SDK. |
errorMessage | String | Combination of the error code and name, and information on how to address the error. |
serverErrorCode | Number | Error code returned from the MPC server. |
closedReason | String | Used for server-side errors when the server closes the socket abruptly. Contains the name of the error and information on how to address it. |
Sample response:
{
code: 20,
errorMessage: "error: code=110 message=hit ratelimit, retry later in 60.000000 seconds",
serverErrorCode: 110,
closedReason: "hit ratelimit, retry later in 60.000000 seconds"
}
Error codes
SDK error codes and server error codes have the same definitions. You can find these definitions in the following table.
Code | Name | Definition | Generated by |
---|---|---|---|
0 | OK | Not an error. Returned on success. | Client |
1 | Cancelled | The operation is cancelled, typically by the caller. | Server |
2 | Unknown | Unknown error. An example of where this error may be returned is if a status value received from another address space belongs to an error-space that is not known in this address space. Also errors raised by APIs that do not return enough error information may be converted to this error. | Client and server |
3 | InvalidArgument | The client specified an invalid argument. | Client and server |
4 | DeadlineExceeded | The operation expired before completion. For operations that change the state of the system, this error may be returned even if the operation has completed successfully. For example, a successful response from the server could have been delayed long enough for the deadline to expire. | Client |
5 | NotFound | A requested entity, such as a file or directory, is not found. | Client and server |
6 | AlreadyExists | An attempt to create an entity failed because one already exists. | Server |
7 | PermissionDenied | The caller doesn't have the permission to execute the specified operation. Don't use this for rejections caused by exhausting some resource—use ResourceExhausted instead. Also don't use if the caller can't be identified—use Unauthenticated instead. | Server |
8 | ResourceExhausted | A resource is exhausted, perhaps a per-user quota, or perhaps the entire file system is out of space. | Server |
10 | Aborted | The operation is aborted, typically due to a concurrency issue. | Server |
12 | Unimplemented | This method or this SDK isn't implemented yet and can't be used. | Client and server |
13 | Internal | Some invariants expected by the underlying system are broken. | Server |
14 | Unavailable | The data isn't ready to accept this request. | Server |
16 | Unauthenticated | The request doesn't have valid authentication credentials for the operation. | Client |
20 | MPCInitializeProtocolFailed | The MPC protocol failed to initialize. | Client and server |
21 | MPCHandshakeProtocolFailed | Failed to handshake with the other party in the MPC protocol. | Client and server |
22 | MPCBadSignature | The resultant MPC signature is malformed. | Client and server |
23 | MPCSignatureVerifyFailed | The signature is received, but an attempt to verify the data with this signature failed. | Client |
24 | MPCServerStoreKeyFailed | Failed to store the key on the keystore server. | Client and server |
25 | MPCBadResult | The MPC protocol successfully completed, but an unexpected bad result occurred. | Client and server |
26 | MPCSendTxRequestFailed | Failed to send the transaction request to the server. | Client |
27 | MPCBadKey | The user's private key shard is malformed or invalid. | Client and server |
28 | MPCSignMessageFailed | The message signing phase failed. | Client and server |
29 | MPCSendTxFailed | Failed to send the transaction to RPC. | Client and server |
30 | MPCAddressAlreadyExisted | The MPC wallet address already exists. | Server |
31 | MPCChallengeFailed | In the backup flow, the MPCChallenge is a phase that verifies whether the user owns the key. This error returns when that phase failed. | Server |
100 | DialSocketFailed | Failed to dial a WebSocket server. | Client |
101 | WriteDataFailed | Failed to write data to a WebSocket channel. | Client and server |
102 | ReadDataFailed | Failed to read data from a WebSocket channel. | Client and server |
103 | BadRPCData | Received an invalid MPC protocol message. | Client and server |
104 | BadSignMessageData | Received malformed or invalid message signing data. | Client and server |
105 | BadTxData | Received malformed or invalid transaction data. | Client and server |
106 | InitHTTPFailed | Failed to initialize the process of the HTTP connection, preventing the app from establishing a valid connection to the server or resource. | Client |
107 | DoHTTPFailed | Failed when calling JSON-RPC API or RESTful API. | Client |
108 | BadHTTPData | Received unexpected data from the server. | Client |
109 | DialRPCNodeFailed | When sending a transaction or any action that requires interaction with an RPC node, the server couldn't connect to the RPC node. | Server |
110 | HitRateLimitUUID | The rate limit exceeded, try again later (up to 60 seconds). | Server |
1001 | PolicyFailed | The user exceeded the limit set by a transaction policy. For example, exceeded the limit of RON transfers or tried to call a smart contract that isn't allowlisted. | Server |
HTTP status codes
Code | Name | Definition |
---|---|---|
400 | Bad Request | The API request itself is incorrect and contains invalid or incorrect values. |
401 | Unauthorized | The API request is sent with invalid authentication information (for example, bad JWT). |
403 | Forbidden | The API request is trying to perform something that the user is not allowed to do. |
404 | Not Found | The API request is trying to query a page that doesn’t exist. |
429 | Too Many Requests | The rate limit is exceeded. For more information, see Rate limits. |