Compare commits
33 Commits
testing-ap
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 0158c875e5 | |||
|
|
dbc5cfda95 | ||
|
|
b61565398c | ||
|
|
d6d3da01e0 | ||
|
|
bb8a5a4dd1 | ||
|
|
712515a990 | ||
|
|
6aa9fa0255 | ||
|
|
fca68debf1 | ||
|
|
c5b61f3c05 | ||
|
|
2e3b0209b5 | ||
|
|
33a972afc6 | ||
|
|
a52b27a4d5 | ||
|
|
74bb2608dc | ||
|
|
c857c04b85 | ||
|
|
cd89411365 | ||
|
|
4403cee668 | ||
|
|
32cc6e166c | ||
|
|
779af53fcb | ||
|
|
f09870bf3a | ||
|
|
74def25d14 | ||
|
|
83e3905408 | ||
|
|
5203782935 | ||
|
|
9412a707d8 | ||
|
|
f28ecf5f3f | ||
|
|
09a4943298 | ||
|
|
22672d0172 | ||
|
|
0b02732f48 | ||
|
|
52de5ff1cd | ||
|
|
1f11a8f286 | ||
|
|
1fd6dbbf9c | ||
|
|
e0c8f9ee7a | ||
|
|
129daf7a5b | ||
|
|
80c74cd9d9 |
32
.github/workflows/push-dappbundle.yml
vendored
Normal file
32
.github/workflows/push-dappbundle.yml
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
name: Workflow push to Dappbundle
|
||||
on: [push]
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: self-hosted
|
||||
steps:
|
||||
- name: Executing remote command
|
||||
uses: appleboy/ssh-action@v1.0.0
|
||||
with:
|
||||
host: ${{ secrets.R_HOST }}
|
||||
username: ${{ secrets.P_USERNAME }}
|
||||
password: ${{ secrets.P_PASSWORD }}
|
||||
port: ${{ secrets.SSH_PORT }}
|
||||
script: |
|
||||
if [ -d "${{ secrets.DEPLOYMENT_LOCATION}}/dappbundle" ]; then
|
||||
echo "Folder exists. Skipping Git clone."
|
||||
else
|
||||
echo "Folder does not exist. Cloning repository..."
|
||||
cd ${{ secrets.DEPLOYMENT_LOCATION}}/ && git clone https://github.com/ranchimall/dappbundle.git
|
||||
fi
|
||||
|
||||
if [ -d "${{ secrets.DEPLOYMENT_LOCATION}}/dappbundle/${{ github.event.repository.name }}" ]; then
|
||||
echo "Repository exists. Remove folder "
|
||||
rm -r "${{ secrets.DEPLOYMENT_LOCATION}}/dappbundle/${{ github.event.repository.name }}"
|
||||
fi
|
||||
|
||||
echo "Cloning repository..."
|
||||
cd ${{ secrets.DEPLOYMENT_LOCATION}}/dappbundle && git clone https://github.com/ranchimall/${{ github.event.repository.name }}
|
||||
|
||||
cd "${{ secrets.DEPLOYMENT_LOCATION}}/dappbundle/${{ github.event.repository.name }}" && rm -rf .gitattributes .git .github .gitignore
|
||||
cd ${{ secrets.DEPLOYMENT_LOCATION}}/dappbundle/ && git add . && git commit -m "Workflow updating files of ${{ github.event.repository.name }}" && git push "https://ranchimalldev:${{ secrets.RM_ACCESS_TOKEN }}@github.com/ranchimall/dappbundle.git"
|
||||
18
.github/workflows/repopush.yml
vendored
18
.github/workflows/repopush.yml
vendored
@ -1,18 +0,0 @@
|
||||
# push contents of this repo to specified repo on github on commit
|
||||
name: push this repo to bundle repo
|
||||
on: [push]
|
||||
permissions:
|
||||
contents: write
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: push to bundle repo
|
||||
uses: ad-m/github-push-action@master
|
||||
with:
|
||||
github_token: ${{ secrets.SM_ACCESS_TOKEN }}
|
||||
branch: master
|
||||
force: true
|
||||
directory: .
|
||||
repository: ranchimall/dappbundle
|
||||
@ -1,4 +1,5 @@
|
||||
# BTC Wallet
|
||||
[](https://github.com/ranchimall/btcwallet/actions/workflows/push-dappbundle.yml)
|
||||
|
||||
BTC Wallet
|
||||
It is a web-based Bitcoin wallet and Bitcoin explorer that promotes self-custody by generating Bitcoin addresses and private keys locally on the user's device. If you are suspicious about internet monitoring, then open the URL, disconnect your computer from the internet, and then generate the Bitcoin address and private key.
|
||||
|
||||
96
css/main.css
96
css/main.css
@ -27,7 +27,6 @@ body {
|
||||
scrollbar-gutter: stable;
|
||||
color: rgba(var(--text-color), 1);
|
||||
background-color: rgba(var(--background-color), 1);
|
||||
transition: background-color 0.3s;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@ -394,6 +393,10 @@ ol li::before {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.align-self-start {
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.align-center {
|
||||
align-items: center;
|
||||
}
|
||||
@ -658,7 +661,7 @@ ol li::before {
|
||||
}
|
||||
|
||||
#main_header {
|
||||
padding: 1.5rem max(1rem, 4vw);
|
||||
padding: 1rem max(1rem, 4vw);
|
||||
}
|
||||
|
||||
.app-brand {
|
||||
@ -683,7 +686,6 @@ ol li::before {
|
||||
grid-template-rows: auto 1fr;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
transition: background-color 0.3s;
|
||||
background-color: rgba(var(--foreground-color), 1);
|
||||
}
|
||||
|
||||
@ -823,6 +825,10 @@ ol li::before {
|
||||
margin-bottom: 0.3rem;
|
||||
}
|
||||
|
||||
#search_query_input {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
#address_balance_card {
|
||||
padding: 1.5rem;
|
||||
border-radius: 0.5rem;
|
||||
@ -837,6 +843,13 @@ ol li::before {
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
#filter_selector {
|
||||
--padding: 0.3rem 0.5rem;
|
||||
}
|
||||
#filter_selector sm-chip {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 0.5rem 0;
|
||||
border: none;
|
||||
@ -871,21 +884,55 @@ ol li::before {
|
||||
border-bottom: thin solid rgba(var(--text-color), 0.3);
|
||||
}
|
||||
|
||||
#tx_details__header:has(#tx_status) time {
|
||||
text-align: left;
|
||||
margin-right: auto;
|
||||
}
|
||||
#tx_details__header:has(:not(#tx_status)) time {
|
||||
text-align: right;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
#transactions_list {
|
||||
display: grid;
|
||||
gap: 2rem;
|
||||
padding-bottom: 4rem;
|
||||
padding-top: 2rem;
|
||||
}
|
||||
|
||||
transaction-card {
|
||||
position: relative;
|
||||
}
|
||||
transaction-card:not(:last-of-type) {
|
||||
padding-bottom: 2rem;
|
||||
}
|
||||
transaction-card:not(:last-of-type)::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
height: 1px;
|
||||
width: calc(100% - 3.5rem);
|
||||
background-color: rgba(var(--text-color), 0.2);
|
||||
}
|
||||
|
||||
.transaction {
|
||||
position: relative;
|
||||
grid-template-columns: auto 1fr;
|
||||
gap: 0.5rem 1rem;
|
||||
align-items: center;
|
||||
content-visibility: auto;
|
||||
contain-intrinsic-height: 8rem;
|
||||
}
|
||||
.transaction:not(:last-of-type) {
|
||||
padding-bottom: 2rem;
|
||||
.transaction.in .transaction__amount {
|
||||
color: var(--green);
|
||||
}
|
||||
.transaction.in .transaction__amount::before {
|
||||
content: "+";
|
||||
}
|
||||
.transaction.out .transaction__amount {
|
||||
color: var(--danger-color);
|
||||
}
|
||||
.transaction.out .transaction__amount::before {
|
||||
content: "-";
|
||||
}
|
||||
.transaction__amount {
|
||||
white-space: nowrap;
|
||||
@ -893,21 +940,9 @@ ol li::before {
|
||||
.transaction.out .transaction__icon .icon {
|
||||
fill: var(--danger-color);
|
||||
}
|
||||
.transaction.out .transaction__amount {
|
||||
color: var(--danger-color);
|
||||
}
|
||||
.transaction.out .transaction__amount::before {
|
||||
content: "- ";
|
||||
}
|
||||
.transaction.in .transaction__icon .icon {
|
||||
fill: var(--green);
|
||||
}
|
||||
.transaction.in .transaction__amount {
|
||||
color: var(--green);
|
||||
}
|
||||
.transaction.in .transaction__amount::before {
|
||||
content: "+ ";
|
||||
}
|
||||
.transaction.unconfirmed-tx .transaction__icon .icon {
|
||||
fill: var(--yellow);
|
||||
}
|
||||
@ -928,6 +963,7 @@ ol li::before {
|
||||
.transaction__time {
|
||||
font-size: 0.9rem;
|
||||
color: rgba(var(--text-color), 0.8);
|
||||
text-wrap: balance;
|
||||
}
|
||||
.transaction__amount {
|
||||
font-size: 1rem;
|
||||
@ -954,6 +990,15 @@ ol li::before {
|
||||
content: ",";
|
||||
}
|
||||
|
||||
#tx_details time {
|
||||
padding: 0.3rem 0.8rem;
|
||||
border-radius: 5rem;
|
||||
background-color: rgba(var(--text-color), 0.06);
|
||||
font-weight: 500;
|
||||
font-size: 0.9rem;
|
||||
text-wrap: balance;
|
||||
}
|
||||
|
||||
#tx_status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -962,11 +1007,23 @@ ol li::before {
|
||||
border-radius: 0.5rem;
|
||||
background-color: rgba(var(--text-color), 0.03);
|
||||
color: var(--danger-color);
|
||||
width: -webkit-fit-content;
|
||||
width: -moz-fit-content;
|
||||
width: fit-content;
|
||||
}
|
||||
#tx_status .icon {
|
||||
fill: var(--danger-color);
|
||||
}
|
||||
|
||||
#tx_amount {
|
||||
font-size: max(2rem, 4vw);
|
||||
font-weight: 700;
|
||||
text-wrap: balance;
|
||||
}
|
||||
#tx_amount:has(+ *) {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
#tx_technicals .tx-detail:first-of-type {
|
||||
position: relative;
|
||||
}
|
||||
@ -1197,6 +1254,7 @@ ol li::before {
|
||||
}
|
||||
#main_header {
|
||||
grid-area: header;
|
||||
padding: 1.5rem max(1rem, 4vw);
|
||||
}
|
||||
#main_navbar {
|
||||
grid-area: nav;
|
||||
|
||||
2
css/main.min.css
vendored
2
css/main.min.css
vendored
File diff suppressed because one or more lines are too long
349
css/main.scss
349
css/main.scss
File diff suppressed because it is too large
Load Diff
1370
index.html
1370
index.html
File diff suppressed because one or more lines are too long
@ -1,19 +1,437 @@
|
||||
(function (EXPORTS) { //btcOperator v1.1.4
|
||||
(function (EXPORTS) { //btcOperator v1.2.10
|
||||
/* BTC Crypto and API Operator */
|
||||
const btcOperator = EXPORTS;
|
||||
const SATOSHI_IN_BTC = 1e8;
|
||||
|
||||
const util = btcOperator.util = {};
|
||||
|
||||
util.Sat_to_BTC = value => parseFloat((value / SATOSHI_IN_BTC).toFixed(8));
|
||||
util.BTC_to_Sat = value => parseInt(value * SATOSHI_IN_BTC);
|
||||
|
||||
const checkIfTor = btcOperator.checkIfTor = () => {
|
||||
return fetch('https://check.torproject.org/api/ip')
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
return res.IsTor
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
return false
|
||||
})
|
||||
}
|
||||
let isTor = false;
|
||||
checkIfTor().then(result => isTor = result);
|
||||
|
||||
async function post(url, data, { asText = false } = {}) {
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
mode: 'no-cors',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
})
|
||||
if (response.ok) {
|
||||
return asText ? await response.text() : await response.json()
|
||||
} else {
|
||||
throw response
|
||||
}
|
||||
} catch (e) {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: some APIs may not support all functions properly hence they are omitted
|
||||
const APIs = btcOperator.APIs = [
|
||||
{
|
||||
url: 'https://api.blockcypher.com/v1/btc/main/',
|
||||
name: 'Blockcypher',
|
||||
balance({ addr }) {
|
||||
return fetch_api(`addrs/${addr}/balance`, { url: this.url })
|
||||
.then(result => util.Sat_to_BTC(result.balance))
|
||||
},
|
||||
async block({ id }) {
|
||||
try {
|
||||
let block = await fetch_api(`blocks/${id}`, { url: this.url })
|
||||
return formatBlock(block)
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
},
|
||||
async broadcast({ rawTxHex, url }) {
|
||||
try {
|
||||
const result = await post(`${url || this.url}pushtx`, { tx: rawTxHex })
|
||||
return result.hash
|
||||
} catch (e) {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
url: 'https://blockstream.info/api/',
|
||||
name: 'Blockstream',
|
||||
hasOnion: true,
|
||||
onionUrl: `http://explorerzydxu5ecjrkwceayqybizmpjjznk5izmitf2modhcusuqlid.onion/api/`,
|
||||
balance({ addr, url }) {
|
||||
return fetch_api(`address/${addr}/utxo`, { url: url || this.url })
|
||||
.then(result => {
|
||||
const balance = result.reduce((t, u) => t + u.value, 0)
|
||||
return util.Sat_to_BTC(balance)
|
||||
})
|
||||
},
|
||||
latestBlock() {
|
||||
return fetch_api(`blocks/tip/height`, { url: this.url })
|
||||
},
|
||||
tx({ txid, url }) {
|
||||
return fetch_api(`tx/${txid}`, { url: url || this.url })
|
||||
.then(result => formatTx(result))
|
||||
},
|
||||
txHex({ txid, url }) {
|
||||
return fetch_api(`tx/${txid}/hex`, { url: url || this.url, asText: true })
|
||||
},
|
||||
txs({ addr, url, ...args }) {
|
||||
let queryParams = Object.entries(args).map(([key, value]) => `${key}=${value}`).join('&')
|
||||
if (queryParams)
|
||||
queryParams = '?' + queryParams
|
||||
return fetch_api(`address/${addr}/txs${queryParams}`, { url: url || this.url })
|
||||
},
|
||||
async block({ id, url }) {
|
||||
// if id is hex string then it is block hash
|
||||
try {
|
||||
let blockHash = id
|
||||
if (!/^[0-9a-f]{64}$/i.test(id))
|
||||
blockHash = await fetch_api(`block-height/${id}`, { url: url || this.url, asText: true })
|
||||
const block = await fetch_api(`block/${blockHash}`, { url: url || this.url })
|
||||
return formatBlock(block)
|
||||
} catch (e) {
|
||||
throw e
|
||||
}
|
||||
},
|
||||
async broadcast({ rawTxHex, url }) {
|
||||
return post(`${url || this.url}tx`, { tx: rawTxHex }, { asText: true })
|
||||
}
|
||||
},
|
||||
{
|
||||
url: 'https://mempool.space/api/',
|
||||
name: 'Mempool',
|
||||
balance({ addr }) {
|
||||
return fetch_api(`address/${addr}`, { url: this.url })
|
||||
.then(result => util.Sat_to_BTC(result.chain_stats.funded_txo_sum - result.chain_stats.spent_txo_sum))
|
||||
},
|
||||
latestBlock() {
|
||||
return fetch_api(`blocks/tip/height`, { url: this.url })
|
||||
},
|
||||
tx({ txid }) {
|
||||
return fetch_api(`tx/${txid}`, { url: this.url })
|
||||
.then(result => formatTx(result))
|
||||
|
||||
},
|
||||
txHex({ txid }) {
|
||||
return fetch_api(`tx/${txid}/hex`, { url: this.url, asText: true })
|
||||
},
|
||||
txs({ addr, ...args }) {
|
||||
let queryParams = Object.entries(args).map(([key, value]) => `${key}=${value}`).join('&')
|
||||
if (queryParams)
|
||||
queryParams = '?' + queryParams
|
||||
return fetch_api(`address/${addr}/txs${queryParams}`, { url: this.url })
|
||||
},
|
||||
async block({ id }) {
|
||||
// if id is hex string then it is block hash
|
||||
try {
|
||||
let blockHash = id
|
||||
if (!/^[0-9a-f]{64}$/i.test(id))
|
||||
blockHash = await fetch_api(`block-height/${id}`, { url: this.url, asText: true })
|
||||
const block = await fetch_api(`block/${blockHash}`, { url: this.url })
|
||||
return formatBlock(block)
|
||||
} catch (e) {
|
||||
throw e
|
||||
}
|
||||
},
|
||||
async broadcast({ rawTxHex, url }) {
|
||||
return post(`${url || this.url}tx`, { tx: rawTxHex }, { asText: true })
|
||||
}
|
||||
},
|
||||
{
|
||||
url: 'https://blockchain.info/',
|
||||
name: 'Blockchain',
|
||||
balance({ addr }) {
|
||||
return fetch_api(`q/addressbalance/${addr}`, { url: this.url })
|
||||
.then(result => util.Sat_to_BTC(result))
|
||||
},
|
||||
unspent({ addr, allowUnconfirmedUtxos = false }) {
|
||||
return fetch_api(`unspent?active=${addr}`, { url: this.url })
|
||||
.then(result => formatUtxos(result.unspent_outputs, allowUnconfirmedUtxos))
|
||||
},
|
||||
tx({ txid }) {
|
||||
return fetch_api(`rawtx/${txid}`, { url: this.url })
|
||||
.then(result => formatTx(result))
|
||||
},
|
||||
txHex({ txid }) {
|
||||
return fetch_api(`rawtx/${txid}?format=hex`, { url: this.url, asText: true })
|
||||
},
|
||||
txs({ addr, ...args }) {
|
||||
let queryParams = Object.entries(args).map(([key, value]) => `${key}=${value}`).join('&')
|
||||
if (queryParams)
|
||||
queryParams = '?' + queryParams
|
||||
return fetch_api(`rawaddr/${addr}${queryParams}`, { url: this.url })
|
||||
.then(result => result.txs)
|
||||
},
|
||||
latestBlock() {
|
||||
return fetch_api(`q/getblockcount`, { url: this.url })
|
||||
},
|
||||
async block({ id }) {
|
||||
try {
|
||||
let block
|
||||
// if id is hex string then it is block hash
|
||||
if (/^[0-9a-f]{64}$/i.test(id))
|
||||
block = await fetch_api(`rawblock/${id}`, { url: this.url })
|
||||
else {
|
||||
const result = await fetch_api(`block-height/${id}?format=json`, { url: this.url })
|
||||
block = result.blocks[0]
|
||||
}
|
||||
return formatBlock(block)
|
||||
} catch (e) {
|
||||
throw e
|
||||
}
|
||||
},
|
||||
async blockTxs({ id }) {
|
||||
try {
|
||||
let block
|
||||
// if id is hex string then it is block hash
|
||||
if (/^[0-9a-f]{64}$/i.test(id))
|
||||
block = await fetch_api(`rawblock/${id}`, { url: this.url })
|
||||
else {
|
||||
const result = await fetch_api(`block-height/${id}?format=json`, { url: this.url })
|
||||
block = result.blocks[0]
|
||||
}
|
||||
return block.tx
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
url: 'https://coinb.in/api/?uid=1&key=12345678901234567890123456789012&setmodule=bitcoin&request=sendrawtransaction',
|
||||
name: 'Coinb.in',
|
||||
broadcast({ rawTxHex }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
fetch(this.url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
body: "rawtx=" + rawTxHex
|
||||
}).then(response => {
|
||||
console.log(response)
|
||||
response.text().then(resultText => {
|
||||
let r = resultText.match(/<result>.*<\/result>/);
|
||||
if (!r)
|
||||
reject(resultText);
|
||||
else {
|
||||
r = r.pop().replace('<result>', '').replace('</result>', '');
|
||||
if (r == '1') {
|
||||
let txid = resultText.match(/<txid>.*<\/txid>/).pop().replace('<txid>', '').replace('</txid>', '');
|
||||
resolve(txid);
|
||||
} else if (r == '0') {
|
||||
let error
|
||||
if (resultText.includes('<message>')) {
|
||||
error = resultText.match(/<message>.*<\/message>/).pop().replace('<message>', '').replace('</message>', '');
|
||||
} else {
|
||||
error = resultText.match(/<response>.*<\/response>/).pop().replace('<response>', '').replace('</response>', '');
|
||||
}
|
||||
reject(decodeURIComponent(error.replace(/\+/g, " ")));
|
||||
} else reject(resultText);
|
||||
}
|
||||
}).catch(error => reject(error))
|
||||
}).catch(error => reject(error))
|
||||
});
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
btcOperator.util.format = {} // functions to homogenize API results
|
||||
const formatBlock = btcOperator.util.format.block = async (block) => {
|
||||
try {
|
||||
const { height, hash, id, time, timestamp, mrkl_root, merkle_root, prev_block, next_block, size } = block;
|
||||
const details = {
|
||||
height,
|
||||
hash: hash || id,
|
||||
time: (time || timestamp) * 1000,
|
||||
merkle_root: merkle_root || mrkl_root,
|
||||
size,
|
||||
}
|
||||
if (prev_block)
|
||||
details.prev_block = prev_block
|
||||
if (next_block)
|
||||
details.next_block = next_block[0]
|
||||
return details
|
||||
} catch (e) {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
const formatUtxos = btcOperator.util.format.utxos = async (utxos, allowUnconfirmedUtxos = false) => {
|
||||
try {
|
||||
if (!allowUnconfirmedUtxos && !utxos || !Array.isArray(utxos))
|
||||
throw {
|
||||
message: "No utxos found",
|
||||
code: 1000 //error code for when issue is not from API but situational (like no utxos found)
|
||||
}
|
||||
return utxos.map(utxo => {
|
||||
const { tx_hash, tx_hash_big_endian, txid, tx_output_n, vout, value, script, confirmations, status: { confirmed } = {} } = utxo;
|
||||
return {
|
||||
confirmations: confirmations || confirmed,
|
||||
tx_hash_big_endian: tx_hash_big_endian || tx_hash || txid,
|
||||
tx_output_n: tx_output_n || vout,
|
||||
value,
|
||||
script
|
||||
}
|
||||
})
|
||||
} catch (e) {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
const formatTx = btcOperator.util.format.tx = async (tx) => {
|
||||
try {
|
||||
let { txid, hash, time, block_height, fee, fees, received,
|
||||
confirmed, size, double_spend, block_hash, confirmations,
|
||||
status: { block_height: statusBlockHeight, block_hash: statusBlockHash, block_time } = {}
|
||||
} = tx;
|
||||
if ((block_height || statusBlockHeight) && confirmations === undefined || confirmations === null) {
|
||||
const latestBlock = await multiApi('latestBlock');
|
||||
confirmations = latestBlock - (block_height || statusBlockHeight);
|
||||
}
|
||||
const inputs = tx.vin || tx.inputs;
|
||||
const outputs = tx.vout || tx.outputs || tx.out;
|
||||
return {
|
||||
hash: hash || txid,
|
||||
size: size,
|
||||
fee: fee || fees,
|
||||
double_spend,
|
||||
time: (time * 1000) || new Date(confirmed || received).getTime() || block_time * 1000 || Date.now(),
|
||||
block_height: block_height || statusBlockHeight,
|
||||
block_hash: block_hash || statusBlockHash,
|
||||
confirmations,
|
||||
inputs: inputs.map(input => {
|
||||
return {
|
||||
index: input.n || input.output_index || input.vout,
|
||||
prev_out: {
|
||||
addr: input.prev_out?.addr || input.addresses?.[0] || input.prev_out?.address || input.addr || input.prevout.scriptpubkey_address,
|
||||
value: input.prev_out?.value || input.output_value || input.prevout.value,
|
||||
},
|
||||
}
|
||||
}),
|
||||
out: outputs.map(output => {
|
||||
return {
|
||||
addr: output.scriptpubkey_address || output.addresses?.[0] || output.scriptpubkey_address || output.addr,
|
||||
value: output.value || output.scriptpubkey_value,
|
||||
}
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
const multiApi = btcOperator.multiApi = async (fnName, { index = 0, ...args } = {}) => {
|
||||
try {
|
||||
let triedOnion = false;
|
||||
while (index < APIs.length) {
|
||||
if (!APIs[index][fnName] || (APIs[index].coolDownTime && APIs[index].coolDownTime > new Date().getTime())) {
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
return await APIs[index][fnName](args);
|
||||
}
|
||||
if (isTor && !triedOnion) {
|
||||
triedOnion = true;
|
||||
index = 0;
|
||||
while (index < APIs.length) {
|
||||
if (!APIs[index].hasOnion || (APIs[index].coolDownTime && APIs[index].coolDownTime > new Date().getTime())) {
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
return await multiApi(fnName, { index: index + 1, ...args, url: APIs[index].onionUrl });
|
||||
}
|
||||
}
|
||||
throw "No API available"
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
if (!APIs[index])
|
||||
throw "No API available"
|
||||
APIs[index].coolDownTime = new Date().getTime() + 1000 * 60 * 10; // 10 minutes
|
||||
return multiApi(fnName, { index: index + 1, ...args });
|
||||
}
|
||||
};
|
||||
|
||||
function parseTx(tx, addressOfTx) {
|
||||
const { txid, hash, time, block_height, inputs, outputs, out, vin, vout, fee, fees, received, confirmed, status: { block_height: statusBlockHeight, block_time } = {} } = tx;
|
||||
let parsedTx = {
|
||||
txid: hash || txid,
|
||||
time: (time * 1000) || new Date(confirmed || received).getTime() || block_time * 1000 || Date.now(),
|
||||
block: block_height || statusBlockHeight,
|
||||
}
|
||||
//sender list
|
||||
parsedTx.tx_senders = {};
|
||||
(inputs || vin).forEach(i => {
|
||||
const address = i.prev_out?.addr || i.addresses?.[0] || i.prev_out?.address || i.addr || i.prevout.scriptpubkey_address;
|
||||
const value = i.prev_out?.value || i.output_value || i.value || i.prevout.value;
|
||||
if (address in parsedTx.tx_senders)
|
||||
parsedTx.tx_senders[address] += value;
|
||||
else parsedTx.tx_senders[address] = value;
|
||||
});
|
||||
parsedTx.tx_input_value = 0;
|
||||
for (let senderAddr in parsedTx.tx_senders) {
|
||||
let val = parsedTx.tx_senders[senderAddr];
|
||||
parsedTx.tx_senders[senderAddr] = util.Sat_to_BTC(val);
|
||||
parsedTx.tx_input_value += val;
|
||||
}
|
||||
parsedTx.tx_input_value = util.Sat_to_BTC(parsedTx.tx_input_value);
|
||||
//receiver list
|
||||
parsedTx.tx_receivers = {};
|
||||
(outputs || out || vout).forEach(o => {
|
||||
const address = o.scriptpubkey_address || o.addresses?.[0] || o.scriptpubkey_address || o.addr;
|
||||
const value = o.value || o.scriptpubkey_value;
|
||||
if (address in parsedTx.tx_receivers)
|
||||
parsedTx.tx_receivers[address] += value;
|
||||
else parsedTx.tx_receivers[address] = value;
|
||||
});
|
||||
parsedTx.tx_output_value = 0;
|
||||
for (let receiverAddr in parsedTx.tx_receivers) {
|
||||
let val = parsedTx.tx_receivers[receiverAddr];
|
||||
parsedTx.tx_receivers[receiverAddr] = util.Sat_to_BTC(val);
|
||||
parsedTx.tx_output_value += val;
|
||||
}
|
||||
parsedTx.tx_output_value = util.Sat_to_BTC(parsedTx.tx_output_value);
|
||||
// tx fee
|
||||
parsedTx.tx_fee = util.Sat_to_BTC(fee || fees || (parsedTx.tx_input_value - parsedTx.tx_output_value));
|
||||
//detect tx type (in, out, self)
|
||||
if (Object.keys(parsedTx.tx_receivers).length === 1 && Object.keys(parsedTx.tx_senders).length === 1 && Object.keys(parsedTx.tx_senders)[0] === Object.keys(parsedTx.tx_receivers)[0]) {
|
||||
parsedTx.type = 'self';
|
||||
parsedTx.amount = parsedTx.tx_receivers[addressOfTx];
|
||||
parsedTx.address = addressOfTx;
|
||||
} else if (addressOfTx in parsedTx.tx_senders && Object.keys(parsedTx.tx_receivers).some(addr => addr !== addressOfTx)) {
|
||||
parsedTx.type = 'out';
|
||||
parsedTx.receiver = Object.keys(parsedTx.tx_receivers).filter(addr => addr != addressOfTx);
|
||||
parsedTx.amount = parsedTx.receiver.reduce((t, addr) => t + parsedTx.tx_receivers[addr], 0) + parsedTx.tx_fee;
|
||||
} else {
|
||||
parsedTx.type = 'in';
|
||||
parsedTx.sender = Object.keys(parsedTx.tx_senders).filter(addr => addr != addressOfTx);
|
||||
parsedTx.amount = parsedTx.tx_receivers[addressOfTx];
|
||||
}
|
||||
return parsedTx;
|
||||
}
|
||||
|
||||
//This library uses API provided by chain.so (https://chain.so/)
|
||||
const URL = "https://blockchain.info/";
|
||||
|
||||
const DUST_AMT = 546,
|
||||
MIN_FEE_UPDATE = 219;
|
||||
|
||||
const fetch_api = btcOperator.fetch = function (api, json_res = true) {
|
||||
const fetch_api = btcOperator.fetch = function (api, { asText = false, url = 'https://blockchain.info/' } = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
console.debug(URL + api);
|
||||
fetch(URL + api).then(response => {
|
||||
console.debug(url + api);
|
||||
fetch(url + api).then(response => {
|
||||
if (response.ok) {
|
||||
(json_res ? response.json() : response.text())
|
||||
(asText ? response.text() : response.json())
|
||||
.then(result => resolve(result))
|
||||
.catch(error => reject(error))
|
||||
} else {
|
||||
@ -25,13 +443,6 @@
|
||||
})
|
||||
};
|
||||
|
||||
const SATOSHI_IN_BTC = 1e8;
|
||||
|
||||
const util = btcOperator.util = {};
|
||||
|
||||
util.Sat_to_BTC = value => parseFloat((value / SATOSHI_IN_BTC).toFixed(8));
|
||||
util.BTC_to_Sat = value => parseInt(value * SATOSHI_IN_BTC);
|
||||
|
||||
function get_fee_rate() {
|
||||
return new Promise((resolve, reject) => {
|
||||
fetch('https://api.blockchain.info/mempool/fees').then(response => {
|
||||
@ -47,6 +458,13 @@
|
||||
|
||||
const broadcastTx = btcOperator.broadcastTx = rawTxHex => new Promise((resolve, reject) => {
|
||||
console.log('txHex:', rawTxHex)
|
||||
// return multiApi('broadcast', { rawTxHex })
|
||||
// .then(result => {
|
||||
// resolve(result)
|
||||
// })
|
||||
// .catch(error => {
|
||||
// reject(error)
|
||||
// })
|
||||
let url = 'https://coinb.in/api/?uid=1&key=12345678901234567890123456789012&setmodule=bitcoin&request=sendrawtransaction';
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
@ -55,6 +473,7 @@
|
||||
},
|
||||
body: "rawtx=" + rawTxHex
|
||||
}).then(response => {
|
||||
console.log(response)
|
||||
response.text().then(resultText => {
|
||||
let r = resultText.match(/<result>.*<\/result>/);
|
||||
if (!r)
|
||||
@ -273,10 +692,11 @@
|
||||
}
|
||||
|
||||
//BTC blockchain APIs
|
||||
|
||||
btcOperator.getBalance = addr => new Promise((resolve, reject) => {
|
||||
fetch_api(`q/addressbalance/${addr}`)
|
||||
.then(result => resolve(util.Sat_to_BTC(result)))
|
||||
if (!validateAddress(addr))
|
||||
return reject("Invalid address");
|
||||
multiApi('balance', { addr })
|
||||
.then(result => resolve(result))
|
||||
.catch(error => reject(error))
|
||||
});
|
||||
|
||||
@ -400,7 +820,11 @@
|
||||
}
|
||||
btcOperator.validateTxParameters = validateTxParameters;
|
||||
|
||||
function createTransaction(senders, redeemScripts, receivers, amounts, fee, change_address, fee_from_receiver, allowUnconfirmedUtxos = false) {
|
||||
const createTransaction = btcOperator.createTransaction = ({
|
||||
senders, redeemScripts, receivers, amounts, fee, change_address,
|
||||
fee_from_receiver, allowUnconfirmedUtxos = false, sendingTx = false,
|
||||
hasInsufficientBalance = false
|
||||
}) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let total_amount = parseFloat(amounts.reduce((t, a) => t + a, 0).toFixed(8));
|
||||
const tx = coinjs.transaction();
|
||||
@ -433,11 +857,16 @@
|
||||
result.output_amount = total_amount - (fee_from_receiver ? result.fee : 0);
|
||||
result.total_size = BASE_TX_SIZE + output_size + result.input_size;
|
||||
result.transaction = tx;
|
||||
resolve(result);
|
||||
if (sendingTx && result.hasOwnProperty('hasInsufficientBalance') && result.hasInsufficientBalance)
|
||||
reject({
|
||||
message: "Insufficient balance",
|
||||
...result
|
||||
});
|
||||
else
|
||||
resolve(result);
|
||||
}).catch(error => reject(error))
|
||||
})
|
||||
}
|
||||
btcOperator.createTransaction = createTransaction;
|
||||
|
||||
function addInputs(tx, senders, redeemScripts, total_amount, fee, output_size, fee_from_receiver, allowUnconfirmedUtxos = false) {
|
||||
return new Promise((resolve, reject) => {
|
||||
@ -479,14 +908,18 @@
|
||||
change_amount: required_amount * -1 //required_amount will be -ve of change_amount
|
||||
});
|
||||
else if (rec_args.n >= senders.length) {
|
||||
return reject("Insufficient Balance");
|
||||
return resolve({
|
||||
hasInsufficientBalance: true,
|
||||
input_size: rec_args.input_size,
|
||||
input_amount: rec_args.input_amount,
|
||||
change_amount: required_amount * -1
|
||||
});
|
||||
}
|
||||
let addr = senders[rec_args.n],
|
||||
rs = redeemScripts[rec_args.n];
|
||||
let addr_type = coinjs.addressDecode(addr).type;
|
||||
let size_per_input = _sizePerInput(addr, rs);
|
||||
fetch_api(`unspent?active=${addr}`).then(result => {
|
||||
let utxos = result.unspent_outputs;
|
||||
multiApi('unspent', { addr, allowUnconfirmedUtxos: rec_args.allowUnconfirmedUtxos }).then(utxos => {
|
||||
//console.debug("add-utxo", addr, rs, required_amount, utxos);
|
||||
for (let i = 0; i < utxos.length && required_amount > 0; i++) {
|
||||
if (utxos.length === 1 && rec_args.allowUnconfirmedUtxos) {
|
||||
@ -534,43 +967,6 @@
|
||||
}
|
||||
btcOperator.addOutputs = addOutputs;
|
||||
|
||||
/*
|
||||
function autoFeeCalc(tx) {
|
||||
return new Promise((resolve, reject) => {
|
||||
get_fee_rate().then(fee_rate => {
|
||||
let tx_size = tx.size();
|
||||
for (var i = 0; i < this.ins.length; i++)
|
||||
switch (tx.extractScriptKey(i).type) {
|
||||
case 'scriptpubkey':
|
||||
tx_size += SIGN_SIZE;
|
||||
break;
|
||||
case 'segwit':
|
||||
case 'multisig':
|
||||
tx_size += SIGN_SIZE * 0.25;
|
||||
break;
|
||||
default:
|
||||
console.warn('Unknown script-type');
|
||||
tx_size += SIGN_SIZE;
|
||||
}
|
||||
resolve(tx_size * fee_rate);
|
||||
}).catch(error => reject(error))
|
||||
})
|
||||
}
|
||||
|
||||
function editFee(tx, current_fee, target_fee, index = -1) {
|
||||
//values are in satoshi
|
||||
index = parseInt(index >= 0 ? index : tx.outs.length - index);
|
||||
if (index < 0 || index >= tx.outs.length)
|
||||
throw "Invalid index";
|
||||
let edit_value = parseInt(current_fee - target_fee), //rip of any decimal places
|
||||
current_value = tx.outs[index].value; //could be BigInterger
|
||||
if (edit_value < 0 && edit_value > current_value)
|
||||
throw "Insufficient value at vout";
|
||||
tx.outs[index].value = current_value instanceof BigInteger ?
|
||||
current_value.add(new BigInteger('' + edit_value)) : parseInt(current_value + edit_value);
|
||||
}
|
||||
*/
|
||||
|
||||
function tx_fetch_for_editing(tx) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (typeof tx == 'string' && /^[0-9a-f]{64}$/i.test(tx)) { //tx is txid
|
||||
@ -793,6 +1189,7 @@
|
||||
|
||||
|
||||
btcOperator.sendTx = function (senders, privkeys, receivers, amounts, fee = null, options = {}) {
|
||||
options.sendingTx = true;
|
||||
return new Promise((resolve, reject) => {
|
||||
createSignedTx(senders, privkeys, receivers, amounts, fee, options).then(result => {
|
||||
broadcastTx(result.transaction.serialize())
|
||||
@ -816,7 +1213,7 @@
|
||||
receivers,
|
||||
amounts,
|
||||
fee,
|
||||
change_address: options.change_address
|
||||
...options
|
||||
}));
|
||||
} catch (e) {
|
||||
return reject(e)
|
||||
@ -831,7 +1228,11 @@
|
||||
if (redeemScripts.includes(null)) //TODO: segwit
|
||||
return reject("Unable to get redeem-script");
|
||||
//create transaction
|
||||
createTransaction(senders, redeemScripts, receivers, amounts, fee, options.change_address || senders[0], options.fee_from_receiver).then(result => {
|
||||
createTransaction({
|
||||
senders, redeemScripts, receivers, amounts, fee,
|
||||
change_address: options.change_address || senders[0],
|
||||
...options
|
||||
}).then(result => {
|
||||
let tx = result.transaction;
|
||||
console.debug("Unsigned:", tx.serialize());
|
||||
new Set(wif_keys).forEach(key => tx.sign(key, 1 /*sighashtype*/)); //Sign the tx using private key WIF
|
||||
@ -864,7 +1265,11 @@
|
||||
if (redeemScripts.includes(null)) //TODO: segwit
|
||||
return reject("Unable to get redeem-script");
|
||||
//create transaction
|
||||
createTransaction(senders, redeemScripts, receivers, amounts, fee, options.change_address || senders[0], options.fee_from_receiver, options.allowUnconfirmedUtxos).then(result => {
|
||||
createTransaction({
|
||||
senders, redeemScripts, receivers, amounts, fee,
|
||||
change_address: options.change_address || senders[0],
|
||||
...options
|
||||
}).then(result => {
|
||||
result.tx_hex = result.transaction.serialize();
|
||||
delete result.transaction;
|
||||
resolve(result);
|
||||
@ -900,7 +1305,12 @@
|
||||
return reject(e)
|
||||
}
|
||||
//create transaction
|
||||
createTransaction([sender], [redeemScript], receivers, amounts, fee, options.change_address || sender, options.fee_from_receiver).then(result => {
|
||||
createTransaction({
|
||||
senders: [sender], redeemScripts: [redeemScript],
|
||||
receivers, amounts, fee,
|
||||
change_address: options.change_address || sender,
|
||||
...options
|
||||
}).then(result => {
|
||||
result.tx_hex = result.transaction.serialize();
|
||||
delete result.transaction;
|
||||
resolve(result);
|
||||
@ -975,7 +1385,7 @@
|
||||
}
|
||||
|
||||
const getTxOutput = (txid, i) => new Promise((resolve, reject) => {
|
||||
fetch_api(`rawtx/${txid}`)
|
||||
multiApi('tx', { txid })
|
||||
.then(result => resolve(result.out[i]))
|
||||
.catch(error => reject(error))
|
||||
});
|
||||
@ -1031,94 +1441,41 @@
|
||||
return Crypto.util.bytesToHex(txid);
|
||||
}
|
||||
|
||||
const getLatestBlock = btcOperator.getLatestBlock = () => new Promise((resolve, reject) => {
|
||||
fetch_api(`q/getblockcount`)
|
||||
.then(result => resolve(result))
|
||||
.catch(error => reject(error))
|
||||
})
|
||||
|
||||
const getTx = btcOperator.getTx = txid => new Promise((resolve, reject) => {
|
||||
fetch_api(`rawtx/${txid}`).then(result => {
|
||||
console.debug("Tx:", result);
|
||||
getLatestBlock().then(latest_block => resolve({
|
||||
const getTx = btcOperator.getTx = txid => new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
const result = await multiApi('tx', { txid });
|
||||
resolve({
|
||||
confirmations: result.confirmations,
|
||||
block: result.block_height,
|
||||
txid: result.hash,
|
||||
time: result.time * 1000,
|
||||
confirmations: result.block_height === null ? 0 : latest_block - result.block_height, //calculate confirmations using latest block number as api doesnt relay it
|
||||
time: result.time,
|
||||
size: result.size,
|
||||
fee: util.Sat_to_BTC(result.fee),
|
||||
inputs: result.inputs.map(i => Object({ address: i.prev_out.addr, value: util.Sat_to_BTC(i.prev_out.value) })),
|
||||
total_input_value: util.Sat_to_BTC(result.inputs.reduce((a, i) => a + i.prev_out.value, 0)),
|
||||
outputs: result.out.map(o => Object({ address: o.addr, value: util.Sat_to_BTC(o.value) })),
|
||||
total_output_value: util.Sat_to_BTC(result.out.reduce((a, o) => a += o.value, 0)),
|
||||
}))
|
||||
}).catch(error => reject(error))
|
||||
});
|
||||
|
||||
getTx.hex = btcOperator.getTx.hex = txid => new Promise((resolve, reject) => {
|
||||
fetch_api(`rawtx/${txid}?format=hex`, false)
|
||||
.then(result => resolve(result))
|
||||
.catch(error => reject(error))
|
||||
})
|
||||
} catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
|
||||
getTx.hex = btcOperator.getTx.hex = txid => multiApi('txHex', { txid });
|
||||
|
||||
btcOperator.getAddressData = address => new Promise((resolve, reject) => {
|
||||
fetch_api(`rawaddr/${address}`).then(data => {
|
||||
let details = {};
|
||||
details.balance = util.Sat_to_BTC(data.final_balance);
|
||||
details.address = data.address;
|
||||
details.txs = data.txs.map(tx => {
|
||||
let d = {
|
||||
txid: tx.hash,
|
||||
time: tx.time * 1000, //s to ms
|
||||
block: tx.block_height,
|
||||
}
|
||||
//sender list
|
||||
d.tx_senders = {};
|
||||
tx.inputs.forEach(i => {
|
||||
if (i.prev_out.addr in d.tx_senders)
|
||||
d.tx_senders[i.prev_out.addr] += i.prev_out.value;
|
||||
else d.tx_senders[i.prev_out.addr] = i.prev_out.value;
|
||||
});
|
||||
d.tx_input_value = 0;
|
||||
for (let s in d.tx_senders) {
|
||||
let val = d.tx_senders[s];
|
||||
d.tx_senders[s] = util.Sat_to_BTC(val);
|
||||
d.tx_input_value += val;
|
||||
}
|
||||
d.tx_input_value = util.Sat_to_BTC(d.tx_input_value);
|
||||
//receiver list
|
||||
d.tx_receivers = {};
|
||||
tx.out.forEach(o => {
|
||||
if (o.addr in d.tx_receivers)
|
||||
d.tx_receivers[o.addr] += o.value;
|
||||
else d.tx_receivers[o.addr] = o.value;
|
||||
});
|
||||
d.tx_output_value = 0;
|
||||
for (let r in d.tx_receivers) {
|
||||
let val = d.tx_receivers[r];
|
||||
d.tx_receivers[r] = util.Sat_to_BTC(val);
|
||||
d.tx_output_value += val;
|
||||
}
|
||||
d.tx_output_value = util.Sat_to_BTC(d.tx_output_value);
|
||||
d.tx_fee = util.Sat_to_BTC(tx.fee);
|
||||
//tx type
|
||||
if (tx.result > 0) { //net > 0, balance inc => type=in
|
||||
d.type = "in";
|
||||
d.amount = util.Sat_to_BTC(tx.result);
|
||||
d.sender = Object.keys(d.tx_senders).filter(s => s !== address);
|
||||
} else if (Object.keys(d.tx_receivers).some(r => r !== address)) { //net < 0, balance dec & receiver present => type=out
|
||||
d.type = "out";
|
||||
d.amount = util.Sat_to_BTC(tx.result * -1);
|
||||
d.receiver = Object.keys(d.tx_receivers).filter(r => r !== address);
|
||||
d.fee = d.tx_fee;
|
||||
} else { //net < 0 (fee) & no other id in receiver list => type=self
|
||||
d.type = "self";
|
||||
d.amount = d.tx_receivers[address];
|
||||
d.address = address
|
||||
}
|
||||
return d;
|
||||
})
|
||||
resolve(details);
|
||||
if (!validateAddress(address))
|
||||
return reject("Invalid address");
|
||||
Promise.all([
|
||||
multiApi('balance', { addr: address }),
|
||||
multiApi('txs', { addr: address })
|
||||
]).then(([balance, txs]) => {
|
||||
const parsedTxs = txs.map(tx => parseTx(tx, address));
|
||||
resolve({
|
||||
address,
|
||||
balance,
|
||||
txs: parsedTxs
|
||||
});
|
||||
}).catch(error => reject(error))
|
||||
});
|
||||
|
||||
|
||||
2
scripts/btcOperator.min.js
vendored
2
scripts/btcOperator.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
scripts/components.min.js
vendored
2
scripts/components.min.js
vendored
File diff suppressed because one or more lines are too long
@ -2,45 +2,45 @@
|
||||
/* FLO Ethereum Operators */
|
||||
/* Make sure you added Taproot, Keccak, FLO and BTC Libraries before */
|
||||
'use strict';
|
||||
const floEthereum = EXPORTS;
|
||||
const floEthereum = EXPORTS;
|
||||
|
||||
const ethPrivateKeyFromWif = floEthereum.ethPrivateKeyFromWif = function(privateKey,){
|
||||
return coinjs.wif2privkey(privateKey).privkey;
|
||||
}
|
||||
|
||||
const ethAddressFromPrivateKey = floEthereum.ethAddressFromPrivateKey = function(privateKey, onlyEvenY = false){
|
||||
var t1,t1_x,t1_y,t1_y_BigInt,t2,t3,t4;
|
||||
var groupOrder = BigInt("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F");
|
||||
const ethPrivateKeyFromWif = floEthereum.ethPrivateKeyFromWif = function (privateKey,) {
|
||||
return coinjs.wif2privkey(privateKey).privkey;
|
||||
}
|
||||
|
||||
t1 = bitjs.newPubkey(privateKey);
|
||||
t1_x = t1.slice(2, 66); t1_y = t1.slice(-64);
|
||||
if (onlyEvenY) {
|
||||
t1_y_BigInt = BigInt("0x"+t1_y);
|
||||
if (t1_y_BigInt % 2n !== 0n) { t1_y_BigInt = (groupOrder-t1_y_BigInt)%groupOrder; t1_y=t1_y_BigInt.toString(16)}
|
||||
};
|
||||
|
||||
t2 = t1_x.toString(16) + t1_y.toString(16);
|
||||
t3 = keccak.keccak_256(Crypto.util.hexToBytes(t2));
|
||||
t4 = keccak.extractLast20Bytes(t3);
|
||||
return "0x" + t4;
|
||||
}
|
||||
const ethAddressFromPrivateKey = floEthereum.ethAddressFromPrivateKey = function (privateKey, onlyEvenY = false) {
|
||||
var t1, t1_x, t1_y, t1_y_BigInt, t2, t3, t4;
|
||||
var groupOrder = BigInt("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F");
|
||||
|
||||
const ethAddressFromCompressedPublicKey = floEthereum.ethAddressFromCompressedPublicKey = function(compressedPublicKey){
|
||||
var t1,t2,t3,t4;
|
||||
t1 = coinjs.compressedToUncompressed(compressedPublicKey);
|
||||
t2 = t1.slice(2);
|
||||
t3 = keccak.keccak_256(Crypto.util.hexToBytes(t2));
|
||||
t4 = keccak.extractLast20Bytes(t3);
|
||||
return "0x" + t4;
|
||||
}
|
||||
t1 = bitjs.newPubkey(privateKey);
|
||||
t1_x = t1.slice(2, 66); t1_y = t1.slice(-64);
|
||||
if (onlyEvenY) {
|
||||
t1_y_BigInt = BigInt("0x" + t1_y);
|
||||
if (t1_y_BigInt % 2n !== 0n) { t1_y_BigInt = (groupOrder - t1_y_BigInt) % groupOrder; t1_y = t1_y_BigInt.toString(16) }
|
||||
};
|
||||
|
||||
const ethAddressFromUncompressedPublicKey = floEthereum.ethAddressFromUncompressedPublicKey = function(unCompressedPublicKey){
|
||||
var t1,t2,t3,t4;
|
||||
t1 = unCompressedPublicKey;
|
||||
t2 = t1.slice(2);
|
||||
t3 = keccak.keccak_256(Crypto.util.hexToBytes(t2));
|
||||
t4 = keccak.extractLast20Bytes(t3);
|
||||
return "0x" + t4;
|
||||
}
|
||||
t2 = t1_x.toString(16) + t1_y.toString(16);
|
||||
t3 = keccak.keccak_256(Crypto.util.hexToBytes(t2));
|
||||
t4 = keccak.extractLast20Bytes(t3);
|
||||
return "0x" + t4;
|
||||
}
|
||||
|
||||
const ethAddressFromCompressedPublicKey = floEthereum.ethAddressFromCompressedPublicKey = function (compressedPublicKey) {
|
||||
var t1, t2, t3, t4;
|
||||
t1 = coinjs.compressedToUncompressed(compressedPublicKey);
|
||||
t2 = t1.slice(2);
|
||||
t3 = keccak.keccak_256(Crypto.util.hexToBytes(t2));
|
||||
t4 = keccak.extractLast20Bytes(t3);
|
||||
return "0x" + t4;
|
||||
}
|
||||
|
||||
const ethAddressFromUncompressedPublicKey = floEthereum.ethAddressFromUncompressedPublicKey = function (unCompressedPublicKey) {
|
||||
var t1, t2, t3, t4;
|
||||
t1 = unCompressedPublicKey;
|
||||
t2 = t1.slice(2);
|
||||
t3 = keccak.keccak_256(Crypto.util.hexToBytes(t2));
|
||||
t4 = keccak.extractLast20Bytes(t3);
|
||||
return "0x" + t4;
|
||||
}
|
||||
|
||||
})('object' === typeof module ? module.exports : window.floEthereum = {});
|
||||
|
||||
1
scripts/floEthereum.min.js
vendored
Normal file
1
scripts/floEthereum.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
!function(EXPORTS){"use strict";const floEthereum="object"===typeof module?module.exports:window.floEthereum={};floEthereum.ethPrivateKeyFromWif=function(privateKey){return coinjs.wif2privkey(privateKey).privkey},floEthereum.ethAddressFromPrivateKey=function(privateKey,onlyEvenY=!1){var t1,t1_x,t1_y,t1_y_BigInt,t2,t3,groupOrder=BigInt("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F");return t1_x=(t1=bitjs.newPubkey(privateKey)).slice(2,66),t1_y=t1.slice(-64),onlyEvenY&&(t1_y_BigInt=BigInt("0x"+t1_y))%2n!==0n&&(t1_y=(t1_y_BigInt=(groupOrder-t1_y_BigInt)%groupOrder).toString(16)),t2=t1_x.toString(16)+t1_y.toString(16),t3=keccak.keccak_256(Crypto.util.hexToBytes(t2)),"0x"+keccak.extractLast20Bytes(t3)},floEthereum.ethAddressFromCompressedPublicKey=function(compressedPublicKey){var t2,t3;return t2=coinjs.compressedToUncompressed(compressedPublicKey).slice(2),t3=keccak.keccak_256(Crypto.util.hexToBytes(t2)),"0x"+keccak.extractLast20Bytes(t3)},floEthereum.ethAddressFromUncompressedPublicKey=function(unCompressedPublicKey){var t2,t3;return t2=unCompressedPublicKey.slice(2),t3=keccak.keccak_256(Crypto.util.hexToBytes(t2)),"0x"+keccak.extractLast20Bytes(t3)}}();
|
||||
Loading…
Reference in New Issue
Block a user