mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-20 04:05:52 +00:00
Refactor generateFaucetWallet to return a Wallet (#1564)
* add Wallet.generate() and * return Wallet in generateFaucetWallet * refactor Wallet tests * rename wallet-generation.ts to generateFaucetWallet.ts * rename and move Wallet.ts to src/wallet/index.ts and update webpack config
This commit is contained in:
committed by
Mayukha Vadari
parent
62538a75b1
commit
e0f4d99d86
@@ -115,7 +115,7 @@ import prepareTrustline from "../transaction/trustline";
|
||||
import { TransactionJSON, Instructions, Prepare } from "../transaction/types";
|
||||
import * as transactionUtils from "../transaction/utils";
|
||||
import { deriveAddress, deriveXAddress } from "../utils/derive";
|
||||
import generateFaucetWallet from "../wallet/wallet-generation";
|
||||
import generateFaucetWallet from "../wallet/generateFaucetWallet";
|
||||
|
||||
import { Connection, ConnectionUserOptions } from "./connection";
|
||||
|
||||
|
||||
@@ -11,4 +11,4 @@ export * from "./utils";
|
||||
// Broadcast client is experimental
|
||||
export { BroadcastClient } from "./client/broadcastClient";
|
||||
|
||||
export * from "./Wallet";
|
||||
export { default as Wallet } from "./wallet";
|
||||
|
||||
@@ -3,11 +3,10 @@ import _ from "lodash";
|
||||
import binaryCodec from "ripple-binary-codec";
|
||||
import keypairs from "ripple-keypairs";
|
||||
|
||||
import { Client } from "..";
|
||||
import { Client, Wallet } from "..";
|
||||
import { SignedTransaction } from "../common/types/objects";
|
||||
import { xrpToDrops } from "../utils";
|
||||
import { computeBinaryTransactionHash } from "../utils/hashes";
|
||||
import Wallet from "../Wallet";
|
||||
|
||||
import { SignOptions, KeyPair, TransactionJSON } from "./types";
|
||||
import * as utils from "./utils";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import https = require("https");
|
||||
|
||||
import { Client } from "..";
|
||||
import { Client, Wallet } from "..";
|
||||
import { errors } from "../common";
|
||||
import { RippledError } from "../common/errors";
|
||||
import { isValidAddress } from "../common/schema-validator";
|
||||
@@ -23,41 +23,44 @@ const MAX_ATTEMPTS = 20; // Maximum attempts to retrieve a balance
|
||||
/**
|
||||
* Generates a random wallet with some amount of XRP (usually 1000 XRP).
|
||||
*
|
||||
* @param this
|
||||
* @param address - An existing XRPL address to fund, if undefined, a new wallet will be created.
|
||||
* @param client - Client.
|
||||
* @param wallet - An existing XRPL Wallet to fund, if undefined, a new Wallet will be created.
|
||||
* @returns A Wallet on the Testnet or Devnet that contains some amount of XRP.
|
||||
* @throws When either Client isn't connected or unable to fund wallet address.
|
||||
*/
|
||||
async function generateFaucetWallet(
|
||||
this: Client,
|
||||
address?: string
|
||||
): Promise<FaucetWallet | void> {
|
||||
if (!this.isConnected()) {
|
||||
client: Client,
|
||||
wallet?: Wallet
|
||||
): Promise<Wallet | void> {
|
||||
if (!client.isConnected()) {
|
||||
throw new RippledError("Client not connected, cannot call faucet");
|
||||
}
|
||||
|
||||
// Initialize some variables
|
||||
let body: Uint8Array | undefined;
|
||||
let startingBalance = 0;
|
||||
const faucetUrl = getFaucetUrl(this);
|
||||
// Generate a new Wallet if no existing Wallet is provided or its address is invalid to fund
|
||||
const fundWallet =
|
||||
wallet && isValidAddress(wallet.classicAddress)
|
||||
? wallet
|
||||
: Wallet.generate();
|
||||
|
||||
// If the user provides an existing wallet to fund
|
||||
if (address && isValidAddress(address)) {
|
||||
// Create the POST request body
|
||||
body = new TextEncoder().encode(
|
||||
const body: Uint8Array | undefined = new TextEncoder().encode(
|
||||
JSON.stringify({
|
||||
destination: address,
|
||||
destination: fundWallet.classicAddress,
|
||||
})
|
||||
);
|
||||
// Retrieve the existing account balance
|
||||
const addressToFundBalance = await getAddressXrpBalance(this, address);
|
||||
const addressToFundBalance = await getAddressXrpBalance(
|
||||
client,
|
||||
fundWallet.classicAddress
|
||||
);
|
||||
|
||||
// Check the address balance is not undefined and is a number
|
||||
if (addressToFundBalance && !isNaN(Number(addressToFundBalance))) {
|
||||
startingBalance = Number(addressToFundBalance);
|
||||
} else {
|
||||
startingBalance = 0;
|
||||
}
|
||||
}
|
||||
const startingBalance =
|
||||
addressToFundBalance && !isNaN(Number(addressToFundBalance))
|
||||
? Number(addressToFundBalance)
|
||||
: 0;
|
||||
|
||||
const faucetUrl = getFaucetUrl(client);
|
||||
|
||||
// Options to pass to https.request
|
||||
const options = {
|
||||
@@ -82,20 +85,20 @@ async function generateFaucetWallet(
|
||||
|
||||
// "application/json; charset=utf-8"
|
||||
if (response.headers["content-type"]?.startsWith("application/json")) {
|
||||
const wallet: FaucetWallet = JSON.parse(body);
|
||||
const classicAddress = wallet.account.classicAddress;
|
||||
const faucetWallet: FaucetWallet = JSON.parse(body);
|
||||
const classicAddress = faucetWallet.account.classicAddress;
|
||||
|
||||
if (classicAddress) {
|
||||
try {
|
||||
// Check at regular interval if the address is enabled on the XRPL and funded
|
||||
const isFunded = await hasAddressBalanceIncreased(
|
||||
this,
|
||||
client,
|
||||
classicAddress,
|
||||
startingBalance
|
||||
);
|
||||
|
||||
if (isFunded) {
|
||||
resolve(wallet);
|
||||
resolve(fundWallet);
|
||||
} else {
|
||||
reject(
|
||||
new errors.XRPLFaucetError(
|
||||
@@ -9,11 +9,11 @@ import {
|
||||
verify,
|
||||
} from "ripple-keypairs";
|
||||
|
||||
import ECDSA from "./common/ecdsa";
|
||||
import { ValidationError } from "./common/errors";
|
||||
import { SignedTransaction } from "./common/types/objects";
|
||||
import { signOffline } from "./transaction/sign";
|
||||
import { SignOptions } from "./transaction/types";
|
||||
import ECDSA from "../common/ecdsa";
|
||||
import { ValidationError } from "../common/errors";
|
||||
import { SignedTransaction } from "../common/types/objects";
|
||||
import { signOffline } from "../transaction/sign";
|
||||
import { SignOptions } from "../transaction/types";
|
||||
|
||||
/**
|
||||
* A utility for deriving a wallet composed of a keypair (publicKey/privateKey).
|
||||
@@ -23,12 +23,27 @@ import { SignOptions } from "./transaction/types";
|
||||
class Wallet {
|
||||
readonly publicKey: string;
|
||||
readonly privateKey: string;
|
||||
readonly classicAddress: string;
|
||||
readonly seed?: string;
|
||||
private static readonly defaultAlgorithm: ECDSA = ECDSA.ed25519;
|
||||
private static readonly defaultDerivationPath: string = "m/44'/144'/0'/0/0";
|
||||
|
||||
constructor(publicKey: string, privateKey: string) {
|
||||
constructor(publicKey: string, privateKey: string, seed?: string) {
|
||||
this.publicKey = publicKey;
|
||||
this.privateKey = privateKey;
|
||||
this.classicAddress = deriveAddress(publicKey);
|
||||
this.seed = seed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a new Wallet using a generated seed.
|
||||
*
|
||||
* @param algorithm - The digital signature algorithm to generate an address for.
|
||||
* @returns A new Wallet derived from a generated seed.
|
||||
*/
|
||||
static generate(algorithm: ECDSA = Wallet.defaultAlgorithm): Wallet {
|
||||
const seed = generateSeed({ algorithm });
|
||||
return Wallet.fromSeed(seed);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,7 +113,7 @@ class Wallet {
|
||||
algorithm: ECDSA = Wallet.defaultAlgorithm
|
||||
): Wallet {
|
||||
const { publicKey, privateKey } = deriveKeypair(seed, { algorithm });
|
||||
return new Wallet(publicKey, privateKey);
|
||||
return new Wallet(publicKey, privateKey, seed);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,7 +151,7 @@ class Wallet {
|
||||
* @returns An X-address.
|
||||
*/
|
||||
getXAddress(tag: number, test = false): string {
|
||||
return classicAddressToXAddress(deriveAddress(this.publicKey), tag, test);
|
||||
return classicAddressToXAddress(this.classicAddress, tag, test);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { assert } from "chai";
|
||||
|
||||
import ECDSA from "../../src/common/ecdsa";
|
||||
import Wallet from "../../src/Wallet";
|
||||
import Wallet from "../../src/wallet";
|
||||
|
||||
/**
|
||||
* Wallet testing.
|
||||
@@ -9,6 +9,49 @@ import Wallet from "../../src/Wallet";
|
||||
* Provides tests for Wallet class.
|
||||
*/
|
||||
describe("Wallet", function () {
|
||||
describe("generate", function () {
|
||||
const classicAddressPrefix = "r";
|
||||
const ed25519KeyPrefix = "ED";
|
||||
const secp256k1PrivateKeyPrefix = "00";
|
||||
|
||||
it("generates a new wallet using default algorithm", function () {
|
||||
const wallet = Wallet.generate();
|
||||
|
||||
assert.isString(wallet.publicKey);
|
||||
assert.isString(wallet.privateKey);
|
||||
assert.isString(wallet.classicAddress);
|
||||
assert.isString(wallet.seed);
|
||||
assert.isTrue(wallet.publicKey.startsWith(ed25519KeyPrefix));
|
||||
assert.isTrue(wallet.privateKey.startsWith(ed25519KeyPrefix));
|
||||
assert.isTrue(wallet.classicAddress.startsWith(classicAddressPrefix));
|
||||
});
|
||||
|
||||
it("generates a new wallet using algorithm ecdsa-secp256k1", function () {
|
||||
const algorithm = ECDSA.secp256k1;
|
||||
const wallet = Wallet.generate(algorithm);
|
||||
|
||||
assert.isString(wallet.publicKey);
|
||||
assert.isString(wallet.privateKey);
|
||||
assert.isString(wallet.classicAddress);
|
||||
assert.isString(wallet.seed);
|
||||
assert.isTrue(wallet.privateKey.startsWith(secp256k1PrivateKeyPrefix));
|
||||
assert.isTrue(wallet.classicAddress.startsWith(classicAddressPrefix));
|
||||
});
|
||||
|
||||
it("generates a new wallet using algorithm ed25519", function () {
|
||||
const algorithm = ECDSA.ed25519;
|
||||
const wallet = Wallet.generate(algorithm);
|
||||
|
||||
assert.isString(wallet.publicKey);
|
||||
assert.isString(wallet.privateKey);
|
||||
assert.isString(wallet.classicAddress);
|
||||
assert.isString(wallet.seed);
|
||||
assert.isTrue(wallet.publicKey.startsWith(ed25519KeyPrefix));
|
||||
assert.isTrue(wallet.privateKey.startsWith(ed25519KeyPrefix));
|
||||
assert.isTrue(wallet.classicAddress.startsWith(classicAddressPrefix));
|
||||
});
|
||||
});
|
||||
|
||||
describe("fromSeed", function () {
|
||||
const seed = "ssL9dv2W5RK8L3tuzQxYY6EaZhSxW";
|
||||
const publicKey =
|
||||
@@ -65,7 +108,7 @@ describe("Wallet", function () {
|
||||
});
|
||||
|
||||
describe("fromEntropy", function () {
|
||||
const entropy: number[] = new Array(16).fill(0);
|
||||
let entropy;
|
||||
const publicKey =
|
||||
"0390A196799EE412284A5D80BF78C3E84CBB80E1437A0AECD9ADF94D7FEAAFA284";
|
||||
const privateKey =
|
||||
@@ -75,6 +118,11 @@ describe("Wallet", function () {
|
||||
const privateKeyED25519 =
|
||||
"ED0B6CBAC838DFE7F47EA1BD0DF00EC282FDF45510C92161072CCFB84035390C4D";
|
||||
|
||||
beforeEach(function () {
|
||||
const entropySize = 16;
|
||||
entropy = new Array(entropySize).fill(0);
|
||||
});
|
||||
|
||||
it("derives a wallet using entropy", function () {
|
||||
const wallet = Wallet.fromEntropy(entropy);
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { assert } from "chai";
|
||||
|
||||
import { getFaucetUrl, FaucetNetwork } from "../src/wallet/wallet-generation";
|
||||
import {
|
||||
getFaucetUrl,
|
||||
FaucetNetwork,
|
||||
} from "../src/wallet/generateFaucetWallet";
|
||||
|
||||
import setupClient from "./setupClient";
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ function getDefaultConfiguration() {
|
||||
},
|
||||
plugins: [
|
||||
new webpack.NormalModuleReplacementPlugin(/^ws$/, './wsWrapper'),
|
||||
new webpack.NormalModuleReplacementPlugin(/^\.\/wallet$/, './wallet-web'),
|
||||
new webpack.NormalModuleReplacementPlugin(/^\.\/wallet\/index$/, './wallet-web'),
|
||||
new webpack.NormalModuleReplacementPlugin(
|
||||
/^.*setup-api$/,
|
||||
'./setup-api-web'
|
||||
|
||||
Reference in New Issue
Block a user