From 822f026c65ff16814890eea44fe1fb1e4af53a2e Mon Sep 17 00:00:00 2001 From: void-57 Date: Tue, 6 Jan 2026 21:29:29 +0530 Subject: [PATCH] feat: Implement multi-chain address recovery from private key and enhance transaction history retrieval with pagination and improved error handling --- README.md | 3 + css/main.css | 458 ++++++++++++++++-- css/main.min.css | 308 ------------ index.html | 1025 ++++++++++++++++++++-------------------- scripts/bscOperator.js | 583 ++++++++++++----------- 5 files changed, 1257 insertions(+), 1120 deletions(-) diff --git a/README.md b/README.md index 5291adc..f6f436e 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,5 @@ # bscwallet Binance Chain Wallet linked with FLO blockchain address +# RanchiMall-bsc-wallet +# RanchiMall-bsc-wallet +# RanchiMall-bsc-wallet diff --git a/css/main.css b/css/main.css index f3293e1..7424383 100644 --- a/css/main.css +++ b/css/main.css @@ -28,7 +28,7 @@ body { background-color: rgba(var(--foreground-color), 1); } -body[data-theme=dark] { +body[data-theme="dark"] { --accent-color: #92a2ff; --accent-color-rgb: 160, 182, 255; --secondary-color: #d60739; @@ -39,7 +39,7 @@ body[data-theme=dark] { --green: #00e676; --yellow: rgb(255, 213, 5); } -body[data-theme=dark] ::-webkit-calendar-picker-indicator { +body[data-theme="dark"] ::-webkit-calendar-picker-indicator { filter: invert(1); } @@ -63,7 +63,7 @@ strong { img { -o-object-fit: cover; - object-fit: cover; + object-fit: cover; } a:where([class]) { @@ -90,7 +90,7 @@ a:any-link:focus-visible { outline: rgba(var(--text-color), 1) 0.1rem solid; } -input[type=datetime-local] { +input[type="datetime-local"] { width: 100%; padding: 0.8rem 0.6rem; border: none; @@ -101,7 +101,7 @@ input[type=datetime-local] { color: inherit; background-color: rgba(var(--text-color), 0.06); } -input[type=datetime-local]:focus { +input[type="datetime-local"]:focus { outline: none; box-shadow: 0 0 0 0.1rem var(--accent-color); } @@ -109,8 +109,8 @@ input[type=datetime-local]:focus { button, .button { -webkit-user-select: none; - -moz-user-select: none; - user-select: none; + -moz-user-select: none; + user-select: none; position: relative; display: inline-flex; border: none; @@ -224,8 +224,8 @@ a:any-link:focus-visible { details summary { display: flex; -webkit-user-select: none; - -moz-user-select: none; - user-select: none; + -moz-user-select: none; + user-select: none; cursor: pointer; align-items: center; gap: 1rem; @@ -264,8 +264,8 @@ sm-chip { --padding: 0.5rem 0.8rem; --background: rgba(var(--text-color), 0.06); -webkit-user-select: none; - -moz-user-select: none; - user-select: none; + -moz-user-select: none; + user-select: none; font-weight: 500; } sm-chip[selected] { @@ -604,7 +604,11 @@ ul { position: absolute; border-radius: 50%; transform: scale(0); - background: radial-gradient(circle, rgba(var(--text-color), 0.3) 0%, rgba(0, 0, 0, 0) 50%); + background: radial-gradient( + circle, + rgba(var(--text-color), 0.3) 0%, + rgba(0, 0, 0, 0) 50% + ); pointer-events: none; } @@ -699,17 +703,17 @@ ul { justify-self: flex-start; } -ul[type=circle], -menu[type=circle] { +ul[type="circle"], +menu[type="circle"] { padding: 1.5rem 2.5rem; list-style: circle; } -ul[type=circle] li, -menu[type=circle] li { +ul[type="circle"] li, +menu[type="circle"] li { margin-bottom: 1rem; } -ul[type=circle] li:last-of-type, -menu[type=circle] li:last-of-type { +ul[type="circle"] li:last-of-type, +menu[type="circle"] li:last-of-type { margin-bottom: 0; } ul, @@ -773,13 +777,13 @@ menu { #meta_mask_status_button .icon-wrapper > * { grid-area: 1/1; } -#meta_mask_status_button[data-status=connected] { +#meta_mask_status_button[data-status="connected"] { pointer-events: none; } -#meta_mask_status_button[data-status=connected] .icon-wrapper::after { +#meta_mask_status_button[data-status="connected"] .icon-wrapper::after { background-color: var(--green); } -#meta_mask_status_button[data-status=disconnected] .icon-wrapper::after { +#meta_mask_status_button[data-status="disconnected"] .icon-wrapper::after { background-color: var(--danger-color); } @@ -847,7 +851,8 @@ main { transition: transform 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275); } .nav-item__title { - transition: opacity 0.2s, transform 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275); + transition: opacity 0.2s, + transform 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275); } .nav-item--active { color: var(--accent-color); @@ -870,17 +875,17 @@ main { overflow: auto; grid-area: pages; } -#page_container[data-page=home] > :nth-child(2) { +#page_container[data-page="home"] > :nth-child(2) { flex: 1; } -#page_container[data-page=send] { +#page_container[data-page="send"] { align-items: flex-start; } -#page_container[data-page=send] > * { +#page_container[data-page="send"] > * { padding: 1rem; margin: 0 auto; } -#page_container[data-page=create] { +#page_container[data-page="create"] { margin: 0 auto; padding: 4vw 1rem; gap: 2rem; @@ -940,17 +945,30 @@ aside h4 { gap: 0.5rem; } -#bsc_balance_wrapper { +#eth_balance_wrapper { background-color: rgba(var(--text-color), 0.06); padding: max(1rem, 1.5vw); border-radius: 0.5rem; width: 100%; } -#bsc_balance_wrapper li:not(:last-of-type) { +#eth_balance_wrapper li:not(:last-of-type) { border-bottom: solid thin rgba(var(--text-color), 0.3); padding-bottom: 0.5rem; } +#address_transactions { + width: 100%; + max-width: 32rem; +} + +.transaction { + width: 100%; +} + +#transactions_list { + width: 100%; +} + #error_section { display: grid; height: 100%; @@ -978,6 +996,7 @@ aside h4 { position: relative; margin-bottom: 2rem; } + .transaction__phase:not(:last-of-type)::after { content: ""; position: absolute; @@ -1004,7 +1023,7 @@ aside h4 { width: 4rem; border-radius: 5rem; -webkit-animation: popup 1s; - animation: popup 1s; + animation: popup 1s; padding: 1rem; } .user-action-result__icon.pending { @@ -1068,17 +1087,24 @@ aside h4 { padding-bottom: 1rem; border-bottom: solid thin rgba(var(--text-color), 0.3); } - +.create-buttons { + display: flex; + max-width: 400px; + gap: 1rem; +} @media only screen and (max-width: 640px) { .hide-on-small { display: none; } - #page_container[data-page=home] { + #page_container[data-page="home"] { flex-direction: column; } - #page_container[data-page=home] > :first-child { + #page_container[data-page="home"] > :first-child { order: 1; } + .create-buttons { + display: grid; + } } @media only screen and (min-width: 640px) { sm-popup { @@ -1166,9 +1192,369 @@ aside h4 { } @media (prefers-reduced-motion) { ::view-transition-group(*), -::view-transition-old(*), -::view-transition-new(*) { + ::view-transition-old(*), + ::view-transition-new(*) { -webkit-animation: none !important; - animation: none !important; + animation: none !important; } -} \ No newline at end of file +} + +.tx-details-container { + max-width: 800px; + margin: 1rem auto; + padding: 1rem; + font-family: inherit; +} + +/* Header styling */ +.tx-header { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 0.5rem; + margin-bottom: 1.5rem; + padding-bottom: 1rem; + border-bottom: 1px solid rgba(var(--text-color), 0.1); +} + +.tx-title { + font-size: 1.5rem; + font-weight: 600; + color: rgba(var(--text-color), 0.95); + margin: 0; +} + +.tx-card { + background-color: rgba(var(--foreground-color), 1); + border-radius: 0.75rem; + overflow: hidden; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); + border: 1px solid rgba(var(--text-color), 0.1); + margin-top: 1rem; +} + +.tx-status-header { + display: flex; + align-items: center; + gap: 1rem; + padding: 1.25rem; + background-color: rgba(var(--text-color), 0.03); + border-bottom: 1px solid rgba(var(--text-color), 0.1); +} + +.status-indicator { + width: 12px; + height: 12px; + border-radius: 50%; + flex-shrink: 0; +} + +.status-indicator.confirmed { + background-color: var(--color-success); + box-shadow: 0 0 0 4px rgba(var(--color-success-rgb), 0.2); +} + +.status-indicator.pending { + background-color: var(--color-warning); + box-shadow: 0 0 0 4px rgba(var(--color-warning-rgb), 0.2); +} + +.status-details { + flex-grow: 1; +} + +.status-title { + font-size: 1.15rem; + font-weight: 600; + color: rgba(var(--text-color), 0.95); + margin: 0; +} + +.status-subtext { + font-size: 0.85rem; + color: rgba(var(--text-color), 0.7); + margin: 0.25rem 0 0; +} + +.tx-info-grid { + padding: 1.25rem; + display: flex; + flex-direction: column; + gap: 1.5rem; +} + +.tx-address-section { + display: flex; + align-items: stretch; + gap: 1rem; + background: rgba(var(--text-color), 0.02); + padding: 1rem; + border-radius: 0.5rem; +} + +.address-card { + flex: 1; + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.address-label { + font-size: 0.75rem; + font-weight: 500; + color: rgba(var(--text-color), 0.6); + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.address-value { + font-family: "Roboto Mono", monospace; + font-size: 0.85rem; + color: rgba(var(--text-color), 0.9); + word-break: break-all; + background: rgba(var(--text-color), 0.05); + padding: 0.5rem 0.75rem; + border-radius: 0.25rem; +} + +.tx-arrow { + font-size: 1.5rem; + color: rgba(var(--text-color), 0.4); + display: flex; + align-items: center; + padding: 0 0.5rem; +} + +.tx-hash-section { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.section-label { + font-size: 0.75rem; + font-weight: 500; + color: rgba(var(--text-color), 0.6); + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.hash-value { + background-color: rgba(var(--text-color), 0.05); + padding: 0.75rem; + border-radius: 0.25rem; + font-family: "Roboto Mono", monospace; + font-size: 0.85rem; + color: rgba(var(--text-color), 0.9); + word-break: break-all; +} + +.tx-metrics-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); + gap: 1rem; +} + +.metric-card { + background-color: rgba(var(--text-color), 0.03); + padding: 1rem; + border-radius: 0.5rem; + text-align: center; + border: 1px solid rgba(var(--text-color), 0.08); + transition: background-color 0.2s ease, transform 0.2s ease; +} + +.metric-card:hover { + background-color: rgba(var(--text-color), 0.06); + transform: translateY(-2px); +} + +.metric-label { + font-size: 0.75rem; + font-weight: 500; + color: rgba(var(--text-color), 0.6); + margin-bottom: 0.5rem; + text-transform: uppercase; + letter-spacing: 0.5px; + display: block; +} + +.metric-value { + font-size: 0.95rem; + font-weight: 500; + color: rgba(var(--text-color), 0.95); + word-break: break-word; +} + +.tx-actions { + display: flex; + justify-content: space-between; + gap: 1rem; + padding: 1.25rem; + border-top: 1px solid rgba(var(--text-color), 0.1); + background-color: rgba(var(--text-color), 0.03); +} + +.tx-actions .button { + flex: 1; +} + +@media (max-width: 768px) { + .tx-address-section { + flex-direction: column; + gap: 0.75rem; + align-items: stretch; + } + + .tx-arrow { + transform: rotate(90deg); + margin: 0.5rem auto; + padding: 0; + } + + .tx-metrics-grid { + grid-template-columns: 1fr 1fr; + } +} + +@media (max-width: 576px) { + .tx-header { + padding-bottom: 0.75rem; + } + + .tx-status-header { + padding: 1rem; + gap: 0.75rem; + } + + .status-title { + font-size: 1.1rem; + } + + .tx-info-grid { + padding: 1rem; + gap: 1.25rem; + } + + .tx-address-section { + padding: 0.75rem; + } + + .tx-metrics-grid { + grid-template-columns: 1fr; + } + + .tx-actions { + flex-direction: column; + padding: 1rem; + } +} + +/* Valuation toggle styles */ +.valuation-toggle { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.85rem; + color: rgba(var(--text-color), 0.8); + margin-top: 0.5rem; + margin-right: 0.5rem; +} + +.toggle-switch { + position: relative; + display: inline-block; + width: 3rem; + height: 1.5rem; +} + +.toggle-switch input { + opacity: 0; + width: 0; + height: 0; +} + +.toggle-slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(var(--text-color), 0.2); + transition: 0.4s; + border-radius: 1.5rem; +} + +.toggle-slider:before { + position: absolute; + content: ""; + height: 1.1rem; + width: 1.1rem; + left: 0.2rem; + bottom: 0.2rem; + background-color: white; + transition: 0.4s; + border-radius: 50%; +} + +input:checked + .toggle-slider { + background-color: var(--accent-color); +} + +input:checked + .toggle-slider:before { + transform: translateX(1.5rem); +} + +.transaction-controls { + display: flex; + align-items: center; + justify-content: space-between; + gap: 1rem; + margin-bottom: 1rem; + padding: 0.5rem; + background-color: rgba(var(--text-color), 0.03); + border-radius: 0.5rem; + border: 1px solid rgba(var(--text-color), 0.08); +} + +.filter-control { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.filter-control label { + font-size: 0.9rem; + color: rgba(var(--text-color), 0.8); + font-weight: 500; +} + +.filter-control select { + padding: 0.4rem 0.6rem; + border-radius: 0.4rem; + border: 1px solid rgba(var(--text-color), 0.1); + background-color: rgba(var(--foreground-color), 1); + color: rgba(var(--text-color), 0.9); + font-size: 0.9rem; + cursor: pointer; + outline: none; +} +.margin-left-auto + .margin-left-auto { + display: none !important; +} + +.filter-control select:focus { + border-color: var(--accent-color); +} + +#bsc_balance_wrapper { + background-color: rgba(var(--text-color), 0.06); + padding: max(1rem, 1.5vw); + border-radius: 0.5rem; + width: 100%; +} +#bsc_balance_wrapper li:not(:last-of-type) { + border-bottom: solid thin rgba(var(--text-color), 0.3); + padding-bottom: 0.5rem; +} diff --git a/css/main.min.css b/css/main.min.css index 4810aa0..4a527d3 100644 --- a/css/main.min.css +++ b/css/main.min.css @@ -1050,311 +1050,3 @@ aside h4 { animation: none !important; } } -.tx-details-container { - max-width: 800px; - margin: 1rem auto; - padding: 1rem; - font-family: inherit; -} -.tx-header { - display: flex; - flex-direction: column; - align-items: flex-start; - gap: 0.5rem; - margin-bottom: 1.5rem; - padding-bottom: 1rem; - border-bottom: 1px solid rgba(var(--text-color), 0.1); -} -.tx-title { - font-size: 1.5rem; - font-weight: 600; - color: rgba(var(--text-color), 0.95); - margin: 0; -} -.tx-card { - background-color: rgba(var(--foreground-color), 1); - border-radius: 0.75rem; - overflow: hidden; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); - border: 1px solid rgba(var(--text-color), 0.1); - margin-top: 1rem; -} -.tx-status-header { - display: flex; - align-items: center; - gap: 1rem; - padding: 1.25rem; - background-color: rgba(var(--text-color), 0.03); - border-bottom: 1px solid rgba(var(--text-color), 0.1); -} -.status-indicator { - width: 12px; - height: 12px; - border-radius: 50%; - flex-shrink: 0; -} -.status-indicator.confirmed { - background-color: var(--color-success); - box-shadow: 0 0 0 4px rgba(var(--color-success-rgb), 0.2); -} -.status-indicator.pending { - background-color: var(--color-warning); - box-shadow: 0 0 0 4px rgba(var(--color-warning-rgb), 0.2); -} -.status-details { - flex-grow: 1; -} -.status-title { - font-size: 1.15rem; - font-weight: 600; - color: rgba(var(--text-color), 0.95); - margin: 0; -} -.status-subtext { - font-size: 0.85rem; - color: rgba(var(--text-color), 0.7); - margin: 0.25rem 0 0; -} -.tx-info-grid { - padding: 1.25rem; - display: flex; - flex-direction: column; - gap: 1.5rem; -} -.tx-address-section { - display: flex; - align-items: stretch; - gap: 1rem; - background: rgba(var(--text-color), 0.02); - padding: 1rem; - border-radius: 0.5rem; -} -.address-card { - flex: 1; - display: flex; - flex-direction: column; - gap: 0.5rem; -} -.address-label { - font-size: 0.75rem; - font-weight: 500; - color: rgba(var(--text-color), 0.6); - text-transform: uppercase; - letter-spacing: 0.5px; -} -.address-value { - font-family: "Roboto Mono", monospace; - font-size: 0.85rem; - color: rgba(var(--text-color), 0.9); - word-break: break-all; - background: rgba(var(--text-color), 0.05); - padding: 0.5rem 0.75rem; - border-radius: 0.25rem; -} -.tx-arrow { - font-size: 1.5rem; - color: rgba(var(--text-color), 0.4); - display: flex; - align-items: center; - padding: 0 0.5rem; -} -.tx-hash-section { - display: flex; - flex-direction: column; - gap: 0.5rem; -} -.section-label { - font-size: 0.75rem; - font-weight: 500; - color: rgba(var(--text-color), 0.6); - text-transform: uppercase; - letter-spacing: 0.5px; -} -.hash-value { - background-color: rgba(var(--text-color), 0.05); - padding: 0.75rem; - border-radius: 0.25rem; - font-family: "Roboto Mono", monospace; - font-size: 0.85rem; - color: rgba(var(--text-color), 0.9); - word-break: break-all; -} -.tx-metrics-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); - gap: 1rem; -} -.metric-card { - background-color: rgba(var(--text-color), 0.03); - padding: 1rem; - border-radius: 0.5rem; - text-align: center; - border: 1px solid rgba(var(--text-color), 0.08); - transition: background-color 0.2s ease, transform 0.2s ease; -} -.metric-card:hover { - background-color: rgba(var(--text-color), 0.06); - transform: translateY(-2px); -} -.metric-label { - font-size: 0.75rem; - font-weight: 500; - color: rgba(var(--text-color), 0.6); - margin-bottom: 0.5rem; - text-transform: uppercase; - letter-spacing: 0.5px; - display: block; -} -.metric-value { - font-size: 0.95rem; - font-weight: 500; - color: rgba(var(--text-color), 0.95); - word-break: break-word; -} -.tx-actions { - display: flex; - justify-content: space-between; - gap: 1rem; - padding: 1.25rem; - border-top: 1px solid rgba(var(--text-color), 0.1); - background-color: rgba(var(--text-color), 0.03); -} -.tx-actions .button { - flex: 1; -} -@media (max-width: 768px) { - .tx-address-section { - flex-direction: column; - gap: 0.75rem; - align-items: stretch; - } - .tx-arrow { - transform: rotate(90deg); - margin: 0.5rem auto; - padding: 0; - } - .tx-metrics-grid { - grid-template-columns: 1fr 1fr; - } -} -@media (max-width: 576px) { - .tx-header { - padding-bottom: 0.75rem; - } - .tx-status-header { - padding: 1rem; - gap: 0.75rem; - } - .status-title { - font-size: 1.1rem; - } - .tx-info-grid { - padding: 1rem; - gap: 1.25rem; - } - .tx-address-section { - padding: 0.75rem; - } - .tx-metrics-grid { - grid-template-columns: 1fr; - } - .tx-actions { - flex-direction: column; - padding: 1rem; - } -} -.valuation-toggle { - display: flex; - align-items: center; - gap: 0.5rem; - font-size: 0.85rem; - color: rgba(var(--text-color), 0.8); - margin-top: 0.5rem; - margin-right: 0.5rem; -} -.toggle-switch { - position: relative; - display: inline-block; - width: 3rem; - height: 1.5rem; -} -.toggle-switch input { - opacity: 0; - width: 0; - height: 0; -} -.toggle-slider { - position: absolute; - cursor: pointer; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(var(--text-color), 0.2); - transition: 0.4s; - border-radius: 1.5rem; -} -.toggle-slider:before { - position: absolute; - content: ""; - height: 1.1rem; - width: 1.1rem; - left: 0.2rem; - bottom: 0.2rem; - background-color: white; - transition: 0.4s; - border-radius: 50%; -} -input:checked + .toggle-slider { - background-color: var(--accent-color); -} -input:checked + .toggle-slider:before { - transform: translateX(1.5rem); -} -.transaction-controls { - display: flex; - align-items: center; - justify-content: space-between; - gap: 1rem; - margin-bottom: 1rem; - padding: 0.5rem; - background-color: rgba(var(--text-color), 0.03); - border-radius: 0.5rem; - border: 1px solid rgba(var(--text-color), 0.08); -} -.filter-control { - display: flex; - align-items: center; - gap: 0.5rem; -} -.filter-control label { - font-size: 0.9rem; - color: rgba(var(--text-color), 0.8); - font-weight: 500; -} -.filter-control select { - padding: 0.4rem 0.6rem; - border-radius: 0.4rem; - border: 1px solid rgba(var(--text-color), 0.1); - background-color: rgba(var(--foreground-color), 1); - color: rgba(var(--text-color), 0.9); - font-size: 0.9rem; - cursor: pointer; - outline: none; -} -.margin-left-auto + .margin-left-auto { - display: none !important; -} -.filter-control select:focus { - border-color: var(--accent-color); -} -#bsc_balance_wrapper { - background-color: rgba(var(--text-color), 0.06); - padding: max(1rem, 1.5vw); - border-radius: 0.5rem; - width: 100%; -} -#bsc_balance_wrapper li:not(:last-of-type) { - border-bottom: solid thin rgba(var(--text-color), 0.3); - padding-bottom: 0.5rem; -} diff --git a/index.html b/index.html index f706c8f..e2b3ec7 100644 --- a/index.html +++ b/index.html @@ -1,11 +1,11 @@ - Binance Smart Chain - + +

