Giter Club home page Giter Club logo

solnet's Introduction

Build Release Coverage Status
Code License Follow on Twitter Discord

Introduction

Solnet is Solana's .NET SDK to integrate with the .NET ecosystem. Wherever you are developing for the Web or Desktop, we are here to help. Learn more about the provided samples, documentation, integrating the SDK into your app, and more here.

Features

  • Full JSON RPC API coverage
  • Full Streaming JSON RPC API coverage
  • Wallet and accounts (sollet and solana-keygen compatible)
  • Keystore (sollet and solana-keygen compatible)
  • Transaction decoding from base64 and wire format and encoding back into wire format
  • Message decoding from base64 and wire format and encoding back into wire format
  • Instruction decompilation
  • TokenWallet object to send SPL tokens and JIT provisioning of Associated Token Accounts
  • Programs
    • Native Programs
      • System Program
      • Stake Program
    • Solana Program Library (SPL)
      • Memo Program
      • Token Program
      • Token Swap Program
      • Associated Token Account Program
      • Name Service Program
      • Shared Memory Program

For the sake of maintainability and sometimes due to the size and complexity of some other programs, this repository will only contain Solana's Native Programs and Programs who are part of the SPL, for a list of other commonly needed programs see below:

Requirements

  • net 5.0

Dependencies

  • Chaos.NaCl.Standard
  • Portable.BouncyCastle

Examples

The Solnet.Examples project contains some code examples, essentially we're trying very hard to make it intuitive and easy to use the library. When trying to run these examples they might lead to errors in cases where they create new accounts, in these cases, the response from the RPC contains an and the transaction simulation logs which state that account address is ... already in use, all you need to do is increment the value that is used to derive that account from the seed being used, i.e wallet.GetAccount(value+1).

Wallets

The Solnet.Wallet project implements wallet and key generation features, these were made compatible with both the keys generated using solana-keygen and the keys generated using the popular browser wallet sollet.io.

Initializing a wallet compatible with sollet

// To initialize a wallet and have access to the same keys generated in sollet (the default)
var sollet = new Wallet("mnemonic words ...", WordList.English);

// Retrieve accounts by derivation path index
var account = sollet.GetAccount(10);

Initializing a wallet compatible with solana-keygen

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

// Retrieve the account
var account = wallet.Account; // the solana-keygen mechanism does not allow account retrieval by derivation path index

Generating new wallets

// Generate a new mnemonic
var newMnemonic = new Mnemonic(WordList.English, WordCount.Twelve);

var wallet = new Wallet(newMnemonic);

KeyStore

The Solnet.KeyStore project implements functionality to be able to securely store keys, seeds, mnemonics, and whatever you so desire. It contains an implementation of the Web3 Secret Storage Definition as well as a SolanaKeyStoreService which can be used to read keys generated by solana-keygen.

Secret KeyStore Service

// Initialize the KeyStore
var secretKeyStoreService = new SecretKeyStoreService();

// Encrypt a private key, seed or mnemonic associated with a certain address
var jsonString = secretKeyStoreService.EncryptAndGenerateDefaultKeyStoreAsJson(password, data, address);

// Or decrypt a web3 secret storage encrypted json data
byte[] data = null;
try { 
    data = KeyStore.DecryptKeyStoreFromJson(password, jsonString);
} catch (Exception) {
    Console.WriteLine("Invalid password!");
}

Solana KeyStore Service

// Initialize the KeyStore
var secretKeyStoreService = new SolanaKeyStoreService();

// Restore a wallet from the json file generated by solana-keygen,
// with the same passphrase used when generating the keys
var wallet = secretKeyStoreService.RestoreKeystore(filePath, passphrase);

RPC and Streaming RPC

The Solnet.Rpc project contains a full-fidelity implementation of the Solana JSON RPC, this implementation is compatible with both the methods expected to be removed in v1.8 and the methods which were added on v1.7 to replace them.

ClientFactory pattern

The client factory allows you to pass a Logger which implements the Microsoft.Extensions.Logging.ILogger interface.

var rpcClient = ClientFactory.GetClient(Cluster.MainNet, logger);
var streamingRpcClient = ClientFactory.GetStreamingClient(Cluster.MainNet, logger);

Using the RPC

// Get a certain account's info
var accountInfo = rpcClient.GetAccountInfo("5omQJtDUHA3gMFdHEQg1zZSvcBUVzey5WaKWYRmqF1Vj");

