mirror of
https://github.com/Xahau/xahau.js.git
synced 2026-01-30 03:25:22 +00:00
Compare commits
8 Commits
ripple-lib
...
data-team-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b857cee98 | ||
|
|
3793012acd | ||
|
|
515407bdaf | ||
|
|
23a15e416c | ||
|
|
8ff9ebe276 | ||
|
|
dcf3473f1c | ||
|
|
ac99b3e25c | ||
|
|
d6da4f820e |
24
.github/workflows/nodejs.yml
vendored
24
.github/workflows/nodejs.yml
vendored
@@ -55,27 +55,3 @@ jobs:
|
||||
env:
|
||||
HOST: localhost
|
||||
PORT: ${{ job.services.rippled.ports['6006'] }}
|
||||
|
||||
browser:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [14.x] # This just needs to be compatible w/ puppeteer
|
||||
|
||||
services:
|
||||
rippled:
|
||||
image: natenichols/rippled-standalone:latest
|
||||
ports:
|
||||
- 6006:6006
|
||||
options:
|
||||
--health-cmd="wget localhost:6006 || exit 1" --health-interval=5s --health-retries=10 --health-timeout=2s
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: yarn install
|
||||
- run: yarn test:browser
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -68,6 +68,3 @@ scripts/cache
|
||||
|
||||
# nyc (istanbul)
|
||||
.nyc_output
|
||||
|
||||
# browser tests
|
||||
test-compiled-for-web
|
||||
|
||||
164
APPLICATIONS.md
164
APPLICATIONS.md
@@ -1 +1,163 @@
|
||||
# [Moved to xrpl.js](https://github.com/XRPLF/xrpl.js/blob/main/APPLICATIONS.md)
|
||||
# Applications using ripple-lib (RippleAPI)
|
||||
|
||||
A curated list of some of the projects and apps that leverage `ripple-lib` in some way.
|
||||
|
||||
**Have one to add?** Please edit this file and open a PR!
|
||||
|
||||
## Notice (disclaimer)
|
||||
|
||||
These sites are independent of Ripple and have not been authorized, endorsed, sponsored or otherwise approved by Ripple or its affiliates.
|
||||
|
||||
Warning: Use at your own risk.
|
||||
|
||||
## Exchanges
|
||||
|
||||
- **[The World Exchange](https://www.theworldexchange.net/)**
|
||||
|
||||
Trade, issue, and send directly on the XRP Ledger. A user interface for the XRPL's decentralized exchange.
|
||||
|
||||
- **[Bitso](https://bitso.com/)**
|
||||
|
||||
Exchange allowing clients to buy and sell XRP, based in Mexico.
|
||||
|
||||
## Explorers
|
||||
|
||||
- **[xrpintel - XRP Intelligence](https://xrpintel.com/)**
|
||||
|
||||
Monitor the XRP Network in real time and explore historical statistics.
|
||||
|
||||
- **[XRP Charts](https://xrpcharts.ripple.com/)** (xrpcharts.ripple.com)
|
||||
|
||||
XRP Charts provides information based on public data, including trade volume, top markets, metrics, transactions, and more.
|
||||
|
||||
- **[Ripple Live](https://gatehub.net/live)** (gatehub.net/live)
|
||||
|
||||
Visualize XRP network transactions.
|
||||
|
||||
- **[XRPL Dev. Dashboard](https://xrp.fans/)** (xrp.fans)
|
||||
|
||||
Debugging dashboard for `rippled-ws-client-pool`, transaction and query explorer, and transaction signing and submission tool.
|
||||
|
||||
- **[XRP Value](http://xrpvalue.com/)**
|
||||
|
||||
Real-time XRP price, trades, and orderbook data from the XRP Ledger.
|
||||
|
||||
- **[Bithomp - XRP Explorer](https://bithomp.com/explorer/)**
|
||||
|
||||
Look up information by entering an address, transaction hash, username, or PayID.
|
||||
|
||||
- **[Bithomp - XRPL validators](https://bithomp.com/validators)**
|
||||
|
||||
List of XRPL validators, nodes, and testnet validators.
|
||||
|
||||
- **[XRP Scan - XRP Ledger explorer](https://xrpscan.com)**
|
||||
|
||||
XRP Ledger explorer, metrics and analytics.
|
||||
|
||||
- **[xrplorer](https://xrplorer.com)**
|
||||
|
||||
XRP Ledger explorer, API, metrics, and analytics using a graph database that is synchronized live with the XRPL.
|
||||
|
||||
## Data monitoring
|
||||
|
||||
- **[zerptracker](https://zerptracker.com)**
|
||||
|
||||
Monitor the XRPL using powerful JSONPath expressions, and receive notifications via email, SMS, webhooks, and more.
|
||||
|
||||
- **[Utility-Scan](https://utility-scan.com)**
|
||||
|
||||
Attempts to detect RippleNet on-demand liquidity (ODL) transactions through known fiat corridors and report these transactions in real time.
|
||||
|
||||
- **[XRPL Rosetta](https://xrpl-rosetta-oepox.ondigitalocean.app)**
|
||||
|
||||
3D Globe written in three.js connected to a Node.js websocket server that is listening to exchanges and the XRPL. The visualization aims to show trading, ODL, and liquidity at exchanges, intra-exchange volume, and flows.
|
||||
|
||||
## Wallets and wallet tools
|
||||
|
||||
- **[XUMM](https://xumm.app/)**
|
||||
|
||||
Users can use the xumm application to track their accounts, balances and transactions. The true power of xumm is the platform available for developers.
|
||||
|
||||
- **[Xpring Wallet](https://xpring.io)** (uses `ripple-keypairs`)
|
||||
|
||||
Non-custodial XRP wallet.
|
||||
|
||||
- **[XRP Toolkit](https://www.xrptoolkit.com)**
|
||||
|
||||
A web interface to the XRP Ledger, supporting both hardware and software wallets.
|
||||
|
||||
- **[Toast Wallet](https://toastwallet.com/)**
|
||||
|
||||
A free, open source XRP Wallet for iOS, Android, Windows, Mac and Linux.
|
||||
|
||||
- **[Toastify Ledger](https://github.com/WietseWind/toastify-ledger)** (uses `ripple-keypairs`)
|
||||
|
||||
Add a Regular Key to a mnemonic XRP Wallet (e.g. Ledger Nano S) to use the account with a Family Seed (secret).
|
||||
|
||||
- **[Bithomp-submit](https://github.com/Bithomp/bithomp-submit)** (GitHub)
|
||||
|
||||
A tool to submit an offline-signed XRPL transaction.
|
||||
|
||||
- **[Kyte](https://kyteapp.co/)** (kyteapp.co) ([Source](https://github.com/WietseWind/Zerp-Wallet)) (Deprecated)
|
||||
|
||||
Web-based XRP wallet.
|
||||
|
||||
- **[XRP Vanity Address Generator](https://github.com/WietseWind/xrp-vanity-generator)** (Node.js)
|
||||
|
||||
A vanity address is a wallet address containing a few characters you like at the beginning or the end of the wallet address.
|
||||
|
||||
- **[XRP Account Mnemonic Recovery](https://github.com/WietseWind/xrp-mnemonic-recovery)** (uses `ripple-keypairs`)
|
||||
|
||||
Recover a 24 word mnemonic if one word is wrong or one word is missing.
|
||||
|
||||
## Send and request payments
|
||||
|
||||
- **[XRP Tip Bot](https://www.xrptipbot.com/)**
|
||||
|
||||
A bot that enables users on reddit, Twitter and Discord to send XRP to each other through reddit comments and Twitter tweets.
|
||||
|
||||
- **[XRP Text](https://xrptext.com/)**
|
||||
|
||||
Send XRP using SMS text messages.
|
||||
|
||||
- **[XRParrot](https://xrparrot.com/)** (uses `ripple-address-codec`)
|
||||
|
||||
Easy EUR (SEPA) to XRP transfer (currency conversion).
|
||||
|
||||
- **[XRP Payment](https://xrpayments.co/)** (xrpayments.co)
|
||||
|
||||
Tool for generating a XRP payment request URI in a QR code, with currency converter.
|
||||
|
||||
## Development tools
|
||||
|
||||
- **[XRP Faucets for Testnet and Devnet](https://xrpl.org/xrp-testnet-faucet.html)**
|
||||
|
||||
Get some test funds for development on the test network. The faucet uses `ripple-lib`.
|
||||
|
||||
## Code samples and libraries
|
||||
|
||||
- **[ilp-plugin-xrp-paychan](https://github.com/interledgerjs/ilp-plugin-xrp-paychan)**
|
||||
|
||||
Send ILP payments using XRP and payment channels (PayChan).
|
||||
|
||||
- **[RunKit: WietseWind](https://runkit.com/wietsewind/)**
|
||||
|
||||
XRP Ledger code samples for Node.js.
|
||||
|
||||
- **[GitHub Gist: WietseWind](https://gist.github.com/WietseWind)**
|
||||
|
||||
XRP Ledger code samples for Node.js and the web (mostly).
|
||||
|
||||
- **[rippled-ws-client-sign](https://github.com/WietseWind/rippled-ws-client-sign)**
|
||||
|
||||
Sign transactions, with support for MultiSign.
|
||||
|
||||
- **[ILP-enabled power switch](https://xrpcommunity.blog/raspberry-pi-interledger-xp-powerswitch-howto/)** ([video](https://www.youtube.com/watch?v=c-eS0HQUuJg)) (uses [`moneyd-uplink-xrp`](https://github.com/interledgerjs/moneyd-uplink-xrp))
|
||||
|
||||
For about $30 in parts (Raspberry Pi, 3.3V Relay board and a few wires) you can build your own power switch that will switch on if a streaming ILP payment comes in. When the payment stream stops, the power turns off.
|
||||
|
||||
## Related apps that do not appear to use ripple-lib
|
||||
|
||||
- **[XRP Stats](https://ledger.exposed/)** (ledger.exposed)
|
||||
|
||||
Rich list, live ledger stats and XRP distribution. Visualize escrows and flow of funds.
|
||||
|
||||
25
HISTORY.md
25
HISTORY.md
@@ -1,29 +1,6 @@
|
||||
# ripple-lib Release History
|
||||
|
||||
# Deprecation Notice
|
||||
|
||||
This library (ripple-lib 1.x) has been deprecated in favor of [xrpl.js version 2+](https://github.com/XRPLF/xrpl.js).
|
||||
|
||||
## 1.10.1 (2022-05-31)
|
||||
|
||||
* Fix bug in parseSettings: AffectedNodes can contain CreatedNode and DeletedNode ([#1907](https://github.com/XRPLF/xrpl.js/pull/1907))
|
||||
|
||||
The SHA-256 checksums for the browser version of this release can be found below.
|
||||
```
|
||||
% shasum -a 256 build/*
|
||||
368ad4fe29bfeee7dd59e319eded72d486f57a2c05a2314d9e5c42db1fd70fcd build/ripple-latest-min.js
|
||||
f8328b2b687757112b63675b1af272ee08bea15df2a392d1c50637604e938a22 build/ripple-latest-min.js.LICENSE.txt
|
||||
2631590b53ddee9a68cb4149af07b20a1135606a8b5d3d746594359e003411d4 build/ripple-latest.js
|
||||
```
|
||||
|
||||
## 1.10.0 (2021-08-12)
|
||||
|
||||
* Add address generation from Devnet/Testnet faucets (#1497)
|
||||
* Fix bug with `getBalances()` ledgerVersion (#1505)
|
||||
* Include lodash in webpack build (#1500)
|
||||
* Documentation Updates:
|
||||
* Export and document AccountSetFlags (#1525)
|
||||
* Add links to example keypair derivation (#1523)
|
||||
Subscribe to [the **ripple-lib-announce** mailing list](https://groups.google.com/forum/#!forum/ripple-lib-announce) for release announcements. We recommend that ripple-lib users stay up-to-date with the latest stable release.
|
||||
|
||||
## 1.9.8 (2021-07-30)
|
||||
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2012-2021 Contributers to xrpl.js
|
||||
Copyright (c) 2012-2015 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
|
||||
25
README.md
25
README.md
@@ -1,18 +1,18 @@
|
||||
## Deprecated
|
||||
|
||||
This library (ripple-lib 1.x) has been deprecated in favor of [xrpl.js version 2+](https://github.com/XRPLF/xrpl.js).
|
||||
|
||||
# ripple-lib (RippleAPI)
|
||||
|
||||
A JavaScript/TypeScript API for interacting with the XRP Ledger
|
||||
|
||||
[](https://www.npmjs.org/package/ripple-lib)
|
||||
|
||||
This library is for integrating a JavaScript/TypeScript app with the XRP Ledger and supports functionality such as IOUs, payment paths, the decentralized exchange, account settings, payment channels, escrows, multi-signing, and more.
|
||||
This is the recommended library for integrating a JavaScript/TypeScript app with the XRP Ledger, especially if you intend to use advanced functionality such as IOUs, payment paths, the decentralized exchange, account settings, payment channels, escrows, multi-signing, and more.
|
||||
|
||||
## [➡️ Reference Documentation](https://github.com/XRPLF/xrpl.js/blob/1.x/docs/index.md)
|
||||
## [➡️ Reference Documentation](https://xrpl.org/rippleapi-reference.html)
|
||||
|
||||
Use the above link to view the full reference documentation.
|
||||
See the full reference documentation on the XRP Ledger Dev Portal.
|
||||
|
||||
## [➡️ Applications and Projects](APPLICATIONS.md)
|
||||
|
||||
What is ripple-lib used for? The applications on the list linked above use `ripple-lib`. Open a PR to add your app or project to the list!
|
||||
|
||||
### Features
|
||||
|
||||
@@ -103,13 +103,12 @@ import ripple from 'https://dev.jspm.io/npm:ripple-lib';
|
||||
+ [RippleAPI Beginners Guide](https://xrpl.org/get-started-with-rippleapi-for-javascript.html)
|
||||
+ [RippleAPI Full Reference Documentation](https://xrpl.org/rippleapi-reference.html) ([in this repo](https://github.com/ripple/ripple-lib/blob/develop/docs/index.md))
|
||||
+ [Code Samples](https://github.com/ripple/ripple-lib/tree/develop/docs/samples)
|
||||
+ [XRP Ledger Dev Portal](https://xrpl.org/)
|
||||
|
||||
### Mailing Lists
|
||||
|
||||
We have a low-traffic mailing list for announcements of new ripple-lib releases. (About 1 email per week)
|
||||
|
||||
+ [Subscribe to xrpl-announce](https://groups.google.com/g/xrpl-announce)
|
||||
+ [Subscribe to ripple-lib-announce](https://groups.google.com/forum/#!forum/ripple-lib-announce)
|
||||
|
||||
If you're using the XRP Ledger in production, you should run a [rippled server](https://github.com/ripple/rippled) and subscribe to the ripple-server mailing list as well.
|
||||
|
||||
@@ -147,3 +146,11 @@ Do not edit `./docs/index.md` directly because it is a generated file.
|
||||
Instead, edit the appropriate `.md.ejs` files in `./docs/src/`.
|
||||
|
||||
If you make changes to the JSON schemas, fixtures, or documentation sources, update the documentation by running `yarn run docgen`.
|
||||
|
||||
## More Information
|
||||
|
||||
+ [ripple-lib-announce mailing list](https://groups.google.com/forum/#!forum/ripple-lib-announce) - subscribe for release announcements
|
||||
+ [RippleAPI Reference](https://xrpl.org/rippleapi-reference.html) - XRP Ledger Dev Portal
|
||||
+ [XRP Ledger Dev Portal](https://xrpl.org/)
|
||||
|
||||
[](https://travis-ci.org/ripple/ripple-lib)
|
||||
|
||||
@@ -93,7 +93,6 @@
|
||||
- [isValidSecret](#isvalidsecret)
|
||||
- [deriveKeypair](#derivekeypair)
|
||||
- [deriveAddress](#deriveaddress)
|
||||
- [generateFaucetWallet](#generatefaucetwallet)
|
||||
- [signPaymentChannelClaim](#signpaymentchannelclaim)
|
||||
- [verifyPaymentChannelClaim](#verifypaymentchannelclaim)
|
||||
- [computeLedgerHash](#computeledgerhash)
|
||||
@@ -102,7 +101,6 @@
|
||||
- [iso8601ToRippleTime](#iso8601torippletime)
|
||||
- [rippleTimeToISO8601](#rippletimetoiso8601)
|
||||
- [txFlags](#txflags)
|
||||
- [AccountSet Flags](#accountset-flags)
|
||||
- [schemaValidator](#schemavalidator)
|
||||
- [schemaValidate](#schemavalidate)
|
||||
- [API Events](#api-events)
|
||||
@@ -5661,8 +5659,8 @@ Name | Type | Description
|
||||
---- | ---- | -----------
|
||||
txJSON | string | Transaction represented as a JSON string in rippled format.
|
||||
keypair | object | *Optional* The private and public key of the account that is initiating the transaction. (This field cannot be used with secret).
|
||||
*keypair.* privateKey | privateKey | The uppercase hexadecimal representation of the secp256k1 or Ed25519 private key. Ed25519 keys are prefixed with 0xED. You can read about how keys are derived [here](https://xrpl.org/cryptographic-keys.html).
|
||||
*keypair.* publicKey | publicKey | The uppercase hexadecimal representation of the secp256k1 or Ed25519 public key. Ed25519 keys are prefixed with 0xED. You can read about how keys are derived [here](https://xrpl.org/cryptographic-keys.html).
|
||||
*keypair.* privateKey | privateKey | The uppercase hexadecimal representation of the secp256k1 or Ed25519 private key.
|
||||
*keypair.* publicKey | publicKey | The uppercase hexadecimal representation of the secp256k1 or Ed25519 public key.
|
||||
options | object | *Optional* Options that control the type of signature to create.
|
||||
*options.* signAs | [address](#address) | *Optional* The account that the signature should count for in multisigning.
|
||||
secret | secret string | *Optional* The secret of the account that is initiating the transaction. (This field cannot be used with keypair).
|
||||
@@ -5697,19 +5695,6 @@ return api.sign(txJSON, secret); // or: api.sign(txJSON, keypair);
|
||||
```
|
||||
|
||||
|
||||
### Example Keypairs
|
||||
|
||||
To learn how keypairs are derived read [here](https://xrpl.org/cryptographic-keys.html#generating-keys).
|
||||
```javascript
|
||||
// secp25519 (33 bytes)
|
||||
const privateKey = "002512BBDFDBB77510883B7DCCBEF270B86DEAC8B64AC762873D75A1BEE6298665"
|
||||
const publicKey = "0390A196799EE412284A5D80BF78C3E84CBB80E1437A0AECD9ADF94D7FEAAFA284"
|
||||
|
||||
// ed25519 (Note the 0xED prefixes a 32 byte value for a total of 33 bytes)
|
||||
const privateKey = "ED0B6CBAC838DFE7F47EA1BD0DF00EC282FDF45510C92161072CCFB84035390C4D"
|
||||
const publicKey = "ED1A7C082846CFF58FF9A892BA4BA2593151CCF1DBA59F37714CC9ED39824AF85F"
|
||||
```
|
||||
|
||||
### Example (multi-signing)
|
||||
|
||||
```javascript
|
||||
@@ -5761,7 +5746,7 @@ const multiSignPaymentTransaction = {
|
||||
};
|
||||
|
||||
const multiSignPaymentInstruction = {
|
||||
signersCount: 2
|
||||
signersCount: 2
|
||||
};
|
||||
|
||||
const api = new RippleAPI({
|
||||
@@ -6092,38 +6077,6 @@ This method returns a string corresponding to the address derived from the publi
|
||||
var address = api.deriveAddress(public_key);
|
||||
```
|
||||
|
||||
## generateFaucetWallet
|
||||
|
||||
`generateFaucetWallet(onTestnet = true)`
|
||||
|
||||
Calls the Testnet or Devnet faucet API in order to generate a new, random wallet with some amount of test XRP. This is for testing purposes only.
|
||||
|
||||
### Example
|
||||
|
||||
**Request**
|
||||
|
||||
Create a new wallet on the Testnet:
|
||||
|
||||
```javascript
|
||||
const wallet = await api.generateFaucetWallet()
|
||||
```
|
||||
|
||||
**Response**
|
||||
|
||||
|
||||
```json
|
||||
{
|
||||
"account": {
|
||||
"xAddress": "T7i2Q8yGcMcCQa2n6d9EvSEptT4CE6ap7Q1r1fmjstkLfsK",
|
||||
"secret": "ssKCsaRqWh669atvv83bdYRaiHomY",
|
||||
"classicAddress": "r9SYfmVxrb7iuCVfNhW2gqqzapfE2r6juG",
|
||||
"address": "r9SYfmVxrb7iuCVfNhW2gqqzapfE2r6juG"
|
||||
},
|
||||
"amount": 1000,
|
||||
"balance": 1000
|
||||
}
|
||||
```
|
||||
|
||||
## signPaymentChannelClaim
|
||||
|
||||
`signPaymentChannelClaim(channel: string, amount: string, privateKey: string): string`
|
||||
@@ -6444,30 +6397,6 @@ The remaining transaction types do not have any flags at this time.
|
||||
* PaymentChannelCreate
|
||||
* PaymentChannelFund
|
||||
|
||||
## AccountSet Flags
|
||||
|
||||
To modify account flags, you can use an AccountSet transaction and its `SetFlag` or `ClearFlag` fields.
|
||||
|
||||
The flags are called [AccountSet flags (asf*)](https://xrpl.org/accountset.html#accountset-flags):
|
||||
|
||||
`RippleAPI.accountSetFlags.requireDestinationTag`: Require a destination tag to send transactions to this account.
|
||||
|
||||
`RippleAPI.accountSetFlags.requireAuthorization`: Require authorization for users to hold balances issued by this address. Can only be enabled if the address has no trust lines connected to it.
|
||||
|
||||
`RippleAPI.accountSetFlags.disallowIncomingXRP`: XRP should not be sent to this account. (Enforced by client applications, not by rippled)
|
||||
|
||||
`RippleAPI.accountSetFlags.disableMasterKey`: Disallow use of the master key pair. Can only be enabled if the account has configured another way to sign transactions.
|
||||
|
||||
`RippleAPI.accountSetFlags.enableTransactionIDTracking`: Track the ID of this account's most recent transaction.
|
||||
|
||||
`RippleAPI.accountSetFlags.noFreeze`: Permanently give up the ability to freeze individual trust lines or disable Global Freeze. This flag can never be disabled after being enabled.
|
||||
|
||||
`RippleAPI.accountSetFlags.globalFreeze`: Freeze all assets issued by this account.
|
||||
|
||||
`RippleAPI.accountSetFlags.defaultRipple`: Enable [rippling](https://xrpl.org/rippling.html) on this account's trust lines by default.
|
||||
|
||||
`RippleAPI.accountSetFlags.depositAuth`:Enable Deposit Authorization on this account.
|
||||
|
||||
## schemaValidator
|
||||
|
||||
Unlike the rest of the ripple-lib API, schemaValidator is a static object on RippleAPI. It provides utility methods that do not use a server.
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
## AccountSet Flags
|
||||
|
||||
To modify account flags, you can use an AccountSet transaction and its `SetFlag` or `ClearFlag` fields.
|
||||
|
||||
The flags are called [AccountSet flags (asf*)](https://xrpl.org/accountset.html#accountset-flags):
|
||||
|
||||
`RippleAPI.accountSetFlags.requireDestinationTag`: Require a destination tag to send transactions to this account.
|
||||
|
||||
`RippleAPI.accountSetFlags.requireAuthorization`: Require authorization for users to hold balances issued by this address. Can only be enabled if the address has no trust lines connected to it.
|
||||
|
||||
`RippleAPI.accountSetFlags.disallowIncomingXRP`: XRP should not be sent to this account. (Enforced by client applications, not by rippled)
|
||||
|
||||
`RippleAPI.accountSetFlags.disableMasterKey`: Disallow use of the master key pair. Can only be enabled if the account has configured another way to sign transactions.
|
||||
|
||||
`RippleAPI.accountSetFlags.enableTransactionIDTracking`: Track the ID of this account's most recent transaction.
|
||||
|
||||
`RippleAPI.accountSetFlags.noFreeze`: Permanently give up the ability to freeze individual trust lines or disable Global Freeze. This flag can never be disabled after being enabled.
|
||||
|
||||
`RippleAPI.accountSetFlags.globalFreeze`: Freeze all assets issued by this account.
|
||||
|
||||
`RippleAPI.accountSetFlags.defaultRipple`: Enable [rippling](https://xrpl.org/rippling.html) on this account's trust lines by default.
|
||||
|
||||
`RippleAPI.accountSetFlags.depositAuth`:Enable Deposit Authorization on this account.
|
||||
@@ -1,19 +0,0 @@
|
||||
## generateFaucetWallet
|
||||
|
||||
`generateFaucetWallet(onTestnet = true)`
|
||||
|
||||
Calls the Testnet or Devnet faucet API in order to generate a new, random wallet with some amount of test XRP. This is for testing purposes only.
|
||||
|
||||
### Example
|
||||
|
||||
**Request**
|
||||
|
||||
Create a new wallet on the Testnet:
|
||||
|
||||
```javascript
|
||||
const wallet = await api.generateFaucetWallet()
|
||||
```
|
||||
|
||||
**Response**
|
||||
|
||||
<%- renderFixture('responses/generate-faucet-wallet.json') %>
|
||||
@@ -59,7 +59,6 @@
|
||||
<%- include('isValidSecret.md.ejs') %>
|
||||
<%- include('deriveKeypair.md.ejs') %>
|
||||
<%- include('deriveAddress.md.ejs') %>
|
||||
<%- include('generateFaucetWallet.md.ejs') %>
|
||||
<%- include('signPaymentChannelClaim.md.ejs') %>
|
||||
<%- include('verifyPaymentChannelClaim.md.ejs') %>
|
||||
<%- include('computeLedgerHash.md.ejs') %>
|
||||
@@ -67,6 +66,5 @@
|
||||
<%- include('iso8601ToRippleTime.md.ejs') %>
|
||||
<%- include('rippleTimeToISO8601.md.ejs') %>
|
||||
<%- include('txFlags.md.ejs') %>
|
||||
<%- include('accountSetFlags.md.ejs') %>
|
||||
<%- include('schemaValidator.md.ejs') %>
|
||||
<%- include('events.md.ejs') %>
|
||||
|
||||
@@ -35,19 +35,6 @@ return api.sign(txJSON, secret); // or: api.sign(txJSON, keypair);
|
||||
|
||||
<%- renderFixture("responses/sign.json") %>
|
||||
|
||||
### Example Keypairs
|
||||
|
||||
To learn how keypairs are derived read [here](https://xrpl.org/cryptographic-keys.html#generating-keys).
|
||||
```javascript
|
||||
// secp25519 (33 bytes)
|
||||
const privateKey = "002512BBDFDBB77510883B7DCCBEF270B86DEAC8B64AC762873D75A1BEE6298665"
|
||||
const publicKey = "0390A196799EE412284A5D80BF78C3E84CBB80E1437A0AECD9ADF94D7FEAAFA284"
|
||||
|
||||
// ed25519 (Note the 0xED prefixes a 32 byte value for a total of 33 bytes)
|
||||
const privateKey = "ED0B6CBAC838DFE7F47EA1BD0DF00EC282FDF45510C92161072CCFB84035390C4D"
|
||||
const publicKey = "ED1A7C082846CFF58FF9A892BA4BA2593151CCF1DBA59F37714CC9ED39824AF85F"
|
||||
```
|
||||
|
||||
### Example (multi-signing)
|
||||
|
||||
```javascript
|
||||
|
||||
20
package.json
20
package.json
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "ripple-lib",
|
||||
"version": "1.10.1",
|
||||
"version": "1.9.8-storm.4",
|
||||
"license": "ISC",
|
||||
"description": "Deprecated - consider migrating to xrpl.js: https://xrpl.org/xrpljs2-migration-guide.html",
|
||||
"description": "A TypeScript/JavaScript API for interacting with the XRP Ledger in Node.js and the browser",
|
||||
"files": [
|
||||
"dist/npm/*",
|
||||
"build/ripple-latest-min.js",
|
||||
@@ -26,9 +26,9 @@
|
||||
"https-proxy-agent": "^5.0.0",
|
||||
"jsonschema": "1.2.2",
|
||||
"lodash": "^4.17.4",
|
||||
"ripple-address-codec": "^4.1.1",
|
||||
"ripple-binary-codec": "^1.1.3",
|
||||
"ripple-keypairs": "^1.0.3",
|
||||
"ripple-address-codec": "4.1.3",
|
||||
"ripple-binary-codec": "1.1.4-beta.2",
|
||||
"ripple-keypairs": "1.0.4-storm.2",
|
||||
"ripple-lib-transactionparser": "0.8.2",
|
||||
"ws": "^7.2.0"
|
||||
},
|
||||
@@ -36,7 +36,7 @@
|
||||
"elliptic": "^6.5.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mocha": "^9.0.0",
|
||||
"@types/mocha": "^8.2.1",
|
||||
"@types/node": "^16.4.3",
|
||||
"@typescript-eslint/eslint-plugin": "^2.3.3",
|
||||
"@typescript-eslint/parser": "^2.27.0",
|
||||
@@ -48,17 +48,12 @@
|
||||
"ejs": "^3.0.1",
|
||||
"eslint": "^6.5.1",
|
||||
"eventemitter2": "^6.0.0",
|
||||
"https-browserify": "^1.0.0",
|
||||
"json-schema-to-markdown-table": "^0.4.0",
|
||||
"mocha": "^9",
|
||||
"nyc": "^15",
|
||||
"path-browserify": "1.0.1",
|
||||
"prettier": "^2.0.5",
|
||||
"process": "^0.11.10",
|
||||
"puppeteer": "10.2.0",
|
||||
"stream-browserify": "^3.0.0",
|
||||
"stream-http": "3.2.0",
|
||||
"ts-loader": "^9.2.5",
|
||||
"ts-node": "^10.1.0",
|
||||
"typescript": "^3.9.9",
|
||||
"url": "^0.11.0",
|
||||
@@ -79,7 +74,6 @@
|
||||
"prepublish": "yarn clean && yarn build",
|
||||
"test": "TS_NODE_PROJECT=src/tsconfig.json nyc mocha --config=test/.mocharc.json --exit",
|
||||
"test:integration": "TS_NODE_PROJECT=src/tsconfig.json nyc mocha ./test/integration/*.ts",
|
||||
"test:browser": "TS_NODE_PROJECT=src/tsconfig.json nyc mocha ./test/browser/*.ts",
|
||||
"test:watch": "TS_NODE_PROJECT=src/tsconfig.json mocha --config=test/.mocharc.json --watch --reporter dot",
|
||||
"format": "prettier --write '{src,test}/**/*.ts'",
|
||||
"lint": "eslint 'src/**/*.ts' 'test/*-test.{ts,js}'",
|
||||
@@ -90,7 +84,7 @@
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@github.com:XRPLF/xrpl.js.git"
|
||||
"url": "git://github.com/ripple/ripple-lib.git"
|
||||
},
|
||||
"readmeFilename": "README.md",
|
||||
"engines": {
|
||||
|
||||
63
src/api.ts
63
src/api.ts
@@ -1,7 +1,6 @@
|
||||
import {EventEmitter} from 'events'
|
||||
import {
|
||||
Connection,
|
||||
constants,
|
||||
errors,
|
||||
validate,
|
||||
xrpToDrops,
|
||||
@@ -48,7 +47,7 @@ import prepareTicketCreate from './transaction/ticket'
|
||||
import sign from './transaction/sign'
|
||||
import combine from './transaction/combine'
|
||||
import submit from './transaction/submit'
|
||||
import {generateAddress, generateXAddress} from './offline/utils'
|
||||
import { generateAddress, generateXAddress } from './offline/utils'
|
||||
import {deriveKeypair, deriveAddress, deriveXAddress} from './offline/derive'
|
||||
import computeLedgerHash from './offline/ledgerhash'
|
||||
import signPaymentChannelClaim from './offline/sign-payment-channel-claim'
|
||||
@@ -86,22 +85,7 @@ import {getServerInfo, getFee} from './common/serverinfo'
|
||||
import {clamp, renameCounterpartyToIssuer} from './ledger/utils'
|
||||
import {TransactionJSON, Instructions, Prepare} from './transaction/types'
|
||||
import {ConnectionUserOptions} from './common/connection'
|
||||
import {
|
||||
classicAddressToXAddress,
|
||||
xAddressToClassicAddress,
|
||||
isValidXAddress,
|
||||
isValidClassicAddress,
|
||||
encodeSeed,
|
||||
decodeSeed,
|
||||
encodeAccountID,
|
||||
decodeAccountID,
|
||||
encodeNodePublic,
|
||||
decodeNodePublic,
|
||||
encodeAccountPublic,
|
||||
decodeAccountPublic,
|
||||
encodeXAddress,
|
||||
decodeXAddress
|
||||
} from 'ripple-address-codec'
|
||||
import {classicAddressToXAddress, xAddressToClassicAddress, isValidXAddress, isValidClassicAddress, encodeSeed, decodeSeed, encodeAccountID, decodeAccountID, encodeNodePublic, decodeNodePublic, encodeAccountPublic, decodeAccountPublic, encodeXAddress, decodeXAddress} from 'ripple-address-codec'
|
||||
import {
|
||||
computeBinaryTransactionHash,
|
||||
computeTransactionHash,
|
||||
@@ -116,8 +100,6 @@ import {
|
||||
computePaymentChannelHash
|
||||
} from './common/hashes'
|
||||
|
||||
import generateFaucetWallet from './wallet/wallet-generation'
|
||||
|
||||
export interface APIOptions extends ConnectionUserOptions {
|
||||
server?: string
|
||||
feeCushion?: number
|
||||
@@ -417,9 +399,6 @@ class RippleAPI extends EventEmitter {
|
||||
computeLedgerHash = computeLedgerHash // @deprecated Invoke from top-level package instead
|
||||
signPaymentChannelClaim = signPaymentChannelClaim // @deprecated Invoke from top-level package instead
|
||||
verifyPaymentChannelClaim = verifyPaymentChannelClaim // @deprecated Invoke from top-level package instead
|
||||
|
||||
generateFaucetWallet = generateFaucetWallet
|
||||
|
||||
errors = errors
|
||||
|
||||
static deriveXAddress = deriveXAddress
|
||||
@@ -430,20 +409,20 @@ class RippleAPI extends EventEmitter {
|
||||
/**
|
||||
* Static methods to expose ripple-address-codec methods
|
||||
*/
|
||||
static classicAddressToXAddress = classicAddressToXAddress
|
||||
static xAddressToClassicAddress = xAddressToClassicAddress
|
||||
static isValidXAddress = isValidXAddress
|
||||
static isValidClassicAddress = isValidClassicAddress
|
||||
static encodeSeed = encodeSeed
|
||||
static decodeSeed = decodeSeed
|
||||
static encodeAccountID = encodeAccountID
|
||||
static decodeAccountID = decodeAccountID
|
||||
static encodeNodePublic = encodeNodePublic
|
||||
static decodeNodePublic = decodeNodePublic
|
||||
static encodeAccountPublic = encodeAccountPublic
|
||||
static decodeAccountPublic = decodeAccountPublic
|
||||
static encodeXAddress = encodeXAddress
|
||||
static decodeXAddress = decodeXAddress
|
||||
static classicAddressToXAddress = classicAddressToXAddress
|
||||
static xAddressToClassicAddress = xAddressToClassicAddress
|
||||
static isValidXAddress = isValidXAddress
|
||||
static isValidClassicAddress = isValidClassicAddress
|
||||
static encodeSeed = encodeSeed
|
||||
static decodeSeed = decodeSeed
|
||||
static encodeAccountID = encodeAccountID
|
||||
static decodeAccountID = decodeAccountID
|
||||
static encodeNodePublic = encodeNodePublic
|
||||
static decodeNodePublic = decodeNodePublic
|
||||
static encodeAccountPublic = encodeAccountPublic
|
||||
static decodeAccountPublic = decodeAccountPublic
|
||||
static encodeXAddress = encodeXAddress
|
||||
static decodeXAddress = decodeXAddress
|
||||
|
||||
/**
|
||||
* Static methods that replace functionality from the now-deprecated ripple-hashes library
|
||||
@@ -455,8 +434,7 @@ class RippleAPI extends EventEmitter {
|
||||
// @deprecated Invoke from top-level package instead
|
||||
static computeTransactionHash = computeTransactionHash // (txJSON: any): string
|
||||
// @deprecated Invoke from top-level package instead
|
||||
static computeBinaryTransactionSigningHash =
|
||||
computeBinaryTransactionSigningHash // (txBlobHex: string): string
|
||||
static computeBinaryTransactionSigningHash = computeBinaryTransactionSigningHash // (txBlobHex: string): string
|
||||
// Compute the hash of an account, given the account's classic address (starting with `r`).
|
||||
// @deprecated Invoke from top-level package instead
|
||||
static computeAccountLedgerObjectID = computeAccountLedgerObjectID // (address: string): string
|
||||
@@ -488,15 +466,14 @@ class RippleAPI extends EventEmitter {
|
||||
rippleTimeToISO8601 = rippleTimeToISO8601 // @deprecated Invoke from top-level package instead
|
||||
iso8601ToRippleTime = iso8601ToRippleTime // @deprecated Invoke from top-level package instead
|
||||
txFlags = txFlags
|
||||
static txFlags = txFlags
|
||||
accountSetFlags = constants.AccountSetFlags
|
||||
static accountSetFlags = constants.AccountSetFlags
|
||||
|
||||
isValidAddress = schemaValidator.isValidAddress
|
||||
isValidSecret = schemaValidator.isValidSecret
|
||||
}
|
||||
|
||||
export {RippleAPI}
|
||||
export {
|
||||
RippleAPI
|
||||
}
|
||||
|
||||
export type {
|
||||
AccountObjectsRequest,
|
||||
|
||||
@@ -81,7 +81,7 @@ function createWebSocket(url: string, config: ConnectionOptions): WebSocket {
|
||||
passphrase: config.passphrase,
|
||||
cert: config.certificate
|
||||
},
|
||||
(value) => value == null
|
||||
value => value == null
|
||||
)
|
||||
const proxyOptions = Object.assign({}, parsedProxyURL, proxyOverrides)
|
||||
let HttpsProxyAgent
|
||||
@@ -103,7 +103,7 @@ function createWebSocket(url: string, config: ConnectionOptions): WebSocket {
|
||||
passphrase: config.passphrase,
|
||||
cert: config.certificate
|
||||
},
|
||||
(value) => value == null
|
||||
value => value == null
|
||||
)
|
||||
const websocketOptions = Object.assign({}, options, optionsOverrides)
|
||||
const websocket = new WebSocket(url, null, websocketOptions)
|
||||
@@ -642,13 +642,4 @@ export class Connection extends EventEmitter {
|
||||
|
||||
return responsePromise
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Websocket connection URL
|
||||
*
|
||||
* @returns The Websocket connection URL
|
||||
*/
|
||||
getUrl(): string {
|
||||
return this._url
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ export interface Settings {
|
||||
defaultRipple?: boolean
|
||||
}
|
||||
|
||||
const AccountSetFlags = {
|
||||
const AccountFlagIndices = {
|
||||
requireDestinationTag: txFlagIndices.AccountSet.asfRequireDest,
|
||||
requireAuthorization: txFlagIndices.AccountSet.asfRequireAuth,
|
||||
depositAuth: txFlagIndices.AccountSet.asfDepositAuth,
|
||||
@@ -96,4 +96,4 @@ const AccountFields = {
|
||||
TickSize: {name: 'tickSize', defaults: 0}
|
||||
}
|
||||
|
||||
export {AccountFields, AccountSetFlags, AccountFlags}
|
||||
export {AccountFields, AccountFlagIndices, AccountFlags}
|
||||
|
||||
@@ -54,8 +54,6 @@ class ResponseFormatError extends ConnectionError {}
|
||||
|
||||
class ValidationError extends RippleError {}
|
||||
|
||||
class XRPLFaucetError extends RippleError {}
|
||||
|
||||
class NotFoundError extends RippleError {
|
||||
constructor(message = 'Not found') {
|
||||
super(message)
|
||||
@@ -92,6 +90,5 @@ export {
|
||||
NotFoundError,
|
||||
PendingLedgerVersionError,
|
||||
MissingLedgerHistoryError,
|
||||
LedgerVersionError,
|
||||
XRPLFaucetError
|
||||
LedgerVersionError
|
||||
}
|
||||
|
||||
@@ -17,11 +17,11 @@
|
||||
"properties": {
|
||||
"privateKey": {
|
||||
"type": "privateKey",
|
||||
"description": "The uppercase hexadecimal representation of the secp256k1 or Ed25519 private key. Ed25519 keys are prefixed with 0xED. You can read about how keys are derived [here](https://xrpl.org/cryptographic-keys.html)."
|
||||
"description": "The uppercase hexadecimal representation of the secp256k1 or Ed25519 private key."
|
||||
},
|
||||
"publicKey": {
|
||||
"type": "publicKey",
|
||||
"description": "The uppercase hexadecimal representation of the secp256k1 or Ed25519 public key. Ed25519 keys are prefixed with 0xED. You can read about how keys are derived [here](https://xrpl.org/cryptographic-keys.html)."
|
||||
"description": "The uppercase hexadecimal representation of the secp256k1 or Ed25519 public key."
|
||||
}
|
||||
},
|
||||
"description": "The private and public key of the account that is initiating the transaction. (This field cannot be used with secret).",
|
||||
|
||||
@@ -5,7 +5,9 @@ const AccountFlags = constants.AccountFlags
|
||||
import parseFields from './fields'
|
||||
|
||||
function getAccountRootModifiedNode(tx: any) {
|
||||
const modifiedNodes = tx.meta.AffectedNodes.filter(node => node.ModifiedNode?.LedgerEntryType === 'AccountRoot');
|
||||
const modifiedNodes = tx.meta.AffectedNodes.filter(
|
||||
(node) => node.ModifiedNode.LedgerEntryType === 'AccountRoot'
|
||||
)
|
||||
assert.ok(modifiedNodes.length === 1)
|
||||
return modifiedNodes[0].ModifiedNode
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ async function getTrustlines(
|
||||
// 2. Make Request
|
||||
const responses = await this._requestAll('account_lines', {
|
||||
account: address,
|
||||
ledger_index: options.ledgerVersion ?? await this.getLedgerVersion(),
|
||||
ledger_index: await this.getLedgerVersion(),
|
||||
limit: options.limit,
|
||||
peer: options.counterparty
|
||||
})
|
||||
|
||||
@@ -1,66 +1,43 @@
|
||||
import * as _ from 'lodash'
|
||||
import binary from 'ripple-binary-codec'
|
||||
import * as utils from './utils'
|
||||
import BigNumber from 'bignumber.js'
|
||||
import {ValidationError} from '../common/errors'
|
||||
import {decodeAccountID} from 'ripple-address-codec'
|
||||
import {validate} from '../common'
|
||||
import {computeBinaryTransactionHash} from '../common/hashes'
|
||||
import {JsonObject} from 'ripple-binary-codec/dist/types/serialized-type'
|
||||
|
||||
/**
|
||||
* The transactions should all be equal except for the 'Signers' field.
|
||||
*/
|
||||
function validateTransactionEquivalence(transactions: Array<JsonObject>) {
|
||||
const exampleTransaction = JSON.stringify({...transactions[0], Signers: null})
|
||||
if (transactions.slice(1).some(tx => JSON.stringify({...tx, Signers: null}) !== exampleTransaction)) {
|
||||
throw new ValidationError('txJSON is not the same for all signedTransactions')
|
||||
}
|
||||
}
|
||||
|
||||
function addressToBigNumber(address) {
|
||||
const hex = Buffer.from(decodeAccountID(address)).toString('hex')
|
||||
return new BigNumber(hex, 16)
|
||||
}
|
||||
|
||||
/**
|
||||
* If presented in binary form, the Signers array must be sorted based on
|
||||
* the numeric value of the signer addresses, with the lowest value first.
|
||||
* (If submitted as JSON, the submit_multisigned method handles this automatically.)
|
||||
* https://xrpl.org/multi-signing.html
|
||||
*/
|
||||
function compareSigners(a, b) {
|
||||
return addressToBigNumber(a.Signer.Account).comparedTo(
|
||||
addressToBigNumber(b.Signer.Account)
|
||||
)
|
||||
}
|
||||
|
||||
function getTransactionWithAllSigners(transactions: Array<JsonObject>): JsonObject {
|
||||
// Signers must be sorted - see compareSigners for more details
|
||||
const sortedSigners = _.flatMap(transactions, tx => tx.Signers)
|
||||
.filter(signer => signer)
|
||||
.sort(compareSigners)
|
||||
|
||||
return {...transactions[0], Signers: sortedSigners}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param signedTransactions A collection of the same transaction signed by different signers. The only difference
|
||||
* between the elements of signedTransactions should be the Signers field.
|
||||
* @returns An object with the combined transaction (now having a sorted list of all signers) which is encoded, along
|
||||
* with a transaction id based on the combined transaction.
|
||||
*/
|
||||
function combine(signedTransactions: Array<string>): object {
|
||||
validate.combine({signedTransactions})
|
||||
|
||||
const transactions: JsonObject[] = signedTransactions.map(binary.decode);
|
||||
validateTransactionEquivalence(transactions)
|
||||
|
||||
const signedTransaction = binary.encode(getTransactionWithAllSigners(transactions))
|
||||
return {
|
||||
signedTransaction: signedTransaction,
|
||||
id: computeBinaryTransactionHash(signedTransaction)
|
||||
// TODO: signedTransactions is an array of strings in the documentation, but
|
||||
// tests and this code handle it as an array of objects. Fix!
|
||||
const txs: any[] = signedTransactions.map(binary.decode)
|
||||
const tx = _.omit(txs[0], 'Signers')
|
||||
if (!txs.every((_tx) => _.isEqual(tx, _.omit(_tx, 'Signers')))) {
|
||||
throw new utils.common.errors.ValidationError(
|
||||
'txJSON is not the same for all signedTransactions'
|
||||
)
|
||||
}
|
||||
const unsortedSigners = txs.reduce(
|
||||
(accumulator, _tx) => accumulator.concat(_tx.Signers || []),
|
||||
[]
|
||||
)
|
||||
const signers = unsortedSigners.sort(compareSigners)
|
||||
const signedTx = Object.assign({}, tx, {Signers: signers})
|
||||
const signedTransaction = binary.encode(signedTx)
|
||||
const id = computeBinaryTransactionHash(signedTransaction)
|
||||
return {signedTransaction, id}
|
||||
}
|
||||
|
||||
export default combine
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as assert from 'assert'
|
||||
import BigNumber from 'bignumber.js'
|
||||
import * as utils from './utils'
|
||||
const validate = utils.common.validate
|
||||
const AccountSetFlags = utils.common.constants.AccountSetFlags
|
||||
const AccountFlagIndices = utils.common.constants.AccountFlagIndices
|
||||
const AccountFields = utils.common.constants.AccountFields
|
||||
import {
|
||||
Instructions,
|
||||
@@ -17,14 +17,14 @@ function setTransactionFlags(
|
||||
txJSON: TransactionJSON,
|
||||
values: FormattedSettings
|
||||
) {
|
||||
const keys = Object.keys(values).filter((key) => AccountSetFlags[key] != null)
|
||||
const keys = Object.keys(values).filter((key) => AccountFlagIndices[key] != null)
|
||||
assert.ok(
|
||||
keys.length <= 1,
|
||||
'ERROR: can only set one setting per transaction'
|
||||
)
|
||||
const flagName = keys[0]
|
||||
const value = values[flagName]
|
||||
const index = AccountSetFlags[flagName]
|
||||
const index = AccountFlagIndices[flagName]
|
||||
if (index != null) {
|
||||
if (value) {
|
||||
txJSON.SetFlag = index
|
||||
|
||||
@@ -1,222 +0,0 @@
|
||||
import https = require('https')
|
||||
|
||||
import {RippleAPI} from '..'
|
||||
import {errors} from '../common'
|
||||
import {GeneratedAddress} from '../offline/generate-address'
|
||||
import {isValidAddress} from '../common/schema-validator'
|
||||
import {RippledError} from '../common/errors'
|
||||
|
||||
export interface FaucetWallet {
|
||||
account: GeneratedAddress
|
||||
amount: number
|
||||
balance: number
|
||||
}
|
||||
|
||||
export enum FaucetNetwork {
|
||||
Testnet = 'faucet.altnet.rippletest.net',
|
||||
Devnet = 'faucet.devnet.rippletest.net'
|
||||
}
|
||||
|
||||
const INTERVAL_SECONDS = 1 // Interval to check an account balance
|
||||
const MAX_ATTEMPTS = 20 // Maximum attempts to retrieve a balance
|
||||
|
||||
/**
|
||||
* Generates a random wallet with some amount of XRP (usually 1000 XRP).
|
||||
*
|
||||
* @param address - An existing XRPL address to fund, if undefined, a new wallet will be created.
|
||||
* @returns - A Wallet on the Testnet or Devnet that contains some amount of XRP.
|
||||
*/
|
||||
async function generateFaucetWallet(
|
||||
this: RippleAPI,
|
||||
address?: string
|
||||
): Promise<FaucetWallet | void> {
|
||||
if(!this.isConnected())
|
||||
throw new RippledError("RippleAPI not connected, cannot call faucet")
|
||||
|
||||
// Initialize some variables
|
||||
let body: Uint8Array
|
||||
let startingBalance = 0
|
||||
let faucetUrl = getFaucetUrl(this)
|
||||
|
||||
// If the user provides an existing wallet to fund
|
||||
if (address && isValidAddress(address)) {
|
||||
// Create the POST request body
|
||||
body = new TextEncoder().encode(
|
||||
JSON.stringify({
|
||||
destination: address
|
||||
})
|
||||
)
|
||||
// Retrieve the existing account balance
|
||||
const addressToFundBalance = await getAddressXrpBalance(this, address)
|
||||
|
||||
// Check the address balance is not undefined and is a number
|
||||
if (addressToFundBalance && !isNaN(+addressToFundBalance)) {
|
||||
startingBalance = +addressToFundBalance
|
||||
} else {
|
||||
startingBalance = 0
|
||||
}
|
||||
}
|
||||
|
||||
// Options to pass to https.request
|
||||
const options = {
|
||||
hostname: faucetUrl,
|
||||
port: 443,
|
||||
path: '/accounts',
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Content-Length': body ? body.length : 0
|
||||
}
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = https.request(options, (response) => {
|
||||
const chunks = []
|
||||
response.on('data', (d) => {
|
||||
chunks.push(d)
|
||||
})
|
||||
response.on('end', async () => {
|
||||
const body = Buffer.concat(chunks).toString()
|
||||
|
||||
// "application/json; charset=utf-8"
|
||||
if (response.headers['content-type'].startsWith('application/json')) {
|
||||
const wallet: FaucetWallet = JSON.parse(body)
|
||||
const classicAddress = wallet.account.classicAddress
|
||||
|
||||
if (classicAddress) {
|
||||
try {
|
||||
// Check at regular interval if the address is enabled on the XRPL and funded
|
||||
const isFunded = await hasAddressBalanceIncreased(
|
||||
this,
|
||||
classicAddress,
|
||||
startingBalance
|
||||
)
|
||||
|
||||
if (isFunded) {
|
||||
resolve(wallet)
|
||||
} else {
|
||||
reject(
|
||||
new errors.XRPLFaucetError(
|
||||
`Unable to fund address with faucet after waiting ${
|
||||
INTERVAL_SECONDS * MAX_ATTEMPTS
|
||||
} seconds`
|
||||
)
|
||||
)
|
||||
}
|
||||
} catch (err) {
|
||||
reject(new errors.XRPLFaucetError(err))
|
||||
}
|
||||
} else {
|
||||
reject(
|
||||
new errors.XRPLFaucetError(
|
||||
`The faucet account classic address is undefined`
|
||||
)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
reject({
|
||||
statusCode: response.statusCode,
|
||||
contentType: response.headers['content-type'],
|
||||
body
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
// POST the body
|
||||
request.write(body ? body : '')
|
||||
|
||||
request.on('error', (error) => {
|
||||
reject(error)
|
||||
})
|
||||
|
||||
request.end()
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an XRPL address XRP balance
|
||||
*
|
||||
* @param api - RippleAPI
|
||||
* @param address - XRPL address.
|
||||
* @returns
|
||||
*/
|
||||
async function getAddressXrpBalance(
|
||||
api: RippleAPI,
|
||||
address: string
|
||||
): Promise<string> {
|
||||
// Get all the account balances
|
||||
try {
|
||||
const balances = await api.getBalances(address)
|
||||
|
||||
// Retrieve the XRP balance
|
||||
const xrpBalance = balances.filter(
|
||||
(balance) => balance.currency.toUpperCase() === 'XRP'
|
||||
)
|
||||
return xrpBalance[0].value
|
||||
} catch (err) {
|
||||
return `Unable to retrieve ${address} balance. Error: ${err}`
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check at regular interval if the address is enabled on the XRPL and funded
|
||||
*
|
||||
* @param api - RippleAPI
|
||||
* @param address - the account address to check
|
||||
* @param originalBalance - the initial balance before the funding
|
||||
* @returns A Promise boolean
|
||||
*/
|
||||
async function hasAddressBalanceIncreased(
|
||||
api: RippleAPI,
|
||||
address: string,
|
||||
originalBalance: number
|
||||
): Promise<boolean> {
|
||||
return new Promise((resolve, reject) => {
|
||||
let attempts = MAX_ATTEMPTS
|
||||
const interval = setInterval(async () => {
|
||||
if (attempts < 0) {
|
||||
clearInterval(interval)
|
||||
resolve(false)
|
||||
} else {
|
||||
attempts--
|
||||
}
|
||||
|
||||
try {
|
||||
const newBalance = +(await getAddressXrpBalance(api, address))
|
||||
if (newBalance > originalBalance) {
|
||||
clearInterval(interval)
|
||||
resolve(true)
|
||||
}
|
||||
} catch (err) {
|
||||
clearInterval(interval)
|
||||
reject(
|
||||
new errors.XRPLFaucetError(
|
||||
`Unable to check if the address ${address} balance has increased. Error: ${err}`
|
||||
)
|
||||
)
|
||||
}
|
||||
}, INTERVAL_SECONDS * 1000)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the faucet URL based on the RippleAPI connection
|
||||
* @param api - RippleAPI
|
||||
* @returns A {@link FaucetNetwork}
|
||||
*/
|
||||
export function getFaucetUrl(api: RippleAPI) {
|
||||
const connectionUrl = api.connection.getUrl()
|
||||
|
||||
// 'altnet' for Ripple Testnet server and 'testnet' for XRPL Labs Testnet server
|
||||
if (connectionUrl.includes('altnet') || connectionUrl.includes('testnet')) {
|
||||
return FaucetNetwork.Testnet
|
||||
}
|
||||
|
||||
if (connectionUrl.includes('devnet')) {
|
||||
return FaucetNetwork.Devnet
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
export default generateFaucetWallet
|
||||
@@ -37,13 +37,4 @@ export default <TestSuite>{
|
||||
'getTrustlines'
|
||||
)
|
||||
},
|
||||
|
||||
'getTrustlines - ledger version option': async (api, address) => {
|
||||
const result = await api.getTrustlines(addresses.FOURTH_ACCOUNT, {ledgerVersion: 5})
|
||||
assertResultMatch(
|
||||
result,
|
||||
RESPONSE_FIXTURES.moreThan400Items,
|
||||
'getTrustlines'
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import assert from 'assert'
|
||||
import puppeteer from 'puppeteer'
|
||||
|
||||
describe("Browser Tests", () => {
|
||||
it("Integration Tests", async () => {
|
||||
const browser = await puppeteer.launch({"headless": true});
|
||||
try {
|
||||
const page = await browser.newPage().catch();
|
||||
await page.goto(`file:///${__dirname}/../localintegrationrunner.html`);
|
||||
|
||||
await page.waitForFunction('document.querySelector("body").innerText.includes("submit multisigned transaction")');
|
||||
|
||||
const fails = await page.evaluate(() => {
|
||||
return document.querySelector('.failures').textContent
|
||||
})
|
||||
const passes = await page.evaluate(() => {
|
||||
return document.querySelector('.passes').textContent
|
||||
})
|
||||
|
||||
assert.equal(fails, "failures: 0")
|
||||
assert.notEqual(passes, "passes: 0")
|
||||
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
assert(false)
|
||||
} finally {
|
||||
await browser.close();
|
||||
}
|
||||
}).timeout(40000)
|
||||
})
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"account": {
|
||||
"xAddress": "T7i2Q8yGcMcCQa2n6d9EvSEptT4CE6ap7Q1r1fmjstkLfsK",
|
||||
"secret": "ssKCsaRqWh669atvv83bdYRaiHomY",
|
||||
"classicAddress": "r9SYfmVxrb7iuCVfNhW2gqqzapfE2r6juG",
|
||||
"address": "r9SYfmVxrb7iuCVfNhW2gqqzapfE2r6juG"
|
||||
},
|
||||
"amount": 1000,
|
||||
"balance": 1000
|
||||
}
|
||||
3
test/fixtures/responses/index.js
vendored
3
test/fixtures/responses/index.js
vendored
@@ -218,6 +218,5 @@ module.exports = {
|
||||
single: require('./combine')
|
||||
},
|
||||
submit: require('./submit'),
|
||||
ledgerEvent: require('./ledger-event'),
|
||||
generateFaucetWallet: require('./generate-faucet-wallet.json')
|
||||
ledgerEvent: require('./ledger-event')
|
||||
};
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
{
|
||||
"id": 1,
|
||||
"result": {
|
||||
"Account": "rsCpncqURaTmiaCy4yWaGb17xLioBPVMiN",
|
||||
"Fee": "10000",
|
||||
"Flags": 2147483648,
|
||||
"LastLedgerSequence": 169578503,
|
||||
"Memos": [
|
||||
{
|
||||
"Memo": {
|
||||
"MemoData": "2265794A68624763694F694A46557A49314E694973496E523563434936496B705856434A392E65794A6A5957356A5A5778735A575266633246735A56397A5A5846315A57356A5A5349364E6A6B304D5441314D7A6373496D4E31636E4A6C626D4E35496A6F694D44497A4D444D784E544532524455794E7A59324E6A52464E4459314E444D344D7A597A4D544D344D7A51774D4441774D4441774D434973496D6C7A6333566C63694936496E497A59314E36635568574D31685252546836556A6468656C6831576D4E57536D6F7961336C71595556314E4649694C434A70595851694F6A45324E4451304D546B774E6A4973496D6C7A63794936496C4E766247396E5A57357059794247623356755A47463061573975496E302E4B6E7A4A4A414B716E4E6473576E4A67734D4434384472375A2D543641467131335A4B5F633732427231596666706C56345962677A423864414156757455454864626262713535375A6C304F35554E5F696F4E56485122"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Memo": {
|
||||
"MemoData": "64386231366631342D363630322D343362312D383438342D633739663866363236383639"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Sequence": 0,
|
||||
"SigningPubKey": "03CDAA7E59BE32A0CF6DC60D94A428B962CB5318CFDE7B504BFA097A421417CF90",
|
||||
"TicketSequence": 69410537,
|
||||
"TransactionType": "AccountSet",
|
||||
"TxnSignature": "3045022100F02A5233BEE411C212DA03342228BB60D814CDE6CE2FAA5590773C15753A1696022077FEDDA19B340674D69AB549E02BBD9449D26CABCADD09A4081205BECA32B959",
|
||||
"date": 697734282,
|
||||
"hash": "7CD33D44FD0474B4598186A9C6BCD7905DAD717CD69934A5DD1C571619BD1169",
|
||||
"inLedger": 69578408,
|
||||
"ledger_index": 69578408,
|
||||
"meta": {
|
||||
"AffectedNodes": [
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"FinalFields": {
|
||||
"Flags": 0,
|
||||
"Owner": "rsCpncqURaTmiaCy4yWaGb17xLioBPVMiN",
|
||||
"RootIndex": "3F6D7C06CECD8300A89F10D60DEE8B0B50A0C9D0459CE322B8087749BCF24537"
|
||||
},
|
||||
"LedgerEntryType": "DirectoryNode",
|
||||
"LedgerIndex": "6E6E7D74CA255FC5DAD0A2A5B1127F7E42472AB526B5CD59B6815EE9ADCED6E5"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"FinalFields": {
|
||||
"Account": "rsCpncqURaTmiaCy4yWaGb17xLioBPVMiN",
|
||||
"Balance": "94106875",
|
||||
"Flags": 0,
|
||||
"OwnerCount": 38,
|
||||
"Sequence": 69410538,
|
||||
"TicketCount": 2
|
||||
},
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"LedgerIndex": "B90A7CFC5D00A25F147686FFED98841B7D30DEBD95C4B6F333088E7F14FB23AD",
|
||||
"PreviousFields": {
|
||||
"Balance": "94116875",
|
||||
"OwnerCount": 39,
|
||||
"TicketCount": 3
|
||||
},
|
||||
"PreviousTxnID": "8F598717167B0357644DC0BBF5A116CDC23E7F4E3D69CD73A147DBBA55B1C790",
|
||||
"PreviousTxnLgrSeq": 69578340
|
||||
}
|
||||
},
|
||||
{
|
||||
"DeletedNode": {
|
||||
"FinalFields": {
|
||||
"Account": "rsCpncqURaTmiaCy4yWaGb17xLioBPVMiN",
|
||||
"Flags": 0,
|
||||
"OwnerNode": "1",
|
||||
"PreviousTxnID": "8F598717167B0357644DC0BBF5A116CDC23E7F4E3D69CD73A147DBBA55B1C790",
|
||||
"PreviousTxnLgrSeq": 69578340,
|
||||
"TicketSequence": 69410537
|
||||
},
|
||||
"LedgerEntryType": "Ticket",
|
||||
"LedgerIndex": "F0BD623DBD0AA7BF134104B4F4D8ACC31ACBE773463F0F20D53DD774A6F51469"
|
||||
}
|
||||
}
|
||||
],
|
||||
"TransactionIndex": 25,
|
||||
"TransactionResult": "tesSUCCESS"
|
||||
},
|
||||
"validated": true,
|
||||
"warnings": [
|
||||
{
|
||||
"id": 1004,
|
||||
"message": "This is a reporting server. The default behavior of a reporting server is to only return validated data. If you are looking for not yet validated data, include \"ledger_index : current\" in your request, which will cause this server to forward the request to a p2p node. If the forward is successful the response will include \"forwarded\" : \"true\""
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": "success",
|
||||
"type": "response"
|
||||
}
|
||||
@@ -12,12 +12,10 @@ import {isValidSecret} from 'ripple-api/common/utils'
|
||||
const TIMEOUT = 20000
|
||||
const INTERVAL = 1000 // how long to wait between checks for validated ledger
|
||||
|
||||
const HOST = process.env.HOST ?? "0.0.0.0"
|
||||
const HOST = process.env.HOST ?? "127.0.0.1"
|
||||
const PORT = process.env.PORT ?? "6006"
|
||||
const serverUrl = `ws://${HOST}:${PORT}`
|
||||
|
||||
console.log(serverUrl)
|
||||
|
||||
function acceptLedger(api) {
|
||||
return api.connection.request({command: 'ledger_accept'})
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<meta charset="utf-8">
|
||||
<!-- encoding must be set for mocha's special characters to render properly -->
|
||||
<link rel="stylesheet" href="../node_modules/mocha/mocha.css" />
|
||||
<script src="./vendor/lodash.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="deb"></div>
|
||||
@@ -17,7 +18,7 @@
|
||||
mocha.ui('bdd')
|
||||
</script>
|
||||
|
||||
<script src="../test-compiled-for-web/integration-test.js"></script>
|
||||
<script src="../test-compiled-for-web/integration/integration-test.js"></script>
|
||||
|
||||
<script>
|
||||
mocha.run()
|
||||
|
||||
@@ -669,11 +669,7 @@ export function createMockRippled(port) {
|
||||
} else if (request.account === addresses.THIRD_ACCOUNT) {
|
||||
conn.send(accountLinesResponse.manyItems(request))
|
||||
} else if (request.account === addresses.FOURTH_ACCOUNT) {
|
||||
if (request.ledger_index === 5) {
|
||||
conn.send(accountLinesResponse.manyItems(request))
|
||||
} else {
|
||||
conn.send(accountLinesResponse.ripplingDisabled(request))
|
||||
}
|
||||
conn.send(accountLinesResponse.ripplingDisabled(request))
|
||||
} else if (request.account === addresses.NOTFOUND) {
|
||||
conn.send(createResponse(request, fixtures.account_info.notfound))
|
||||
} else {
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import assert from 'assert-diff'
|
||||
import accountSetWithDeletedNode from './fixtures/rippled/account-set-with-deleted-node.json'
|
||||
import parseSettings from '../src/ledger/parse/settings'
|
||||
|
||||
describe('Settings unit tests', function () {
|
||||
it('parseSettings does not error with DeletedNode', function () {
|
||||
assert.deepStrictEqual(
|
||||
parseSettings(accountSetWithDeletedNode.result),
|
||||
{}
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -1,3 +1,6 @@
|
||||
{
|
||||
"extends": "../tsconfig-base.json",
|
||||
"compilerOptions": {
|
||||
"noEmit": true,
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
import assert from 'assert-diff'
|
||||
|
||||
import setupAPI from './setup-api'
|
||||
import {getFaucetUrl, FaucetNetwork} from '../src/wallet/wallet-generation'
|
||||
|
||||
describe('Get Faucet URL', function () {
|
||||
beforeEach(setupAPI.setup)
|
||||
afterEach(setupAPI.teardown)
|
||||
|
||||
it('returns the Devnet URL', function () {
|
||||
const expectedFaucet = FaucetNetwork.Devnet
|
||||
this.api.connection._url = FaucetNetwork.Devnet
|
||||
|
||||
assert.strictEqual(getFaucetUrl(this.api), expectedFaucet)
|
||||
})
|
||||
|
||||
it('returns the Testnet URL', function () {
|
||||
const expectedFaucet = FaucetNetwork.Testnet
|
||||
this.api.connection._url = FaucetNetwork.Testnet
|
||||
|
||||
assert.strictEqual(getFaucetUrl(this.api), expectedFaucet)
|
||||
})
|
||||
|
||||
it('returns the Testnet URL with the XRPL Labs server', function () {
|
||||
const expectedFaucet = FaucetNetwork.Testnet
|
||||
this.api.connection._url = 'wss://testnet.xrpl-labs.com'
|
||||
|
||||
assert.strictEqual(getFaucetUrl(this.api), expectedFaucet)
|
||||
})
|
||||
|
||||
it('returns undefined if not a Testnet or Devnet server URL', function () {
|
||||
// Info: setupAPI.setup creates a connection to 'localhost'
|
||||
assert.strictEqual(getFaucetUrl(this.api), undefined)
|
||||
})
|
||||
})
|
||||
@@ -1,7 +1,6 @@
|
||||
'use strict';
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const assert = require('assert');
|
||||
const {BundleAnalyzerPlugin} = require('webpack-bundle-analyzer');
|
||||
|
||||
function getDefaultConfiguration() {
|
||||
@@ -9,6 +8,9 @@ function getDefaultConfiguration() {
|
||||
cache: true,
|
||||
performance: { hints: false },
|
||||
stats: 'errors-only',
|
||||
externals: [{
|
||||
'lodash': '_'
|
||||
}],
|
||||
entry: './dist/npm/index.js',
|
||||
output: {
|
||||
library: 'ripple',
|
||||
@@ -32,85 +34,20 @@ function getDefaultConfiguration() {
|
||||
"assert": require.resolve("assert/"),
|
||||
"url": require.resolve("url/"),
|
||||
"stream": require.resolve("stream-browserify"),
|
||||
"crypto": require.resolve("crypto-browserify"),
|
||||
"https": require.resolve("https-browserify"),
|
||||
"http": require.resolve('stream-http')
|
||||
"crypto": require.resolve("crypto-browserify")
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function webpackForTest(testFileName) {
|
||||
const match = testFileName.match(/\/?([^\/]*)-test.ts$/);
|
||||
if (!match) {
|
||||
assert(false, 'wrong filename:' + testFileName);
|
||||
}
|
||||
|
||||
const test = {
|
||||
cache: true,
|
||||
externals: [{
|
||||
'lodash': '_',
|
||||
'ripple-api': 'ripple',
|
||||
'net': 'null'
|
||||
}],
|
||||
entry: testFileName,
|
||||
output: {
|
||||
library: match[1].replace(/-/g, '_'),
|
||||
path: path.join(__dirname, './test-compiled-for-web/'),
|
||||
filename: match[1] + '-test.js'
|
||||
},
|
||||
plugins: [
|
||||
new webpack.ProvidePlugin({ process: 'process/browser' }),
|
||||
new webpack.ProvidePlugin({ Buffer: ['buffer', 'Buffer'] })
|
||||
],
|
||||
module: {
|
||||
rules: [{
|
||||
test: /jayson/,
|
||||
use: 'null',
|
||||
}, {
|
||||
test: /\.ts$/,
|
||||
use: [{
|
||||
loader: 'ts-loader',
|
||||
options: {
|
||||
compilerOptions: {
|
||||
composite: false,
|
||||
declaration: false,
|
||||
declarationMap: false
|
||||
}
|
||||
},
|
||||
}],
|
||||
}]
|
||||
},
|
||||
node: {
|
||||
global: true,
|
||||
__filename: false,
|
||||
__dirname: true,
|
||||
},
|
||||
resolve: {
|
||||
extensions: [ '.ts', '.js', '.json' ],
|
||||
fallback: {
|
||||
"buffer": require.resolve("buffer/"),
|
||||
"assert": require.resolve("assert/"),
|
||||
"url": require.resolve("url/"),
|
||||
"stream": require.resolve("stream-browserify"),
|
||||
"crypto": require.resolve("crypto-browserify"),
|
||||
"path": require.resolve("path-browserify"),
|
||||
"http": require.resolve("stream-http"),
|
||||
"fs": false
|
||||
}
|
||||
}
|
||||
};
|
||||
return Object.assign({}, getDefaultConfiguration(), test);
|
||||
}
|
||||
|
||||
module.exports = [
|
||||
(env, argv) => {
|
||||
function(env, argv) {
|
||||
const config = getDefaultConfiguration();
|
||||
config.mode = 'development';
|
||||
config.output.filename = `ripple-latest.js`;
|
||||
return config;
|
||||
},
|
||||
(env, argv) => {
|
||||
function(env, argv) {
|
||||
const config = getDefaultConfiguration();
|
||||
config.mode = 'production';
|
||||
config.output.filename = `ripple-latest-min.js`;
|
||||
@@ -119,5 +56,4 @@ module.exports = [
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(env, argv) => webpackForTest('./test/integration/integration-test.ts'),
|
||||
];
|
||||
Reference in New Issue
Block a user