npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

send-crypto

v0.2.38

Published

A minimal JavaScript library / wallet for sending crypto assets

Downloads

744

Readme

A minimal JavaScript library for sending crypto assets.

Currently doesn't support hierarchical or single-use addresses.

Supported assets

  • BTC

  • LTC

  • ZEC (transparent txs only)

  • BCH

  • FIL

  • ETH

  • ERC20 tokens

Usage

npm install --save send-crypto
# or
yarn add send-crypto

Replace "BTC" with any supported asset:

const CryptoAccount = require("send-crypto");

/* Load account from private key */
const privateKey = process.env.PRIVATE_KEY || CryptoAccount.newPrivateKey();
const account = new CryptoAccount(privateKey);

/* Print address */
console.log(await account.address("BTC"));
// > "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"

/* Print balance */
console.log(await account.getBalance("BTC"));
// > 0.01

/* Send 0.01 BTC */
const txHash = await account
    .send("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", 0.01, "BTC")
    .on("transactionHash", console.log)
    // > "3387418aaddb4927209c5032f515aa442a6587d6e54677f08a03b8fa7789e688"
    .on("confirmation", console.log);
// > 1
// > 2 ...

UNITS: getBalance and send can be replaced with getBalanceInSats and sendSats respectively to use the blockchain's smallest units (satoshis for BTC, wei for ETH, etc.).

CONFIG: Each of the functions address, getBalance and send accept an optional options parameter. See the available options in the sections "All balance and transaction options" for each asset below.

Examples

Setup

.env:

PRIVATE_KEY="1234512341"

Use the dotenv library (installed with npm i -D dotenv) or run source .env before running:

require("dotenv").config();
const CryptoAccount = require("send-crypto");
const account = new CryptoAccount(process.env.PRIVATE_KEY);
const privateKey = CryptoAccount.newPrivateKey();
console.log(`Save your key somewhere: ${privateKey}`);
const account = new CryptoAccount(privateKey);

BTC, ZEC, BCH

const CryptoAccount = require("send-crypto");
const account = new CryptoAccount(process.env.PRIVATE_KEY);

// Send BTC
await account.send("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", 0.01, "BTC");
const CryptoAccount = require("send-crypto");
const account = new CryptoAccount(process.env.PRIVATE_KEY);

// Send ZEC
await account.send("t3Vz22vK5z2LcKEdg16Yv4FFneEL1zg9ojd", 0.01, "ZEC");

CashAddr, BitPay and legacy addresses are supported.

const CryptoAccount = require("send-crypto");
const account = new CryptoAccount(process.env.PRIVATE_KEY);

// Send BCH
await account.send(
    "bitcoincash:qp3wjpa3tjlj042z2wv7hahsldgwhwy0rq9sywjpyy",
    0.01,
    "BCH"
);

You can replace "BTC" with "ZEC" or "BCH" in the following examples:

const balance = await account.getBalance("BTC");
await account.send("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", balance, "BTC", {
    subtractFee: true,
});

// Or using sats as the unit
const balanceInSats = await account.getBalanceInSats("BTC");
await account.sendSats(
    "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
    balanceInSats,
    "BTC",
    { subtractFee: true }
);
await new Promise((resolve, reject) =>
    account.send("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", 0.01, "BTC")
        .on("confirmation", confirmations => { if (confirmations >= 6) { resolve(); } })
        .catch(reject);
);
const testnetAccount = new CryptoAccount(process.env.PRIVATE_KEY, {
    network: "testnet",
});
await testnetAccount.send("12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX", 0.01, "BTC");

The getBalance and getBalanceInSats options are:

{
    // Get the balance of an address other than the current account's
    address?: string;

    // The number of confirmations UTXOs must have to be included in the balance
    confirmations?: number; // defaults to 0
}

The send and sendSats options are:

{
    // The number of confirmations UTXOs must have to be included in the inputs
    confirmations?: number; // defaults to 0

    // The fee in satoshis/zatoshis to use
    fee?: number;           // defaults to 10000

    // Whether the fee should be included or excluded from `value`
    subtractFee?: boolean;  // defaults to false
}

ETH, ERC20

const CryptoAccount = require("send-crypto");
const account = new CryptoAccount(process.env.PRIVATE_KEY);

// Send ETH
await account.send("0x05a56e2d52c817161883f50c441c3228cfe54d9f", 0.01, "ETH");

You can transfer arbitrary ERC20 tokens by providing the token's address:

