mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-20 12:15:51 +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 { TransactionJSON, Instructions, Prepare } from "../transaction/types";
|
||||||
import * as transactionUtils from "../transaction/utils";
|
import * as transactionUtils from "../transaction/utils";
|
||||||
import { deriveAddress, deriveXAddress } from "../utils/derive";
|
import { deriveAddress, deriveXAddress } from "../utils/derive";
|
||||||
import generateFaucetWallet from "../wallet/wallet-generation";
|
import generateFaucetWallet from "../wallet/generateFaucetWallet";
|
||||||
|
|
||||||
import { Connection, ConnectionUserOptions } from "./connection";
|
import { Connection, ConnectionUserOptions } from "./connection";
|
||||||
|
|
||||||
|
|||||||
@@ -11,4 +11,4 @@ export * from "./utils";
|
|||||||
// Broadcast client is experimental
|
// Broadcast client is experimental
|
||||||
export { BroadcastClient } from "./client/broadcastClient";
|
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 binaryCodec from "ripple-binary-codec";
|
||||||
import keypairs from "ripple-keypairs";
|
import keypairs from "ripple-keypairs";
|
||||||
|
|
||||||
import { Client } from "..";
|
import { Client, Wallet } from "..";
|
||||||
import { SignedTransaction } from "../common/types/objects";
|
import { SignedTransaction } from "../common/types/objects";
|
||||||
import { xrpToDrops } from "../utils";
|
import { xrpToDrops } from "../utils";
|
||||||
import { computeBinaryTransactionHash } from "../utils/hashes";
|
import { computeBinaryTransactionHash } from "../utils/hashes";
|
||||||
import Wallet from "../Wallet";
|
|
||||||
|
|
||||||
import { SignOptions, KeyPair, TransactionJSON } from "./types";
|
import { SignOptions, KeyPair, TransactionJSON } from "./types";
|
||||||
import * as utils from "./utils";
|
import * as utils from "./utils";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import https = require("https");
|
import https = require("https");
|
||||||
|
|
||||||
import { Client } from "..";
|
import { Client, Wallet } from "..";
|
||||||
import { errors } from "../common";
|
import { errors } from "../common";
|
||||||
import { RippledError } from "../common/errors";
|
import { RippledError } from "../common/errors";
|
||||||
import { isValidAddress } from "../common/schema-validator";
|
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).
|
* Generates a random wallet with some amount of XRP (usually 1000 XRP).
|
||||||
*
|
*
|
||||||
* @param this
|
* @param client - Client.
|
||||||
* @param address - An existing XRPL address to fund, if undefined, a new wallet will be created.
|
* @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.
|
* @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(
|
async function generateFaucetWallet(
|
||||||
this: Client,
|
client: Client,
|
||||||
address?: string
|
wallet?: Wallet
|
||||||
): Promise<FaucetWallet | void> {
|
): Promise<Wallet | void> {
|
||||||
if (!this.isConnected()) {
|
if (!client.isConnected()) {
|
||||||
throw new RippledError("Client not connected, cannot call faucet");
|
throw new RippledError("Client not connected, cannot call faucet");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize some variables
|
// Generate a new Wallet if no existing Wallet is provided or its address is invalid to fund
|
||||||
let body: Uint8Array | undefined;
|
const fundWallet =
|
||||||
let startingBalance = 0;
|
wallet && isValidAddress(wallet.classicAddress)
|
||||||
const faucetUrl = getFaucetUrl(this);
|
? wallet
|
||||||
|
: Wallet.generate();
|
||||||
|
|
||||||
// If the user provides an existing wallet to fund
|
|
||||||
if (address && isValidAddress(address)) {
|
|
||||||
// Create the POST request body
|
// Create the POST request body
|
||||||
body = new TextEncoder().encode(
|
const body: Uint8Array | undefined = new TextEncoder().encode(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
destination: address,
|
destination: fundWallet.classicAddress,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
// Retrieve the existing account balance
|
// 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
|
// Check the address balance is not undefined and is a number
|
||||||
if (addressToFundBalance && !isNaN(Number(addressToFundBalance))) {
|
const startingBalance =
|
||||||
startingBalance = Number(addressToFundBalance);
|
addressToFundBalance && !isNaN(Number(addressToFundBalance))
|
||||||
} else {
|
? Number(addressToFundBalance)
|
||||||
startingBalance = 0;
|
: 0;
|
||||||
}
|
|
||||||
}
|
const faucetUrl = getFaucetUrl(client);
|
||||||
|
|
||||||
// Options to pass to https.request
|
// Options to pass to https.request
|
||||||
const options = {
|
const options = {
|
||||||
@@ -82,20 +85,20 @@ async function generateFaucetWallet(
|
|||||||
|
|
||||||
// "application/json; charset=utf-8"
|
// "application/json; charset=utf-8"
|
||||||
if (response.headers["content-type"]?.startsWith("application/json")) {
|
if (response.headers["content-type"]?.startsWith("application/json")) {
|
||||||
const wallet: FaucetWallet = JSON.parse(body);
|
const faucetWallet: FaucetWallet = JSON.parse(body);
|
||||||
const classicAddress = wallet.account.classicAddress;
|
const classicAddress = faucetWallet.account.classicAddress;
|
||||||
|
|
||||||
if (classicAddress) {
|
if (classicAddress) {
|
||||||
try {
|
try {
|
||||||
// Check at regular interval if the address is enabled on the XRPL and funded
|
// Check at regular interval if the address is enabled on the XRPL and funded
|
||||||
const isFunded = await hasAddressBalanceIncreased(
|
const isFunded = await hasAddressBalanceIncreased(
|
||||||
this,
|
client,
|
||||||
classicAddress,
|
classicAddress,
|
||||||
startingBalance
|
startingBalance
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isFunded) {
|
if (isFunded) {
|
||||||
resolve(wallet);
|
resolve(fundWallet);
|
||||||
} else {
|
} else {
|
||||||
reject(
|
reject(
|
||||||
new errors.XRPLFaucetError(
|
new errors.XRPLFaucetError(
|
||||||
@@ -9,11 +9,11 @@ import {
|
|||||||
verify,
|
verify,
|
||||||
} from "ripple-keypairs";
|
} from "ripple-keypairs";
|
||||||
|
|
||||||
import ECDSA from "./common/ecdsa";
|
import ECDSA from "../common/ecdsa";
|
||||||
import { ValidationError } from "./common/errors";
|
import { ValidationError } from "../common/errors";
|
||||||
import { SignedTransaction } from "./common/types/objects";
|
import { SignedTransaction } from "../common/types/objects";
|
||||||
import { signOffline } from "./transaction/sign";
|
import { signOffline } from "../transaction/sign";
|
||||||
import { SignOptions } from "./transaction/types";
|
import { SignOptions } from "../transaction/types";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A utility for deriving a wallet composed of a keypair (publicKey/privateKey).
|
* A utility for deriving a wallet composed of a keypair (publicKey/privateKey).
|
||||||
@@ -23,12 +23,27 @@ import { SignOptions } from "./transaction/types";
|
|||||||
class Wallet {
|
class Wallet {
|
||||||
readonly publicKey: string;
|
readonly publicKey: string;
|
||||||
readonly privateKey: string;
|
readonly privateKey: string;
|
||||||
|
readonly classicAddress: string;
|
||||||
|
readonly seed?: string;
|
||||||
private static readonly defaultAlgorithm: ECDSA = ECDSA.ed25519;
|
private static readonly defaultAlgorithm: ECDSA = ECDSA.ed25519;
|
||||||
private static readonly defaultDerivationPath: string = "m/44'/144'/0'/0/0";
|
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.publicKey = publicKey;
|
||||||
this.privateKey = privateKey;
|
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
|
algorithm: ECDSA = Wallet.defaultAlgorithm
|
||||||
): Wallet {
|
): Wallet {
|
||||||
const { publicKey, privateKey } = deriveKeypair(seed, { algorithm });
|
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.
|
* @returns An X-address.
|
||||||
*/
|
*/
|
||||||
getXAddress(tag: number, test = false): string {
|
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 { assert } from "chai";
|
||||||
|
|
||||||
import ECDSA from "../../src/common/ecdsa";
|
import ECDSA from "../../src/common/ecdsa";
|
||||||
import Wallet from "../../src/Wallet";
|
import Wallet from "../../src/wallet";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wallet testing.
|
* Wallet testing.
|
||||||
@@ -9,6 +9,49 @@ import Wallet from "../../src/Wallet";
|
|||||||
* Provides tests for Wallet class.
|
* Provides tests for Wallet class.
|
||||||
*/
|
*/
|
||||||
describe("Wallet", function () {
|
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 () {
|
describe("fromSeed", function () {
|
||||||
const seed = "ssL9dv2W5RK8L3tuzQxYY6EaZhSxW";
|
const seed = "ssL9dv2W5RK8L3tuzQxYY6EaZhSxW";
|
||||||
const publicKey =
|
const publicKey =
|
||||||
@@ -65,7 +108,7 @@ describe("Wallet", function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("fromEntropy", function () {
|
describe("fromEntropy", function () {
|
||||||
const entropy: number[] = new Array(16).fill(0);
|
let entropy;
|
||||||
const publicKey =
|
const publicKey =
|
||||||
"0390A196799EE412284A5D80BF78C3E84CBB80E1437A0AECD9ADF94D7FEAAFA284";
|
"0390A196799EE412284A5D80BF78C3E84CBB80E1437A0AECD9ADF94D7FEAAFA284";
|
||||||
const privateKey =
|
const privateKey =
|
||||||
@@ -75,6 +118,11 @@ describe("Wallet", function () {
|
|||||||
const privateKeyED25519 =
|
const privateKeyED25519 =
|
||||||
"ED0B6CBAC838DFE7F47EA1BD0DF00EC282FDF45510C92161072CCFB84035390C4D";
|
"ED0B6CBAC838DFE7F47EA1BD0DF00EC282FDF45510C92161072CCFB84035390C4D";
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
const entropySize = 16;
|
||||||
|
entropy = new Array(entropySize).fill(0);
|
||||||
|
});
|
||||||
|
|
||||||
it("derives a wallet using entropy", function () {
|
it("derives a wallet using entropy", function () {
|
||||||
const wallet = Wallet.fromEntropy(entropy);
|
const wallet = Wallet.fromEntropy(entropy);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import { assert } from "chai";
|
import { assert } from "chai";
|
||||||
|
|
||||||
import { getFaucetUrl, FaucetNetwork } from "../src/wallet/wallet-generation";
|
import {
|
||||||
|
getFaucetUrl,
|
||||||
|
FaucetNetwork,
|
||||||
|
} from "../src/wallet/generateFaucetWallet";
|
||||||
|
|
||||||
import setupClient from "./setupClient";
|
import setupClient from "./setupClient";
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ function getDefaultConfiguration() {
|
|||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
new webpack.NormalModuleReplacementPlugin(/^ws$/, './wsWrapper'),
|
new webpack.NormalModuleReplacementPlugin(/^ws$/, './wsWrapper'),
|
||||||
new webpack.NormalModuleReplacementPlugin(/^\.\/wallet$/, './wallet-web'),
|
new webpack.NormalModuleReplacementPlugin(/^\.\/wallet\/index$/, './wallet-web'),
|
||||||
new webpack.NormalModuleReplacementPlugin(
|
new webpack.NormalModuleReplacementPlugin(
|
||||||
/^.*setup-api$/,
|
/^.*setup-api$/,
|
||||||
'./setup-api-web'
|
'./setup-api-web'
|
||||||
|
|||||||
Reference in New Issue
Block a user