-
Notifications
You must be signed in to change notification settings - Fork 127
Expand file tree
/
Copy pathSolanaWalletAdapterWebGL.cs
More file actions
318 lines (274 loc) · 13.4 KB
/
Copy pathSolanaWalletAdapterWebGL.cs
File metadata and controls
318 lines (274 loc) · 13.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
using AOT;
using Solana.Unity.Rpc.Models;
using Solana.Unity.Wallet;
using System;
using System.Threading.Tasks;
using UnityEngine;
// ReSharper disable once CheckNamespace
namespace Solana.Unity.SDK
{
[Serializable]
public class SolanaWalletAdapterWebGLOptions
{
public GameObject walletAdapterButtonPrefab;
public GameObject walletAdapterUIPrefab;
}
public class SolanaWalletAdapterWebGL: WalletBase
{
private static SolanaWalletAdapterWebGLOptions _walletOptions;
private static TaskCompletionSource<Account> _loginTaskCompletionSource;
private static TaskCompletionSource<string> _getWalletsTaskCompletionSource;
private static TaskCompletionSource<bool> _walletsInitializedTaskCompletionSource;
private static TaskCompletionSource<Transaction> _signedTransactionTaskCompletionSource;
private static TaskCompletionSource<Transaction[]> _signedAllTransactionsTaskCompletionSource;
private static TaskCompletionSource<byte[]> _signedMessageTaskCompletionSource;
private static Account _account;
public static GameObject WalletAdapterUI { get; private set; }
[Serializable]
public class WalletSpecs
{
public string name;
public bool installed;
public string icon;
public override string ToString()
{
return $"{name}: installed? {installed}";
}
}
[Serializable]
public class WalletSpecsObject
{
public WalletSpecs[] wallets;
}
public static WalletSpecs[] Wallets { get; private set; }
private static WalletSpecs _currentWallet;
private static string _clusterName;
[Obsolete("Use SolanaWalletAdapter class instead, which is the cross platform wrapper.")]
public SolanaWalletAdapterWebGL(
SolanaWalletAdapterWebGLOptions solanaWalletOptions,
RpcCluster rpcCluster = RpcCluster.DevNet,
string customRpcUri = null,
string customStreamingRpcUri = null,
bool autoConnectOnStartup = false)
: base(rpcCluster, customRpcUri, customStreamingRpcUri, autoConnectOnStartup)
{
_walletOptions = solanaWalletOptions;
if (Application.platform != RuntimePlatform.WebGLPlayer)
{
throw new Exception("SolanaWalletAdapterWebGL can only be used on WebGL");
}
_clusterName = RPCNameMap[(int)RpcCluster];
}
private static async Task InitWallets() {
_currentWallet = null;
_walletsInitializedTaskCompletionSource = new TaskCompletionSource<bool>();
InitWalletAdapter(OnWalletsInitialized, _clusterName);
bool isXnft = await _walletsInitializedTaskCompletionSource.Task;
if (isXnft){
_currentWallet = new WalletSpecs()
{
name = "XNFT",
icon = "",
installed = true
};
} else{
_getWalletsTaskCompletionSource = new TaskCompletionSource<string>();
ExternGetWallets(OnWalletsLoaded);
var walletsData = await _getWalletsTaskCompletionSource.Task;
Wallets = JsonUtility.FromJson<WalletSpecsObject>(walletsData).wallets;
}
}
/// <summary>
/// Check whether it's an XNFT or not
/// </summary>
/// <returns> true if it's an XNFT, false otherwise</returns>
public static async Task<bool> IsXnft(){
if(RuntimePlatform.WebGLPlayer != Application.platform){
return false;
}
await InitWallets();
return _currentWallet != null && _currentWallet.name == "XNFT";
}
protected override async Task<Account> _Login(string password = null)
{
await SetCurrentWallet();
_loginTaskCompletionSource = new TaskCompletionSource<Account>();
try
{
ExternConnectWallet(_currentWallet.name, OnWalletConnected);
}
catch (Exception e)
{
Debug.LogError("WalletAdapter _Login -> Exception: " + e);
_loginTaskCompletionSource.SetResult(null);
}
if (WalletAdapterUI != null ){
WalletAdapterUI.SetActive(false);
}
return await _loginTaskCompletionSource.Task;
}
private static async Task SetCurrentWallet()
{
await InitWallets();
if (_currentWallet == null)
{
if (WalletAdapterUI == null)
{
WalletAdapterUI = GameObject.Instantiate(_walletOptions.walletAdapterUIPrefab);
}
var waitForWalletSelectionTask = new TaskCompletionSource<string>();
var walletAdapterScreen =
WalletAdapterUI.transform.GetChild(0).gameObject.GetComponent<WalletAdapterScreen>();
walletAdapterScreen.viewPortContent = WalletAdapterUI.transform.GetChild(0).Find("Scroll View")
.Find("Viewport").Find("Content").GetComponent<RectTransform>();
walletAdapterScreen.buttonPrefab = _walletOptions.walletAdapterButtonPrefab;
walletAdapterScreen.OnSelectedAction = walletName =>
{
waitForWalletSelectionTask.SetResult(walletName);
};
WalletAdapterUI.SetActive(true);
var walletName = await waitForWalletSelectionTask.Task;
_currentWallet = Array.Find(Wallets, wallet => wallet.name == walletName);
}
}
protected override Task<Transaction> _SignTransaction(Transaction transaction)
{
_signedTransactionTaskCompletionSource = new TaskCompletionSource<Transaction>();
var base64TransactionStr = Convert.ToBase64String(transaction.Serialize()) ;
ExternSignTransactionWallet(_currentWallet.name,base64TransactionStr, OnTransactionSigned);
return _signedTransactionTaskCompletionSource.Task;
}
public override Task<byte[]> SignMessage(byte[] message)
{
_signedMessageTaskCompletionSource = new TaskCompletionSource<byte[]>();
var base64MessageStr = Convert.ToBase64String(message) ;
ExternSignMessageWallet(_currentWallet.name, base64MessageStr, OnMessageSigned);
return _signedMessageTaskCompletionSource.Task;
}
protected override Task<Account> _CreateAccount(string mnemonic = null, string password = null)
{
throw new NotImplementedException();
}
protected override Task<Transaction[]> _SignAllTransactions(Transaction[] transactions)
{
_signedAllTransactionsTaskCompletionSource = new TaskCompletionSource<Transaction[]>();
string[] base64Transactions = new string[transactions.Length];
for (int i = 0; i < transactions.Length; i++)
{
base64Transactions[i] = Convert.ToBase64String(transactions[i].Serialize());
}
var base64TransactionsStr = string.Join(",", base64Transactions);
ExternSignAllTransactionsWallet(_currentWallet.name,base64TransactionsStr, OnAllTransactionsSigned);
return _signedAllTransactionsTaskCompletionSource.Task;
}
#region WebGL Callbacks
/// <summary>
/// Called from javascript when the wallet adapter approves the connection
/// </summary>
[MonoPInvokeCallback(typeof(Action<string>))]
private static void OnWalletConnected(string walletPubKey)
{
if (walletPubKey == null)
{
_loginTaskCompletionSource.TrySetException(new Exception("Login cancelled"));
_loginTaskCompletionSource.TrySetResult(null);
return;
}
Debug.Log($"Wallet {walletPubKey} connected!");
_account = new Account("", walletPubKey);
_loginTaskCompletionSource.TrySetResult(_account);
}
/// <summary>
/// Called from javascript when the wallet signed the transaction and return the signature
/// that we then need to put into the transaction before we send it out.
/// </summary>
[MonoPInvokeCallback(typeof(Action<string>))]
public static void OnTransactionSigned(string transaction)
{
SolanaWalletAdapter.TriggerUserApprovedTransaction(transaction != null);
if (transaction == null)
{
_signedTransactionTaskCompletionSource.TrySetException(new Exception("Transaction signing cancelled"));
_signedTransactionTaskCompletionSource.TrySetResult(null);
return;
}
var tx = Transaction.Deserialize(Convert.FromBase64String(transaction));
_signedTransactionTaskCompletionSource.SetResult(tx);
}
/// <summary>
/// Called from javascript when the wallet signs all transactions and returns the signed transactions,
/// which we then need to send out.
/// </summary>
[MonoPInvokeCallback(typeof(Action<string>))]
public static void OnAllTransactionsSigned(string transactions)
{
SolanaWalletAdapter.TriggerUserApprovedTransaction(transactions != null);
if (transactions == null)
{
_signedAllTransactionsTaskCompletionSource.TrySetException(new Exception("Transactions signing cancelled"));
_signedAllTransactionsTaskCompletionSource.TrySetResult(null);
return;
}
string[] transactionsList = transactions.Split(',');
var txList = new Transaction[transactionsList.Length];
for (int i = 0; i < transactionsList.Length; i++)
{
txList[i] = Transaction.Deserialize(transactionsList[i]);
}
_signedAllTransactionsTaskCompletionSource.SetResult(txList);
}
/// <summary>
/// Called from javascript when the wallet adapter signed the message and return the signature.
/// </summary>
[MonoPInvokeCallback(typeof(Action<string>))]
public static void OnMessageSigned(string signature)
{
SolanaWalletAdapter.TriggerUserApprovedTransaction(signature != null);
if (signature == null)
{
_signedMessageTaskCompletionSource.TrySetException(new Exception("Message signing cancelled"));
_signedMessageTaskCompletionSource.TrySetResult(null);
return;
}
_signedMessageTaskCompletionSource.SetResult(Convert.FromBase64String(signature));
}
/// <summary>
/// Called from javascript when the wallet adapter script is loaded. Returns whether it's an XNFT or not.
/// </summary>
[MonoPInvokeCallback(typeof(Action<bool>))]
private static void OnWalletsInitialized(bool isXnft)
{
_walletsInitializedTaskCompletionSource.SetResult(isXnft);
}
/// <summary>
/// Called from javascript when the wallets are loaded
/// </summary>
[MonoPInvokeCallback(typeof(Action<string>))]
private static void OnWalletsLoaded(string walletsData)
{
_getWalletsTaskCompletionSource.SetResult(walletsData);
}
#endregion
#if UNITY_WEBGL
[DllImport("__Internal")]
private static extern void ExternConnectWallet(string walletName,Action<string> callback);
[DllImport("__Internal")]
private static extern void ExternSignTransactionWallet(string walletName, string transaction, Action<string> callback);
[DllImport("__Internal")]
private static extern void ExternSignAllTransactionsWallet(string walletName, string transactions, Action<string> callback);
[DllImport("__Internal")]
private static extern void ExternSignMessageWallet(string walletName, string messageBase64, Action<string> callback);
[DllImport("__Internal")]
private static extern string ExternGetWallets(Action<string> callback);
[DllImport("__Internal")]
private static extern void InitWalletAdapter(Action<bool> callback, string clusterName);
#else
private static void ExternConnectWallet(string walletName, Action<string> callback){}
private static void ExternSignTransactionWallet(string walletName, string transaction, Action<string> callback){}
private static void ExternSignAllTransactionsWallet(string walletName, string transactions, Action<string> callback){}
private static void ExternSignMessageWallet(string walletName, string messageBase64, Action<string> callback){}
private static string ExternGetWallets(Action<string> callback){return null;}
private static void InitWalletAdapter(Action<bool> callback, string clusterName){}
#endif
}
}