Skip to main content

Get Started with CardanoSharp Wallet

CardanoSharp Wallet is a .NET library for Creating/Managing Wallets and Building/Signing Transactions.

Getting Started​

CardanoSharp.Wallet is installed from NuGet.

Install-Package CardanoSharp.Wallet

Create Mnemonics​

The MnemonicService has operations tbat help with generating and restoring Mnemonics. It is built for use in DI containers (ie. the interface IMnemonicService).

IMnemonicService service = new MnemonicService();

Generate Mnemonic​

IMnemonicService service = new MnemonicService();
Mnemonic rememberMe = service.Generate(24, WordLists.English);
System.Console.WriteLine(rememberMe.Words);

Restore Mnemonic​

string words = "art forum devote street sure rather head chuckle guard poverty release quote oak craft enemy";
Mnemonic mnemonic = MnemonicService.Restore(words);

Create Private and Public Keys​

Use powerful extensions to create and derive keys.

// The rootKey is a PrivateKey made of up of the 
// - byte[] Key
// - byte[] Chaincode
PrivateKey rootKey = mnemonic.GetRootKey();

// This path will give us our Payment Key on index 0
string paymentPath = $"m/1852'/1815'/0'/0/0";
// The paymentPrv is Private Key of the specified path.
PrivateKey paymentPrv = rootKey.Derive(paymentPath);
// Get the Public Key from the Private Key
PublicKey paymentPub = paymentPrv.GetPublicKey(false);

// This path will give us our Stake Key on index 0
string stakePath = $"m/1852'/1815'/0'/2/0";
// The stakePrv is Private Key of the specified path
PrivateKey stakePrv = rootKey.Derive(stakePath);
// Get the Public Key from the Stake Private Key
PublicKey stakePub = stakePrv.GetPublicKey(false);

If you want to learn more about key paths, read this article About Address Derivation

Create Addresses​

The AddressService lets you Create Addresses from Keys. It is built for use in DI containers (ie. the interface IAddressService)

IAddressService addressService = new AddressService();

From the public keys we generated above, we can now get the public address.

// add using
using CardanoSharp.Wallet.Models.Addresses;

// Creating Addresses require the Public Payment and Stake Keys
Address baseAddr = addressService.GetAddress(
paymentPub,
stakePub,
NetworkType.Testnet,
AddressType.Base);

If you already have an address.

Address baseAddr = new Address("addr_test1qz2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3jcu5d8ps7zex2k2xt3uqxgjqnnj83ws8lhrn648jjxtwq2ytjqp");

Fluent Key Derivation​

A fluent API helps navigate the derivation paths.

// Add using
using CardanoSharp.Wallet.Extensions.Models;

// Restore a Mnemonic
var mnemonic = new MnemonicService().Restore(words);

// Fluent derivation API
var derivation = mnemonic
.GetMasterNode("password") // IMasterNodeDerivation
.Derive(PurposeType.Shelley) // IPurposeNodeDerivation
.Derive(CoinType.Ada) // ICoinNodeDerivation
.Derive(0) // IAccountNodeDerivation
.Derive(RoleType.ExternalChain) // IRoleNodeDerivation
//or .Derive(RoleType.Staking)
.Derive(0); // IIndexNodeDerivation

PrivateKey privateKey = derivation.PrivateKey;
PublicKey publicKey = derivation.PublicKey;

Build and Sign Transactions​

CardanoSharp.Wallet requires input from the chain in order to build transactions. Lets assume we have gathered the following information.

uint currentSlot = 40000000;
ulong minFeeA = 44;
ulong minFeeB = 155381;
string inputTx = "0000000000000000000000000000000000000000000000000000000000000000";

Lets derive a few keys to use while building transactions.

// Derive down to our Account Node
var accountNode = rootKey.Derive()
.Derive(PurposeType.Shelley)
.Derive(CoinType.Ada)
.Derive(0);

// Derive our Change Node on Index 0
var changeNode = accountNode
.Derive(RoleType.InternalChain)
.Derive(0);

// Derive our Staking Node on Index 0
var stakingNode = accountNode
.Derive(RoleType.Staking)
.Derive(0);

// Deriving our Payment Node
// note: We did not derive down to the index.
var paymentNode = accountNode
.Derive(RoleType.ExternalChain);

Simple Transaction​

