Compare commits
10 Commits
11f16d5ce9
...
84bb370415
| Author | SHA1 | Date | |
|---|---|---|---|
| 84bb370415 | |||
| 35dc5770b8 | |||
| d89a151ffe | |||
| 05c0dad235 | |||
| 7bf5065742 | |||
| 1fc5b1cb33 | |||
| c1c7f30c3b | |||
| 386dfdc848 | |||
| 399acbeccf | |||
| 354e5bbb6e |
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://saketongit:${{ secrets.RM_ACCESS_TOKEN }}@github.com/ranchimall/dappbundle.git"
|
||||
60
index.html
60
index.html
@ -417,9 +417,14 @@
|
||||
<div class="balance-display">
|
||||
<div class="balance-amount" id="send-balance">0 <span class="currency">XLM</span></div>
|
||||
</div>
|
||||
<div class="address-display">
|
||||
<span class="address-label">Address:</span>
|
||||
<span class="address-value" id="send-from-address">-</span>
|
||||
<div class="detail-row">
|
||||
<label>From Address</label>
|
||||
<div class="detail-value-wrapper">
|
||||
<code id="send-from-address" class="detail-value">-</code>
|
||||
<button class="input-action-btn clear-btn" onclick="copyToClipboard('send-from-address')" title="Copy address">
|
||||
<i class="fa-regular fa-copy"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1016,7 +1021,7 @@
|
||||
balance = accountInfo.balanceXlm;
|
||||
|
||||
const balanceEl = document.getElementById('xlm-balance');
|
||||
balanceEl.innerHTML = balance.toFixed(6) + ' <span class="currency">xlm</span>';
|
||||
balanceEl.innerHTML = balance.toFixed(6) + ' <span class="currency">XLM</span>';
|
||||
|
||||
txNextToken = null;
|
||||
await loadTransactions(true);
|
||||
@ -1026,7 +1031,7 @@
|
||||
|
||||
// Show 0 balance for inactive accounts
|
||||
const balanceEl = document.getElementById('xlm-balance');
|
||||
balanceEl.innerHTML = '0 <span class="currency">xlm</span>';
|
||||
balanceEl.innerHTML = '0 <span class="currency">XLM</span>';
|
||||
document.getElementById('tx-history').innerHTML = '<div class="tx-empty">Account is inactive or not funded yet</div>';
|
||||
}
|
||||
|
||||
@ -1155,7 +1160,7 @@
|
||||
|
||||
const balanceEl = document.getElementById('send-balance');
|
||||
balanceEl.innerHTML = accountInfo.balanceXlm.toFixed(7) + ' <span class="currency">XLM</span>';
|
||||
document.getElementById('send-from-address').textContent = address.substring(0, 12) + '...' + address.substring(address.length - 12);
|
||||
document.getElementById('send-from-address').textContent = address;
|
||||
document.getElementById('send-wallet-info').style.display = 'block';
|
||||
} catch (error) {
|
||||
document.getElementById('send-wallet-info').style.display = 'none';
|
||||
@ -1203,19 +1208,39 @@
|
||||
hasMoreTransactions = true;
|
||||
}
|
||||
|
||||
// Load only one batch of transactions
|
||||
// Load transactions - fetch one extra to check if there's more
|
||||
if (hasMoreTransactions) {
|
||||
// Fetch ITEMS_PER_PAGE + 1 to check if there's a next page
|
||||
const result = await xlmAPI.getTransactions(currentxlmAddress, {
|
||||
limit: 10,
|
||||
limit: ITEMS_PER_PAGE + 1,
|
||||
next: txNextToken
|
||||
});
|
||||
|
||||
allTransactions = [...allTransactions, ...result.transactions];
|
||||
txNextToken = result.nextToken;
|
||||
hasMoreTransactions = result.hasMore;
|
||||
// Get all fetched transactions
|
||||
const fetchedTransactions = result.transactions || [];
|
||||
|
||||
// Add all fetched transactions to our array
|
||||
allTransactions = [...allTransactions, ...fetchedTransactions];
|
||||
|
||||
// Determine if there are more transactions:
|
||||
// If we got exactly limit+1 transactions, there might be more
|
||||
// Also check the API's hasMore flag
|
||||
if (fetchedTransactions.length === ITEMS_PER_PAGE + 1 && result.hasMore) {
|
||||
hasMoreTransactions = true;
|
||||
txNextToken = result.nextToken;
|
||||
} else {
|
||||
|
||||
hasMoreTransactions = false;
|
||||
txNextToken = null;
|
||||
}
|
||||
}
|
||||
|
||||
totalPages = Math.ceil(allTransactions.length / ITEMS_PER_PAGE);
|
||||
// If we have no transactions at all
|
||||
if (allTransactions.length === 0) {
|
||||
hasMoreTransactions = false;
|
||||
}
|
||||
|
||||
totalPages = Math.ceil(allTransactions.length / ITEMS_PER_PAGE) || 1;
|
||||
if (hasMoreTransactions) {
|
||||
totalPages++; // Add one more page to show "next" is available
|
||||
}
|
||||
@ -1267,8 +1292,9 @@
|
||||
|
||||
// Update button states
|
||||
document.getElementById('prev-page-btn').disabled = currentPage <= 1;
|
||||
// Enable next button if we're not on last page OR if there are more transactions to load
|
||||
document.getElementById('next-page-btn').disabled = currentPage >= totalPages && !hasMoreTransactions;
|
||||
// Disable next button if we're on the last page AND there are no more transactions to load
|
||||
const isLastPage = currentPage >= totalPages;
|
||||
document.getElementById('next-page-btn').disabled = isLastPage && !hasMoreTransactions;
|
||||
|
||||
paginationEl.style.display = filteredTransactions.length > 0 ? 'flex' : 'none';
|
||||
}
|
||||
@ -1592,15 +1618,15 @@
|
||||
</div>
|
||||
<div class="tx-detail-row">
|
||||
<span class="detail-label">Transaction Fee</span>
|
||||
<span class="detail-value fee">${feeXLM.toFixed(6)} XLM</span>
|
||||
<span class="detail-value fee">${feeXlm.toFixed(6)} XLM</span>
|
||||
</div>
|
||||
<div class="tx-detail-row highlight" style="color: var(--error-color);">
|
||||
<span class="detail-label">Total Required</span>
|
||||
<span class="detail-value">${totalXLM.toFixed(6)} XLM</span>
|
||||
<span class="detail-value">${totalXlm.toFixed(6)} XLM</span>
|
||||
</div>
|
||||
<div class="tx-detail-row" style="color: var(--error-color); font-weight: 600;">
|
||||
<span class="detail-label">Shortfall</span>
|
||||
<span class="detail-value">${(totalXLM - currentBalance).toFixed(6)} XLM</span>
|
||||
<span class="detail-value">${(totalXlm - currentBalance).toFixed(6)} XLM</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -247,7 +247,9 @@
|
||||
|
||||
// Get fee stats
|
||||
const feeStats = await server.feeStats();
|
||||
const fee = feeStats.max_fee.mode || (StellarSdk.BASE_FEE || '100');
|
||||
|
||||
// fee_charged.mode is typically 100 stroops (0.00001 XLM)
|
||||
const fee = feeStats.fee_charged?.mode || feeStats.last_ledger_base_fee || '100';
|
||||
|
||||
// Build transaction
|
||||
let transaction = new StellarSdk.TransactionBuilder(sourceAccount, {
|
||||
@ -311,12 +313,23 @@
|
||||
}
|
||||
|
||||
try {
|
||||
// Parse the XDR back to a transaction
|
||||
const transaction = new StellarSdk.Transaction(transactionXDR, StellarSdk.Networks.PUBLIC);
|
||||
// Parse the XDR back to a transaction using TransactionBuilder
|
||||
const transaction = StellarSdk.TransactionBuilder.fromXDR(transactionXDR, StellarSdk.Networks.PUBLIC);
|
||||
|
||||
console.log('Submitting transaction to Stellar network...');
|
||||
|
||||
// Submit to network
|
||||
const result = await server.submitTransaction(transaction);
|
||||
|
||||
console.log('✅ Transaction submitted successfully!');
|
||||
console.log('Transaction Details:', {
|
||||
hash: result.hash,
|
||||
ledger: result.ledger,
|
||||
successful: result.successful,
|
||||
envelope_xdr: result.envelope_xdr,
|
||||
result_xdr: result.result_xdr
|
||||
});
|
||||
|
||||
return {
|
||||
hash: result.hash,
|
||||
ledger: result.ledger,
|
||||
@ -324,7 +337,7 @@
|
||||
txId: result.hash
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error submitting transaction:', error);
|
||||
console.error('❌ Error submitting transaction:', error);
|
||||
|
||||
// Parse Stellar error
|
||||
if (error.response && error.response.data) {
|
||||
|
||||
108
stellarCrypto.js
108
stellarCrypto.js
@ -28,6 +28,22 @@
|
||||
};
|
||||
}
|
||||
|
||||
// Calculate CRC16-XModem checksum (shared function)
|
||||
function crc16XModem(data) {
|
||||
let crc = 0x0000;
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
crc ^= data[i] << 8;
|
||||
for (let j = 0; j < 8; j++) {
|
||||
if (crc & 0x8000) {
|
||||
crc = (crc << 1) ^ 0x1021;
|
||||
} else {
|
||||
crc = crc << 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return crc & 0xFFFF;
|
||||
}
|
||||
|
||||
// --- Multi-chain Generator (BTC, FLO, XLM) ---
|
||||
stellarCrypto.generateMultiChain = async function (inputWif) {
|
||||
const versions = {
|
||||
@ -74,19 +90,35 @@
|
||||
}
|
||||
|
||||
const decodedBytes = new Uint8Array(decoded);
|
||||
// Extract seed (skip version byte 0x90, take 32 bytes, ignore checksum)
|
||||
|
||||
// Validate checksum
|
||||
if (decodedBytes.length < 35) {
|
||||
throw new Error('Invalid Stellar secret key: too short');
|
||||
}
|
||||
|
||||
// Extract components: [version(1)] + [seed(32)] + [checksum(2)]
|
||||
const payload = decodedBytes.slice(0, 33); // version + seed
|
||||
const providedChecksum = (decodedBytes[34] << 8) | decodedBytes[33]; // little-endian
|
||||
|
||||
// Calculate expected checksum
|
||||
const expectedChecksum = crc16XModem(payload);
|
||||
|
||||
// Verify checksum matches
|
||||
if (providedChecksum !== expectedChecksum) {
|
||||
throw new Error(`Invalid Stellar secret key: checksum mismatch (expected ${expectedChecksum.toString(16)}, got ${providedChecksum.toString(16)})`);
|
||||
}
|
||||
|
||||
// Verify version byte
|
||||
if (decodedBytes[0] !== 0x90) {
|
||||
throw new Error(`Invalid Stellar secret key: wrong version byte (expected 0x90, got 0x${decodedBytes[0].toString(16)})`);
|
||||
}
|
||||
|
||||
// Extract seed (skip version byte, take 32 bytes)
|
||||
const seed = decodedBytes.slice(1, 33);
|
||||
privKeyHex = bytesToHex(seed);
|
||||
} catch (e) {
|
||||
console.warn("Invalid Stellar secret key:", e);
|
||||
// Fall through to generate new key
|
||||
const newKey = generateNewID();
|
||||
const decode = Bitcoin.Base58.decode(newKey.privKey);
|
||||
const keyWithVersion = decode.slice(0, decode.length - 4);
|
||||
let key = keyWithVersion.slice(1);
|
||||
if (key.length >= 33 && key[key.length - 1] === 0x01)
|
||||
key = key.slice(0, key.length - 1);
|
||||
privKeyHex = bytesToHex(key);
|
||||
console.error("Invalid Stellar secret key:", e.message);
|
||||
throw new Error(`Failed to recover Stellar secret key: ${e.message}`);
|
||||
}
|
||||
} else if (hexOnly && (trimmedInput.length === 64 || trimmedInput.length === 128)) {
|
||||
privKeyHex =
|
||||
@ -94,6 +126,36 @@
|
||||
} else {
|
||||
try {
|
||||
const decode = Bitcoin.Base58.decode(trimmedInput);
|
||||
|
||||
// Validate WIF checksum
|
||||
if (decode.length < 37) {
|
||||
throw new Error('Invalid WIF key: too short');
|
||||
}
|
||||
|
||||
// WIF format: [version(1)] + [private_key(32)] + [compression_flag(0-1)] + [checksum(4)]
|
||||
const payload = decode.slice(0, decode.length - 4);
|
||||
const providedChecksum = decode.slice(decode.length - 4);
|
||||
|
||||
// Calculate expected checksum using double SHA256
|
||||
const hash1 = Crypto.SHA256(payload, { asBytes: true });
|
||||
const hash2 = Crypto.SHA256(hash1, { asBytes: true });
|
||||
const expectedChecksum = hash2.slice(0, 4);
|
||||
|
||||
// Verify checksum matches
|
||||
let checksumMatch = true;
|
||||
for (let i = 0; i < 4; i++) {
|
||||
if (providedChecksum[i] !== expectedChecksum[i]) {
|
||||
checksumMatch = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!checksumMatch) {
|
||||
const providedHex = providedChecksum.map(b => b.toString(16).padStart(2, '0')).join('');
|
||||
const expectedHex = expectedChecksum.map(b => b.toString(16).padStart(2, '0')).join('');
|
||||
throw new Error(`Invalid WIF key: checksum mismatch (expected ${expectedHex}, got ${providedHex})`);
|
||||
}
|
||||
|
||||
const keyWithVersion = decode.slice(0, decode.length - 4);
|
||||
let key = keyWithVersion.slice(1);
|
||||
if (key.length >= 33 && key[key.length - 1] === 0x01) {
|
||||
@ -102,14 +164,8 @@
|
||||
}
|
||||
privKeyHex = bytesToHex(key);
|
||||
} catch (e) {
|
||||
console.warn("Invalid WIF, generating new key:", e);
|
||||
const newKey = generateNewID();
|
||||
const decode = Bitcoin.Base58.decode(newKey.privKey);
|
||||
const keyWithVersion = decode.slice(0, decode.length - 4);
|
||||
let key = keyWithVersion.slice(1);
|
||||
if (key.length >= 33 && key[key.length - 1] === 0x01)
|
||||
key = key.slice(0, key.length - 1);
|
||||
privKeyHex = bytesToHex(key);
|
||||
console.error("Invalid WIF key:", e.message);
|
||||
throw new Error(`Failed to recover from WIF key: ${e.message}`);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -153,22 +209,6 @@
|
||||
const versionByte = 0x30; // Results in 'G' prefix for public keys
|
||||
const payload = new Uint8Array([versionByte, ...pubKey]);
|
||||
|
||||
// Calculate CRC16-XModem checksum
|
||||
function crc16XModem(data) {
|
||||
let crc = 0x0000;
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
crc ^= data[i] << 8;
|
||||
for (let j = 0; j < 8; j++) {
|
||||
if (crc & 0x8000) {
|
||||
crc = (crc << 1) ^ 0x1021;
|
||||
} else {
|
||||
crc = crc << 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return crc & 0xFFFF;
|
||||
}
|
||||
|
||||
const checksum = crc16XModem(payload);
|
||||
// Checksum is stored in little-endian format
|
||||
const checksumBytes = new Uint8Array([checksum & 0xFF, (checksum >> 8) & 0xFF]);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user