const CryptoAccount = require("send-crypto");
const account = new CryptoAccount(process.env.PRIVATE_KEY);

await account.send("0x05a56e2d52c817161883f50c441c3228cfe54d9f", 1.234, {
    type: "ERC20",
    address: "0x408e41876cccdc0f92210600ef50372656052a38",
});

A few well known ERC20 tokens can be referenced by name:

await account.send("0x05a56e2d52c817161883f50c441c3228cfe54d9f", 1.234, {
    type: "ERC20",
    name: "DAI",
});

See the ERC20s.ts to see the tokens than can be referenced by name.

The supported testnets are mainnet, ropsten, kovan, rinkeby and goerli.

// Use "testnet" BTC, BCH & ZEC; use "ropsten" ETH.
const testnetAccount = new CryptoAccount(process.env.PRIVATE_KEY, {
    network: "testnet",
});
const testnetAccount = new CryptoAccount(process.env.PRIVATE_KEY, {
    network: "ropsten",
});
// Use "testnet" BTC, BCH & ZEC; use "kovan" ETH.
const testnetAccount = new CryptoAccount(process.env.PRIVATE_KEY, {
    network: "kovan",
});

The getBalance and getBalanceInSats options are:

{
    // Get the balance of an address other than the current account's
    address?: string;
}

The send and sendSats options are:

{
    // Gas limit
    gas?: number | string;

    // Gas price in WEI
    gasPrice?: number | string | BN;

    // Include data with the transfer
    data?: string;

    // Override the transaction nonce
    nonce?: number;

    // [ETH] Whether the fee should be included or excluded from `value`
    subtractFee?: boolean;  // defaults to false

    // [ERC20] Approve instead of transferring
    approve?: boolean; // defaults to false
}

Custom assets

If you want to send a cryptocurrency or token that isn't supported by the library, or enhance support for one of the assets above (e.g. add support for a new address format), you can write your own handler using the instructions below.

Handlers must implement the (TypeScript) interface below.

The handlesAsset function is called to ask if the handler can handle an asset.

All other functions are optional. If a function isn't provided, the next handler is called instead.

export abstract class Handler<
    ConstructorOptions = {},
    AddressOptions = {},
    BalanceOptions extends { address?: string } = { address?: string },
    TxOptions = {}
> {
    // sharedState allows multiple handlers access common state.
    constructor(
        privateKey: string,
        network: string,
        constructorOptions?: ConstructorOptions,
        sharedState?: any
    ) {
        /* */
    }

    // Returns whether or not this can handle the asset
    public handlesAsset!: (asset: Asset) => boolean;

    // Returns the address of the account
    public address?: (
        asset: Asset,
        options?: AddressOptions,
        deferHandler?: DeferHandler
    ) => Promise<string>;

    // Returns the balance of the account
    public getBalance?: (
        asset: Asset,
        options?: BalanceOptions,
        deferHandler?: DeferHandler
    ) => Promise<BigNumber>;
    public getBalanceInSats?: (
        asset: Asset,
        options?: BalanceOptions,
        deferHandler?: DeferHandler
    ) => Promise<BigNumber>;

    // Transfers the asset to the provided address
    public send?: (
        to: string,
        value: BigNumber,
        asset: Asset,
        options?: TxOptions,
        deferHandler?: DeferHandler
    ) => PromiEvent<string>;
    public sendSats?: (
        to: string,
        value: BigNumber,
        asset: Asset,
        options?: TxOptions,
        deferHandler?: DeferHandler
    ) => PromiEvent<string>;
}

And then register the handler:

const CryptoAccount = require("send-crypto");
const account = new CryptoAccount(process.env.PRIVATE_KEY);
account.registerHandler(MyCystomHandler);

registerHandler accepts an optional priority parameter for setting the order of handlers (see index.ts to see the default ordering).

You can wrap around other handlers by using the defer parameter passed in to each function. For example, to add support for ENS names for Ethereum, you can resolve the to address and then call defer:

class ENSResolver {
    /* ... */

    handlesAsset = (asset: Asset) => asset === "ETH";

    resolveENSName = (to: string): Promise<string> => {
        /* ... */
    };

    send = async (
        to: string,
        value: BigNumber,
        asset: Asset,
        deferHandler: Handler
    ): PromiEvent<string> => {
        return deferHandler.send(await resolveENSName(to), value, asset);
    };
}

See the following handlers as references: