Skip to content

Commit

Permalink
Merge pull request #27 from RemarkableTools/wallet
Browse files Browse the repository at this point in the history
Wallet
  • Loading branch information
axenteoctavian authored May 26, 2023
2 parents df49083 + e68b1dd commit e006e46
Show file tree
Hide file tree
Showing 9 changed files with 145 additions and 58 deletions.
81 changes: 61 additions & 20 deletions docs/advanced.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,37 +20,45 @@ Get a valid [`NetworkConfig`](https://github.com/RemarkableTools/Mx.NET.SDK/blob
```csharp
var networkConfig = await NetworkConfig.GetFromNetwork(provider);
```
Create a [`Signer`](https://github.com/RemarkableTools/Mx.NET.SDK/blob/master/src/Mx.NET.SDK.Wallet/Wallet/Signer.cs) instance by providing the key file and the associated password
Create a [`Signer`](https://github.com/RemarkableTools/Mx.NET.SDK/blob/master/src/Mx.NET.SDK.Wallet/Wallet/WalletSigner.cs) instance by providing the key file and the associated password
```csharp
var filePath = "PATH/TO/KEYFILE.json";
var password = "PASSWORD";
var signer = Signer.FromKeyFile(filePath, password);
var signer = WalletSigner.FromKeyFile(filePath, password);
```
Set up my Account and Receiver Address
```csharp
var myAccount = Account.From(await provider.GetAccount(signer.GetAddress().Bech32));
var account = Account.From(await provider.GetAccount(signer.GetAddress().Bech32));
var receiverAddress = Address.FromBech32("RECEIVER_ADDRESS");
```
Get a token from network
```csharp
var token = Token.From(await provider.GetToken("OFE-29eb54"));
var token = Token.From(await provider.GetToken("BUSD-632f7d"));
```
Create the [`Transaction Request`](https://github.com/RemarkableTools/Mx.NET.SDK/blob/master/src/Mx.NET.SDK/Domain/TransactionRequest.cs)
```csharp
var transactionRequest = TokenTransactionRequest.TokenTransfer(
networkConfig,
account,
receiverAddress,
token.Identifier.Value,
ESDTAmount.ESDT("100", token.GetESDT()).Value);
token.Identifier,
ESDTAmount.ESDT("100", token.GetESDT()));
```
Use the [`Wallet Methods`](https://github.com/RemarkableTools/Mx.NET.SDK/blob/master/src/Mx.NET.SDK.Wallet/WalletMethods.cs) to sign the transaction
```csharp
var signedTransaction = transactionRequest.Sign(signer);
var signedTransaction = signer.SignTransaction(transactionRequest);
```
POST the transaction to MultiversX API
```csharp
var response = await provider.SendTransaction(signedTransaction);
try
{
var response = await provider.SendTransaction(signedTransaction);
//other instructions
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
```
Get the [`Transaction`](https://github.com/RemarkableTools/Mx.NET.SDK/blob/master/src/Mx.NET.SDK/Domain/Data/Transaction/Transaction.cs) from response and await for execution to finalize
```csharp
Expand All @@ -65,25 +73,58 @@ Console.WriteLine($"Transaction executed with status {transaction.Status}");
The example is created for the [adder](https://github.com/multiversx/mx-sdk-rs/tree/master/contracts/examples/adder) contract.
#### Create a [`EGLD Transaction Request`](https://github.com/RemarkableTools/Mx.NET.SDK/blob/master/src/Mx.NET.SDK/TransactionsManager/EGLDTransactionRequest.cs) to a Smart Contract, sign it and send it to the network
```csharp
var transactionRequest = EGLDTransactionRequest.EGLDTransferToSmartContract(
networkConfig,
account,
smartContractAddress,
ESDTAmount.Zero(),
"add",
NumericValue.BigUintValue(10));
var signedTransaction = transactionRequest.Sign(signer);
var response = await provider.SendTransaction(signedTransaction);
var transaction = Transaction.From(response.TxHash);
await transaction.AwaitExecuted(provider);
Console.WriteLine($"Transaction executed with status {transaction.Status}");
try
{
var transactionRequest = EGLDTransactionRequest.EGLDTransferToSmartContract(
networkConfig,
account,
smartContractAddress,
ESDTAmount.Zero(),
"add",
NumericValue.BigUintValue(10));
var signedTransaction = signer.SignTransaction(transactionRequest);
var response = await provider.SendTransaction(signedTransaction);
var transaction = Transaction.From(response.TxHash);
await transaction.AwaitExecuted(provider);
Console.WriteLine($"Transaction executed with status {transaction.Status}");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
```
#### Query smart contract
```csharp
var smartContractAddress = Address.FromBech32("CONTRACT_BECH32_ADDRESS");
var outputType = TypeValue.BigUintTypeValue;
var queryResult = await SmartContract.QuerySmartContract<NumericValue>(provider,
smartContractAddress,
outputType,
"getSum");
Console.WriteLine(queryResult.Number);

// query array from Smart Contract (random example)
var queryArrayResult = await SmartContract.QueryArraySmartContract<Address>(provider,
smartContractAddress,
TypeValue.AddressValue,
"getUsers");
foreach (var user in queryArrayResult)
Console.WriteLine(user.Bech32);

// more complex reading from Smart Contract storage (random example)
uint day = 1;
var dayRewards = await SmartContract.QueryArraySmartContract<StructValue>(provider,
smartContractAddress,
TypeValue.StructValue("EsdtTokenPayment", new FieldDefinition[3]
{
new FieldDefinition("token_identifier", "", TypeValue.TokenIdentifierValue),
new FieldDefinition("token_nonce", "", TypeValue.U64TypeValue),
new FieldDefinition("amount", "", TypeValue.BigUintTypeValue)
}),
"getDayRewards",
null,
NumericValue.U32Value(day));
foreach(var esdt in dayRewards)
Console.WriteLine($"{esdt.Fields[0].Value} {esdt.Fields[1].Value} {esdt.Fields[2].Value}");
// You can map the StructValue from response to you custom class object for easier usage, if you need
```
2 changes: 2 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ var provider = new MultiversxProvider(new MultiversxNetworkConfiguration(Network
```
With this provider you can query the MultiversX API data like in the following examples:
```csharp
// You can integrate intructions in Try..Catch block
var networkConfig = await NetworkConfig.GetFromNetwork(provider);
var account = Account.From(await provider.GetAccount("erd1sdslvlxvfnnflzj42l8czrcngq3xjjzkjp3rgul4ttk6hntr4qdsv6sets"));
var transaction = Transaction.From(await provider.GetTransaction("0a94708e9653b79665ba41a6292ec865ab09e51a32be4b96b6f76ba272665f01"));
Expand Down
2 changes: 1 addition & 1 deletion src/Mx.NET.SDK.Wallet/Mx.NET.SDK.Wallet.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<RepositoryUrl>https://github.com/RemarkableTools/Mx.NET.SDK/tree/main/src/Mx.NET.SDK.Wallet</RepositoryUrl>
<RepositoryType>GitHub</RepositoryType>
<Company>Remarkable Tools</Company>
<Version>1.0.0</Version>
<Version>1.0.1</Version>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
<Title>RemarkableTools.Mx.Wallet</Title>
Expand Down
36 changes: 34 additions & 2 deletions src/Mx.NET.SDK.Wallet/Wallet/Mnemonic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,48 @@ namespace Mx.NET.SDK.Wallet.Wallet
{
public class Mnemonic
{
private const int MNEMONIC_STRENGTH = 256;
private const string HdPrefix = "m/44'/508'/0'/0'";

public static byte[] DecryptSecretKey(string mnemonic, int accountIndex = 0)
public string SeedPhrase { get; set; } = string.Empty;

private Mnemonic(string phrase)
{
SeedPhrase = phrase;
}

public static Mnemonic Generate()
{
var bip39 = new BIP39();
return new Mnemonic(bip39.GenerateMnemonic(MNEMONIC_STRENGTH, BIP39Wordlist.English));
}

public static Mnemonic FromString(string seedPhrase)
{
seedPhrase = seedPhrase.Trim();

var bip39 = new BIP39();
if (bip39.ValidateMnemonic(seedPhrase, BIP39Wordlist.English))
return new Mnemonic(seedPhrase);
else
throw new Exception("Bad mnemonic");
}

public string[] GetWords()
{
if (string.IsNullOrEmpty(SeedPhrase))
throw new Exception("Seed phrase is empty");
return SeedPhrase.Split(' ');
}

public static byte[] DecryptSecretKey(string mnemonic, int addressIndex = 0)
{
try
{
var bip39 = new BIP39();
var seedHex = bip39.MnemonicToSeedHex(mnemonic, "");

var hdPath = $"{HdPrefix}/{accountIndex}'";
var hdPath = $"{HdPrefix}/{addressIndex}'";
var kv = DerivePath(hdPath, seedHex);
var secretKey = kv.Key;
return secretKey;
Expand Down
23 changes: 18 additions & 5 deletions src/Mx.NET.SDK.Wallet/Wallet/PemFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,29 @@ public static byte[] DecryptSecretKey(string filePath, int index = 0)

public static string BuildPemFile(WalletSecretKey secretKey)
{
throw new NotImplementedException();

var publicKey = secretKey.GeneratePublicKey();
var address = publicKey.ToAddress().Bech32;
string header = $"-----BEGIN PRIVATE KEY for {address}-----";
string footer = $"-----END PRIVATE KEY for {address}-----";

var hex = Converter.ToHexString(secretKey.GetKey().Concat(publicKey.GetKey()).ToArray());
var text = Convert.ToBase64String(Encoding.UTF8.GetBytes(hex));
return text;
var keys = secretKey.GetKey().Concat(publicKey.GetKey()).ToArray();
var hex = Encoding.UTF8.GetBytes(Converter.ToHexString(keys));
var base64Key = Convert.ToBase64String(hex);

var lines = SplitBy(base64Key, 64);
var text = string.Join(Environment.NewLine, lines);
return string.Join(Environment.NewLine, new[] { header, text, footer });
}

private static IEnumerable<string> SplitBy(string text, int chunkSize)
{
var chunks = new List<string>();
for (var i = 0; i < text.Length; i += chunkSize)
{
var chunk = text.Substring(i, Math.Min(chunkSize, text.Length - i));
chunks.Add(chunk);
}
return chunks;
}
}
}
4 changes: 2 additions & 2 deletions src/Mx.NET.SDK.Wallet/Wallet/Wallet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@ public Account GetAccount()
/// Get the wallet signer
/// </summary>
/// <returns>Signer</returns>
public Signer GetSigner()
public WalletSigner GetSigner()
{
return new Signer(_secretKey);
return new WalletSigner(_secretKey);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
using System;
using System.Text;
using System.Text;
using Mx.NET.SDK.Core.Domain.Values;

namespace Mx.NET.SDK.Wallet.Wallet
{
public class Signer
public class WalletSigner
{
private WalletSecretKey _secretKey;
private readonly WalletSecretKey _secretKey;

public Signer(byte[] secretKey)
public WalletSigner(byte[] secretKey)
{
_secretKey = new WalletSecretKey(secretKey);
}

public Signer(WalletSecretKey secretKey)
public WalletSigner(WalletSecretKey secretKey)
{
_secretKey = secretKey;
}
Expand All @@ -24,9 +23,9 @@ public Signer(WalletSecretKey secretKey)
/// <param name="mnemonic">The mnemonic phrase</param>
/// <param name="accountIndex">The account index, default 0</param>
/// <returns></returns>
public static Signer FromMnemonic(string mnemonic, int accountIndex = 0)
public static WalletSigner FromMnemonic(string mnemonic, int accountIndex = 0)
{
return new Signer(Mnemonic.DecryptSecretKey(mnemonic, accountIndex));
return new WalletSigner(Mnemonic.DecryptSecretKey(mnemonic, accountIndex));
}

/// <summary>
Expand All @@ -35,19 +34,19 @@ public static Signer FromMnemonic(string mnemonic, int accountIndex = 0)
/// <param name="filePath">The KeyFile path</param>
/// <param name="password">The password</param>
/// <returns></returns>
public static Signer FromKeyFile(string filePath, string password)
public static WalletSigner FromKeyFile(string filePath, string password)
{
return new Signer(KeyFile.DecryptSecretKey(filePath, password));
return new WalletSigner(KeyFile.DecryptSecretKey(filePath, password));
}

/// <summary>
/// Derive a signer from PemFile
/// </summary>
/// <param name="filePath">The PemFile path</param>
/// <returns></returns>
public static Signer FromPemFile(string filePath)
public static WalletSigner FromPemFile(string filePath)
{
return new Signer(PemFile.DecryptSecretKey(filePath));
return new WalletSigner(PemFile.DecryptSecretKey(filePath));
}

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion src/Mx.NET.SDK.Wallet/Wallet/WalletVerifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Mx.NET.SDK.Wallet.Wallet
{
public class WalletVerifier
{
private WalletPublicKey _publicKey;
private readonly WalletPublicKey _publicKey;

public WalletVerifier(byte[] publicKey)
{
Expand Down
30 changes: 15 additions & 15 deletions src/Mx.NET.SDK.Wallet/WalletMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace Mx.NET.SDK.Wallet
{
public static class WalletMethods
{
public static TransactionRequestDto Sign(this TransactionRequest transactionRequest, Signer signer)
public static TransactionRequestDto SignTransaction(this WalletSigner signer, TransactionRequest transactionRequest)
{
var transactionRequestDto = transactionRequest.GetTransactionRequest();
var json = JsonWrapper.Serialize(transactionRequestDto);
Expand All @@ -20,20 +20,7 @@ public static TransactionRequestDto Sign(this TransactionRequest transactionRequ
return transactionRequestDto;
}

public static bool VerifySign(this TransactionRequest transactionRequest, string signature)
{
var transactionRequestDto = transactionRequest.GetTransactionRequest();
var message = JsonWrapper.Serialize(transactionRequestDto);

var verifier = WalletVerifier.FromAddress(transactionRequest.Sender);
return verifier.VerifyRaw(new SignableMessage()
{
Message = message,
Signature = signature
});
}

public static TransactionRequestDto[] MultiSign(this TransactionRequest[] transactionsRequest, Signer signer)
public static TransactionRequestDto[] SignTransactions(this WalletSigner signer, TransactionRequest[] transactionsRequest)
{
var transactions = new List<TransactionRequestDto>();

Expand All @@ -49,5 +36,18 @@ public static TransactionRequestDto[] MultiSign(this TransactionRequest[] transa

return transactions.ToArray();
}

public static bool VerifySignature(this TransactionRequest transactionRequest, string signature)
{
var transactionRequestDto = transactionRequest.GetTransactionRequest();
var message = JsonWrapper.Serialize(transactionRequestDto);

var verifier = WalletVerifier.FromAddress(transactionRequest.Sender);
return verifier.VerifyRaw(new SignableMessage()
{
Message = message,
Signature = signature
});
}
}
}

0 comments on commit e006e46

Please sign in to comment.