Skip to content

Support BigInteger ids and network switching at runtime #96

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 61 additions & 29 deletions Assets/Thirdweb/Core/Scripts/ThirdwebSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ public ThirdwebSession(ThirdwebSDK.Options options, BigInteger chainId, string r

#endregion

#region Public Methods
#region Internal Methods

public async Task<string> Connect(WalletConnection walletConnection)
internal async Task<string> Connect(WalletConnection walletConnection)
{
switch (walletConnection.provider)
{
Expand Down Expand Up @@ -91,7 +91,14 @@ public async Task<string> Connect(WalletConnection walletConnection)
Web3 = await ActiveWallet.GetWeb3();
Web3.Client.OverridingRequestInterceptor = new ThirdwebInterceptor(ActiveWallet);

await EnsureCorrectNetwork();
try
{
await EnsureCorrectNetwork(ChainId);
}
catch (System.Exception e)
{
Debug.LogWarning("WalletProvider unable to switch chains, proceeding anyway. Error:" + e.Message);
}

var addy = await ActiveWallet.GetAddress();

Expand All @@ -100,59 +107,84 @@ public async Task<string> Connect(WalletConnection walletConnection)
return addy;
}

public async Task Disconnect()
internal async Task Disconnect()
{
await ActiveWallet.Disconnect();
ThirdwebManager.Instance.SDK.session = new ThirdwebSession(Options, ChainId, RPC);
}

public async Task<T> Request<T>(string method, params object[] parameters)
internal async Task<T> Request<T>(string method, params object[] parameters)
{
var request = new RpcRequest(Nonce, method, parameters);
Nonce++;
return await Web3.Client.SendRequestAsync<T>(request);
}

#endregion
internal async Task EnsureCorrectNetwork(BigInteger newChainId)
{
ThirdwebChainData newChainData = null;
try
{
newChainData = Options.supportedChains.ToList().Find(x => x.chainId == new HexBigInteger(newChainId).HexValue);
}
catch
{
throw new UnityException("The chain you are trying to switch to is not part of the ThirdwebManager's supported chains.");
}

#region Private Methods
NetworkSwitchAction switchResult = await ActiveWallet.PrepareForNetworkSwitch(newChainId, newChainData.rpcUrls[0]);

private async Task EnsureCorrectNetwork()
{
var hexChainId = await Request<string>("eth_chainId");
var connectedChainId = (int)hexChainId.HexToBigInteger(false);
if (connectedChainId != ChainId)
switch (switchResult)
{
try
{
await SwitchNetwork(new ThirdwebChain() { chainId = CurrentChainData.chainId });
}
catch (System.Exception e)
{
Debug.LogWarning("Switching chain error, attempting to add chain: " + e.Message);
try
case NetworkSwitchAction.ContinueSwitch:
var hexChainId = await Request<string>("eth_chainId");
var connectedChainId = hexChainId.HexToBigInteger(false);
if (connectedChainId != ChainId)
{
await AddNetwork(CurrentChainData);
await SwitchNetwork(new ThirdwebChain() { chainId = CurrentChainData.chainId });
try
{
await SwitchNetwork(new ThirdwebChain() { chainId = newChainData.chainId });
}
catch (System.Exception e)
{
Debug.LogWarning("Switching chain error, attempting to add chain: " + e.Message);
try
{
await AddNetwork(newChainData);
await SwitchNetwork(new ThirdwebChain() { chainId = newChainData.chainId });
}
catch (System.Exception f)
{
throw new UnityException("Adding chain error: " + f.Message);
}
}
}
catch (System.Exception f)
{
Debug.LogWarning("Adding chain error: " + f.Message);
}
}
break;
case NetworkSwitchAction.Handled:
break;
case NetworkSwitchAction.Unsupported:
throw new UnityException("Network switching is not supported by the active wallet.");
}

ChainId = newChainId;
CurrentChainData = newChainData;
RPC = CurrentChainData.rpcUrls[0];
Web3 = await ActiveWallet.GetWeb3();
Web3.Client.OverridingRequestInterceptor = new ThirdwebInterceptor(ActiveWallet);
}

#endregion

#region Private Methods

private async Task SwitchNetwork(ThirdwebChain newChain)
{
await Request<object>("wallet_switchEthereumChain", new object[] { newChain });
CurrentChainData.chainId = newChain.chainId;
}

private async Task AddNetwork(ThirdwebChainData newChainData)
{
await Request<object>("wallet_addEthereumChain", new object[] { newChainData });
CurrentChainData = newChainData;
}

public static ThirdwebChainData FetchChainData(BigInteger chainId, string rpcOverride = null)
Expand Down
19 changes: 14 additions & 5 deletions Assets/Thirdweb/Core/Scripts/Wallet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ public async Task<bool> IsConnected()
/// Gets the connected chainId.
/// </summary>
/// <returns>The connected chainId as an integer.</returns>
public async Task<int> GetChainId()
public async Task<BigInteger> GetChainId()
{
if (Utils.IsWebGLBuild())
{
Expand All @@ -311,7 +311,7 @@ public async Task<int> GetChainId()
else
{
var hexChainId = await ThirdwebManager.Instance.SDK.session.Request<string>("eth_chainId");
return (int)hexChainId.HexToBigInteger(false);
return hexChainId.HexToBigInteger(false);
}
}

Expand All @@ -320,15 +320,24 @@ public async Task<int> GetChainId()
/// </summary>
/// <param name="chainId">The chainId to switch to.</param>
/// <returns>A task representing the switching process.</returns>
public async Task SwitchNetwork(int chainId)
public async Task SwitchNetwork(BigInteger chainId)
{
if (!await IsConnected())
throw new Exception("No account connected!");

if (Utils.IsWebGLBuild())
{
await Bridge.SwitchNetwork(chainId);
int safeId;
if (!int.TryParse(chainId.ToString(), out safeId))
{
throw new Exception("Chain ID too large for WebGL platforms");
}

await Bridge.SwitchNetwork(safeId);
}
else
{
throw new UnityException("This functionality is not yet available on your current platform.");
await ThirdwebManager.Instance.SDK.session.EnsureCorrectNetwork(chainId);
}
}

Expand Down
80 changes: 80 additions & 0 deletions Assets/Thirdweb/Core/Scripts/Wallets/IThirdwebWallet.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,100 @@
using System.Numerics;
using System.Threading.Tasks;
using Nethereum.Web3;
using Nethereum.Web3.Accounts;

namespace Thirdweb.Wallets
{
/// <summary>
/// Interface for interacting with a Thirdweb wallet.
/// </summary>
public interface IThirdwebWallet
{
/// <summary>
/// Main Connect call - should fully connect to the wallet and return the address.
/// </summary>
/// <param name="walletConnection">The wallet connection details.</param>
/// <param name="rpc">The RPC endpoint.</param>
/// <returns>The address of the connected wallet.</returns>
Task<string> Connect(WalletConnection walletConnection, string rpc);

/// <summary>
/// Main Disconnect call - should fully disconnect from the wallet and reset any variables.
/// </summary>
Task Disconnect();

/// <summary>
/// Get the local account if any, return null otherwise.
/// </summary>
/// <returns>The local account, or null if not available.</returns>
Account GetLocalAccount();

/// <summary>
/// Return the address of the main account.
/// </summary>
/// <returns>The address of the main account.</returns>
Task<string> GetAddress();

/// <summary>
/// Return the address of the signer account (if any, otherwise return GetAddress).
/// </summary>
/// <returns>The address of the signer account.</returns>
Task<string> GetSignerAddress();

/// <summary>
/// Return the WalletProvider you added above.
/// </summary>
/// <returns>The WalletProvider.</returns>
WalletProvider GetProvider();

/// <summary>
/// Return the WalletProvider of the signer account (if any, otherwise return GetProvider).
/// </summary>
/// <returns>The WalletProvider of the signer account.</returns>
WalletProvider GetSignerProvider();

/// <summary>
/// Return the Web3 Nethereum provider for the main account - must override Task<RpcResponseMessage> SendAsync.
/// </summary>
/// <returns>The Web3 Nethereum provider for the main account.</returns>
Task<Web3> GetWeb3();

/// <summary>
/// Return the Web3 Nethereum provider for the signer account (if any, otherwise return GetWeb3).
/// </summary>
/// <returns>The Web3 Nethereum provider for the signer account.</returns>
Task<Web3> GetSignerWeb3();

/// <summary>
/// Return whether the wallet is currently connected (e.g. Web3 != null).
/// </summary>
/// <returns>True if the wallet is connected; otherwise, false.</returns>
Task<bool> IsConnected();

/// <summary>
/// Prepares the wallet for a network switch and returns an actionable response.
/// </summary>
/// <param name="newChainId">The new chain ID to switch to.</param>
/// <param name="newRpc">The new RPC endpoint to switch to.</param>
/// <returns>A <see cref="NetworkSwitchAction"/> indicating the action to be taken.</returns>
Task<NetworkSwitchAction> PrepareForNetworkSwitch(BigInteger newChainId, string newRpc);
}

public enum NetworkSwitchAction
{
/// <summary>
/// Indicates that the network switch can proceed. The SDK should continue with the wallet_switchEthereumChain RPC call.
/// </summary>
ContinueSwitch,

/// <summary>
/// Indicates that the wallet has already handled the network switch internally. There's no need to make the wallet_switchEthereumChain RPC call.
/// </summary>
Handled,

/// <summary>
/// Indicates that the network switching feature is completely unsupported for the current wallet implementation.
/// </summary>
Unsupported
}
}
6 changes: 6 additions & 0 deletions Assets/Thirdweb/Core/Scripts/Wallets/ThirdwebHyperplay.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Numerics;
using System.Threading.Tasks;
using Nethereum.Web3;
using Nethereum.Web3.Accounts;
Expand Down Expand Up @@ -75,5 +76,10 @@ public Task<bool> IsConnected()
{
return Task.FromResult(_web3 != null);
}

public Task<NetworkSwitchAction> PrepareForNetworkSwitch(BigInteger newChainId, string newRpc)
{
return Task.FromResult(NetworkSwitchAction.ContinueSwitch);
}
}
}
8 changes: 8 additions & 0 deletions Assets/Thirdweb/Core/Scripts/Wallets/ThirdwebLocalWallet.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Numerics;
using System.Threading.Tasks;
using Nethereum.Web3;
using Nethereum.Web3.Accounts;
Expand Down Expand Up @@ -75,5 +76,12 @@ public Task<bool> IsConnected()
{
return Task.FromResult(_web3 != null);
}

public Task<NetworkSwitchAction> PrepareForNetworkSwitch(BigInteger newChainId, string newRpc)
{
_account = new Account(_account.PrivateKey, newChainId);
_web3 = new Web3(_account, newRpc);
return Task.FromResult(NetworkSwitchAction.Handled);
}
}
}
6 changes: 6 additions & 0 deletions Assets/Thirdweb/Core/Scripts/Wallets/ThirdwebMagicLink.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Numerics;
using System.Threading.Tasks;
using link.magic.unity.sdk;
using link.magic.unity.sdk.Relayer;
Expand Down Expand Up @@ -82,5 +83,10 @@ public Task<bool> IsConnected()
{
return Task.FromResult(_web3 != null);
}

public Task<NetworkSwitchAction> PrepareForNetworkSwitch(BigInteger newChainId, string newRpc)
{
return Task.FromResult(NetworkSwitchAction.ContinueSwitch);
}
}
}
6 changes: 6 additions & 0 deletions Assets/Thirdweb/Core/Scripts/Wallets/ThirdwebMetamask.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Numerics;
using System.Threading.Tasks;
using MetaMask.NEthereum;
using MetaMask.Unity;
Expand Down Expand Up @@ -81,5 +82,10 @@ public Task<bool> IsConnected()
{
return Task.FromResult(_web3 != null);
}

public Task<NetworkSwitchAction> PrepareForNetworkSwitch(BigInteger newChainId, string newRpc)
{
return Task.FromResult(NetworkSwitchAction.ContinueSwitch);
}
}
}
Loading