cardanosharp / cardanosharp-wallet Goto Github PK
View Code? Open in Web Editor NEWCardanoSharp.Wallet is a Cardano Cryptographic and Serialization library for .NET applications.
License: MIT License
CardanoSharp.Wallet is a Cardano Cryptographic and Serialization library for .NET applications.
License: MIT License
Following the readme assigned to this project, the witness creation process states the following...
new VKeyWitness() { VKey = paymentPub.Key, SKey = paymentPrv.Key }
However, reviewing the documentation on the following page...
developers.cardano.org
shows the following...
new VKeyWitness() { VKey = paymentPub, SKey = paymentPrv }
The original Github readme gives the impression that you should be able to pass the publicKey.key and privateKey.key values for VKey and SKey, however, the developer.cardano.org link gives the impression that you need to pass the chaincode as well and generate the Tuple assigning them to the VKey and SKey values.
Can you clarify?
When using the C# SDK. They can help where we can insert in the code the creation of addresses for Testenet and Mainnet. And any transaction or functionality. From what I've seen it only works on Node Mainnet. Correct?
thanks
When creating a new Address
object, the public Address(string address)
constructor only checks if the address
is not null or whitespace. It seems that parsing and validation is implemented in Bech32.Decode(...)
which is called within a try-catch block.
As a result, we can have Address
objects that are invalid and do not represent any Cardano address. Furthermore, no exceptions are thrown when creating a transaction using such an invalid Address
.
The generated CBOR will have null
for the address field when used as an output. That is actually not a valid CBOR as per the specification and will likely result in an invalid CBOR/input exception when used with other software - Nami wallet in our case.
I would suggest that an Address
should only be constructed if it is valid. That would be 'fail fast' and save people time and headaches.
When creating a transaction via the Cardano Serialization Lib which contains one or more additional required signers, these are not correctly parsed when deserializing the CBOR via CardanoSharp.Wallet.
However, when the same transaction is build via the Cardano Serialization Lib without the additional required signers, everything works as expected.
Having the following CBOR constructed by the CSL containing required signers set via CSL's method add_required_signer
:
84a700818258207043c6744f9c699e2e5f43df08ffc3dbb259a89383a4023d133cc3b8bc5b5bae0101838258390005acdfa65952ac8a7b0cf168d4912535b117f5998967ee742644dcc405bc59d947656b4167d84497085d372b58f74b2057c8c542a11c22be1a001e848082583900c010c94c93bbe65283293a9946719b9277b325a0ab07e787b78572bac151e09314abc3031eae0f450ac4f3bbac851b9947ab4651433d47a81a003d0900825839001bcb60012703fcb4d6c97f1a177163e1797133b859a2b4feed0635657bcba45a50d5547e72cbad7a330c051a14b410a08dca551ed40e998d821a0048d9a3a1581c373b7956817434b15b87717075359307b1f1f6911ee03c4cb2004eb3a1581c6f636361656361746943686965664d6f62696c6974794f726368657301021a0003719d031a00bc177f0758206d600030ef909d098d217e07f0f34efee88a9b67740ad747a2833ec06cb091ae09a1581c373b7956817434b15b87717075359307b1f1f6911ee03c4cb2004eb3a1581c6f636361656361746943686965664d6f62696c6974794f7263686573010e82581c25ab8e56be62c4bf8a22db139f2c6a90793d21c17b99b9aaebf5f5f2581c20763045c339d6f996e02a0a81ca5f41aa3898e964fd3206caf12f75a101818201818200581c20763045c339d6f996e02a0a81ca5f41aa3898e964fd3206caf12f75f5f6
When this CBOR is deserialized by this library into an instance of Transaction and then serialized back to CBOR, the resulting CBOR is not identical to the one created by the CSL:
Code to deserialize/serialize:
var cborToSign = "84a7008182...2f75f5f6";
var cbor = CBORObject.DecodeFromBytes(Convert.FromHexString(cborToSign), new CBOREncodeOptions("keepkeyorder=true"));
var transaction = cbor.GetTransaction();
var cborOut = Convert.ToHexString(transaction.GetCBOR().EncodeToBytes()).ToLowerInvariant();
The CBOR constructed by this library after deserialization/serialization is different than the original CSL cbor most likely lacking the required signers being deserialized.
84a600818258207043c6744f9c699e2e5f43df08ffc3dbb259a89383a4023d133cc3b8bc5b5bae0101838258390005acdfa65952ac8a7b0cf168d4912535b117f5998967ee742644dcc405bc59d947656b4167d84497085d372b58f74b2057c8c542a11c22be1a001e848082583900c010c94c93bbe65283293a9946719b9277b325a0ab07e787b78572bac151e09314abc3031eae0f450ac4f3bbac851b9947ab4651433d47a81a003d0900825839001bcb60012703fcb4d6c97f1a177163e1797133b859a2b4feed0635657bcba45a50d5547e72cbad7a330c051a14b410a08dca551ed40e998d821a0048d9a3a1581c373b7956817434b15b87717075359307b1f1f6911ee03c4cb2004eb3a1581c6f636361656361746943686965664d6f62696c6974794f726368657301021a0003719d031a00bc177f0758206d600030ef909d098d217e07f0f34efee88a9b67740ad747a2833ec06cb091ae09a1581c373b7956817434b15b87717075359307b1f1f6911ee03c4cb2004eb3a1581c6f636361656361746943686965664d6f62696c6974794f726368657301a101818201818200581c20763045c339d6f996e02a0a81ca5f41aa3898e964fd3206caf12f75f5f6
When the CBOR constructed by the CSL does not contain additional required signers, everything works as expected.
Original CSL CBOR:
84a600818258207043c6744f9c699e2e5f43df08ffc3dbb259a89383a4023d133cc3b8bc5b5bae0101838258390005acdfa65952ac8a7b0cf168d4912535b117f5998967ee742644dcc405bc59d947656b4167d84497085d372b58f74b2057c8c542a11c22be1a001e848082583900c010c94c93bbe65283293a9946719b9277b325a0ab07e787b78572bac151e09314abc3031eae0f450ac4f3bbac851b9947ab4651433d47a81a003d0900825839001bcb60012703fcb4d6c97f1a177163e1797133b859a2b4feed0635657bcba45a50d5547e72cbad7a330c051a14b410a08dca551ed40e998d821a0048ea23a1581c373b7956817434b15b87717075359307b1f1f6911ee03c4cb2004eb3a1581c6f6d6e697350726f647563745365637572697479526570726573656e01021a0003611d031a00bc1a200758200a175108e98a082f86696662c3cbf4ea588edf60a09831ccc0be04279de4886d09a1581c373b7956817434b15b87717075359307b1f1f6911ee03c4cb2004eb3a1581c6f6d6e697350726f647563745365637572697479526570726573656e01a101818201818200581c20763045c339d6f996e02a0a81ca5f41aa3898e964fd3206caf12f75f5f6
Deserialized => Serialized CBOR via this library (identical to the original one):
84a600818258207043c6744f9c699e2e5f43df08ffc3dbb259a89383a4023d133cc3b8bc5b5bae0101838258390005acdfa65952ac8a7b0cf168d4912535b117f5998967ee742644dcc405bc59d947656b4167d84497085d372b58f74b2057c8c542a11c22be1a001e848082583900c010c94c93bbe65283293a9946719b9277b325a0ab07e787b78572bac151e09314abc3031eae0f450ac4f3bbac851b9947ab4651433d47a81a003d0900825839001bcb60012703fcb4d6c97f1a177163e1797133b859a2b4feed0635657bcba45a50d5547e72cbad7a330c051a14b410a08dca551ed40e998d821a0048ea23a1581c373b7956817434b15b87717075359307b1f1f6911ee03c4cb2004eb3a1581c6f6d6e697350726f647563745365637572697479526570726573656e01021a0003611d031a00bc1a200758200a175108e98a082f86696662c3cbf4ea588edf60a09831ccc0be04279de4886d09a1581c373b7956817434b15b87717075359307b1f1f6911ee03c4cb2004eb3a1581c6f6d6e697350726f647563745365637572697479526570726573656e01a101818201818200581c20763045c339d6f996e02a0a81ca5f41aa3898e964fd3206caf12f75f5f6
We need to convert the deconstructed returns to use types.
This will help us add better developer experience
Private/Public keys need to implement CIP005 for Bech32 output format.
Is there are any way to check address validity?
Something like
public static bool IsValid(string address){
....
}
We are calculating the fee based on the length of the hexadecimal string (see ToStringHex()
) below instead of the byte length.
public static uint CalculateFee(this Transaction transaction, uint? a = null, uint? b = null)
{
if (!a.HasValue) a = FeeStructure.Coefficient;
if (!b.HasValue) b = FeeStructure.Constant;
return ((uint)transaction.Serialize().ToStringHex().Length * a.Value) + b.Value;
}
However removing the ToStringHex()
(see solution below) calculates a fee 132 lovelaces less than it should be, and given a value of 44
for a
, suggests we are off by a factor of three bytes. This potentially highlights either a missing field, an incorrect data type, or a bug in CBOR generation when serialising the transaction using the rules defined in the CDDL
public static uint CalculateFee(this Transaction transaction, uint? a = null, uint? b = null)
{
if (!a.HasValue) a = FeeStructure.Coefficient;
if (!b.HasValue) b = FeeStructure.Constant;
return ((uint)transaction.Serialize().Length * a.Value) + b.Value;
}
AddressService GetAddress is returning the payment address instead of the stake/reward address. This is due to the same code being executed for both Enterprise and Reward addresses using the payment public key AddressService:38
byte[] addressArray;
switch (addressType)
{
case AddressType.Base:
addressArray = new byte[1 + paymentEncoded.Length + stakeEncoded.Length];
addressArray[0] = header;
Buffer.BlockCopy(paymentEncoded, 0, addressArray, 1, paymentEncoded.Length);
Buffer.BlockCopy(stakeEncoded, 0, addressArray, paymentEncoded.Length + 1, stakeEncoded.Length);
break;
case AddressType.Enterprise:
case AddressType.Reward:
addressArray = new byte[1 + paymentEncoded.Length];
addressArray[0] = header;
Buffer.BlockCopy(paymentEncoded, 0, addressArray, 1, paymentEncoded.Length);
break;
default:
throw new Exception("Unknown address type");
}
It should also still be valid with a null payment public key parameter which will currently throw an exception. Following from that, generating an enterprise address should also work with a null stake public key parameter. An easy fix can be applied with some null friendly args but perhaps a discussion is warranted - should we have SDK methods per address type? e.g. GetBaseAddress(PublicKey payment, PublicKey stake, NetworkType networkType); GetEnterpriseAddress(PublicKey payment, NetworkType networkType); GetRewardAddress(PublicKey stake, NetworkType networkType);
this should save our SDK consumers from any unexpected behaviour and make things explicit.
Currently file IO to load all available words for a word list happens every time the KeyService BIP39 methods are called (Generate/Restore).
Since they are immutable arrays can we pre-load these upon initialisation of the KeyService instance? Perhaps even initialised instance variables aren't required and a static readonly IDictionary<WordLists, string[]> master-lookup using an approach like https://github.com/realindiahotel/BIP39.NET/tree/master/BIP39.NET/BIP39.NET/Wordlists could work?
Currently ByteString type CBORObjects are decoded to byte[] using the long form expression
var byteArray = ((string)cborObject.DecodeValueByCborType()).HexToByteArray();
Where DecodeValueByCborType()
exists in CBORExtensions.cs as
public static object DecodeValueByCborType(this CBORObject cborObject)
{
object result = new object();
switch (cborObject.Type)
{
case CBORType.ByteString:
result = cborObject.ToString().Replace("h", "").Replace("'", "");
break;
}
}
This approach is found in hot paths for Transaction CBOR deserialization:
For the CardanoSharp.Wallet SDK to be used in performance-oriented applications we should use the simpler form which doesn't incur additional computation or allocation overhead yet returns same result.
var byteArray = cborObject.GetByteString();
Test to validate equivalence of the approaches
[Fact]
public void GetByteStringEquivalence()
{
var bytes = new byte[]
{
0x01, 0x01, 0x06, 0x00, 0x00, 0x00, 0x3d, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x0b, 0x82, 0x01, 0xfc, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
var cborBytestring = CBORObject.FromObject(bytes);
var decodedFromStringHex = ((string)cborBytestring.DecodeValueByCborType()).HexToByteArray();
var decodedByteString = cborBytestring.GetByteString();
Assert.Equal(decodedFromStringHex.Length, decodedByteString.Length);
for (int i = 0; i < bytes.Length; i++)
{
Assert.Equal(bytes[i], decodedFromStringHex[i]);
Assert.Equal(bytes[i], decodedByteString[i]);
}
}
TransactionOutputValue uses uint
type for output coins.
In c# uint
refers to UInt32 type with max value of 4294967295
Cardano has divisibility of 6 places so, 4294967295 lovelace is 4294.967295 ADA.
Today's cardano price in USD is 2.28 so maximum amount to send is only 9792 USD.
It's not enough to cover all needs.
My suggestion is to use ulong
type for output coins and fees.
Wanted to ask if it is possible to generate skey that would be compatible with cardano-cli
In cli we can create skey using this command
cardano-cli address key-gen
--verification-key-file payment.vkey
--signing-key-file payment.skey
Currently I have scenario that I have nami account for which I have 24 words.
I was wondering how I can create skey for it
IMnemonicService mnemonicService= new MnemonicService();
Mnemonic mnemonic = mnemonicService.Restore("24_words");
PrivateKey rootKey = mnemonic.GetRootKey();
System.IO.File.WriteAllBytes(@"c:\temp\some.skey", rootKey.Key);
However the content of this file isn't similar to what is generated with cardano-cli.
We have most of the system written with cardano-cli wrapper so we are not yet ready to fully migrate to cardanosharp-wallet
hence I would like to use MnemonicService and generate signing key and use it in the cardano-cli command.
Hi,
I've been using this library for a while now and recall coming across an issue when submitting a transaction I get an error:
TextEnvelope decode error: DecoderErrorDeserialiseFailure "Shelley Tx" (DeserialiseFailure 1 "Size mismatch when decoding \nRecord RecD.\nExpected 4, but found 3.").
My unity game is using version 2.0.0 and works fine, I never see this error. I forgot I downgraded it to 2.0.0 rather than a newer version due to some bug around sending tokens.
Recently I created an api that uses 2.6.0 of this library and this error came back again. Downgrading my dependency on this library to 2.0.0 from 2.6.0 in nuget and everything works fine.
Any idea why this would happen?
I reviewed the signed transactions manually and found they were like 1 byte different
Hello. Could you point me on how to generate bytes from daedalus address?
DdzFFzCqrht9W56zJGEFvHHywdeXZiGVYGqVhoZj6SRrS9o2HNLmorEzZhKm7khqfBKvCaTKGLtTnQSToxuvdzJTkQqcAf6f2ErxbSKS
-> byte[]
Hi! I was trying to use the library following the example code on the readme, but the code on the Calculate Fee section does not work any more. I wanted to fix the example, but I couldn't find the way.
Hope I can help in the future. Thanks in advance!
just a backing issue for future improvement
cardanosharp-wallet/CardanoSharp.Wallet/Encoding/Bech32.cs
Lines 159 to 164 in 4d0ffa7
Maybe the implementations of System.Convert.ToBase64String() and/or Base32 can give a hint on how to improve this method
That said, keeping it clsoe to the reference implementations of BIP32 is a good idea
Discovered while testing bech32 decoding in cscli PR CardanoSharp/cscli#3 using extended keys and policy keys.
Examples
Expected Bech32.IsValid
to return true
but is returning false
GetPrefixHeader does not work for all values. Examples that do not work, but are valid addresses (as defined in https://github.com/cardano-foundation/CIPs/tree/master/CIP-0019#test-vectors):
It seems that updating the below snippet could be sufficient:
public static string GetPrefixHeader(AddressType addressType)
{
switch (addressType)
{
case AddressType.Base:
return "addr";
case AddressType.Script:
return "addr";
case AddressType.ScriptWithScriptDelegation:
return "addr";
case AddressType.Enterprise:
return "addr";
case AddressType.EnterpriseScript:
return "addr";
case AddressType.Stake:
return "stake";
default:
throw new Exception("Unknown address type");
}
}
Generate this readonly member variable in the constructor? Seems like the equation is entropy = wordLength + (wordLength/3)
How public key hash can be derived in c# similary to this article
https://cardano.stackexchange.com/questions/8021/derive-public-key-hash-from-bech32-address-using-python
using cardanosharp-wallet?
Update the library to support Plutus Script V2.
Wallet users work primarily with base payment addresses but our backend code is more interested in the wallet account's total balance and assets instead of a single payment address.
BlockFrost and Koios expose very useful Acccount-based endpoints accepting a stake address as a request parameter. Lucky for us all the Cardano wallets conveniently share the same underlying stake key and address for all the base payment addresses and we can extract it with a bit of byte data shuffling.
Add a new method to IAddressService
and implement it
Address ExtractRewardAddress(Address baseAddress);
Now that we have the reward address we can then query BlockFrost and Koios.
Key criteria
Is it possible to calculate transaction hash before submitting the transaction?
I'm not sure if it is possible but if so can you add extension method or provide info how to derive stake address from address in c#
As per the example shown on how to create a transaction, addressService.GetAddressBytes is used during TransactionOutput, however is not available via Nuget package, nor via browsing the repository.
The Cardano testnet is not usable right now, therefore 2 new public test networks were created.
Cardanosharp hasn't been updated to support those new networks yet.
TransactionBuilder
Reading through the Transactions Section in the README I noticed that TransactionBuilder
doesn't really help build
the transaction.
So I extracted the interface for TransactionBuilder
and noticed that it looks more like a TransactionSerializer
public interface ITransactionBuilder
{
long CalculateFee(byte[] transaction);
byte[] SerializeBody(TransactionBody transactionBody);
byte[] SerializeTransaction(Transaction transaction);
}
We should name it accordingly. And since Serializers
don't CalculateFees
but TransactionBuilders
could lets assume we do this for now...
public interface ITransactionSerializer
{
byte[] SerializeBody(TransactionBody transactionBody);
byte[] SerializeTransaction(Transaction transaction);
}
public interface ITransactionBuilder
{
Transaction Transaction { get; }
int Fees { get; }
// more to come...
}
Since we have a TransactionSerializer
this should also be able to DeserializeBody
and DeserializeTransaction
public interface ITransactionSerializer
{
byte[] SerializeBody(TransactionBody transactionBody);
byte[] SerializeTransaction(Transaction transaction);
TransactionBody DeserializeBody(byte[] transactionBody);
Transaction DeserializeTransaction(byte[] transaction);
}
But of course we need a TransactionBuilder
that helps create Transaction
we can serialize/deserialize by ITransactionSerializer
.
TransactionBuilder
So what are transactions? They modify the ledger and can:
So I propose the ITransactionBuilder
to look like this:
public interface ITransactionBuilder
{
Transaction Transaction { get; }
int Fees { get; }
ITransactionBuilder AddCertificate(Certificate certificate);
ITransactionBuilder AddMetadata(Metadata metadata); // base type for PoolMetadata
ITransactionBuilder AddInput(Address addr, int index);
ITransactionBuilder AddOutput(Address addr, int amount);
ITransactionBuilder AddScript(NativeScript script);
Transaction Sign(params PrivateKey[] witnesses);
}
Notice:
StringBuilder
...AddXXX(...)
are like StringBuilder.Append
Sign(...)
is the equivalent to StringBuilder.ToString
Build()
method, since Transaction
is updated and maintained by the TransactionBuilder
"as-we-build"...new Transaction
so that we don't "loose" our building progress if the transaction fails.Transaction
propertyWhat are your thoughts / inputs? Should we pursue this?
How can I convert byte[] privetkey to privet key ?
Hi,
I love this project, thank you!
I'm trying to mimic how Daedalus does a few things. Currently Daedalus shows used and unused addresses in the Receive tab.
I am capable of deriving 20 addresses at a time and checking with a server to determine if these addresses are used or not. I end up with a list of x used addresses and the next 20 available unused addresses.
I have this all working and it's exactly the same as the Daedalus receive screen. The only issue I'm facing is how to achieve this without access to the private keys as they're encrypted with the spending password.
I have a couple of implementation examples of this but sadly if they're IAccountNodeDerivation or simply byte[] Key, byte[] Chaincode for both private key and public key that means I need the private key to derive the next 20 addresses.
Thanks for your help,
Dan
I've just tested blazor wasm with dotnet7
public partial class Index: ComponentBase
{
private IAddressService addressService = new AddressService();
private IMnemonicService service = new MnemonicService();
public int privateKeyStr { get; set; }
protected override void OnInitialized()
{
string q = "";
string words = "art forum devote street sure rather head chuckle guard poverty release quote oak craft enemy";
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;
base.OnInitialized();
}
}
And it works!
looks promising!
Found a situation where a transaction is intending to calculate with no witnesses and only mocks. At the time of calculate, TransactionWitnessSet is null which causes an exception.
Question: Is this implementation ok and we should expect at least an initialized TransactionWitnessSet
. Or should we initialize it in this method?
Im getting the error "The binary key cannot have an odd number of digits" when trying to perform a coin selection using "UseRandomImprove" strategy on cardano testnet environment, please check the code below:
`List encodedUtxos = new List()
{
"8282582046ecc0033a70b360fe45537091ca9dcb52238060545235ca203129448cf4bac50082583900c9545514cf191b89f2a27063adfefceaff866557c192df5482942ae438cc1f407e7de7c548a6a62e4814744c949ba1c6282238c55a6acd3a1a001bc33b",
"82825820a09658c3e19301210a505c00c426115406515c6245f835b00e82abd2ba5ba26e0182583900c9545514cf191b89f2a27063adfefceaff866557c192df5482942ae438cc1f407e7de7c548a6a62e4814744c949ba1c6282238c55a6acd3a821a004e659aa3581c82c9d41020cada347ca3c48ea38354955159916b6ddda937fc61de13a44a3344416d69676f333339014a3344416d69676f343331014a3344416d69676f343337014a3344416d69676f35333401581ca4b4a513e4345fda49cca6276bdfc0fe898fc44b9acbb5bb9675d094b82248416d69676f3235330148416d69676f3538300148416d69676f3830340148416d69676f3832340148416d69676f3835310148416d69676f3837390149416d69676f313032300149416d69676f313035320149416d69676f313132390149416d69676f313133360149416d69676f313338380149416d69676f313536340149416d69676f313631320149416d69676f313633310149416d69676f313633380149416d69676f313637300149416d69676f313831360149416d69676f313835360149416d69676f313937390149416d69676f323136340149416d69676f323236350149416d69676f323237340149416d69676f323430330149416d69676f323434360149416d69676f323534310149416d69676f323539330149416d69676f323734320149416d69676f323735310149416d69676f323834300149416d69676f323836340149416d69676f323837340149416d69676f323935330149416d69676f333430310149416d69676f3334383701581cdf3c4544b4a43d7565e9bcc41958b395a1e338a4fe30cd4d46da3b96a249416d69676f313534320149416d69676f3234303601",
"828258202013fb4b69f5e83dd98678ec34f0677c17939957fc0ea22afb2b83e64123c83c0082583900c9545514cf191b89f2a27063adfefceaff866557c192df5482942ae438cc1f407e7de7c548a6a62e4814744c949ba1c6282238c55a6acd3a1a001bc33b",
"828258208741c6b5b913a53bdcaae7344f9d0bf4dd7a9d8ea5e5b1e67742fcec539897570082583900c9545514cf191b89f2a27063adfefceaff866557c192df5482942ae438cc1f407e7de7c548a6a62e4814744c949ba1c6282238c55a6acd3a1a001bc33b",
"828258200c32034146fb94e581bdd5b04af436a44e519e7bec3b845a2ae68bd32f4760630082583900c9545514cf191b89f2a27063adfefceaff866557c192df5482942ae438cc1f407e7de7c548a6a62e4814744c949ba1c6282238c55a6acd3a1a001bc33b",
"828258205ea25e963a1d309fc7c312a6b637b72bc2cd716d2273f4d170f5a60131fdd6270082583900c9545514cf191b89f2a27063adfefceaff866557c192df5482942ae438cc1f407e7de7c548a6a62e4814744c949ba1c6282238c55a6acd3a1a001bc33b",
"828258205ea25e963a1d309fc7c312a6b637b72bc2cd716d2273f4d170f5a60131fdd6270082583900c9545514cf191b89f2a27063adfefceaff866557c192df5482942ae438cc1f407e7de7c548a6a62e4814744c949ba1c6282238c55a6acd3a1a001bc33b",
"828258201b59da607cf691f92080f38f43cfd14f57902d8e2c080a7cc3fcd3c300210bdf0082583900c9545514cf191b89f2a27063adfefceaff866557c192df5482942ae438cc1f407e7de7c548a6a62e4814744c949ba1c6282238c55a6acd3a1a001bc33b",
"82825820896257251e70af85fecff43ce87d11f242bd787c637b39b33179dc97399178800082583900c9545514cf191b89f2a27063adfefceaff866557c192df5482942ae438cc1f407e7de7c548a6a62e4814744c949ba1c6282238c55a6acd3a1a001bc33b",
"828258206c69975778efed999ad96366c431d47575707e7c09ff6c004dd27166b2036d660082583900c9545514cf191b89f2a27063adfefceaff866557c192df5482942ae438cc1f407e7de7c548a6a62e4814744c949ba1c6282238c55a6acd3a1a0018ff36",
"828258206b388166c06e39e8ff278a76f2ae90ae88b26547140b857e6c3ae83e1e1dc8960182583900c9545514cf191b89f2a27063adfefceaff866557c192df5482942ae438cc1f407e7de7c548a6a62e4814744c949ba1c6282238c55a6acd3a821a001e8480a1581c231aff70355327339e962028873bab4af1a3e1605d472806f30b4aada146546f6b656e3101",
"82825820c20dfc86db12c57285be5d04ec0b6e183480eacf4d77041ee01333d6e69685e10182583900c9545514cf191b89f2a27063adfefceaff866557c192df5482942ae438cc1f407e7de7c548a6a62e4814744c949ba1c6282238c55a6acd3a821a001e8480a1581c231aff70355327339e962028873bab4af1a3e1605d472806f30b4aada146546f6b656e3101",
"828258209822c29d6d60d66de34ce9fa199563380c42f8ac60e028344561d09c3bff52be0182583900c9545514cf191b89f2a27063adfefceaff866557c192df5482942ae438cc1f407e7de7c548a6a62e4814744c949ba1c6282238c55a6acd3a821a001e8480a1581c231aff70355327339e962028873bab4af1a3e1605d472806f30b4aada146546f74696f3101",
"82825820461836a03e45a5ebda2ab659822fa3f506d7eac89ff40b93ac7e255753c1f2190182583900c9545514cf191b89f2a27063adfefceaff866557c192df5482942ae438cc1f407e7de7c548a6a62e4814744c949ba1c6282238c55a6acd3a821a001e8480a1581c231aff70355327339e962028873bab4af1a3e1605d472806f30b4aada146546f74696f3101",
"828258201133f32071a317b7f809f56b2bd0d020a2c4b1d3a93be5906d6ee15b21b6a47c0182583900c9545514cf191b89f2a27063adfefceaff866557c192df5482942ae438cc1f407e7de7c548a6a62e4814744c949ba1c6282238c55a6acd3a821a001e8480a1581c231aff70355327339e962028873bab4af1a3e1605d472806f30b4aada146546f74696f3101",
"8282582071d5e26b48bb2dd2aadd3c3bee6db99bd6d18a3e6d8bd9b912131382cf0d6e7d0182583900c9545514cf191b89f2a27063adfefceaff866557c192df5482942ae438cc1f407e7de7c548a6a62e4814744c949ba1c6282238c55a6acd3a821a001e8480a1581c231aff70355327339e962028873bab4af1a3e1605d472806f30b4aada146546f6b656e3101",
"8282582071d5e26b48bb2dd2aadd3c3bee6db99bd6d18a3e6d8bd9b912131382cf0d6e7d0282583900c9545514cf191b89f2a27063adfefceaff866557c192df5482942ae438cc1f407e7de7c548a6a62e4814744c949ba1c6282238c55a6acd3a1a328fc20b",
"82825820448b9b507030d270e287907e3549f33d659cd2541434895040d33a3c1e70bd070082583900c9545514cf191b89f2a27063adfefceaff866557c192df5482942ae438cc1f407e7de7c548a6a62e4814744c949ba1c6282238c55a6acd3a1a006acfc0"
};
PolicyManager policyManager = new PolicyManager();
Address paymentAddr = new Address("addr_test1qprk02ddd6ar60qjlh4gmvgy3hxqcezx4yl452yl0tley9kx6zphf83h8hh9d7nfnmlt3gs8n545k2u38kq30f3mzlxqd7rhtf");
List<Utxo> utxos = new List<Utxo>();
try
{
encodedUtxos.ForEach(e => utxos.Add(UtxoExtensions.GetUtxo(CBORObject.DecodeFromBytes(e.HexToByteArray()))));
var customerAddressString = utxos[0].OutputAddress;
Address customerAddr = new Address(customerAddressString);
var scriptPolicy = policyManager.GetPolicyScript();
var transactionBody = TransactionBodyBuilder.Create;
transactionBody.AddOutput(paymentAddr.GetBytes(), 7000000);
var policyId = scriptPolicy.Build().GetPolicyId();
ITokenBundleBuilder tokenBundleBuilder = TokenBundleBuilder.Create
.AddToken(policyId, "Token1".ToBytes(), 1);
transactionBody.AddOutput(customerAddr.GetBytes(), 2000000, tokenBundleBuilder, outputPurpose: OutputPurpose.Mint);
transactionBody.SetMint(tokenBundleBuilder);
var coinSelection = ((TransactionBodyBuilder)transactionBody).UseRandomImprove(utxos, customerAddr.ToString(), tokenBundleBuilder);
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}`
cardanosharp-wallet/CardanoSharp.Wallet.Test/Bech32Tests.cs
Lines 26 to 61 in 71b9923
BIP173 defines valid test vectors for Bech32 here.
All parameters for InlineDataAttribute
need review and must be fixed first. (ie. expectedWitVer
and expectedHrp
)
The example shows:
// Calculate Fee
var fee = transaction.CalculateFee(minFeeA, minFeeB);
// Update Fee and Rebuild
transactionBody.SetFee(fee);
Transaction transaction = transactionBuilder.Build();
Note that transaction is created only after the var fee = transaction.... is called? This wouldn't even compile!
It would be helpful to add an example that shows what you do with the transaction after it's built, personally I had to struggle to figure out that I can take the signed transactions cbor string hex and use that!
Great project everyone, just needs a bit more documentation showing an entire flow from start to finish.
I'd suggest something that shows how to create/restore a wallet, see balances of assets/ada, tx history, submit of actual transaction.
Tokens and NFTs require metadata with some field names that represent unique values. PolicyID and AssetName must appear in the metadata as the actual field names in the JSON structure.
{ "721": { "f46ba28b19f7f35df3a5dbb9efeddee4bdd61ea33ba7fee2c31c655a": { "Token012382": { "name": "Fun tokens, just for fun.", "image": "ipfs://ipfs/QmVE1Ssn2wzc9Rg1emQCtxkMuWdG2AW21VpgbrDftmqnz7" } } } }
AuxiliaryDataBuilder is provided to assign metadata. The AddMetadata method accepts an object type which is then used to generate the JSON structure. All examples show using an anonymous type.
C# code using variables for field names.
metadataBuilder.AddMetadata(721, new { policyIdVariable = new { assetNameVariable = new { name = "Fun tokens, just for fun.", image = "ipfs://ipfs/QmVE1Ssn2wzc9Rg1emQCtxkMuWdG2AW21VpgbrDftmqnz7" } } });
The code above fails to use the variable's (policyIdVariable, assetNameVariable) values as field names. This is because anonymous types are defined at compile-time. As a test, to confirm the challenge, I hardcoded the PolicyID and AssetName as illustrated below.
C# hardcoded
metadataBuilder.AddMetadata(721, new { f46ba28b19f7f35df3a5dbb9efeddee4bdd61ea33ba7fee2c31c655a = new { Token012382 = new { name = "Fun tokens, just for fun.", image = "ipfs://ipfs/QmVE1Ssn2wzc9Rg1emQCtxkMuWdG2AW21VpgbrDftmqnz7" } } });
This did generate the desired metadata for a token minting transaction. There may be an easy way to dynamically define a field name on an anonymous type, but I am unaware of it. Is it possible to pass a JSON formatted string to the AddMetadata method instead of using a class structure?
Any advice or help is appreciated.
Hello:
I want to know if I can create a C# project to do the following job.
I need one deposit address on two networks, one is for HECO network, another is for ERC20 network.
And I can buy some currency tokens, for example, this token RUFF (Ruff Chain), and send the Ruff tokens to the deposit address I created via HECO network, as the exchange where I buy the RUFF tokens supports only HECO network. After the RUFF tokens arrive, I want to change the token network, from HECO to ERC20, then send the tokens to another wallet address, which is also on ERC20 network.
Simply put, I want to create one C# project, which can switch network for the same cryptocurrency, it is rather useful, as I can see that many exchanges have the same currency tokens, but they may be on different networks, if I want to send the same tokens from one exchange to another exchange, then the tokens have to be on the same network, but quite often, it is impossible.
Please advise if I can do this with the repo.
I can create MetaMask wallet, and MetaMask has some SDK in JavaScript, which I don’t have much experience, but I have good experience with C# .NET on Windows 10.
I have no experience with Cardano wallet, can I create a wallet just like I use MetaMask wallet?
And can I have Cardano wallet to connect with different networks, like HECO, REC20, BEP20, TRC20 and others?
So, I want to know if I can create Cardano wallet with dual networks support (HECO and ERC20), then use C# and this repo to receive some tokens (like RUFF), and change the network (from HECO to ERC20), then transfer the RUFF tokens to another wallet address (outside the MetaMask wallet control)?
If yes, please provide a general idea on how to proceed.
My OS: Windows 10 (22H2), IDE: Visual Studio 2022 (Version 17.4.1) with .net 7 support.
Thanks,
NativeScript types (i.e. ScriptAll/ScriptAny/ScriptNofK) with an internal array will incorrectly serialize to CBOR with an extra nested array. This results in transactions built by CardanoSharp being rejected by the node if they require NativeScripts (e.g. A NativeScript minting policy) with more than one internal scripts within a TransactionWitnessSet.
Given the following ScriptAll
var scriptAll = new ScriptAll();
scriptAll.NativeScripts.Add(
new NativeScript
{
InvalidAfter = new ScriptInvalidAfter
{
After = 96997186U
}
});
scriptAll.NativeScripts.Add(
new NativeScript
{
ScriptPubKey = new ScriptPubKey
{
KeyHash = "ee367222ee4d74903eb182757e90b82b3be306fe8b06c88bb2382def".HexToByteArray()
}
});
Get the CBOR
var scriptAllCbor = CBORObject.NewArray().Add(1);
foreach (var nativeScript in scriptAll.NativeScripts)
{
scriptAllCbor.Add(nativeScript.GetCBOR());
}
Console.WriteLine(scriptAllCbor.ToJSONString());
Address service is removed in v5.0.1, how can we have the same functionality in this version?
In a multi-threaded environment when multiple calls to Generate() and/or Restore() run concurrently, multiple threads mutating the wordIndexes
and allWords
arrays could have unintended side-effects in the outputs.
Suggestion: Declare and use local method-scoped variables for both arrays instead of instance member variables. E.g. allWords
will be declared in the methods and bound accordingly, SetWordsFromEntropy(entropy)
can have return type of uint[] which can then be assigned to another local method variable wordIndexes
and passed to GetMnemonic(wordIndexes, allWords)
?
//set outputs
var utxos = await GetUtxos(address.ToString());
ITokenBundleBuilder tbb = TokenBundleBuilder.Create
.AddToken(scriptPolicy.Build().GetPolicyId(), "Awesome NFT".ToBytes(), 1);
transactionBody.AddOutput(address.GetBytes(), 2000000, tbb);
transactionBody.AddOutput(sendPaymentToAddress.GetBytes(), 100000000);
//perform coin selection
var coinSelection = ((TransactionBodyBuilder)transactionBody).UseLargestFirstWithImprove(utxos);
The above will fail. The TokenBundle is for an asset that will be minted in this sample transaction. The strategies does not take into account for this scenario
After using the new mock witnesses functionality, I found myself taking extra steps to remove the mocked witnesses. Since we have an IsMock
flag on the VKeyWitness
object, maybe we can make a condition during the serialization to ignore mocked witnesses?
Algorithms
This section describes the coin selection algorithms used by Cardano Wallet, along with step-by-step descriptions of the computations involved.
All algorithms implement a common interface, as described in the Interface section.
There are two main algorithms used by Cardano Wallet:
Largest-First
Random-Improve
In general, Cardano Wallet gives priority to the Random-Improve algorithm, as experimental evidence shows that it performs better at minimising dust and maintaining a UTxO set with useful outputs. (See Self Organisation in Coin Selection for more details.)
However, in rare cases, the Random-Improve algorithm may fail to produce a result. In such cases, Cardano Wallet will fall back to the Largest-First algorithm.
I create my transaction and build it like the readme file. but I get this error.
Do you know how I can fix it?
transaction submit era mismatchEraMismatch {ledger Name = "Babbage", other Name = "Alonzo"
I have a TestNet Daedalus acting as a node in my machine, and cardano-submit-api running on top of it
When I try to submit a transaction created with Cardano Sharp I get a 500 and the following error on the Node
HandshakeError (Refused NodeToClientV_11 "version data mismatch: NodeToClientVersionData {networkMagic = NetworkMagic {unNetworkMagic = 1097911063}} /= NodeToClientVersionData {networkMagic = NetworkMagic {unNetworkMagic = 0}}")
I have also tried posting to https://submit-api.testnet.dandelion.link/api/submit/tx and also get a 500 (can't see the server error)
Any ideas? Seems to be something regarding the version, but I have the latest versions of Daeadalus and CardanoSharp
#17 introduced the PrivateKey
, PublicKey
, Mnemonic
, WalletPath
, and the various DerivationNode
types...
The main types left are going to be around NativeScript
. Technically these are already created but I believe they need some refactoring. Its not a very good experience build NativeScript
which are used for Minting and MultiSig.
Originally posted by @nothingalike in #5 (comment)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.