- +
@@ -260,17 +262,13 @@
-

Did you forget your Binance Address?

+

Did you forget your addresses?

- If you have your BSC/BTC/FLO Private Key, enter it here and recover - your Binance Address. + If you have your BSC/BTC/FLO Private Key, enter it here to recover + all your addresses and private keys.

- +
- + @@ -354,13 +353,11 @@ - + + - \ No newline at end of file diff --git a/scripts/bscOperator.js b/scripts/bscOperator.js index 29e3df4..4e9a892 100644 --- a/scripts/bscOperator.js +++ b/scripts/bscOperator.js @@ -1,305 +1,330 @@ - -(function (EXPORTS) { //bscOperator v1.0.2 +(function (EXPORTS) { + //bscOperator v1.0.2 /* ETH Crypto and API Operator */ - if (!window.ethers) - return console.error('ethers.js not found') + if (!window.ethers) return console.error("ethers.js not found"); const bscOperator = EXPORTS; - const isValidAddress = bscOperator.isValidAddress = (address) => { + const isValidAddress = (bscOperator.isValidAddress = (address) => { try { // Check if the address is a valid checksum address const isValidChecksum = ethers.utils.isAddress(address); // Check if the address is a valid non-checksum address - const isValidNonChecksum = ethers.utils.getAddress(address) === address.toLowerCase(); + const isValidNonChecksum = + ethers.utils.getAddress(address) === address.toLowerCase(); return isValidChecksum || isValidNonChecksum; } catch (error) { return false; } - } + }); const BEP20ABI = [ { - "constant": true, - "inputs": [], - "name": "name", - "outputs": [ + constant: true, + inputs: [], + name: "name", + outputs: [ { - "name": "", - "type": "string" - } + name: "", + type: "string", + }, ], - "payable": false, - "stateMutability": "view", - "type": "function" + payable: false, + stateMutability: "view", + type: "function", }, { - "constant": false, - "inputs": [ + constant: false, + inputs: [ { - "name": "_spender", - "type": "address" + name: "_spender", + type: "address", }, { - "name": "_value", - "type": "uint256" - } + name: "_value", + type: "uint256", + }, ], - "name": "approve", - "outputs": [ + name: "approve", + outputs: [ { - "name": "", - "type": "bool" - } + name: "", + type: "bool", + }, ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" + payable: false, + stateMutability: "nonpayable", + type: "function", }, { - "constant": true, - "inputs": [], - "name": "totalSupply", - "outputs": [ + constant: true, + inputs: [], + name: "totalSupply", + outputs: [ { - "name": "", - "type": "uint256" - } + name: "", + type: "uint256", + }, ], - "payable": false, - "stateMutability": "view", - "type": "function" + payable: false, + stateMutability: "view", + type: "function", }, { - "constant": false, - "inputs": [ + constant: false, + inputs: [ { - "name": "_from", - "type": "address" + name: "_from", + type: "address", }, { - "name": "_to", - "type": "address" + name: "_to", + type: "address", }, { - "name": "_value", - "type": "uint256" - } + name: "_value", + type: "uint256", + }, ], - "name": "transferFrom", - "outputs": [ + name: "transferFrom", + outputs: [ { - "name": "", - "type": "bool" - } + name: "", + type: "bool", + }, ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" + payable: false, + stateMutability: "nonpayable", + type: "function", }, { - "constant": true, - "inputs": [], - "name": "decimals", - "outputs": [ + constant: true, + inputs: [], + name: "decimals", + outputs: [ { - "name": "", - "type": "uint8" - } + name: "", + type: "uint8", + }, ], - "payable": false, - "stateMutability": "view", - "type": "function" + payable: false, + stateMutability: "view", + type: "function", }, { - "constant": true, - "inputs": [ + constant: true, + inputs: [ { - "name": "_owner", - "type": "address" - } + name: "_owner", + type: "address", + }, ], - "name": "balanceOf", - "outputs": [ + name: "balanceOf", + outputs: [ { - "name": "balance", - "type": "uint256" - } + name: "balance", + type: "uint256", + }, ], - "payable": false, - "stateMutability": "view", - "type": "function" + payable: false, + stateMutability: "view", + type: "function", }, { - "constant": true, - "inputs": [], - "name": "symbol", - "outputs": [ + constant: true, + inputs: [], + name: "symbol", + outputs: [ { - "name": "", - "type": "string" - } + name: "", + type: "string", + }, ], - "payable": false, - "stateMutability": "view", - "type": "function" + payable: false, + stateMutability: "view", + type: "function", }, { - "constant": false, - "inputs": [ + constant: false, + inputs: [ { - "name": "_to", - "type": "address" + name: "_to", + type: "address", }, { - "name": "_value", - "type": "uint256" - } + name: "_value", + type: "uint256", + }, ], - "name": "transfer", - "outputs": [ + name: "transfer", + outputs: [ { - "name": "", - "type": "bool" - } + name: "", + type: "bool", + }, ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" + payable: false, + stateMutability: "nonpayable", + type: "function", }, { - "constant": true, - "inputs": [ + constant: true, + inputs: [ { - "name": "_owner", - "type": "address" + name: "_owner", + type: "address", }, { - "name": "_spender", - "type": "address" - } + name: "_spender", + type: "address", + }, ], - "name": "allowance", - "outputs": [ + name: "allowance", + outputs: [ { - "name": "", - "type": "uint256" - } + name: "", + type: "uint256", + }, ], - "payable": false, - "stateMutability": "view", - "type": "function" + payable: false, + stateMutability: "view", + type: "function", }, { - "payable": true, - "stateMutability": "payable", - "type": "fallback" + payable: true, + stateMutability: "payable", + type: "fallback", }, { - "anonymous": false, - "inputs": [ + anonymous: false, + inputs: [ { - "indexed": true, - "name": "owner", - "type": "address" + indexed: true, + name: "owner", + type: "address", }, { - "indexed": true, - "name": "spender", - "type": "address" + indexed: true, + name: "spender", + type: "address", }, { - "indexed": false, - "name": "value", - "type": "uint256" - } + indexed: false, + name: "value", + type: "uint256", + }, ], - "name": "Approval", - "type": "event" + name: "Approval", + type: "event", }, { - "anonymous": false, - "inputs": [ + anonymous: false, + inputs: [ { - "indexed": true, - "name": "from", - "type": "address" + indexed: true, + name: "from", + type: "address", }, { - "indexed": true, - "name": "to", - "type": "address" + indexed: true, + name: "to", + type: "address", }, { - "indexed": false, - "name": "value", - "type": "uint256" - } + indexed: false, + name: "value", + type: "uint256", + }, ], - "name": "Transfer", - "type": "event" - } - ] + name: "Transfer", + type: "event", + }, + ]; const CONTRACT_ADDRESSES = { usdc: "0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d", - usdt: "0x55d398326f99059ff775485246999027b3197955" - } + usdt: "0x55d398326f99059ff775485246999027b3197955", + }; function getProvider() { // switches provider based on whether the user is using MetaMask or not const bscMainnet = { chainId: 56, - name: 'binance', - rpc: 'https://bsc-dataseed.binance.org/', - explorer: 'https://bscscan.com' + name: "binance", + rpc: "https://bsc-dataseed.binance.org/", + explorer: "https://bscscan.com", }; - - + if (window.ethereum) { return new ethers.providers.Web3Provider(window.ethereum); } else { - return new ethers.providers.JsonRpcProvider(bscMainnet.rpc, bscMainnet) + return new ethers.providers.JsonRpcProvider(bscMainnet.rpc, bscMainnet); } } function connectToMetaMask() { return new Promise((resolve, reject) => { // if (typeof window.ethereum === "undefined") // return reject("MetaMask not installed"); - return resolve(true) + return resolve(true); ethereum - .request({ method: 'eth_requestAccounts' }) + .request({ method: "eth_requestAccounts" }) .then((accounts) => { - console.log('Connected to MetaMask') - return resolve(accounts) + console.log("Connected to MetaMask"); + return resolve(accounts); }) .catch((err) => { - console.log(err) - return reject(err) - }) - }) + console.log(err); + return reject(err); + }); + }); } const getTransactionHistory = (bscOperator.getTransactionHistory = async ( - address + address, + cursor = null ) => { try { if (!address || !isValidAddress(address)) return new Error("Invalid address"); - const url = `https://api.bscscan.com/api?module=account&action=txlist&address=${address}&startblock=0&endblock=99999999&sort=desc&apikey=N7BFDPT7X927YVKWW4XT7VWI6RP2CH38RR`; + + // Moralis API endpoint for BSC transactions + const MORALIS_API_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJub25jZSI6IjQyZWNiMjk0LTBiMGItNDg4Yy1hNjUwLTE4NmJhMjFjNjNhYyIsIm9yZ0lkIjoiNDg4NzAzIiwidXNlcklkIjoiNTAyODExIiwidHlwZUlkIjoiZjE5ZmZjYTYtNDllMS00NTdlLTllNjgtMGI1MDIyODU2N2Q4IiwidHlwZSI6IlBST0pFQ1QiLCJpYXQiOjE3Njc1NDkxNDQsImV4cCI6NDkyMzMwOTE0NH0.yr_jtBCrrid4Y5d48iTwJ4PwgMOZn8mwWyiQ7dAmNvw"; + + // Fetch 10 transactions per page + const url = `https://deep-index.moralis.io/api/v2/${address}?chain=bsc&limit=10${cursor ? `&cursor=${cursor}` : ''}`; - const response = await fetch(url); + const response = await fetch(url, { + headers: { + "Accept": "application/json", + "X-API-Key": MORALIS_API_KEY + } + }); const data = await response.json(); - if (data.status === "1") { - return data.result.map((tx) => ({ + if (data.result && Array.isArray(data.result)) { + // Get current block number to calculate confirmations + const provider = getProvider(); + const currentBlockNumber = await provider.getBlockNumber(); + + const transactions = data.result.map((tx) => ({ hash: tx.hash, - from: tx.from, - to: tx.to, + from: tx.from_address, + to: tx.to_address, value: tx.value, - timeStamp: tx.timeStamp, - blockNumber: tx.blockNumber, - confirmations: tx.confirmations || 0, - gasPrice: tx.gasPrice, - gasUsed: tx.gasUsed, + timeStamp: Math.floor(new Date(tx.block_timestamp).getTime() / 1000), + blockNumber: tx.block_number, + confirmations: currentBlockNumber - parseInt(tx.block_number), + gasPrice: tx.gas_price, + gasUsed: tx.receipt_gas_used, })); + + // Return transactions along with cursor for next page + return { + transactions: transactions, + nextCursor: data.cursor || null, + hasMore: !!data.cursor + }; } else { - console.error("Error fetching transaction history:", data.message); - return []; + console.error("Error fetching transaction history:", data.message || "No results"); + return { + transactions: [], + nextCursor: null, + hasMore: false + }; } } catch (error) { console.error("Error:", error.message); @@ -319,9 +344,12 @@ const receipt = await provider.getTransactionReceipt(txHash); let timestamp = null; + let confirmations = 0; if (tx.blockNumber) { const block = await provider.getBlock(tx.blockNumber); timestamp = block.timestamp; + const currentBlockNumber = await provider.getBlockNumber(); + confirmations = currentBlockNumber - tx.blockNumber; } return { @@ -333,6 +361,7 @@ gasUsed: receipt ? receipt.gasUsed : null, blockNumber: tx.blockNumber, timeStamp: timestamp, + confirmations: confirmations, status: receipt ? (receipt.status ? "success" : "failed") : "pending", }; } catch (error) { @@ -340,90 +369,80 @@ return null; } }); - - - const getBalance = bscOperator.getBalance = async (address) => { + + const getBalance = (bscOperator.getBalance = async (address) => { try { - if (!address || !isValidAddress(address)) - return new Error('Invalid address'); - // Get the balance + if (!address || !isValidAddress(address)) { + return new Error("Invalid address"); + } + const provider = getProvider(); const balanceWei = await provider.getBalance(address); const balanceEth = parseFloat(ethers.utils.formatEther(balanceWei)); return balanceEth; } catch (error) { - console.error('Error:', error.message); + console.error("Error in getBalance:", error); return error; } - } + }); - - - - - - - - const getTokenBalance = bscOperator.getTokenBalance = async (address, token, { contractAddress } = {}) => { + const getTokenBalance = (bscOperator.getTokenBalance = async ( + address, + token, + { contractAddress } = {} + ) => { try { if (!address) { + throw new Error("Address not specified"); } if (!token) { + throw new Error("Token not specified"); } if (!CONTRACT_ADDRESSES[token] && !contractAddress) { + throw new Error("Contract address of token not available"); } - - const provider = getProvider(); // Ensure this returns a valid provider for BSC - const contract = new ethers.Contract(CONTRACT_ADDRESSES[token] || contractAddress, BEP20ABI, provider); - + + const provider = getProvider(); + const contract = new ethers.Contract( + CONTRACT_ADDRESSES[token] || contractAddress, + BEP20ABI, + provider + ); + let balance = await contract.balanceOf(address); - - // Assuming 18 decimals for most tokens like USDT and USDC***************************************************** - // const decimals = 0.00; + const decimals = 18; - const formattedDecimals = decimals.toFixed(1); // This will convert 18 to "18.00" - console.log(formattedDecimals); // Outputs: "18.0" + balance = parseFloat(ethers.utils.formatUnits(balance, decimals)); - balance = parseFloat(ethers.utils.formatUnits(balance, decimals)); - - // Format the balance to 2 decimal places for display - balance = balance.toFixed(2); - return balance; - } - catch (e) { - // console.error("Error getting token balance:", e.message); - // throw new Error("Failed to get token balance"); + } catch (e) { + console.error("Error in getTokenBalance:", e); + throw e; } - } - + }); // Example usage: // Ensure MetaMask is connected and BSC network is selected in MetaMask - const address = '0xYourAddressHere'; // Replace with your actual address + const address = "0xYourAddressHere"; // Replace with your actual address (async () => { try { - const usdtBalance = await getTokenBalance(address, 'USDT'); - const bnbBalance = await getTokenBalance(address, 'BNB'); - console.log('USDT Balance:', usdtBalance); - console.log('BNB Balance:', bnbBalance); + const usdtBalance = await getTokenBalance(address, "USDT"); + const bnbBalance = await getTokenBalance(address, "BNB"); + console.log("USDT Balance:", usdtBalance); + console.log("BNB Balance:", bnbBalance); } catch (error) { - console.error('Error fetching balances:', error.message); + console.error("Error fetching balances:", error.message); } })(); - - - - - - - - - const estimateGas = bscOperator.estimateGas = async ({ privateKey, receiver, amount }) => { + const estimateGas = (bscOperator.estimateGas = async ({ + privateKey, + receiver, + amount, + }) => { try { const provider = getProvider(); const signer = new ethers.Wallet(privateKey, provider); @@ -433,61 +452,79 @@ value: ethers.utils.parseUnits(amount, "ether"), }); } catch (e) { - throw new Error(e) + throw new Error(e); } - } + }); - const sendTransaction = bscOperator.sendTransaction = async ({ privateKey, receiver, amount }) => { + const sendTransaction = (bscOperator.sendTransaction = async ({ + privateKey, + receiver, + amount, + }) => { try { const provider = getProvider(); const signer = new ethers.Wallet(privateKey, provider); - const limit = await estimateGas({ privateKey, receiver, amount }) - // Creating and sending the transaction object - return signer.sendTransaction({ + + const limit = await estimateGas({ privateKey, receiver, amount }); + + const tx = await signer.sendTransaction({ to: receiver, value: ethers.utils.parseUnits(amount, "ether"), gasLimit: limit, - nonce: signer.getTransactionCount(), + nonce: await signer.getTransactionCount(), maxPriorityFeePerGas: ethers.utils.parseUnits("2", "gwei"), - }) + }); + + return tx; } catch (e) { - throw new Error(e) + console.error("Error in sendTransaction:", e); + throw e; } - }; - + }); - const sendToken = bscOperator.sendToken = async ({ token, privateKey, amount, receiver, contractAddress }) => { - // Create a wallet using the private key - const wallet = new ethers.Wallet(privateKey, getProvider()); + const sendToken = (bscOperator.sendToken = async ({ + token, + privateKey, + amount, + receiver, + contractAddress, + }) => { + try { + const wallet = new ethers.Wallet(privateKey, getProvider()); - // Contract interface - const tokenContract = new ethers.Contract(CONTRACT_ADDRESSES[token] || contractAddress, BEP20ABI, wallet); + const tokenContract = new ethers.Contract( + CONTRACT_ADDRESSES[token] || contractAddress, + BEP20ABI, + wallet + ); - // Fetch the correct number of decimals for the token - const decimals = await tokenContract.decimals(); + const decimals = await tokenContract.decimals(); - // Convert the amount to the smallest unit of the token - const amountWei = ethers.utils.parseUnits(amount.toString(), decimals); + const amountWei = ethers.utils.parseUnits(amount.toString(), decimals); - // Estimate gas limit for the transaction - const gasLimit = await tokenContract.estimateGas.transfer(receiver, amountWei); + const gasLimit = await tokenContract.estimateGas.transfer( + receiver, + amountWei + ); - // Get the current gas price - const gasPrice = await wallet.provider.getGasPrice(); + const gasPrice = await wallet.provider.getGasPrice(); - // Calculate the gas cost - const gasCost = gasPrice.mul(gasLimit); + const gasCost = gasPrice.mul(gasLimit); - console.log(`Gas cost: ${ethers.utils.formatEther(gasCost)} BNB`); + const balance = await wallet.getBalance(); - // Check if wallet has enough balance to cover gas fees - const balance = await wallet.getBalance(); - if (balance.lt(gasCost)) { - throw new Error("Insufficient funds for gas fee"); + if (balance.lt(gasCost)) { + throw new Error("Insufficient funds for gas fee"); + } + + const tx = await tokenContract.transfer(receiver, amountWei, { + gasLimit, + gasPrice, + }); + return tx; + } catch (e) { + console.error("Error in sendToken:", e); + throw e; } - - - // Call the transfer function on the USDC contract - return tokenContract.transfer(receiver, amountWei, { gasLimit, gasPrice }); - } -})('object' === typeof module ? module.exports : window.bscOperator = {}); \ No newline at end of file + }); +})("object" === typeof module ? module.exports : (window.bscOperator = {}));