Lets assume the following...

  • You have 100 ADA on path: m/1852'/1815'/0'/0/0
  • You want to send 25 ADA to path: m/1852'/1815'/0'/0/1

Build Transaction Body​

// Generate the Receiving Address
Address paymentAddr = addressService.GetAddress(
paymentNode.Derive(1).PublicKey,
stakingNode.PublicKey,
NetworkType.Testnet,
AddressType.Base);

// Generate an Address for changes
Address changeAddr = addressService.GetAddress(
changeNode.PublicKey,
stakingNode.PublicKey,
NetworkType.Testnet,
AddressType.Base);

var transactionBody = TransactionBodyBuilder.Create
.AddInput(inputTx, 0)
.AddOutput(paymentAddr, 25)
.AddOutput(changeAddr, 75)
.SetTtl(currentSlot + 1000)
.SetFee(0)
.Build();

Build Transaction Witnesses​

For this simple transaction we really only need to add our keys. This is how we sign our transactions.

// Derive Sender Keys
var senderKeys = paymentNode.Derive(0);

var witnesses = TransactionWitnessSetBuilder.Create
.AddVKeyWitness(senderKeys.PublicKey, senderKeys.PrivateKey);

Calculate Fee​

// Construct Transaction Builder
var transactionBuilder = TransactionBuilder.Create
.SetBody(transactionBody)
.SetWitnesses(witnesses);

// Calculate Fee
var fee = transaction.CalculateFee(minFeeA, minFeeB);

// Update Fee and Rebuild
transactionBody.SetFee(fee);
Transaction transaction = transactionBuilder.Build();
transaction.TransactionBody.TransactionOutputs.Last().Value.Coin -= fee;

Metadata Transaction​

Building the Body and Witnesses are the same as the Simple Transaction.

If you would like to read more about Metadata, please read this article on Tx Metadata

// Build Metadata and Add to Transaction
var auxData = AuxiliaryDataBuilder.Create
.AddMetadata(1234, new { name = "simple message" });

var transaction = TransactionBuilder.Create
.SetBody(transactionBody)
.SetWitnesses(witnesses)
.SetAuxData(auxData)
.Build();

Minting Transaction​

Before we can mint a token, we need to create a policy.

If you would like to read more about policy scripts, please read this article on Simple Scripts.

// Generate a Key Pair for your new Policy
var keyPair = KeyPair.GenerateKeyPair();
var policySkey = keyPair.PrivateKey;
var policyVkey = keyPair.PublicKey;
var policyKeyHash = HashUtility.Blake2b244(policyVkey.Key);

// Create a Policy Script with a type of Script All
var policyScript = ScriptAllBuilder.Create
.SetScript(NativeScriptBuilder.Create.SetKeyHash(policyKeyHash))
.Build();

// Generate the Policy Id
var policyId = policyScript.GetPolicyId();

Now lets define our token.

// Create the AWESOME Token
string tokenName = "AWESOME";
uint tokenQuantity = 1;

var tokenAsset = TokenBundleBuilder.Create
.AddToken(policyId, tokenName.ToBytes(), tokenQuantity);

When minting, we will need to add our new token to one of the outputs of our Transaction Body.

// Generate an Address to send the Token
Address baseAddr = addressService.GetAddress(
paymentNode.Derive(1).PublicKey,
stakingNode.PublicKey,
NetworkType.Testnet,
AddressType.Base);

// Build Transaction Body with Token Bundle
var transactionBody = TransactionBodyBuilder.Create
.AddInput(inputTx, 0)
// Sending to Base Address, includes 100 ADA and the Token we are minting
.AddOutput(baseAddr, 100, tokenAsset)
.SetTtl(currentSlot + 1000)
.SetFee(0)
.Build();

Handling Token Bundles​

When building transaction, we need to ensure we handle tokens properly.

var tokenBundle = TokenBundleBuilder.Create
.AddToken(policyId, "Token1".ToBytes(), 100)
.AddToken(policyId, "Token2".ToBytes(), 200);

Address baseAddr = addressService.GetAddress(
paymentNode.Derive(1).PublicKey,
stakingNode.PublicKey,
NetworkType.Testnet,
AddressType.Base);

var transactionBody = TransactionBodyBuilder.Create
.AddInput(inputTx, 0)
.AddOutput(baseAddr, 2, tokenBundle)
.AddOutput(changeAddr, 98)
.SetTtl(currentSlot + 1000)
.SetFee(0)
.Build();