Compare commits
10 Commits
aa0f266dbb
...
a5934d830f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a5934d830f | ||
|
|
fc5f8b250f | ||
| 66c96f06ea | |||
|
|
f39de21198 | ||
|
|
e3efa64271 | ||
| 31b0e5c81a | |||
| 637c14952f | |||
| a0df5f1986 | |||
| a7504d6248 | |||
| 7b2dae1b07 |
2
.github/workflows/repopush.yml
vendored
2
.github/workflows/repopush.yml
vendored
@ -29,4 +29,4 @@ jobs:
|
||||
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"
|
||||
cd ${{ secrets.DEPLOYMENT_LOCATION}}/dappbundle/ && git add . && git commit -m "Workflow updating files of ${{ github.event.repository.name }}" && git push "https://saketongit:${{ secrets.RM_ACCESS_TOKEN }}@github.com/ranchimall/dappbundle.git"
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
# FLO Wallet
|
||||
FLO Web Wallet
|
||||
RanchiMall FLO Web Wallet
|
||||
|
||||
These are client-side scripts that can generate FLO addresses, send FLO transactions to the blockchain and monitor FLO data from the blockchain
|
||||
|
||||
|
||||
112
index.html
112
index.html
@ -2339,6 +2339,7 @@
|
||||
resetBalance();
|
||||
}
|
||||
}
|
||||
|
||||
function openAddressSelector(e) {
|
||||
floGlobals.addressSelectorTarget = e.target.closest('sm-input')
|
||||
openPopup('saved_ids_popup')
|
||||
@ -2368,7 +2369,7 @@
|
||||
const tokenName = selectedToken.value
|
||||
const tokenBalance = parseFloat(selectedToken.dataset.balance)
|
||||
getRef('flo_data_wrapper').classList.add('hidden')
|
||||
getRef('tx_flo_amount').value = '0.001'
|
||||
getRef('tx_flo_amount').value = '0.00001'
|
||||
getRef('tx_flo_amount').classList.add('hidden')
|
||||
getRef('tx_receiver_wrapper').innerHTML = ''
|
||||
addTokenReceiver()
|
||||
@ -2499,43 +2500,80 @@
|
||||
}
|
||||
|
||||
async function checkBalance(address) {
|
||||
try {
|
||||
getRef('address_details_wrapper').classList.remove('hidden')
|
||||
floWebWallet.getLabels().then(allLabels => {
|
||||
if (allLabels[queriedFloId]) {
|
||||
getRef('queried_flo_address').innerHTML = `<h4>${allLabels[queriedFloId]}</h4> <sm-copy clip-text value=${queriedFloId}></sm-copy>`;
|
||||
} else {
|
||||
getRef('queried_flo_address').innerHTML = `
|
||||
<p class="label">FLO Address </p>
|
||||
<h4><sm-copy clip-text value=${queriedFloId}></sm-copy></h4>
|
||||
`;
|
||||
}
|
||||
})
|
||||
const queriedFloId = address || getRef('search_query_input').value.trim()
|
||||
getRef('token_list_wrapper').classList.add('hidden')
|
||||
getRef('flo_balance').innerHTML = `<sm-spinner></sm-spinner>`;
|
||||
const [floBalance, tokenBalances] = await Promise.all([
|
||||
floWebWallet.getBalance(queriedFloId),
|
||||
fetchJson(`${floGlobals.tokenURL}api/v2/floAddressBalance/${queriedFloId}`).then(({ floAddressBalances }) => floAddressBalances)
|
||||
])
|
||||
let ownedTokens = []
|
||||
for (const token in tokenBalances) {
|
||||
ownedTokens.push(html`
|
||||
<li class="token-item">
|
||||
<span>${token}: </span><span>${parseFloat((tokenBalances[token].balance || 0).toFixed(8))}</span>
|
||||
</li>
|
||||
`)
|
||||
}
|
||||
if (ownedTokens.length) {
|
||||
renderElem(getRef('token_list'), html`${ownedTokens}`)
|
||||
getRef('token_list_wrapper').classList.remove('hidden')
|
||||
}
|
||||
// retrieve FLO balance
|
||||
getRef('flo_balance').textContent = `${parseFloat(floBalance.toFixed(8))} FLO`;
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
try {
|
||||
const queriedFloId = address || getRef('search_query_input').value.trim(); // define first
|
||||
|
||||
getRef('address_details_wrapper').classList.remove('hidden');
|
||||
getRef('token_list_wrapper').classList.add('hidden');
|
||||
getRef('token_list').innerHTML = '';
|
||||
getRef('flo_balance').innerHTML = `<sm-spinner></sm-spinner>`;
|
||||
|
||||
// Show label (if any)
|
||||
floWebWallet.getLabels().then(allLabels => {
|
||||
if (allLabels?.[queriedFloId]) {
|
||||
getRef('queried_flo_address').innerHTML =
|
||||
`<h4>${allLabels[queriedFloId]}</h4> <sm-copy clip-text value=${queriedFloId}></sm-copy>`;
|
||||
} else {
|
||||
getRef('queried_flo_address').innerHTML = `
|
||||
<p class="label">FLO Address</p>
|
||||
<h4><sm-copy clip-text value=${queriedFloId}></sm-copy></h4>
|
||||
`;
|
||||
}
|
||||
}).catch(() => {
|
||||
// non-fatal
|
||||
getRef('queried_flo_address').innerHTML = `
|
||||
<p class="label">FLO Address</p>
|
||||
<h4><sm-copy clip-text value=${queriedFloId}></sm-copy></h4>
|
||||
`;
|
||||
});
|
||||
|
||||
// Fetch both, but don't let one failure block the other
|
||||
const [floBalRes, tokenRes] = await Promise.allSettled([
|
||||
floWebWallet.getBalance(queriedFloId),
|
||||
fetchJson(`${floGlobals.tokenURL}api/v2/floAddressBalance/${queriedFloId}`)
|
||||
]);
|
||||
|
||||
// --- FLO coin ---
|
||||
if (floBalRes.status === 'fulfilled') {
|
||||
const floBal = Number(floBalRes.value || 0);
|
||||
getRef('flo_balance').textContent = `${floBal.toFixed(8)} FLO`;
|
||||
} else {
|
||||
// show fallback, but don't throw
|
||||
getRef('flo_balance').textContent = `—`;
|
||||
}
|
||||
|
||||
// --- FLO-based tokens ---
|
||||
if (tokenRes.status === 'fulfilled') {
|
||||
const tokenBalances = tokenRes.value?.floAddressBalances || {};
|
||||
const ownedTokens = [];
|
||||
|
||||
for (const token in tokenBalances) {
|
||||
const bal = Number(tokenBalances[token]?.balance || 0);
|
||||
// Only list tokens you want (optionally filter > 0)
|
||||
ownedTokens.push(html`
|
||||
<li class="token-item">
|
||||
<span>${token}: </span><span>${bal.toFixed(8)}</span>
|
||||
</li>
|
||||
`);
|
||||
}
|
||||
|
||||
if (ownedTokens.length) {
|
||||
renderElem(getRef('token_list'), html`${ownedTokens}`);
|
||||
getRef('token_list_wrapper').classList.remove('hidden');
|
||||
} else {
|
||||
getRef('token_list_wrapper').classList.add('hidden');
|
||||
}
|
||||
} else {
|
||||
// token call failed — keep tokens hidden
|
||||
getRef('token_list_wrapper').classList.add('hidden');
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
// optional: show an error toast / keep current UI as-is
|
||||
}
|
||||
}
|
||||
|
||||
function categorizeText(text) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (text.length == 34 && floCrypto.validateFloID(text)) {
|
||||
@ -2802,7 +2840,7 @@
|
||||
|
||||
function hasUnconfirmedTransactions(address) {
|
||||
return new Promise((resolve, reject) => {
|
||||
fetchJson(`https://blockbook.flocard.app/api/v2/address/${address}?details=basic`)
|
||||
fetchJson(`https://blockbook.ranchimall.net/api/v2/address/${address}?details=basic`)
|
||||
.then(details => {
|
||||
resolve(details.unconfirmedTxs > 0)
|
||||
if (details.unconfirmedTxs > 0) {
|
||||
|
||||
3153
index.min.html
3153
index.min.html
File diff suppressed because one or more lines are too long
@ -49,63 +49,80 @@
|
||||
}
|
||||
|
||||
function formatTx(address, tx) {
|
||||
let result = {
|
||||
time: tx.time,
|
||||
block: tx.blockheight,
|
||||
blockhash: tx.blockhash,
|
||||
txid: tx.txid,
|
||||
floData: tx.floData,
|
||||
confirmations: tx.confirmations
|
||||
}
|
||||
const result = {
|
||||
time: tx.time,
|
||||
block: tx.blockheight,
|
||||
blockhash: tx.blockhash,
|
||||
txid: tx.txid,
|
||||
floData: tx.floData,
|
||||
confirmations: tx.confirmations
|
||||
};
|
||||
|
||||
//format receivers
|
||||
let receivers = {};
|
||||
for (let vout of tx.vout) {
|
||||
if (vout.scriptPubKey.isAddress) {
|
||||
let id = vout.scriptPubKey.addresses[0];
|
||||
if (id in receivers)
|
||||
receivers[id] += vout.value;
|
||||
else receivers[id] = vout.value;
|
||||
}
|
||||
// ---- Receivers (outputs) ----
|
||||
const receivers = {};
|
||||
for (const vout of tx.vout || []) {
|
||||
const outAddrs =
|
||||
(vout.addresses && vout.addresses.length ? vout.addresses :
|
||||
vout.scriptPubKey && vout.scriptPubKey.addresses ? vout.scriptPubKey.addresses : null);
|
||||
if (outAddrs && outAddrs.length) {
|
||||
const id = outAddrs[0];
|
||||
receivers[id] = (receivers[id] || 0) + Number(vout.value || 0);
|
||||
}
|
||||
result.receivers = receivers;
|
||||
//format senders (or mined)
|
||||
if (!tx.vin[0].isAddress) { //mined (ie, coinbase)
|
||||
let coinbase = tx.vin[0].coinbase;
|
||||
result.mine = coinbase;
|
||||
result.mined = { [coinbase]: tx.valueOut }
|
||||
} else {
|
||||
result.sender = tx.vin[0].addresses[0];
|
||||
result.receiver = tx.vout[0].scriptPubKey.addresses[0];
|
||||
result.fees = tx.fees;
|
||||
let senders = {};
|
||||
for (let vin of tx.vin) {
|
||||
if (vin.isAddress) {
|
||||
let id = vin.addresses[0];
|
||||
if (id in senders)
|
||||
senders[id] += vin.value;
|
||||
else senders[id] = vin.value;
|
||||
}
|
||||
}
|
||||
result.senders = senders;
|
||||
}
|
||||
result.receivers = receivers;
|
||||
|
||||
//remove change amounts
|
||||
for (let id in senders) {
|
||||
if (id in receivers) {
|
||||
if (senders[id] > receivers[id]) {
|
||||
senders[id] -= receivers[id];
|
||||
delete receivers[id];
|
||||
} else if (senders[id] < receivers[id]) { //&& id != address
|
||||
receivers[id] -= senders[id];
|
||||
delete senders[id];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// ---- Coinbase vs normal ----
|
||||
const isCoinbase = !!(tx.vin && tx.vin[0] && tx.vin[0].coinbase);
|
||||
|
||||
if (isCoinbase) {
|
||||
const coinbase = tx.vin[0].coinbase; // string
|
||||
result.mine = coinbase;
|
||||
result.mined = { [coinbase]: Number(tx.valueOut || 0) };
|
||||
return result;
|
||||
}
|
||||
|
||||
// ---- Normal tx: senders, fees, first-party heuristics ----
|
||||
result.fees = tx.fees;
|
||||
|
||||
const firstInAddr = (tx.vin && tx.vin[0] && tx.vin[0].addresses && tx.vin[0].addresses[0]) || undefined;
|
||||
const firstOutAddrs =
|
||||
(tx.vout && tx.vout[0] &&
|
||||
((tx.vout[0].addresses && tx.vout[0].addresses[0]) ||
|
||||
(tx.vout[0].scriptPubKey && tx.vout[0].scriptPubKey.addresses && tx.vout[0].scriptPubKey.addresses[0]))) || undefined;
|
||||
if (firstInAddr) result.sender = firstInAddr;
|
||||
if (firstOutAddrs) result.receiver = firstOutAddrs;
|
||||
|
||||
const senders = {};
|
||||
for (const vin of tx.vin || []) {
|
||||
const inAddrs = vin.addresses;
|
||||
if (inAddrs && inAddrs.length) {
|
||||
const id = inAddrs[0];
|
||||
senders[id] = (senders[id] || 0) + Number(vin.value || 0);
|
||||
}
|
||||
}
|
||||
result.senders = senders;
|
||||
|
||||
// ---- Remove change (net flow) ----
|
||||
for (const id of Object.keys(senders)) {
|
||||
if (receivers[id] != null) {
|
||||
if (senders[id] > receivers[id]) {
|
||||
senders[id] -= receivers[id];
|
||||
delete receivers[id];
|
||||
} else if (senders[id] < receivers[id]) {
|
||||
receivers[id] -= senders[id];
|
||||
delete senders[id];
|
||||
} else {
|
||||
// equal -> cancel both
|
||||
delete senders[id];
|
||||
delete receivers[id];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
floWebWallet.listTransactions = function (address, page_options = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let options = {};
|
||||
@ -141,4 +158,4 @@
|
||||
})
|
||||
}
|
||||
|
||||
})(window.floWebWallet = {});
|
||||
})(window.floWebWallet = {});
|
||||
|
||||
@ -9,9 +9,9 @@
|
||||
FLO: ['https://blockbook.ranchimall.net/'],
|
||||
FLO_TEST: ['https://blockbook-testnet.ranchimall.net/']
|
||||
},
|
||||
sendAmt: 0.0003,
|
||||
fee: 0.0002,
|
||||
minChangeAmt: 0.0002,
|
||||
sendAmt: 0.00001,
|
||||
fee: 0.00001,
|
||||
minChangeAmt: 0.00001,
|
||||
receiverID: floGlobals.adminID
|
||||
};
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user