// Or get the token accounts owned by a certain account
var tokenAccounts = rpcClient.GetTokenAccountsByOwner("5omQJtDUHA3gMFdHEQg1zZSvcBUVzey5WaKWYRmqF1Vj");

// Or even filter the token accounts by the token's mint
var wrappedSolAccounts = rpcClient.GetTokenAccountsByOwner("5omQJtDUHA3gMFdHEQg1zZSvcBUVzey5WaKWYRmqF1Vj", "So11111111111111111111111111111111111111112");

// The following address represents Serum's address and it can be used for a number of things
var serumAddress = "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin";

// You can query the accounts owned by a certain program, and filter based on their account data!
var programAccounts = rpcClient.GetProgramAccounts(serumAddress);
var filters = new List<MemCmp>(){ new MemCmp{ Offset = 45, Bytes = OwnerAddress } };

var filteredProgramAccounts = rpcClient.GetProgramAccounts(serumAddress, memCmpList: filters);

Using the Streaming RPC

// After having sent a transaction
var txSig = rpcClient.SendTransaction(tx);

// You can subscribe to that transaction's signature to be finalized
var subscription = streaminRpcClient.SubscribeSignature(txSig.Result, (subscriptionState, response) => {
    // do something
}, Commitment.Finalized);

Sending a transaction

// Initialize the rpc client and a wallet
var rpcClient = ClientFactory.GetClient(Cluster.MainNet);
var wallet = new 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.GetLatestBlockHash();

// 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).
        SetFeePayer(fromAccount).
        AddInstruction(MemoProgram.NewMemo(fromAccount, "Hello from Sol.Net :)")).
        AddInstruction(SystemProgram.Transfer(fromAccount, toAccount.GetPublicKey, 100000)).
        Build(fromAccount);

var firstSig = rpcClient.SendTransaction(tx);

Create, Initialize and Mint

var wallet = new Wallet.Wallet(MnemonicWords);

var blockHash = rpcClient.GetLatestBlockHash();
var minBalanceForExemptionAcc = rpcClient.GetMinimumBalanceForRentExemption(TokenProgram.TokenAccountDataSize).Result;

var minBalanceForExemptionMint =rpcClient.GetMinimumBalanceForRentExemption(TokenProgram.MintAccountDataSize).Result;

var mintAccount = wallet.GetAccount(21);
var ownerAccount = wallet.GetAccount(10);
var initialAccount = wallet.GetAccount(22);

var tx = new TransactionBuilder().
    SetRecentBlockHash(blockHash.Result.Value.Blockhash).
    SetFeePayer(ownerAccount).
    AddInstruction(SystemProgram.CreateAccount(
        ownerAccount,
        mintAccount,
        minBalanceForExemptionMint,
        TokenProgram.MintAccountDataSize,
        TokenProgram.ProgramIdKey)).
    AddInstruction(TokenProgram.InitializeMint(
        mintAccount.PublicKey,
        2,
        ownerAccount.PublicKey,
        ownerAccount.PublicKey)).
    AddInstruction(SystemProgram.CreateAccount(
        ownerAccount,
        initialAccount,
        minBalanceForExemptionAcc,
        TokenProgram.TokenAccountDataSize,
        TokenProgram.ProgramIdKey)).
    AddInstruction(TokenProgram.InitializeAccount(
        initialAccount.PublicKey,
        mintAccount.PublicKey,
        ownerAccount.PublicKey)).
    AddInstruction(TokenProgram.MintTo(
        mintAccount.PublicKey,
        initialAccount.PublicKey,
        25000,
        ownerAccount)).
    AddInstruction(MemoProgram.NewMemo(initialAccount, "Hello from Sol.Net")).
    Build(new List<Account>{ ownerAccount, mintAccount, initialAccount });

Transfer a Token to a new Token Account

// Initialize the rpc client and a wallet
var rpcClient = ClientFactory.GetClient(Cluster.MainNet);
var wallet = new Wallet();

var blockHash = rpcClient.GetLatestBlockHash();
var minBalanceForExemptionAcc =
    rpcClient.GetMinimumBalanceForRentExemption(TokenProgram.TokenAccountDataSize).Result;

var mintAccount = wallet.GetAccount(21);
var ownerAccount = wallet.GetAccount(10);
var initialAccount = wallet.GetAccount(22);
var newAccount = wallet.GetAccount(23);

var tx = new TransactionBuilder().
    SetRecentBlockHash(blockHash.Result.Value.Blockhash).
    SetFeePayer(ownerAccount).
    AddInstruction(SystemProgram.CreateAccount(
        ownerAccount,
        newAccount,
        minBalanceForExemptionAcc,
        TokenProgram.TokenAccountDataSize,
        TokenProgram.ProgramIdKey)).
    AddInstruction(TokenProgram.InitializeAccount(
        newAccount.PublicKey,
        mintAccount.PublicKey,
        ownerAccount.PublicKey)).
    AddInstruction(TokenProgram.Transfer(
        initialAccount.PublicKey,
        newAccount.PublicKey,
        25000,
        ownerAccount)).
    AddInstruction(MemoProgram.NewMemo(initialAccount, "Hello from Sol.Net")).
    Build(new List<Account>{ ownerAccount, newAccount });

Transaction and Message decoding

// Given a message or transaction encoded as base64 or a byte array, you can decode it into their structures
var tx = Transaction.Deserialize(txData);
var msg = Message.Deserialize(msgData)

// This allows you to sign messages crafted by other components using Solnet
var signedTx = Transaction.Populate(msg, new List<byte[]> { account.Sign(msgData) });

Programs

The Solnet.Programs project contains our implementation of several Native and SPL programs, for brevity, they are not exemplified in depth here, but you should check out Solnet.Examples which contains numerous examples, such as how to do multi signature operations.

Hello Solana World

var memoInstruction = MemoProgram.NewMemo(wallet.Account, "Hello Solana World, using Solnet :)");

var recentHash = rpcClient.GetRecentBlockHash();

var tx = new TransactionBuilder().
    SetFeePayer(wallet.Account).
    AddInstruction(memoInstruction).
    SetRecentBlockHash(recentHash.Result.Value.Blockhash).
    Build(wallet.Account);

Creating and sending tokens to an Associated Token Account

var recentHash = rpcClient.GetRecentBlockHash();

// By taking someone's address, derive the associated token account for their address and a corresponding mint
// NOTE: You should check if that person already has an associated token account for that mint!
PublicKey associatedTokenAccountOwner = new ("65EoWs57dkMEWbK4TJkPDM76rnbumq7r3fiZJnxggj2G");
PublicKey associatedTokenAccount =
    AssociatedTokenAccountProgram.DeriveAssociatedTokenAccount(associatedTokenAccountOwner, mintAccount);

byte[] txBytes = new TransactionBuilder().
    SetRecentBlockHash(recentHash.Result.Value.Blockhash).
    SetFeePayer(ownerAccount).
    AddInstruction(AssociatedTokenAccountProgram.CreateAssociatedTokenAccount(
        ownerAccount,
        associatedTokenAccountOwner,
        mintAccount)).
    AddInstruction(TokenProgram.Transfer(
        initialAccount,
        associatedTokenAccount,
        25000,
        ownerAccount)).// the ownerAccount was set as the mint authority
    AddInstruction(MemoProgram.NewMemo(ownerAccount, "Hello from Sol.Net")).
    Build(new List<Account> {ownerAccount});

string signature = rpcClient.SendTransaction(txBytes)

Instruction decoding

// For more advanced usage, this package also has an instruction decoder
// You can deserialize a transaction's message similar to how you would using web3.js
var msg = Message.Deserialize(msgBase64);

// And you can decode all of the instructions in that message into a friendly structure
// which holds the program name, the instruction name, and parameters relevant to the instruction itself
var decodedInstructions = InstructionDecoder.DecodeInstructions(msg);

Display token balances of a wallet

// load Solana token list and get RPC client
var tokens = TokenMintResolver.Load();
var client = ClientFactory.GetClient(Cluster.MainNet);

// load snapshot of wallet and sub-accounts
TokenWallet tokenWallet = TokenWallet.Load(client, tokens, ownerAccount);
var balances = tokenWallet.Balances();

// show individual token accounts
var maxsym = balances.Max(x => x.Symbol.Length);
var maxname = balances.Max(x => x.TokenName.Length);
Console.WriteLine("Individual Accounts...");
foreach (var account in tokenWallet.TokenAccounts())
{
    Console.WriteLine($"{account.Symbol.PadRight(maxsym)} {account.BalanceDecimal,14} {account.TokenName.PadRight(maxname)} {account.PublicKey} {(account.IsAssociatedTokenAccount ? "[ATA]" : "")}");
}
Console.WriteLine();

Sending an SPL token

