Skip to content

Commit

Permalink
Adding transactions, more rpc endpoints and documenting code.
Browse files Browse the repository at this point in the history
  • Loading branch information
hoakbuilds committed May 28, 2021
1 parent c10043d commit bc68ee4
Show file tree
Hide file tree
Showing 28 changed files with 1,405 additions and 193 deletions.
70 changes: 65 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,71 @@ Solnet is Solana's .NET integration library.

Solnet was developed targeting net 5.0 because we believe a modern decentralized platform deserves a modern technology framework to go along with it, although we are actively working on targeting earlier versions such as:

- netstandard 1.1
- netstandard 2.0
- netcore 2.1
- netcore 3.1
- net451
## Requirements
- net 5.0

## Dependencies
- NBitcoin
- Chaos.NaCl.Standard
- Portable.BouncyCastle

## Examples

The [Solnet.Examples](https://github.com/bmresearch/Solnet/src/Solnet.Examples/) project contains some code examples, but essentially we're trying very hard to
make it intuitive and easy to use the library.

### Initializing both wallets from Sollet and solana-keygen

```c#
// To initialize a wallet and have access to the same keys generated in solana-keygen
var wallet = new Wallet("mnemonic words ...", Wordlist.English, "passphrase");

// To initialize a wallet and have access to the same keys generated in sollet
var sollet = new Wallet("mnemonic words ...", Wordlist.English);
// Retrieve accounts by derivation path index
var account = sollet.GetAccount(10);

// Or initialize a mnemonic from NBitcoin before and use it
var mnemonic = new Mnemonic("mnemonic words ...");
var wallet = new Wallet(mnemonic);

```

### Sending a transaction

```c#
// Initialize the rpc client and a wallet
var rpcClient = new SolanaRpcClient("https://testnet.solana.com");
var wallet = new Wallet.Wallet();
// Get the source account
var fromAccount = wallet.GetAccount(0);
// Get the destination account
var toAccount = wallet.GetAccount(1);
// Get a recent block hash to include in the transaction
var blockHash = rpcClient.GetRecentBlockHash();

// Initialize a transaction builder and chain as many instructions as you want before building the message
var tx = new TransactionBuilder().
SetRecentBlockHash(blockHash.Result.Value.Blockhash).
AddInstruction(MemoProgram.NewMemo(fromAccount, "Hello from Sol.Net :)")).
AddInstruction(SystemProgram.Transfer(fromAccount.GetPublicKey, toAccount.GetPublicKey, 100000)).
Build(fromAccount);

var firstSig = rpcClient.SendTransaction(tx);
```


## Contribution

We encourage everyone to contribute, submit issues, PRs, discuss. Every kind of help is welcome.

## Contributors

* **Hugo** - *Maintainer* - [murlokito](https://github.com/murlokito)
* **Tiago** - *Maintainer* - [tiago](https://github.com/tiago18c)

See also the list of [contributors](https://github.com/bmresearch/Solnet/contributors) who participated in this project.

## License

This project is licensed under the MIT License - see the [LICENSE.md](https://github.com/bmresearch/Solnet/LICENSE.md) file for details
6 changes: 6 additions & 0 deletions Solnet.sln
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Solnet.Util", "src\Solnet.U
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Solnet.Rpc.Test", "test\Solnet.Rpc.Test\Solnet.Rpc.Test.csproj", "{E907F659-7FDE-4B98-85CD-1FBC6B1656CD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Solnet.Programs", "src\Solnet.Programs\Solnet.Programs.csproj", "{45D7F47F-4807-4918-BD78-F9EB2863B5A2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -51,6 +53,10 @@ Global
{E907F659-7FDE-4B98-85CD-1FBC6B1656CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E907F659-7FDE-4B98-85CD-1FBC6B1656CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E907F659-7FDE-4B98-85CD-1FBC6B1656CD}.Release|Any CPU.Build.0 = Release|Any CPU
{45D7F47F-4807-4918-BD78-F9EB2863B5A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{45D7F47F-4807-4918-BD78-F9EB2863B5A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{45D7F47F-4807-4918-BD78-F9EB2863B5A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{45D7F47F-4807-4918-BD78-F9EB2863B5A2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
1 change: 1 addition & 0 deletions src/Solnet.Examples/Solnet.Examples.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

<ItemGroup>
<ProjectReference Include="..\Solnet.KeyStore\Solnet.KeyStore.csproj" />
<ProjectReference Include="..\Solnet.Programs\Solnet.Programs.csproj" />
<ProjectReference Include="..\Solnet.Rpc\Solnet.Rpc.csproj" />
<ProjectReference Include="..\Solnet.Models\Solnet.Models.csproj" />
<ProjectReference Include="..\Solnet.Util\Solnet.Util.csproj" />
Expand Down
45 changes: 34 additions & 11 deletions src/Solnet.Examples/SolnetRpcTester.cs
Original file line number Diff line number Diff line change
@@ -1,35 +1,58 @@
using System;
using Solnet.Rpc;
using Solnet.Rpc.Core.Sockets;
using Solnet.Rpc.Models;

namespace Solnet.Examples
{
class SolnetRpcTester
{
static void Main(string[] args)
{

var c = new SolanaRpcClient("https://testnet.solana.com");

//var accInfo = c.GetAccountInfo("4K1oSvRCvALnJAaQdyxXLenV4fcxHyXDY2nYY6WDyKZT");
//var balance = c.GetBalance("9UGxCidmZtU1PM7Tbhv2twQ8ChsS6S3HdL1xo56fSVWn");
//var accInfo = c.GetGenesisHash();

//var blockCommitment = c.GetBlockCommitment(78561320);

//var blockTime = c.GetBlockTime(78561320);

var cn = c.GetClusterNodes();
//var cn = c.GetClusterNodes();

/* Large accounts for Token Mint PubKey
var largeAccounts = c.GetTokenLargestAccounts("7ugkvt26sFjMdiFQFP5AQX8m8UkxWaW7rk2nBk4R6Gf2");
foreach (var cluster in cn.Result)
foreach (var acc in largeAccounts.Result.Value)
{
Console.WriteLine("cluster node " + cluster.PubKey );
Console.WriteLine($"Acc: {acc.Address} Balance: {acc.UiAmountString}");
}
*/

/* Token balance for Account PubKey
var tokenBalance = c.GetTokenAccountBalance("7247amxcSBamBSKZJrqbj373CiJSa1v21cRav56C3WfZ");
Console.WriteLine($"Token Balance: {tokenBalance.Result.Value.UiAmountString}");
*/

var tokenAccounts = c.GetTokenAccountsByOwner(
"9we6kjtbcZ2vy3GSLLsZTEhbAqXPTRvEyoxa8wxSqKp5",null, "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
foreach (var acc in tokenAccounts.Result.Value)
{
Console.WriteLine("--------------------------------------------------------------------");
var accInfo = c.GetAccountInfoJson(acc.PublicKey);
TokenAccountData tokenAccData = null;
var data = accInfo.Result.Value.TryGetAccountData(out tokenAccData);
Console.WriteLine(
$"Token Account:\n" +
$"\tAccount PubKey: {acc.PublicKey}\n" +
$"\tAccount Lamport Balance: {acc.Account.Lamports}\n" +
$"\tAccount Encoded Data: {acc.Account.Data}\n" +
$"Account Info for {acc.PublicKey}:\n" +
$"\tAccount Owner: {tokenAccData.Parsed.Info.Owner}\n" +
$"\tToken Balance: {tokenAccData.Parsed.Info.TokenAmount.UiAmountString}\n" +
$"\tToken Mint: {tokenAccData.Parsed.Info.Mint}"
);
Console.WriteLine("--------------------------------------------------------------------");
}
}

private static void Sub_SubscriptionChanged(object sender, SubscriptionEvent e)
{
Console.WriteLine("subcription changed to: " + e.Status);
}
}
}
74 changes: 74 additions & 0 deletions src/Solnet.Examples/TransactionBuilderExample.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using System;
using System.Linq;
using System.Text;
using Solnet.Programs;
using Solnet.Rpc;
using Solnet.Rpc.Builders;
using Solnet.Rpc.Messages;
using Solnet.Rpc.Models;
using Solnet.Util;
using Solnet.Wallet;

namespace Solnet.Examples
{
public class TransactionBuilderExample
{
public static string PrettyPrintTransactionSimulationLogs(Log logMessage)
{
var logString = "";
foreach (var log in logMessage.Logs)
{
logString += $"\t\t{log}\n";
}

return logString;
}

static void TransactionAndMemoExample(string[] args)
{
var rpcClient = new SolanaRpcClient("https://testnet.solana.com");
var wallet = new Wallet.Wallet("route clerk disease box emerge airport loud waste attitude film army tray forward deal onion eight catalog surface unit card window walnut wealth medal");

var fromAccount = wallet.GetAccount(0);
var toAccount = wallet.GetAccount(1);

var blockHash = rpcClient.GetRecentBlockHash();
//Console.WriteLine($"BlockHash >> {blockHash.Blockhash}");

var tx = new TransactionBuilder().
SetRecentBlockHash(blockHash.Result.Value.Blockhash).
AddInstruction(MemoProgram.NewMemo(fromAccount, "Hello from Sol.Net :)")).
AddInstruction(SystemProgram.Transfer(fromAccount.GetPublicKey, toAccount.GetPublicKey, 10000000)).
Build(fromAccount);

Console.WriteLine($"Tx base64: {Convert.ToBase64String(tx)}");
var txSim = rpcClient.SimulateTransaction(tx);
var logs = PrettyPrintTransactionSimulationLogs(txSim.Result.Value);
Console.WriteLine($"Transaction Simulation:\n\tError: {txSim.Result.Value.Error}\n\tLogs: \n" + logs );
var firstSig = rpcClient.SendTransaction(tx);
Console.WriteLine($"First Tx Signature: {firstSig.Result}");
}

static void TokenMintExample(string[] args)
{
var rpcClient = new SolanaRpcClient("https://testnet.solana.com");
var wallet = new Wallet.Wallet("route clerk disease box emerge airport loud waste attitude film army tray forward deal onion eight catalog surface unit card window walnut wealth medal");

var blockHash = rpcClient.GetRecentBlockHash();
var minBalanceForExemption = rpcClient.GetMinimumBalanceForRentExemption(SystemProgram.AccountDataSize).Result.Value;

var mintAccount = wallet.GetAccount(3);
var ownerAccount = wallet.GetAccount(4);
var initialAccount = wallet.GetAccount(5);

var txBuilder = new TransactionBuilder().SetRecentBlockHash(blockHash.Result.Value.Blockhash)
.AddInstruction(SystemProgram.CreateAccount(
ownerAccount.GetPublicKey,
mintAccount.GetPublicKey,
minBalanceForExemption,
SystemProgram.AccountDataSize,
TokenProgram.ProgramId));

}
}
}
50 changes: 50 additions & 0 deletions src/Solnet.Programs/MemoProgram.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System.Collections.Generic;
using System.Text;
using NBitcoin.DataEncoders;
using Solnet.Rpc.Models;
using Solnet.Wallet;

namespace Solnet.Programs
{
/// <summary>
/// Helper class for the Memo Program.
/// <remarks>
/// Used to write UTF-8 data into Solana transactions.
/// </remarks>
/// </summary>
public static class MemoProgram
{
/// <summary>
/// The base58 encoder instance.
/// </summary>
private static readonly Base58Encoder Encoder = new ();

/// <summary>
/// The address of the Memo Program.
/// </summary>
private static string ProgramId = "Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo";

/// <summary>
/// Initialize a new transaction instruction which interacts with the Memo Program.
/// </summary>
/// <param name="account">The account associated with the memo.</param>
/// <param name="memo">The memo to be included in the transaction.</param>
/// <returns>The <see cref="TransactionInstruction"/> which includes the memo data.</returns>
public static TransactionInstruction NewMemo(Account account, string memo)
{
var keys = new List<AccountMeta>
{
new (account.PublicKey, true, false)
};
var memoBytes = Encoding.UTF8.GetBytes(memo);

return new TransactionInstruction
{
ProgramId = Encoder.DecodeData(ProgramId),
Keys = keys,
Data = memoBytes
};
}

}
}
11 changes: 11 additions & 0 deletions src/Solnet.Programs/Solnet.Programs.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Solnet.Rpc\Solnet.Rpc.csproj" />
</ItemGroup>

</Project>
Loading

0 comments on commit bc68ee4

Please sign in to comment.