Following crypto standard libraries, BIPs, SLIPs, etc this library provides a generic solution which lets developers have a standard way to manage wallets
Audited by an independent security firm
npm install casper-storage
// or
yarn add casper-storage
<script src="https://cdn.jsdelivr.net/npm/casper-storage"></script>
<script>
const wallet = new CasperStorage.CasperHDWallet("master-key", CasperStorage.EncryptionType.Ed25519);
</script>
Due to missing features of JavascriptCore, we need to polyfill and override some features (e.g randombytes, encoding, etc)
Click here for more detailed information
A new user (Alice) accesses a wallet management (CasperWallet
) for the very first time
Alice asks for a new wallet
CasperWallet
generates 12-words keyphrase and shows her on the next screen, then asks her to back-up it (by writing down)
import { KeyFactory } from "casper-storage";
const keyphrase = KeyFactory.getInstance().generate();
// Example: "picture sight smart spike squeeze invest rose need basket error garden ski"
Alice confirms she keeps this master keyphrase in a safe place
CasperWallet
asks her to choose an encryption mode
(either secp256k1 or ed25519)
CasperWallet
recommends her to chooseed25519
over secp256k1 due to security and performance, unless Alice explicitly wants to use secp256k1 because of Bitcoin, Ethereum compatible
import { EncryptionType } from "casper-storage"
const encryptionType = EncryptionType.Ed25519;
CasperWallet
asks her for a secure password
, Alice gives Abcd1234
and CasperWallet
tries to intialize a User
instanceimport { User } from "caper-storage";
// Alice's password
const password = "Abcd1234";
// Initialize a new user with password
const user = new User(password);
CasperWallet
rejects because the given password is not strong enough
CasperWallet
asks her to give another one which is stronger and more secure
Alice gives AliP2sw0rd.1
and CasperWallet
re-tries
const password = "AliP2sw0rd.1";
const user = new User(password);
// Successfully created the user instance, let's set the HDWallet information
user.setHDWallet(keyphrase, encryptionType);
CasperWallet
creates the first wallet accountconst wallet = await user.addWalletAccount(0, new WalletDescriptor("Account 1"));
CasperWallet
serializes user's information and store it into storageimport { Storage } from "casper-storage";
const userInfo = user.serialize();
await Storage.getInstance().set("casperwallet_userinformation", userInfo);
const wallet = await user.getWalletAccount(0);
user.setWalletInfo(wallet.getReferenceKey(), "Salary account");
const userInfo = user.serialize();
await Storage.getInstance().set("casperwallet_userinformation", userInfo);
Alice locks her wallet
Alice comes back, CasperStorage
asks for the password. Assuming that she gives the right password, CasperStorage
retrieves back the user's information
import { Storage, User } from "casper-storage";
const userInfo = await Storage.getInstance().get("casperwallet_userinformation");
const user = new User(password);
user.deserialize(userInfo);
import { KeyFactory } from "casper-storage";
const keyManager = KeyFactory.getInstance();
keyManager.generate();
// output will be something like: basket pluck awesome prison unveil umbrella spy safe powder lock swallow shuffle
// By default, the outpult will be a phrase with 12 words, we can ask for more
keyManager.generate(24);
// Convert a master-key to a seed as a hex string
const seed: string = keyManager.toSeed("your keyphrase here");
// Convert a master-key to a seed as byte-array
const seed: Uint8Array = keyManager.toSeedArray("your keyphrase here");
const isValid: boolean = keyManager.validate("your keyphrase here");
import { KeyFactory, EncryptionType, CasperHDWallet } from "casper-storage"
const keyManager = KeyFactory.getInstance();
const masterKey = keyManager.generate()
const masterSeed = keyManager.toSeed(masterKey)
const masterSeedArray = keyManager.toSeedArray(masterKey)
const hdWallet = new CasperHDWallet(masterSeed, EncryptionType.Ed25519);
const acc0 = await hdWallet.getAccount(0)
const acc1 = await hdWallet.getAccount(1)
// Get the private key of wallet
acc0.getPrivateKey()
// Get the raw public key of wallet, which is computed, untouched data from private key
await acc0.getRawPublicKey()
// Get the public key of wallet, which is alternated depends on the chain
// For examples: Casper will prefix 01 or 02 depends on the encryption type
await acc0.getPublicKey()
// Get the public address of wallet, which is computed from public key
await acc0.getPublicAddress()
import { KeyFactory, EncryptionType, CasperLegacyWallet } from "casper-storage"
Prepare a private key (input from user, or read from a file)
Create a new instance of CasperLegacyWallet with that private key (either hex string or Uint8Array data)
const wallet = new CasperLegacyWallet("a-private-key-hex-string", EncryptionType.Ed25519)
const wallet = new CasperLegacyWallet(privateUint8ArrayData, EncryptionType.Secp256k1)
If users have PEM files (which are exported from casper-signer), we need to use KeyParser to parse it into a hex private string.
await wallet.getPublicKey()
await wallet.getPublicAddress()
import { User } from "casper-storage"
const user = new User("user-password")
By default, user-password will be verified to ensure it is strong enough (at least 10 characters, including lowercase, uppercase, numeric and a special character). We can override the validator by giving user options
By default, the derivation path of HD wallet is m/PURPOSE'/COINT_TYPE'/INDEX'
. We can override it by giving the third option e.g m/PURPOSE'/COINT_TYPE'/0'/0/INDEX
// With a regex
const user = new User("user-password", {
passwordValidator: {
validatorRegex: "passwordRegexValidation",
},
"m/PURPOSE'/COINT_TYPE'/INDEX'"
});
// or with a custom function
const user = new User("user-password", {
passwordValidator: {
validatorFunc: function (password) {
if (!password || password.length <= 10) {
return new ValidationResult(
false,
"Password length must be greater than or equal to 10"
);
} else {
return new ValidationResult(true);
}
},
},
"m/PURPOSE'/COINT_TYPE'/0'/0/INDEX"
});
// we can also update the password if needed
user.updatePassword("new-user-password");
// By default, new-user-password will be also verified to ensure it is strong enough
// we can override the validator by giving options
user.updatePassword("new-user-password", {
passwordValidator: {
validatorRegex: "passwordRegexValidation",
}
});
// master-key is a keyphrase 12-24 words
await user.setHDWallet("master-key", EncryptionType.Ed25519);
// we can retrieve back the master key
const entropy = user.getHDWallet().keyEntropy;
const masterKey: string[] = KeyFactory.getInstance().toKey(entropy);
// We can call addWalletAccount
user.addWalletAccount(0, new WalletDescriptor("Account 1"));
// or if we have the wallet account already
const acc0 = await user.getWalletAccount(0);
user.setWalletInfo(acc0.getReferenceKey(), new WalletDescriptor("Account 1"));
Scan all available users's account (index from 1+, maximum up to 20 following BIP's standard) and add them into the user instance
Optional, add user's legacy wallets
const wallet = new LegacyWallet("user-wallet-private-key", EncryptionType.Ed25519);
user.addLegacyWallet(wallet, new WalletDescriptor("Legacy wallet 1"));
// HDWallet account
const walletsInfo: WalletInfo[] = user.getHDWallet().derivedWallets;
// Legacy wallets
const legacyWalletsInfo: WalletInfo[] = user.getLegacyWallets();
// Wallet infornation
const walletInfo: WalletInfo = walletsInfo[0];
const refKey: string = walletInfo.key;
const encryptionType: EncryptionType = walletInfo.encryptionType;
const name: string = walletInfo.descriptor.name;
// Construct HD wallet's accounts
const wallet: IWallet<IHDKey> = await user.getWalletAccountByRefKey(refKey);
// or
const wallet: IWallet<IHDKey> = await user.getWalletAccount(walletInfo.index); // only for HD wallets
// Construct legacy wallet
const legacyWalletInfo = legacyWalletsInfo[0];
const wallet = new CasperLegacyWallet(legacyWalletInfo.key, legacyWalletInfo.encryptionType);
WalletInfo
represents a legacy wallet or a derived HD wallet, which is available inUser
WalletInfo
contains 2 main things// Asume that we have the wallet infornation at anytime
const storedWalletInfo: WalletInfo = walletsInfo[0];
// We store either id/uid to storage
const id = storedWalletInfo.id;
const uid = storedWalletInfo.uid;
// We have 2 ways to retrieve back wallet information
const walletInfo: WalletInfo = user.getWalletInfo(storedWalletInfo.id);
const walletInfo: WalletInfo = user.getWalletInfo(storedWalletInfo.uid);
// Both above calls return the same instance
// However we recommend developers to store `uid` of wallet info,
// and use it to retrieve back information from user later
// Serialize the user information to a secure encrypted string
const user = new User("user-password");
const userInfo = user.serialize();
// Deserialize the user information from a secure encrypted string
const user2 = new User("user-password", encryptedUserInfo);
user2.deserialize("user-encrypted-information");
// or
const user2 = User.deserializeFrom("user-password", "user-encrypted-information");
// In additional, User also exposes 2 methods to encrypt/decrypt data with user's password
const encryptedText = user.encrypt("Raw string value");
const decryptedText = user.decrypt(encryptedText);
import { StorageManager } from "casper-storage";
// Create a secured password
const password = new Password("Abcd1234.");
// Retrieve a secured storage with your password
const storage = StorageManager.getInstance(password);
// In the other hand, user also exposes getting storage method
const storage = user.getStorage();
// Set item into storage
await storage.set("key", "value");
// Get items from storage
const value = await storage.get("key");
// Update password, it will automatically re-sync existing keys
await storage.updatePassword(newPassword);
// or while updating password for user, existing keys will also be re-synced automatically
await user.updatePassword(newPassword);
// Other utils
const exists = await storage.has("key");
await storage.remove("key");
await storage.clear();
Get private keys in PEM formats
const pem = wallet.getPrivateKeyInPEM();
Parse exported PEM files from Casper signer
import { KeyParser } from "casper-storage";
// If you know exactly the encryption type, e.g Ed25519
const keyParser = KeyParser.getInstance(EncryptionType.Ed25519);
const keyValue = keyParser.convertPEMToPrivateKey(yourPEMContent);
// Otherwise, let it tries to guess
// It will try to detect encryption type, if not possible then it will throw an error
const keyParser = KeyParser.getInstance();
const keyValue = keyParser.convertPEMToPrivateKey(yourPEMContent);
// The keyValue exposes 2 properties
// Imported private-key
const key = keyValue.key;
// and its encryption type (EncryptionType.Ed25519 or EncryptionType.Secp256k1)
const encryptionType = keyValue.encryptionType;
Casper-storage is production-ready
yarn
to manage packages (npm install -g yarn
)typescript
tooling to developjest
to write unit-testsyarn lint
to ensure coding standardsyarn test
to run testsyarn testci
to run tests with test coverageyarn build
to compile typescript
to javascript
with declarationsyarn build-all
to build the library to final outputyarn docs
to generate document with typedoc
Apache License 2.0
Generated using TypeDoc