var wallet = new Wallet(MnemonicWords);
var ownerAccount = wallet.GetAccount(10); // fee payer

// load wallet and its token accounts
var client = ClientFactory.GetClient(Cluster.MainNet, logger);
var tokenDefs = new TokenMintResolver();
var tokenWallet = TokenWallet.Load(client, tokenDefs, ownerAccount);

// find source of funds
var source = tokenWallet.TokenAccounts().ForToken(WellKnownTokens.Serum).WithAtLeast(12.75M).FirstOrDefault();

// single-line SPL send - sends 12.75 SRM to target wallet ATA 
// if required, ATA will be created funded by ownerAccount.
// transaction is signed by you in the txBuilder callback to avoid passing your private keys out of this scope
var sig = tokenWallet.Send(source, 12.75M, target, txBuilder => txBuilder.Build(ownerAccount));
Console.WriteLine($"tx: {sig}");

Support

Consider supporting us:

  • Sol Address: oaksGKfwkFZwCniyCF35ZVxHDPexQ3keXNTiLa7RCSp
  • Mango Ref Link

Contribution

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

Maintainers

See also the list of contributors who participated in this project.

License

This project is licensed under the MIT License - see the LICENSE file for details

solnet's People

Contributors

albinmonsimier avatar aph5nt avatar bifrosttitan avatar crazynorman avatar gabrielepicco avatar hoakbuilds avatar lastbattle avatar liquizard avatar martinwhitman avatar mcanerizci avatar nazbrok avatar neo-vortex avatar qanonnetworklab avatar rebelecheveria avatar skynode avatar thesoftwarejedi avatar tiago18c avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

solnet's Issues

Improve coding style

Add a .editor config.
Run dotnet format.
Fix the consistency of the API enpoints that have lists or arrays (move into List instead of arrays)

Add commitment parameters

Add commitment parameters to the following RPC requests:

  • getAccountInfo
  • getBalance
  • getBlock
  • getBlockHeight
  • getBlockProduction
  • getBlocks
  • getBlocksWithLimit
  • getEpochInfo
  • getFeeCalculatorForBlockhash
  • getFees
  • getInflationGovernor
  • getInflationReward
  • getLargestAccounts
  • getLeaderSchedule
  • getMinimumBalanceForRentExemption
  • getMultipleAccounts
  • getProgramAccounts
  • getRecentBlockhash
  • getSignaturesForAddress
  • getSignatureStatuses
  • getSlot
  • getSlotLeader
  • getStakeActivation
  • getSupply
  • getTokenAccountBalance
  • getTokenAccountsByDelegate
  • getTokenAccountsByOwner
  • getTokenLargestAccounts
  • getTokenSupply
  • getTransaction
  • getTransactionCount
  • getVoteAccounts
  • sendTransaction
  • simulateTransaction

Implement missing SystemProgram methods

Implement the following missing SystemProgram methods:

  • Assign
  • CreateAccountWithSeed
  • CreateNonceAccount
  • CreateNonceAccountWithSeed
  • InitializeNonce
  • AdvanceNonce
  • WithdrawNonce
  • AuthorizeNonce
  • Allocate
  • AllocateWithSeed
  • AssignWithSeed

Update API urls

Migrate DevNet and TesNet urls to the newest ones.

DevNet: api.devnet.solana.com
TestNet: api.testnet.solana.com

Slash dependencies

After analyzing how long it takes to load a Blazor Wasm application we realized we can do a better job at keeping the library lightweight so the idea is to slash some of the following dependencies:

  • NBitcoin is only used because of the Mnemonic, we could easily add only the needed code to the Solnet.Wallet project and save around ~4MB of total size.

  • BouncyCastle is referenced in the Solnet.Wallet project but isn't used there, only in the KeyStore.

Add Solnet.Wallet tests

Coverage goals:

  • BIP39 key generation (solana-keygen compatible)
  • BIP32 key derivation (Sollet compatible)

Improve the KeyStore

Improve the KeyStore to allow recovering wallets from a string which represents the private key's byte array, as follows:

[69,191,12,22,125,16,119,72,240,150,74,197,249,221,54,164,172,222,248,202,22,242,96,43,105,164,101,52,155,41,46,6,107,27,120,68,31,183,113,110,148,151,206,38,195,198,108,78,97,66,196,191,82,41,240,33,253,9,89,19,75,196,171,104]

Add logger to clients and implement client factory with client interfaces

Add a way to inject a logger that implements Microsoft.Extensions.Logging.ILogger and implement a client factory as well as interfaces to not expose the client's constructors


  • RPC Client Logging
  • Streaming RPC Client Logging

  • RPC Client Interface
  • Streaming RPC Client Interface

  • Client Factory

Run dotnet format project wide

Because we're still having issues regarding code formatting due to different IDEs (Rider vs VS) and the .editorconfig and dotnet-format have been added to the project, just send it.

Add tests for Solnet.Rpc

Add tests to the RPC project attempting to cover the most important methods:

  • GetAccountInfo
  • GetBalance
  • GetRecentBlockhash
  • GetSupply
  • GetTokenAccountBalance
  • GetTokenAccountsByDelegate
  • GetTokenAccountsByOwner
  • GetTokenSupply
  • GetTokenLargestAccounts

Simplify AccountInfo

AccountInfo Data field cannot be deduced. Revert to returning the encoded string + encoding

Attempt to collapse Solnet.Util

Ideally we want to keep the the projects self contained, for this we'd prefer if there were no need for a utilities project.

i.e.

  • Move the utility methods related to Solnet.KeyStore string / byte array conversion to the keystore project
  • Check if any other utility methods can be converted to the project that refers them

Fix the cake build order

As it stands the cake build is skipping steps and jumping from the Test task straight to the Pack task, this should be fixed, Pack should be dependent on Publish, so it only happens when everything is OK.

Full JSON RPC API coverage

Provide an implementation for each of the following HTTP JSON RPC API methods:

  • getAccountInfo
  • getBalance
  • getBlock
  • getBlockHeight
  • getBlockProduction
  • getBlockCommitment
  • getBlocks
  • getBlocksWithLimit
  • getBlockTime
  • getClusterNodes
  • getEpochInfo
  • getEpochSchedule
  • getFeeCalculatorForBlockhash
  • getFeeRateGovernor
  • getFees
  • getFirstAvailableBlock
  • getGenesisHash
  • getHealth
  • getIdentity
  • getInflationGovernor
  • getInflationRate
  • getInflationReward
  • getLargestAccounts
  • getLeaderSchedule
  • getMaxRetransmitSlot
  • getMaxShredInsertSlot
  • getMinimumBalanceForRentExemption
  • getMultipleAccounts
  • getProgramAccounts
  • getRecentBlockhash
  • getRecentPerformanceSamples
  • getSignaturesForAddress
  • getSignatureStatuses
  • getSlot
  • getSlotLeader
  • getSlotLeaders
  • getStakeActivation
  • getSupply
  • getTokenAccountBalance
  • getTokenAccountsByDelegate
  • getTokenAccountsByOwner
  • getTokenLargestAccounts
  • getTokenSupply
  • getTransaction
  • getTransactionCount
  • getVersion
  • getVoteAccounts
  • minimumLedgerSlot
  • requestAirdrop
  • sendTransaction
  • simulateTransaction

Provide an implementation for each of the following WS JSON RPC API methods:

  • accountSubscribe
  • accountUnsubscribe
  • logsSubscribe
  • logsUnsubscribe
  • programSubscribe
  • programUnsubscribe
  • signatureSubscribe
  • signatureUnsubscribe
  • slotSubscribe
  • slotUnsubscribe

Mark them as done as we go, once all are done, close the issue and move on, cool guys don't look at explosions.

Add Solnet.KeyStore tests

Add tests to the keystore project, essentially the goal is to cover the solana-keygen output file and some web3 secret storage examples.

Implement missing TokenProgram methods

Implement the missing TokenProgram methods.

  • InitializeMultisig
  • Approve
  • Revoke
  • Burn
  • CloseAccount
  • FreezeAccount
  • SetAuthority
  • ApproveChecked
  • BurnChecked
  • MintChecked

Implement the Solana Program Library (SPL)

Implement the following Solana Program Library on-chain programs:

  • Token Program ( #76 )
  • Token Swap Program ( #260 )
  • Associated Token Account Program ( #106 )
  • Memo Program
  • Name Service ( #101 )
  • Shared memory Program ( #105 )
  • Stake Pool Program

Update:

After some thought we've decided not to include the Feature Proposal Program in our SPL implementation for v1

Update 2:

Discarded program implementations.

Token-Lending Program ( #254 )

Refactor StreamingClient

Refactor the SolanaRpcStreamingClient to handle the following scenarios:

  • Logger injection (and logging of exchanged messages)
  • Error handling

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.