diff --git a/docs/.nojekyll b/docs/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/docs/CNAME b/docs/CNAME new file mode 100644 index 0000000..e83ee5b --- /dev/null +++ b/docs/CNAME @@ -0,0 +1 @@ +bouquet.dark.florist diff --git a/docs/css/index.css b/docs/css/index.css new file mode 100644 index 0000000..2e08b6a --- /dev/null +++ b/docs/css/index.css @@ -0,0 +1,1230 @@ +/* +! tailwindcss v3.2.7 | MIT License | https://tailwindcss.com +*/ + +/* +1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) +2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) +*/ + +*, +::before, +::after { + box-sizing: border-box; + /* 1 */ + border-width: 0; + /* 2 */ + border-style: solid; + /* 2 */ + border-color: #e5e7eb; + /* 2 */ +} + +::before, +::after { + --tw-content: ''; +} + +/* +1. Use a consistent sensible line-height in all browsers. +2. Prevent adjustments of font size after orientation changes in iOS. +3. Use a more readable tab size. +4. Use the user's configured `sans` font-family by default. +5. Use the user's configured `sans` font-feature-settings by default. +*/ + +html { + line-height: 1.5; + /* 1 */ + -webkit-text-size-adjust: 100%; + /* 2 */ + -moz-tab-size: 4; + /* 3 */ + -o-tab-size: 4; + tab-size: 4; + /* 3 */ + font-family: + ui-sans-serif, + system-ui, + -apple-system, + BlinkMacSystemFont, + 'Segoe UI', + Roboto, + 'Helvetica Neue', + Arial, + 'Noto Sans', + sans-serif, + 'Apple Color Emoji', + 'Segoe UI Emoji', + 'Segoe UI Symbol', + 'Noto Color Emoji'; + /* 4 */ + font-feature-settings: normal; + /* 5 */ +} + +/* +1. Remove the margin in all browsers. +2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. +*/ + +body { + margin: 0; + /* 1 */ + line-height: inherit; + /* 2 */ +} + +/* +1. Add the correct height in Firefox. +2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) +3. Ensure horizontal rules are visible by default. +*/ + +hr { + height: 0; + /* 1 */ + color: inherit; + /* 2 */ + border-top-width: 1px; + /* 3 */ +} + +/* +Add the correct text decoration in Chrome, Edge, and Safari. +*/ + +abbr:where([title]) { + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; +} + +/* +Remove the default font size and weight for headings. +*/ + +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: inherit; + font-weight: inherit; +} + +/* +Reset links to optimize for opt-in styling instead of opt-out. +*/ + +a { + color: inherit; + text-decoration: inherit; +} + +/* +Add the correct font weight in Edge and Safari. +*/ + +b, +strong { + font-weight: bolder; +} + +/* +1. Use the user's configured `mono` font family by default. +2. Correct the odd `em` font sizing in all browsers. +*/ + +code, +kbd, +samp, +pre { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace; + /* 1 */ + font-size: 1em; + /* 2 */ +} + +/* +Add the correct font size in all browsers. +*/ + +small { + font-size: 80%; +} + +/* +Prevent `sub` and `sup` elements from affecting the line height in all browsers. +*/ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* +1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) +2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) +3. Remove gaps between table borders by default. +*/ + +table { + text-indent: 0; + /* 1 */ + border-color: inherit; + /* 2 */ + border-collapse: collapse; + /* 3 */ +} + +/* +1. Change the font styles in all browsers. +2. Remove the margin in Firefox and Safari. +3. Remove default padding in all browsers. +*/ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + /* 1 */ + font-size: 100%; + /* 1 */ + font-weight: inherit; + /* 1 */ + line-height: inherit; + /* 1 */ + color: inherit; + /* 1 */ + margin: 0; + /* 2 */ + padding: 0; + /* 3 */ +} + +/* +Remove the inheritance of text transform in Edge and Firefox. +*/ + +button, +select { + text-transform: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Remove default button styles. +*/ + +button, +[type='button'], +[type='reset'], +[type='submit'] { + -webkit-appearance: button; + /* 1 */ + background-color: transparent; + /* 2 */ + background-image: none; + /* 2 */ +} + +/* +Use the modern Firefox focus style for all focusable elements. +*/ + +:-moz-focusring { + outline: auto; +} + +/* +Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) +*/ + +:-moz-ui-invalid { + box-shadow: none; +} + +/* +Add the correct vertical alignment in Chrome and Firefox. +*/ + +progress { + vertical-align: baseline; +} + +/* +Correct the cursor style of increment and decrement buttons in Safari. +*/ + +::-webkit-inner-spin-button, +::-webkit-outer-spin-button { + height: auto; +} + +/* +1. Correct the odd appearance in Chrome and Safari. +2. Correct the outline style in Safari. +*/ + +[type='search'] { + -webkit-appearance: textfield; + /* 1 */ + outline-offset: -2px; + /* 2 */ +} + +/* +Remove the inner padding in Chrome and Safari on macOS. +*/ + +::-webkit-search-decoration { + -webkit-appearance: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Change font properties to `inherit` in Safari. +*/ + +::-webkit-file-upload-button { + -webkit-appearance: button; + /* 1 */ + font: inherit; + /* 2 */ +} + +/* +Add the correct display in Chrome and Safari. +*/ + +summary { + display: list-item; +} + +/* +Removes the default spacing and border for appropriate elements. +*/ + +blockquote, +dl, +dd, +h1, +h2, +h3, +h4, +h5, +h6, +hr, +figure, +p, +pre { + margin: 0; +} + +fieldset { + margin: 0; + padding: 0; +} + +legend { + padding: 0; +} + +ol, +ul, +menu { + list-style: none; + margin: 0; + padding: 0; +} + +/* +Prevent resizing textareas horizontally by default. +*/ + +textarea { + resize: vertical; +} + +/* +1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) +2. Set the default placeholder color to the user's configured gray 400 color. +*/ + +input::-moz-placeholder, +textarea::-moz-placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +input::placeholder, +textarea::placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +/* +Set the default cursor for buttons. +*/ + +button, +[role='button'] { + cursor: pointer; +} + +/* +Make sure disabled buttons don't get the pointer cursor. +*/ + +:disabled { + cursor: default; +} + +/* +1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) +2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) + This can trigger a poorly considered lint error in some tools but is included by design. +*/ + +img, +svg, +video, +canvas, +audio, +iframe, +embed, +object { + display: block; + /* 1 */ + vertical-align: middle; + /* 2 */ +} + +/* +Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) +*/ + +img, +video { + max-width: 100%; + height: auto; +} + +/* Make elements with the HTML hidden attribute stay hidden by default */ + +[hidden] { + display: none; +} + +input::-webkit-outer-spin-button, +input::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; +} + +input[type='number'] { + -moz-appearance: textfield; + /* Firefox */ +} + +*, +::before, +::after { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; +} + +::backdrop { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; +} + +.fixed { + position: fixed; +} + +.inset-0 { + top: 0px; + right: 0px; + bottom: 0px; + left: 0px; +} + +.ml-2 { + margin-left: 0.5rem; +} + +.mt-4 { + margin-top: 1rem; +} + +.mt-auto { + margin-top: auto; +} + +.block { + display: block; +} + +.inline-block { + display: inline-block; +} + +.flex { + display: flex; +} + +.hidden { + display: none; +} + +.h-12 { + height: 3rem; +} + +.h-16 { + height: 4rem; +} + +.h-4 { + height: 1rem; +} + +.h-6 { + height: 1.5rem; +} + +.h-8 { + height: 2rem; +} + +.h-96 { + height: 24rem; +} + +.h-full { + height: 100%; +} + +.h-max { + height: -moz-max-content; + height: max-content; +} + +.min-h-\[96px\] { + min-height: 96px; +} + +.min-h-screen { + min-height: 100vh; +} + +.w-10 { + width: 2.5rem; +} + +.w-16 { + width: 4rem; +} + +.w-24 { + width: 6rem; +} + +.w-4 { + width: 1rem; +} + +.w-8 { + width: 2rem; +} + +.w-96 { + width: 24rem; +} + +.w-full { + width: 100%; +} + +.w-max { + width: -moz-max-content; + width: max-content; +} + +.w-min { + width: -moz-min-content; + width: min-content; +} + +.w-screen { + width: 100vw; +} + +.max-w-full { + max-width: 100%; +} + +.max-w-screen-xl { + max-width: 1280px; +} + +.max-w-xl { + max-width: 36rem; +} + +.flex-grow { + flex-grow: 1; +} + +@keyframes spin { + to { + transform: rotate(360deg); + } +} + +.animate-spin { + animation: spin 1s linear infinite; +} + +.flex-row { + flex-direction: row; +} + +.flex-col { + flex-direction: column; +} + +.flex-col-reverse { + flex-direction: column-reverse; +} + +.flex-wrap { + flex-wrap: wrap; +} + +.items-end { + align-items: flex-end; +} + +.items-center { + align-items: center; +} + +.justify-center { + justify-content: center; +} + +.justify-between { + justify-content: space-between; +} + +.justify-around { + justify-content: space-around; +} + +.gap-1 { + gap: 0.25rem; +} + +.gap-2 { + gap: 0.5rem; +} + +.gap-4 { + gap: 1rem; +} + +.gap-6 { + gap: 1.5rem; +} + +.gap-8 { + gap: 2rem; +} + +.overflow-hidden { + overflow: hidden; +} + +.truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.break-all { + word-break: break-all; +} + +.border { + border-width: 1px; +} + +.border-green-400 { + --tw-border-opacity: 1; + border-color: rgb(74 222 128 / var(--tw-border-opacity)); +} + +.border-green-400\/50 { + border-color: rgb(74 222 128 / 0.5); +} + +.border-orange-400\/50 { + border-color: rgb(251 146 60 / 0.5); +} + +.border-red-400 { + --tw-border-opacity: 1; + border-color: rgb(248 113 113 / var(--tw-border-opacity)); +} + +.border-red-400\/50 { + border-color: rgb(248 113 113 / 0.5); +} + +.border-slate-400\/30 { + border-color: rgb(148 163 184 / 0.3); +} + +.border-white\/50 { + border-color: rgb(255 255 255 / 0.5); +} + +.border-white\/90 { + border-color: rgb(255 255 255 / 0.9); +} + +.bg-black { + --tw-bg-opacity: 1; + background-color: rgb(0 0 0 / var(--tw-bg-opacity)); +} + +.bg-gray-500\/30 { + background-color: rgb(107 114 128 / 0.3); +} + +.bg-gray-500\/50 { + background-color: rgb(107 114 128 / 0.5); +} + +.bg-green-200\/10 { + background-color: rgb(187 247 208 / 0.1); +} + +.bg-green-400\/10 { + background-color: rgb(74 222 128 / 0.1); +} + +.bg-orange-400\/10 { + background-color: rgb(251 146 60 / 0.1); +} + +.bg-red-200\/10 { + background-color: rgb(254 202 202 / 0.1); +} + +.bg-red-400\/10 { + background-color: rgb(248 113 113 / 0.1); +} + +.bg-transparent { + background-color: transparent; +} + +.bg-white\/10 { + background-color: rgb(255 255 255 / 0.1); +} + +.p-2 { + padding: 0.5rem; +} + +.p-4 { + padding: 1rem; +} + +.p-6 { + padding: 1.5rem; +} + +.px-2 { + padding-left: 0.5rem; + padding-right: 0.5rem; +} + +.px-4 { + padding-left: 1rem; + padding-right: 1rem; +} + +.px-8 { + padding-left: 2rem; + padding-right: 2rem; +} + +.py-1 { + padding-top: 0.25rem; + padding-bottom: 0.25rem; +} + +.py-2 { + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} + +.py-3 { + padding-top: 0.75rem; + padding-bottom: 0.75rem; +} + +.py-4 { + padding-top: 1rem; + padding-bottom: 1rem; +} + +.py-8 { + padding-top: 2rem; + padding-bottom: 2rem; +} + +.pl-4 { + padding-left: 1rem; +} + +.text-right { + text-align: right; +} + +.font-mono { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace; +} + +.font-serif { + font-family: Inter, sans-serif; +} + +.text-2xl { + font-size: 1.5rem; + line-height: 2rem; +} + +.text-4xl { + font-size: 2.25rem; + line-height: 2.5rem; +} + +.text-lg { + font-size: 1.125rem; + line-height: 1.75rem; +} + +.text-sm { + font-size: 0.875rem; + line-height: 1.25rem; +} + +.text-xl { + font-size: 1.25rem; + line-height: 1.75rem; +} + +.text-xs { + font-size: 0.75rem; + line-height: 1rem; +} + +.font-bold { + font-weight: 700; +} + +.font-extrabold { + font-weight: 800; +} + +.font-medium { + font-weight: 500; +} + +.font-semibold { + font-weight: 600; +} + +.leading-tight { + line-height: 1.25; +} + +.text-accent { + --tw-text-opacity: 1; + color: rgb(108 207 246 / var(--tw-text-opacity)); +} + +.text-gray-500 { + --tw-text-opacity: 1; + color: rgb(107 114 128 / var(--tw-text-opacity)); +} + +.text-orange-600 { + --tw-text-opacity: 1; + color: rgb(234 88 12 / var(--tw-text-opacity)); +} + +.text-primary { + --tw-text-opacity: 1; + color: rgb(255 255 252 / var(--tw-text-opacity)); +} + +.text-white { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); +} + +.text-white\/50 { + color: rgb(255 255 255 / 0.5); +} + +.text-white\/75 { + color: rgb(255 255 255 / 0.75); +} + +.underline { + text-decoration-line: underline; +} + +.opacity-25 { + opacity: 0.25; +} + +.opacity-75 { + opacity: 0.75; +} + +.outline-none { + outline: 2px solid transparent; + outline-offset: 2px; +} + +.filter { + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); +} + +.duration-200 { + transition-duration: 200ms; +} + +.toggle-switch span span { + display: none; +} + +.toggle-switch { + display: inline-block; + position: relative; + overflow: visible; + padding: 0; + cursor: pointer; + width: 200px; + border: 1px solid #ccc; + border-radius: 5px; + height: 34px; +} + +.toggle-switch * { + box-sizing: border-box; +} + +.toggle-switch label, +.toggle-switch > span { + line-height: 20px; + height: 20px; + vertical-align: middle; +} + +.toggle-switch input:focus ~ a, +.toggle-switch input:focus + label { + outline: none; +} + +.toggle-switch label { + position: relative; + z-index: 3; + display: block; + width: 100%; +} + +.toggle-switch input { + position: absolute; + opacity: 0; + z-index: 5; +} + +.toggle-switch > span { + position: absolute; + left: 0; + width: calc(100% - 6px); + margin: 0; + text-align: left; + white-space: nowrap; + margin: 0 3px; +} + +.toggle-switch > span span { + position: absolute; + top: 0; + z-index: 5; + display: block; + margin-left: 50px; + text-align: left; + font-size: 0.9em; + width: auto; + left: 0; + top: -1px; + opacity: 1; + width: 40%; + text-align: center; + line-height: 34px; +} + +.toggle-switch a { + position: absolute; + right: 50%; + z-index: 4; + display: block; + top: 3px; + bottom: 3px; + padding: 0; + left: 3px; + width: 50%; + background-color: #666; + border-radius: 4px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.toggle-switch > span span:first-of-type { + color: #fff; + opacity: 1; + left: 0; + margin: 0; + width: 50%; +} + +.toggle-switch > span span:last-of-type { + left: auto; + right: 0; + color: #999; + margin: 0; + width: 50%; +} + +.toggle-switch > span:before { + content: ''; + display: block; + width: 100%; + height: 100%; + position: absolute; + left: 0; + top: -2px; + border-radius: 30px; +} + +.toggle-switch input:checked ~ a { + left: calc(50% - 3px); +} + +.toggle-switch input:checked ~ span span:first-of-type { + left: 0; + color: #999; +} + +.toggle-switch input:checked ~ span span:last-of-type { + color: #fff; +} + +.placeholder\:text-gray-600::-moz-placeholder { + --tw-text-opacity: 1; + color: rgb(75 85 99 / var(--tw-text-opacity)); +} + +.placeholder\:text-gray-600::placeholder { + --tw-text-opacity: 1; + color: rgb(75 85 99 / var(--tw-text-opacity)); +} + +.focus-within\:border-white\/80:focus-within { + border-color: rgb(255 255 255 / 0.8); +} + +.focus-within\:border-white\/90:focus-within { + border-color: rgb(255 255 255 / 0.9); +} + +.focus-within\:bg-white\/5:focus-within { + background-color: rgb(255 255 255 / 0.05); +} + +.hover\:rotate-45:hover { + --tw-rotate: 45deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.hover\:underline:hover { + text-decoration-line: underline; +} + +.focus\:border-white\/90:focus { + border-color: rgb(255 255 255 / 0.9); +} + +.focus\:bg-gray-500:focus { + --tw-bg-opacity: 1; + background-color: rgb(107 114 128 / var(--tw-bg-opacity)); +} + +.focus\:bg-gray-500\/20:focus { + background-color: rgb(107 114 128 / 0.2); +} + +.disabled\:opacity-50:disabled { + opacity: 0.5; +} + +@media (min-width: 640px) { + .sm\:block { + display: block; + } + + .sm\:flex-row { + flex-direction: row; + } + + .sm\:p-4 { + padding: 1rem; + } + + .sm\:px-0 { + padding-left: 0px; + padding-right: 0px; + } + + .sm\:py-6 { + padding-top: 1.5rem; + padding-bottom: 1.5rem; + } + + .sm\:text-base { + font-size: 1rem; + line-height: 1.5rem; + } + + .sm\:text-lg { + font-size: 1.125rem; + line-height: 1.75rem; + } +} + +@media (min-width: 768px) { + .md\:pt-24 { + padding-top: 6rem; + } +} diff --git a/docs/favicon.webp b/docs/favicon.webp new file mode 100644 index 0000000..11dcc7c Binary files /dev/null and b/docs/favicon.webp differ diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..4641066 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,31 @@ + + + + bouquet + + + + + + + + + +
+ + + + + diff --git a/docs/js/components/App.d.ts b/docs/js/components/App.d.ts new file mode 100644 index 0000000..1ce870f --- /dev/null +++ b/docs/js/components/App.d.ts @@ -0,0 +1,2 @@ +export declare function App(): import("preact").JSX.Element; +//# sourceMappingURL=App.d.ts.map \ No newline at end of file diff --git a/docs/js/components/App.d.ts.map b/docs/js/components/App.d.ts.map new file mode 100644 index 0000000..f77e1bb --- /dev/null +++ b/docs/js/components/App.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../ts/components/App.tsx"],"names":[],"mappings":"AAWA,wBAAgB,GAAG,iCA6BlB"} \ No newline at end of file diff --git a/docs/js/components/App.js b/docs/js/components/App.js new file mode 100644 index 0000000..cda52cb --- /dev/null +++ b/docs/js/components/App.js @@ -0,0 +1,16 @@ +import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "preact/jsx-runtime"; +import { Import } from './Import.js'; +import { Submit } from './Submit.js'; +import { Button } from './Button.js'; +import { Transactions } from './Transactions.js'; +import { connectBrowserProvider } from '../library/provider.js'; +import { Navbar } from './Navbar.js'; +import { createGlobalState } from '../stores.js'; +import { Footer } from './Footer.js'; +import { ConfigureKeys } from './ConfigureKeys.js'; +import { ConfigureFunding } from './ConfigureFunding.js'; +export function App() { + const state = createGlobalState(); + return (_jsxs("main", { class: 'bg-black text-primary w-screen max-w-screen overflow-hidden min-h-screen sm:p-4 p-6 gap-4 font-serif flex flex-col items-center max-w-screen-xl', children: [_jsx(Navbar, { ...state }), _jsx("div", { className: 'p-4 mt-4 flex flex-col gap-8 w-full', children: !state.provider.value && state.bundle.value ? (_jsxs("article", { className: 'items-center flex flex-col gap-4 py-8', children: [_jsx("h2", { class: 'text-2xl font-bold', children: "Welcome Back" }), _jsx(Button, { onClick: () => connectBrowserProvider(state.provider, state.blockInfo, state.bundle.peek()?.containsFundingTx ? state.signers : undefined, state.bouquetSettings), children: "Connect Wallet" })] })) : (_jsxs(_Fragment, { children: [_jsx(Import, { ...state }), state.bundle.value ? _jsx(Transactions, { ...state }) : null, _jsxs("h2", { className: 'font-bold text-2xl', children: [_jsx("span", { class: 'text-gray-500', children: "2." }), " Configure"] }), state.bundle.value ? (_jsxs(_Fragment, { children: [_jsx(ConfigureKeys, { ...state }), _jsx(ConfigureFunding, { ...state })] })) : _jsx("p", { children: "No transactions imported yet." }), _jsx(Submit, { ...state })] })) }), _jsx(Footer, {})] })); +} +//# sourceMappingURL=App.js.map \ No newline at end of file diff --git a/docs/js/components/App.js.map b/docs/js/components/App.js.map new file mode 100644 index 0000000..2cd824e --- /dev/null +++ b/docs/js/components/App.js.map @@ -0,0 +1 @@ +{"version":3,"file":"App.js","sourceRoot":"","sources":["../../ts/components/App.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAChD,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAA;AAC/D,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAExD,MAAM,UAAU,GAAG;IAClB,MAAM,KAAK,GAAG,iBAAiB,EAAE,CAAA;IAEjC,OAAO,CACN,gBAAM,KAAK,EAAC,iJAAiJ,aAC3J,KAAC,MAAM,OAAK,KAAK,GAAI,EACrB,cAAK,SAAS,EAAC,qCAAqC,YAClD,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAC9C,mBAAS,SAAS,EAAC,uCAAuC,aACzD,aAAI,KAAK,EAAC,oBAAoB,6BAAkB,EAChD,KAAC,MAAM,IACN,OAAO,EAAE,GAAG,EAAE,CAAC,sBAAsB,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,iBAAiB,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,EAAE,KAAK,CAAC,eAAe,CAAC,+BAGzJ,IACA,CACV,CAAC,CAAC,CAAC,CACH,8BACC,KAAC,MAAM,OAAK,KAAK,GAAI,EACpB,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,KAAC,YAAY,OAAK,KAAK,GAAI,CAAC,CAAC,CAAC,IAAI,EACxD,cAAI,SAAS,EAAC,oBAAoB,aAAC,eAAM,KAAK,EAAC,eAAe,mBAAU,kBAAe,EACtF,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,8BAAE,KAAC,aAAa,OAAK,KAAK,GAAI,EAAA,KAAC,gBAAgB,OAAK,KAAK,GAAI,IAAG,CAAC,CAAC,CAAC,CAAC,wDAAoC,EAC/H,KAAC,MAAM,OAAK,KAAK,GAAI,IACnB,CACH,GACI,EACP,KAAC,MAAM,KAAG,IACJ,CACP,CAAA;AACF,CAAC","sourcesContent":["import { Import } from './Import.js'\nimport { Submit } from './Submit.js'\nimport { Button } from './Button.js'\nimport { Transactions } from './Transactions.js'\nimport { connectBrowserProvider } from '../library/provider.js'\nimport { Navbar } from './Navbar.js'\nimport { createGlobalState } from '../stores.js'\nimport { Footer } from './Footer.js'\nimport { ConfigureKeys } from './ConfigureKeys.js'\nimport { ConfigureFunding } from './ConfigureFunding.js'\n\nexport function App() {\n\tconst state = createGlobalState()\n\n\treturn (\n\t\t
\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t{!state.provider.value && state.bundle.value ? (\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

Welcome Back

\n\t\t\t\t\t\t\t connectBrowserProvider(state.provider, state.blockInfo, state.bundle.peek()?.containsFundingTx ? state.signers : undefined, state.bouquetSettings)}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\tConnect Wallet\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{state.bundle.value ? : null}\n\t\t\t\t\t\t\t

2. Configure

\n\t\t\t\t\t\t\t{state.bundle.value ? (<>) :

No transactions imported yet.

}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t)}\n\t\t\t\t
\n\t\t\t
\n\t)\n}\n"]} \ No newline at end of file diff --git a/docs/js/components/Blockie.d.ts b/docs/js/components/Blockie.d.ts new file mode 100644 index 0000000..899aa4f --- /dev/null +++ b/docs/js/components/Blockie.d.ts @@ -0,0 +1,10 @@ +import { JSX } from 'preact/jsx-runtime'; +import { ReadonlySignal, Signal } from '@preact/signals'; +interface BlockieProps { + address: Signal | ReadonlySignal; + scale?: Signal; + style?: JSX.CSSProperties; +} +export declare function Blockie(props: BlockieProps): JSX.Element; +export {}; +//# sourceMappingURL=Blockie.d.ts.map \ No newline at end of file diff --git a/docs/js/components/Blockie.d.ts.map b/docs/js/components/Blockie.d.ts.map new file mode 100644 index 0000000..b0d528d --- /dev/null +++ b/docs/js/components/Blockie.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"Blockie.d.ts","sourceRoot":"","sources":["../../ts/components/Blockie.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAA;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,EAAmB,MAAM,iBAAiB,CAAA;AA6CzE,UAAU,YAAY;IACrB,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IACvB,KAAK,CAAC,EAAE,GAAG,CAAC,aAAa,CAAA;CACzB;AA+HD,wBAAgB,OAAO,CAAC,KAAK,EAAE,YAAY,eAmB1C"} \ No newline at end of file diff --git a/docs/js/components/Blockie.js b/docs/js/components/Blockie.js new file mode 100644 index 0000000..45a6bfa --- /dev/null +++ b/docs/js/components/Blockie.js @@ -0,0 +1,149 @@ +import { jsx as _jsx } from "preact/jsx-runtime"; +import { useSignalEffect } from '@preact/signals'; +import { useAsyncState } from '../library/asyncState.js'; +import { DataURLCache } from '../library/DataURLCache.js'; +const dataURLCache = new DataURLCache(); +class Future { + constructor() { + this.then = (onfulfilled, onrejected) => { + return this.promise.then(onfulfilled, onrejected); + }; + this.resolve = (value) => { + this.resolveFunction(value); + }; + this.reject = (reason) => { + this.rejectFunction(reason); + }; + let resolveFunction; + let rejectFunction; + this.promise = new Promise((resolve, reject) => { + resolveFunction = resolve; + rejectFunction = reject; + }); + // the function passed to the Promise constructor is called before the constructor returns, so we can be sure the resolve and reject functions have been set by here even if the compiler can't verify + this.resolveFunction = resolveFunction; + this.rejectFunction = rejectFunction; + } + get asPromise() { return this.promise; } +} +function addressString(address) { + return `0x${address.toString(16).padStart(40, '0')}`; +} +function generateIdenticon(address, scale, canvasRef) { + // NOTE -- Majority of this code is referenced from: https://github.com/alexvandesande/blockies + // Mostly to ensure congruence to Ethereum Mist's Identicons + // The random number is a js implementation of the Xorshift PRNG + const randseed = new Array(4); // Xorshift: [x, y, z, w] 32 bit values + function seedrand(seed) { + for (let i = 0; i < randseed.length; i++) { + randseed[i] = 0; + } + for (let i = 0; i < seed.length; i++) { + randseed[i % 4] = ((randseed[i % 4] << 5) - randseed[i % 4]) + seed.charCodeAt(i); + } + } + function rand() { + // based on Java's String.hashCode(), expanded to 4 32bit values + const t = randseed[0] ^ (randseed[0] << 11); + randseed[0] = randseed[1]; + randseed[1] = randseed[2]; + randseed[2] = randseed[3]; + randseed[3] = (randseed[3] ^ (randseed[3] >> 19) ^ t ^ (t >> 8)); + return (randseed[3] >>> 0) / ((1 << 31) >>> 0); + } + function createColor() { + // saturation is the whole color spectrum + const h = Math.floor(rand() * 360); + // saturation goes from 40 to 100, it avoids greyish colors + const s = ((rand() * 60) + 40) + '%'; + // lightness can be anything from 0 to 100, but probabilities are a bell curve around 50% + const l = ((rand() + rand() + rand() + rand()) * 25) + '%'; + const color = 'hsl(' + h + ',' + s + ',' + l + ')'; + return color; + } + function createImageData(size) { + const width = size; // Only support square icons for now + const height = size; + const dataWidth = Math.ceil(width / 2); + const mirrorWidth = width - dataWidth; + const data = []; + for (let y = 0; y < height; y++) { + let row = []; + for (let x = 0; x < dataWidth; x++) { + // this makes foreground and background color to have a 43% (1/2.3) probability + // spot color has 13% chance + row[x] = Math.floor(rand() * 2.3); + } + const r = row.slice(0, mirrorWidth); + r.reverse(); + row = row.concat(r); + for (let i = 0; i < row.length; i++) { + data.push(row[i]); + } + } + return data; + } + function setCanvas(identicon, imageData, color, scale, bgcolor, spotcolor) { + const width = Math.sqrt(imageData.length); + const size = width * scale; + identicon.width = size; + identicon.style.width = `${size}px`; + identicon.height = size; + identicon.style.height = `${size}px`; + const cc = identicon.getContext('2d'); + cc.fillStyle = bgcolor; + cc.fillRect(0, 0, identicon.width, identicon.height); + cc.fillStyle = color; + for (let i = 0; i < imageData.length; i++) { + // if data is 2, choose spot color, if 1 choose foreground + cc.fillStyle = (imageData[i] === 1) ? color : spotcolor; + // if data is 0, leave the background + if (imageData[i]) { + const row = Math.floor(i / width); + const col = i % width; + cc.fillRect(col * scale, row * scale, scale, scale); + } + } + } + const seed = addressString(address); + seedrand(seed); + const color = createColor(); + const bgcolor = createColor(); + const spotcolor = createColor(); + const imageData = createImageData(8); + const canvas = setCanvas(canvasRef, imageData, color, scale, bgcolor, spotcolor); + return canvas; +} +async function renderBlockieToUrl(address, scale) { + const key = `${address.value}!${scale?.value || 4}`; + const cacheResult = dataURLCache.get(key); + if (cacheResult !== undefined) + return cacheResult; + const future = new Future(); + const element = document.createElement('canvas'); + generateIdenticon(address.value, scale?.value || 4, element); + element.toBlob((blob) => { + if (!blob) + return; + const dataUrl = URL.createObjectURL(blob); + dataURLCache.set(dataUrl, key); + future.resolve(dataUrl); + }); + return await future; +} +export function Blockie(props) { + const dimension = 8 * (props.scale?.value || 4); + const { value: dataURL, waitFor } = useAsyncState(); + useSignalEffect(() => { + props.address.value; + waitFor(async () => renderBlockieToUrl(props.address, props.scale)); + }); + return _jsx("img", { src: dataURL.value.state !== 'resolved' ? 'data:image/gif;base64,R0lGODlhAQABAAAAACwAAAAAAQABAAA=' : dataURL.value.value, style: { + ...props.style, + width: `${dimension}px`, + height: `${dimension}px`, + minWidth: `${dimension}px`, + minHeight: `${dimension}px`, + } }); +} +//# sourceMappingURL=Blockie.js.map \ No newline at end of file diff --git a/docs/js/components/Blockie.js.map b/docs/js/components/Blockie.js.map new file mode 100644 index 0000000..d7368da --- /dev/null +++ b/docs/js/components/Blockie.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Blockie.js","sourceRoot":"","sources":["../../ts/components/Blockie.tsx"],"names":[],"mappings":"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","sourcesContent":["import { JSX } from 'preact/jsx-runtime'\nimport { ReadonlySignal, Signal, useSignalEffect } from '@preact/signals'\nimport { useAsyncState } from '../library/asyncState.js'\nimport { DataURLCache } from '../library/DataURLCache.js'\n\nconst dataURLCache = new DataURLCache()\n\nclass Future implements PromiseLike {\n\tprivate promise: Promise\n\tprivate resolveFunction: (value: T | PromiseLike) => void\n\tprivate rejectFunction: (reason: Error) => void\n\n\tconstructor() {\n\t\tlet resolveFunction: (value: T | PromiseLike) => void\n\t\tlet rejectFunction: (reason: Error) => void\n\t\tthis.promise = new Promise((resolve: (value: T | PromiseLike) => void, reject: (reason: Error) => void) => {\n\t\t\tresolveFunction = resolve\n\t\t\trejectFunction = reject\n\t\t})\n\t\t// the function passed to the Promise constructor is called before the constructor returns, so we can be sure the resolve and reject functions have been set by here even if the compiler can't verify\n\t\tthis.resolveFunction = resolveFunction!\n\t\tthis.rejectFunction = rejectFunction!\n\t}\n\n\tpublic get asPromise() { return this.promise }\n\n\tpublic readonly then = (\n\t\tonfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null,\n\t\tonrejected?: ((reason: Error) => TResult2 | PromiseLike) | undefined | null\n\t): PromiseLike => {\n\t\treturn this.promise.then(onfulfilled, onrejected)\n\t}\n\n\tpublic readonly resolve = (value: T | PromiseLike) => {\n\t\tthis.resolveFunction!(value)\n\t}\n\n\tpublic readonly reject = (reason: Error) => {\n\t\tthis.rejectFunction!(reason)\n\t}\n}\n\nfunction addressString(address: bigint) {\n\treturn `0x${address.toString(16).padStart(40, '0')}`\n}\n\ninterface BlockieProps {\n\taddress: Signal | ReadonlySignal,\n\tscale?: Signal,\n\tstyle?: JSX.CSSProperties\n}\n\nfunction generateIdenticon(address: bigint, scale: number, canvasRef: HTMLCanvasElement) {\n\t// NOTE -- Majority of this code is referenced from: https://github.com/alexvandesande/blockies\n\t// Mostly to ensure congruence to Ethereum Mist's Identicons\n\n\t// The random number is a js implementation of the Xorshift PRNG\n\tconst randseed = new Array(4) // Xorshift: [x, y, z, w] 32 bit values\n\n\tfunction seedrand(seed: string) {\n\t\tfor (let i = 0; i < randseed.length; i++) {\n\t\t\trandseed[i] = 0\n\t\t}\n\t\tfor (let i = 0; i < seed.length; i++) {\n\t\t\trandseed[i % 4] = ((randseed[i % 4] << 5) - randseed[i % 4]) + seed.charCodeAt(i)\n\t\t}\n\t}\n\n\tfunction rand() {\n\t\t// based on Java's String.hashCode(), expanded to 4 32bit values\n\t\tconst t = randseed[0] ^ (randseed[0] << 11)\n\n\t\trandseed[0] = randseed[1]\n\t\trandseed[1] = randseed[2]\n\t\trandseed[2] = randseed[3]\n\t\trandseed[3] = (randseed[3] ^ (randseed[3] >> 19) ^ t ^ (t >> 8))\n\n\t\treturn (randseed[3] >>> 0) / ((1 << 31) >>> 0)\n\t}\n\n\tfunction createColor() {\n\t\t// saturation is the whole color spectrum\n\t\tconst h = Math.floor(rand() * 360)\n\t\t// saturation goes from 40 to 100, it avoids greyish colors\n\t\tconst s = ((rand() * 60) + 40) + '%'\n\t\t// lightness can be anything from 0 to 100, but probabilities are a bell curve around 50%\n\t\tconst l = ((rand() + rand() + rand() + rand()) * 25) + '%'\n\n\t\tconst color = 'hsl(' + h + ',' + s + ',' + l + ')'\n\t\treturn color\n\t}\n\n\tfunction createImageData(size: number) {\n\t\tconst width = size // Only support square icons for now\n\t\tconst height = size\n\n\t\tconst dataWidth = Math.ceil(width / 2)\n\t\tconst mirrorWidth = width - dataWidth\n\n\t\tconst data = []\n\t\tfor (let y = 0; y < height; y++) {\n\t\t\tlet row = []\n\t\t\tfor (let x = 0; x < dataWidth; x++) {\n\t\t\t\t// this makes foreground and background color to have a 43% (1/2.3) probability\n\t\t\t\t// spot color has 13% chance\n\t\t\t\trow[x] = Math.floor(rand() * 2.3)\n\t\t\t}\n\t\t\tconst r = row.slice(0, mirrorWidth)\n\t\t\tr.reverse()\n\t\t\trow = row.concat(r)\n\n\t\t\tfor (let i = 0; i < row.length; i++) {\n\t\t\t\tdata.push(row[i])\n\t\t\t}\n\t\t}\n\n\t\treturn data\n\t}\n\n\tfunction setCanvas(identicon: HTMLCanvasElement, imageData: number[], color: string, scale: number, bgcolor: string, spotcolor: string) {\n\t\tconst width = Math.sqrt(imageData.length)\n\t\tconst size = width * scale\n\n\t\tidenticon.width = size\n\t\tidenticon.style.width = `${size}px`\n\n\t\tidenticon.height = size\n\t\tidenticon.style.height = `${size}px`\n\n\t\tconst cc = identicon.getContext('2d')\n\t\tcc!.fillStyle = bgcolor\n\t\tcc!.fillRect(0, 0, identicon.width, identicon.height)\n\t\tcc!.fillStyle = color\n\n\t\tfor (let i = 0; i < imageData.length; i++) {\n\t\t\t// if data is 2, choose spot color, if 1 choose foreground\n\t\t\tcc!.fillStyle = (imageData[i] === 1) ? color : spotcolor\n\n\t\t\t// if data is 0, leave the background\n\t\t\tif (imageData[i]) {\n\t\t\t\tconst row = Math.floor(i / width)\n\t\t\t\tconst col = i % width\n\n\t\t\t\tcc!.fillRect(col * scale, row * scale, scale, scale)\n\t\t\t}\n\t\t}\n\t}\n\n\tconst seed = addressString(address)\n\n\tseedrand(seed)\n\n\tconst color = createColor()\n\tconst bgcolor = createColor()\n\tconst spotcolor = createColor()\n\tconst imageData = createImageData(8)\n\tconst canvas = setCanvas(canvasRef, imageData, color, scale, bgcolor, spotcolor)\n\n\treturn canvas\n}\n\nasync function renderBlockieToUrl(address: Signal, scale: Signal | undefined) {\n\tconst key = `${address.value}!${scale?.value || 4}`\n\tconst cacheResult = dataURLCache.get(key)\n\tif (cacheResult !== undefined) return cacheResult\n\tconst future = new Future()\n\tconst element = document.createElement('canvas')\n\tgenerateIdenticon(address.value, scale?.value || 4, element)\n\telement.toBlob((blob) => {\n\t\tif (!blob) return\n\t\tconst dataUrl = URL.createObjectURL(blob)\n\t\tdataURLCache.set(dataUrl, key)\n\t\tfuture.resolve(dataUrl)\n\t})\n\treturn await future\n}\n\nexport function Blockie(props: BlockieProps) {\n\tconst dimension = 8 * (props.scale?.value || 4)\n\tconst { value: dataURL, waitFor } = useAsyncState()\n\tuseSignalEffect(() => {\n\t\tprops.address.value\n\t\twaitFor(async () => renderBlockieToUrl(props.address, props.scale))\n\t})\n\treturn unknown; +}) => import("preact").JSX.Element; +//# sourceMappingURL=Button.d.ts.map \ No newline at end of file diff --git a/docs/js/components/Button.d.ts.map b/docs/js/components/Button.d.ts.map new file mode 100644 index 0000000..777cf8f --- /dev/null +++ b/docs/js/components/Button.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"Button.d.ts","sourceRoot":"","sources":["../../ts/components/Button.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAA;AAQ1C,eAAO,MAAM,MAAM;cAMR,iBAAiB;;;aAGlB,MAAM,OAAO;kCAOtB,CAAA"} \ No newline at end of file diff --git a/docs/js/components/Button.js b/docs/js/components/Button.js new file mode 100644 index 0000000..c8d4322 --- /dev/null +++ b/docs/js/components/Button.js @@ -0,0 +1,10 @@ +import { jsx as _jsx } from "preact/jsx-runtime"; +const classNames = { + primary: 'h-12 px-4 border border-white/50 bg-gray-500/50 outline-none focus:border-white/90 focus:bg-gray-500/20 flex items-center gap-2 justify-center disabled:opacity-50', + secondary: 'h-12 px-4 border border-white/50 bg-black outline-none focus:border-white/90 focus:bg-gray-500/20 text-white flex items-center gap-2 justify-center disabled:opacity-50', + full: 'px-4 h-16 border border-white/50 text-lg bg-white/10 flex items-center gap-2 justify-center outline-none focus:border-white/90 focus:bg-gray-500 disabled:opacity-50' +}; +export const Button = ({ children, disabled, variant, onClick, }) => { + return (_jsx("button", { onClick: onClick, disabled: disabled ?? false, className: classNames[variant ?? 'primary'], children: children })); +}; +//# sourceMappingURL=Button.js.map \ No newline at end of file diff --git a/docs/js/components/Button.js.map b/docs/js/components/Button.js.map new file mode 100644 index 0000000..adb4480 --- /dev/null +++ b/docs/js/components/Button.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Button.js","sourceRoot":"","sources":["../../ts/components/Button.tsx"],"names":[],"mappings":";AAEA,MAAM,UAAU,GAAG;IAClB,OAAO,EAAE,oKAAoK;IAC7K,SAAS,EAAE,yKAAyK;IACpL,IAAI,EAAE,sKAAsK;CAC5K,CAAA;AAED,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,EACtB,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,OAAO,GAMP,EAAE,EAAE;IACJ,OAAO,CACN,iBAAQ,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,IAAI,KAAK,EAAE,SAAS,EAAE,UAAU,CAAC,OAAO,IAAI,SAAS,CAAC,YAChG,QAAQ,GACD,CACT,CAAA;AACF,CAAC,CAAA","sourcesContent":["import { ComponentChildren } from 'preact'\n\nconst classNames = {\n\tprimary: 'h-12 px-4 border border-white/50 bg-gray-500/50 outline-none focus:border-white/90 focus:bg-gray-500/20 flex items-center gap-2 justify-center disabled:opacity-50',\n\tsecondary: 'h-12 px-4 border border-white/50 bg-black outline-none focus:border-white/90 focus:bg-gray-500/20 text-white flex items-center gap-2 justify-center disabled:opacity-50',\n\tfull: 'px-4 h-16 border border-white/50 text-lg bg-white/10 flex items-center gap-2 justify-center outline-none focus:border-white/90 focus:bg-gray-500 disabled:opacity-50'\n}\n\nexport const Button = ({\n\tchildren,\n\tdisabled,\n\tvariant,\n\tonClick,\n}: {\n\tchildren: ComponentChildren,\n\tdisabled?: boolean\n\tvariant?: 'primary' | 'secondary' | 'full'\n\tonClick: () => unknown\n}) => {\n\treturn (\n\t\t\n\t)\n}\n"]} \ No newline at end of file diff --git a/docs/js/components/ConfigureFunding.d.ts b/docs/js/components/ConfigureFunding.d.ts new file mode 100644 index 0000000..0ecff5a --- /dev/null +++ b/docs/js/components/ConfigureFunding.d.ts @@ -0,0 +1,14 @@ +import { ReadonlySignal, Signal } from '@preact/signals'; +import { JSX } from 'preact/jsx-runtime'; +import { ProviderStore } from '../library/provider.js'; +import { BlockInfo, Bundle, Signers } from '../types/types.js'; +import { BouquetSettings } from '../types/bouquetTypes.js'; +export declare const ConfigureFunding: ({ provider, bouquetSettings, bundle, fundingAmountMin, signers, blockInfo, }: { + provider: Signal; + bundle: Signal; + signers: Signal; + fundingAmountMin: ReadonlySignal; + blockInfo: Signal; + bouquetSettings: Signal; +}) => JSX.Element; +//# sourceMappingURL=ConfigureFunding.d.ts.map \ No newline at end of file diff --git a/docs/js/components/ConfigureFunding.d.ts.map b/docs/js/components/ConfigureFunding.d.ts.map new file mode 100644 index 0000000..b1569b2 --- /dev/null +++ b/docs/js/components/ConfigureFunding.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"ConfigureFunding.d.ts","sourceRoot":"","sources":["../../ts/components/ConfigureFunding.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAS,cAAc,EAAE,MAAM,EAA2C,MAAM,iBAAiB,CAAA;AAExG,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAA;AAGxC,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAGtD,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAG9D,OAAO,EAAkB,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAG1E,eAAO,MAAM,gBAAgB;cAQlB,OAAO,aAAa,GAAG,SAAS,CAAC;YACnC,OAAO,MAAM,GAAG,SAAS,CAAC;aACzB,OAAO,OAAO,CAAC;sBACN,eAAe,MAAM,CAAC;eAC7B,OAAO,SAAS,CAAC;qBACX,OAAO,eAAe,CAAC;iBA6FxC,CAAA"} \ No newline at end of file diff --git a/docs/js/components/ConfigureFunding.js b/docs/js/components/ConfigureFunding.js new file mode 100644 index 0000000..6af58fc --- /dev/null +++ b/docs/js/components/ConfigureFunding.js @@ -0,0 +1,111 @@ +import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "preact/jsx-runtime"; +import { batch, useComputed, useSignal, useSignalEffect } from '@preact/signals'; +import { EtherSymbol, formatEther, getAddress, JsonRpcProvider } from 'ethers'; +import { useAsyncState } from '../library/asyncState.js'; +import { getMaxBaseFeeInFutureBlock } from '../library/bundleUtils.js'; +import { addressString } from '../library/utils.js'; +import { EthereumAddress } from '../types/ethereumTypes.js'; +import { Button } from './Button.js'; +import { SingleNotice } from './Warns.js'; +import { getNetwork } from '../constants.js'; +export const ConfigureFunding = ({ provider, bouquetSettings, bundle, fundingAmountMin, signers, blockInfo, }) => { + const signerKeys = useSignal({}); + const bouquetNetwork = useComputed(() => getNetwork(bouquetSettings.value, provider.value?.chainId || 1n)); + useSignalEffect(() => { + if (!bundle.value) + signerKeys.value = {}; + }); + if (bundle.peek() && Object.keys(signerKeys.peek()).length === 0) { + signerKeys.value = + bundle.value && Object.keys(signerKeys.peek()).length === 0 + ? bundle.value.uniqueSigners.reduce((curr, address) => { + curr[getAddress(address)] = { input: '', wallet: null }; + return curr; + }, {}) + : {}; + } + blockInfo.subscribe(() => { + if (provider.value && signers.value.burner) { + provider.value.provider.getBalance(signers.value.burner.address).then((balance) => (signers.value.burnerBalance = balance)); + } + }); + function copyBurnerToClipboard() { + if (!signers.value.burner) + return; + navigator.clipboard.writeText(signers.value.burner.address); + } + const showWithdrawModal = useSignal(false); + function openWithdrawModal() { + showWithdrawModal.value = true; + } + return (_jsxs(_Fragment, { children: [_jsx(WithdrawModal, { display: showWithdrawModal, blockInfo, signers, bouquetNetwork, provider }), bundle.value && bundle.value.containsFundingTx && signers.value.burner ? (_jsxs("div", { className: 'flex flex-col w-full gap-4', children: [_jsx("h3", { className: 'text-2xl font-semibold', children: "Deposit To Funding Account" }), _jsx("p", { className: 'text-orange-600 font-semibold', children: "This is a temporary account, send only enough needed plus a tiny bit to account for possible rising gas price changes." }), _jsxs("div", { className: 'flex items-center gap-2 flex-wrap', children: [_jsx(Button, { variant: 'secondary', onClick: copyBurnerToClipboard, children: _jsxs(_Fragment, { children: [_jsx("span", { className: 'text-xs sm:text-base', children: signers.value.burner.address }), _jsx("svg", { className: 'h-8 inline-block', "aria-hidden": 'true', fill: 'none', stroke: 'currentColor', "stroke-width": '1.5', viewBox: '0 0 24 24', xmlns: 'http://www.w3.org/2000/svg', children: _jsx("path", { d: 'M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6', "stroke-linecap": 'round', "stroke-linejoin": 'round' }) })] }) }), _jsx(Button, { variant: 'primary', onClick: openWithdrawModal, children: _jsxs("span", { className: 'flex gap-2 text-sm items-center', children: ["Withdraw ETH", _jsx("svg", { class: 'h-8 inline-block', "aria-hidden": 'true', fill: 'none', stroke: 'currentColor', "stroke-width": '1.5', viewBox: '0 0 24 24', xmlns: 'http://www.w3.org/2000/svg', children: _jsx("path", { d: 'M8.25 9.75h4.875a2.625 2.625 0 010 5.25H12M8.25 9.75L10.5 7.5M8.25 9.75L10.5 12m9-7.243V21.75l-3.75-1.5-3.75 1.5-3.75-1.5-3.75 1.5V4.757c0-1.108.806-2.057 1.907-2.185a48.507 48.507 0 0111.186 0c1.1.128 1.907 1.077 1.907 2.185z', "stroke-linecap": 'round', "stroke-linejoin": 'round' }) })] }) })] }), _jsxs("p", { className: 'font-semibold sm:text-lg', children: ["Wallet Balance: ", _jsxs("span", { className: 'font-medium font-mono', children: [EtherSymbol, formatEther(signers.value.burnerBalance)] }), _jsx("br", {}), "Minimum Required Balance: ", _jsxs("span", { className: 'font-medium font-mono', children: [EtherSymbol, formatEther(fundingAmountMin.value)] })] })] })) : null] })); +}; +const WithdrawModal = ({ display, blockInfo, signers, provider, bouquetNetwork }) => { + if (!display.value) + return null; + const recipientAddress = useSignal({ input: '' }); + const inputStyle = useComputed(() => `flex flex-col justify-center border h-16 outline-none px-4 focus-within:bg-white/5 bg-transparent ${recipientAddress.value.address ? 'border-green-400' : (recipientAddress.value.input ? 'border-red-400' : 'border-white/50 focus-within:border-white/80')}`); + function parseInput(input) { + const address = EthereumAddress.safeParse(input); + recipientAddress.value = { input, address: address.success ? address.value : undefined }; + } + const withdrawAmount = useComputed(() => { + let maxFeePerGas = getMaxBaseFeeInFutureBlock(blockInfo.value.baseFee, 5n) + blockInfo.value.priorityFee; + let fee = maxFeePerGas * 21000n; + let amount = signers.value.burnerBalance - fee; + return { amount, fee, maxFeePerGas }; + }); + const { value: signedMessage, waitFor } = useAsyncState(); + // Default check if we know the network, can also switch to true if sending to known RPC fails + const useBrowserProvider = useSignal(false); + useSignalEffect(() => { useBrowserProvider.value = Boolean(provider.value && bouquetNetwork.value.mempoolSubmitRpcEndpoint === undefined); }); + function withdraw() { + waitFor(async () => { + if (withdrawAmount.value.amount <= 0n) + throw 'Funding account\'s balance is to small to withdraw'; + if (!signers.value.burner) + throw 'No funding account found'; + if (!provider.value) + throw 'User not connected'; + if (!recipientAddress.value.address) + throw 'No recipient provided'; + if (useBrowserProvider.value === true) { + try { + const burnerWithBrowserProvider = signers.value.burner.connect(provider.value.provider); + const txInput = await burnerWithBrowserProvider.populateTransaction({ chainId: provider.value.chainId, from: signers.value.burner.address, to: addressString(recipientAddress.value.address), gasLimit: 21000, type: 2, value: withdrawAmount.value.amount, maxFeePerGas: withdrawAmount.value.maxFeePerGas }); + const tx = await burnerWithBrowserProvider.signTransaction(txInput); + const txHash = await provider.value.provider.send('eth_sendRawTransaction', [tx]); + return txHash; + } + catch (error) { + throw error; + } + } + if (bouquetNetwork.value.mempoolSubmitRpcEndpoint === undefined) + throw new Error('No RPC URL set and not connected to wallet'); + const fundingWithProvider = signers.value.burner.connect(new JsonRpcProvider(bouquetNetwork.value.mempoolSubmitRpcEndpoint)); + try { + const tx = await fundingWithProvider.sendTransaction({ chainId: provider.value.chainId, from: signers.value.burner.address, to: addressString(recipientAddress.value.address), gasLimit: 21000, type: 2, value: withdrawAmount.value.amount, maxFeePerGas: withdrawAmount.value.maxFeePerGas }); + fundingWithProvider.provider?.destroy(); + return tx.hash; + } + catch (error) { + console.warn('Error sending burner withdraw tx to known RPC:', bouquetNetwork.value.mempoolSubmitRpcEndpoint, ' error:', error); + fundingWithProvider.provider?.destroy(); + useBrowserProvider.value = true; + throw 'Unknown network! If you have Interceptor installed and simulation mode on please switch to signing mode and try again.'; + } + }); + } + function close() { + batch(() => { + display.value = false; + recipientAddress.value = { input: '' }; + signedMessage.value.state = 'inactive'; + }); + } + return (_jsx("div", { onClick: close, className: 'bg-white/10 w-full h-full inset-0 fixed p-4 flex flex-col items-center md:pt-24', children: _jsxs("div", { class: 'h-max w-full max-w-xl px-8 py-4 flex flex-col gap-4 bg-black', onClick: (e) => e.stopPropagation(), children: [_jsx("h2", { className: 'text-xl font-semibold', children: "Withdraw From Funding Account" }), _jsxs("div", { className: inputStyle.value, children: [_jsx("span", { className: 'text-sm text-gray-500', children: "ETH Recipient" }), _jsx("input", { onInput: (e) => parseInput(e.currentTarget.value), type: 'text', className: 'bg-transparent outline-none placeholder:text-gray-600', placeholder: '0x...' })] }), withdrawAmount.value.amount > 0n + ? (_jsxs("p", { children: ["Withdraw ", EtherSymbol, " ", formatEther(withdrawAmount.value.amount), " + ", EtherSymbol, " ", formatEther(withdrawAmount.value.fee), " fee"] })) + : (_jsxs("p", { children: ["Transfer fee (", EtherSymbol, " ", formatEther(withdrawAmount.value.fee), ") is more than funding account balance"] })), _jsx("div", { className: 'flex gap-2', children: _jsx(Button, { onClick: withdraw, variant: 'primary', children: "Withdraw" }) }), _jsx("p", { children: signedMessage.value.state === 'rejected' ? _jsx(SingleNotice, { variant: 'error', description: signedMessage.value.error.message, title: 'Error Withdrawing' }) : '' }), _jsx("p", { children: signedMessage.value.state === 'resolved' ? _jsx(SingleNotice, { variant: 'success', description: bouquetNetwork.value.blockExplorer ? _jsxs("span", { children: ["Transaction submitted with TX Hash ", _jsx("a", { className: 'hover:underline', href: `${bouquetNetwork.value.blockExplorer}tx/${signedMessage.value.value}`, target: '_blank', children: signedMessage.value.value })] }) : _jsxs("span", { children: ["Transaction submitted with TX Hash ", signedMessage.value.value] }), title: 'Transaction Submitted' }) : '' })] }) })); +}; +//# sourceMappingURL=ConfigureFunding.js.map \ No newline at end of file diff --git a/docs/js/components/ConfigureFunding.js.map b/docs/js/components/ConfigureFunding.js.map new file mode 100644 index 0000000..6c83177 --- /dev/null +++ b/docs/js/components/ConfigureFunding.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ConfigureFunding.js","sourceRoot":"","sources":["../../ts/components/ConfigureFunding.tsx"],"names":[],"mappings":"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","sourcesContent":["import { batch, ReadonlySignal, Signal, useComputed, useSignal, useSignalEffect } from '@preact/signals'\nimport { EtherSymbol, formatEther, getAddress, JsonRpcProvider, Wallet } from 'ethers'\nimport { JSX } from 'preact/jsx-runtime'\nimport { useAsyncState } from '../library/asyncState.js'\nimport { getMaxBaseFeeInFutureBlock } from '../library/bundleUtils.js'\nimport { ProviderStore } from '../library/provider.js'\nimport { addressString } from '../library/utils.js'\nimport { EthereumAddress } from '../types/ethereumTypes.js'\nimport { BlockInfo, Bundle, Signers } from '../types/types.js'\nimport { Button } from './Button.js'\nimport { SingleNotice } from './Warns.js'\nimport { BouquetNetwork, BouquetSettings } from '../types/bouquetTypes.js'\nimport { getNetwork } from '../constants.js'\n\nexport const ConfigureFunding = ({\n\tprovider,\n\tbouquetSettings,\n\tbundle,\n\tfundingAmountMin,\n\tsigners,\n\tblockInfo,\n}: {\n\tprovider: Signal\n\tbundle: Signal\n\tsigners: Signal\n\tfundingAmountMin: ReadonlySignal\n\tblockInfo: Signal,\n\tbouquetSettings: Signal\n}) => {\n\tconst signerKeys = useSignal<{\n\t\t[address: string]: { input: string; wallet: Wallet | null }\n\t}>({})\n\n\tconst bouquetNetwork = useComputed(() => getNetwork(bouquetSettings.value, provider.value?.chainId || 1n))\n\n\tuseSignalEffect(() => {\n\t\tif (!bundle.value) signerKeys.value = {}\n\t})\n\n\tif (bundle.peek() && Object.keys(signerKeys.peek()).length === 0) {\n\t\tsignerKeys.value =\n\t\t\tbundle.value && Object.keys(signerKeys.peek()).length === 0\n\t\t\t\t? bundle.value.uniqueSigners.reduce(\n\t\t\t\t\t(\n\t\t\t\t\t\tcurr: {\n\t\t\t\t\t\t\t[address: string]: { input: string; wallet: Wallet | null }\n\t\t\t\t\t\t},\n\t\t\t\t\t\taddress,\n\t\t\t\t\t) => {\n\t\t\t\t\t\tcurr[getAddress(address)] = { input: '', wallet: null }\n\t\t\t\t\t\treturn curr\n\t\t\t\t\t},\n\t\t\t\t\t{},\n\t\t\t\t)\n\t\t\t\t: {}\n\t}\n\n\tblockInfo.subscribe(() => {\n\t\tif (provider.value && signers.value.burner) {\n\t\t\tprovider.value.provider.getBalance(signers.value.burner.address).then((balance) => (signers.value.burnerBalance = balance))\n\t\t}\n\t})\n\n\tfunction copyBurnerToClipboard() {\n\t\tif (!signers.value.burner) return\n\t\tnavigator.clipboard.writeText(signers.value.burner.address)\n\t}\n\n\tconst showWithdrawModal = useSignal(false)\n\n\tfunction openWithdrawModal() {\n\t\tshowWithdrawModal.value = true\n\t}\n\n\treturn (\n\t\t<>\n\t\t\t\n\t\t\t{bundle.value && bundle.value.containsFundingTx && signers.value.burner ? (\n\t\t\t\t
\n\t\t\t\t\t

Deposit To Funding Account

\n\t\t\t\t\t

This is a temporary account, send only enough needed plus a tiny bit to account for possible rising gas price changes.

\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t

\n\t\t\t\t\t\tWallet Balance: {EtherSymbol}{formatEther(signers.value.burnerBalance)}\n\t\t\t\t\t\t
\n\t\t\t\t\t\tMinimum Required Balance: {EtherSymbol}{formatEther(fundingAmountMin.value)}\n\t\t\t\t\t

\n\t\t\t\t
\n\t\t\t) : null}\n\t\t\n\t)\n}\n\nconst WithdrawModal = ({ display, blockInfo, signers, provider, bouquetNetwork }: { display: Signal, provider: Signal, signers: Signal, blockInfo: Signal, bouquetNetwork: Signal }) => {\n\tif (!display.value) return null\n\n\tconst recipientAddress = useSignal<{ input: string, address?: EthereumAddress }>({ input: '' })\n\tconst inputStyle = useComputed(() => `flex flex-col justify-center border h-16 outline-none px-4 focus-within:bg-white/5 bg-transparent ${recipientAddress.value.address ? 'border-green-400' : (recipientAddress.value.input ? 'border-red-400' : 'border-white/50 focus-within:border-white/80')}`)\n\tfunction parseInput(input: string) {\n\t\tconst address = EthereumAddress.safeParse(input)\n\t\trecipientAddress.value = { input, address: address.success ? address.value : undefined }\n\t}\n\n\tconst withdrawAmount = useComputed(() => {\n\t\tlet maxFeePerGas = getMaxBaseFeeInFutureBlock(blockInfo.value.baseFee, 5n) + blockInfo.value.priorityFee;\n\t\tlet fee = maxFeePerGas * 21000n\n\t\tlet amount = signers.value.burnerBalance - fee\n\t\treturn { amount, fee, maxFeePerGas }\n\t})\n\n\tconst { value: signedMessage, waitFor } = useAsyncState()\n\n\t// Default check if we know the network, can also switch to true if sending to known RPC fails\n\tconst useBrowserProvider = useSignal(false)\n\tuseSignalEffect(() => { useBrowserProvider.value = Boolean(provider.value && bouquetNetwork.value.mempoolSubmitRpcEndpoint === undefined) })\n\n\tfunction withdraw() {\n\t\twaitFor(async () => {\n\t\t\tif (withdrawAmount.value.amount <= 0n) throw 'Funding account\\'s balance is to small to withdraw'\n\t\t\tif (!signers.value.burner) throw 'No funding account found'\n\t\t\tif (!provider.value) throw 'User not connected'\n\t\t\tif (!recipientAddress.value.address) throw 'No recipient provided'\n\n\t\t\tif (useBrowserProvider.value === true) {\n\t\t\t\ttry {\n\t\t\t\t\tconst burnerWithBrowserProvider = signers.value.burner.connect(provider.value.provider)\n\t\t\t\t\tconst txInput = await burnerWithBrowserProvider.populateTransaction({ chainId: provider.value.chainId, from: signers.value.burner.address, to: addressString(recipientAddress.value.address), gasLimit: 21000, type: 2, value: withdrawAmount.value.amount, maxFeePerGas: withdrawAmount.value.maxFeePerGas })\n\t\t\t\t\tconst tx = await burnerWithBrowserProvider.signTransaction(txInput)\n\t\t\t\t\tconst txHash = await provider.value.provider.send('eth_sendRawTransaction', [tx])\n\t\t\t\t\treturn txHash as string\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthrow error\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (bouquetNetwork.value.mempoolSubmitRpcEndpoint === undefined) throw new Error('No RPC URL set and not connected to wallet')\n\t\t\tconst fundingWithProvider = signers.value.burner.connect(new JsonRpcProvider(bouquetNetwork.value.mempoolSubmitRpcEndpoint))\n\t\t\ttry {\n\t\t\t\tconst tx = await fundingWithProvider.sendTransaction({ chainId: provider.value.chainId, from: signers.value.burner.address, to: addressString(recipientAddress.value.address), gasLimit: 21000, type: 2, value: withdrawAmount.value.amount, maxFeePerGas: withdrawAmount.value.maxFeePerGas })\n\t\t\t\tfundingWithProvider.provider?.destroy()\n\t\t\t\treturn tx.hash\n\t\t\t} catch (error) {\n\t\t\t\tconsole.warn('Error sending burner withdraw tx to known RPC:', bouquetNetwork.value.mempoolSubmitRpcEndpoint, ' error:', error)\n\t\t\t\tfundingWithProvider.provider?.destroy()\n\t\t\t\tuseBrowserProvider.value = true\n\t\t\t\tthrow 'Unknown network! If you have Interceptor installed and simulation mode on please switch to signing mode and try again.'\n\t\t\t}\n\t\t})\n\t}\n\n\tfunction close() {\n\t\tbatch(() => {\n\t\t\tdisplay.value = false\n\t\t\trecipientAddress.value = { input: '' }\n\t\t\tsignedMessage.value.state = 'inactive'\n\t\t})\n\t}\n\n\treturn (\n\t\t
\n\t\t\t
e.stopPropagation()}>\n\t\t\t\t

Withdraw From Funding Account

\n\t\t\t\t
\n\t\t\t\t\tETH Recipient\n\t\t\t\t\t) => parseInput(e.currentTarget.value)} type='text' className='bg-transparent outline-none placeholder:text-gray-600' placeholder='0x...' />\n\t\t\t\t
\n\t\t\t\t{withdrawAmount.value.amount > 0n\n\t\t\t\t\t? (

Withdraw {EtherSymbol} {formatEther(withdrawAmount.value.amount)} + {EtherSymbol} {formatEther(withdrawAmount.value.fee)} fee

)\n\t\t\t\t\t: (

Transfer fee ({EtherSymbol} {formatEther(withdrawAmount.value.fee)}) is more than funding account balance

)}\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t

{signedMessage.value.state === 'rejected' ? : ''}

\n\t\t\t\t

{signedMessage.value.state === 'resolved' ? Transaction submitted with TX Hash {signedMessage.value.value} : Transaction submitted with TX Hash {signedMessage.value.value}} title='Transaction Submitted' /> : ''}

\n\t\t\t
\n\t\t
\n\t)\n}\n\n"]} \ No newline at end of file diff --git a/docs/js/components/ConfigureKeys.d.ts b/docs/js/components/ConfigureKeys.d.ts new file mode 100644 index 0000000..241f1a0 --- /dev/null +++ b/docs/js/components/ConfigureKeys.d.ts @@ -0,0 +1,11 @@ +import { Signal } from '@preact/signals'; +import { JSX } from 'preact/jsx-runtime'; +import { ProviderStore } from '../library/provider.js'; +import { BlockInfo, Bundle, Signers } from '../types/types.js'; +export declare const ConfigureKeys: ({ provider, bundle, signers, blockInfo, }: { + provider: Signal; + bundle: Signal; + signers: Signal; + blockInfo: Signal; +}) => JSX.Element; +//# sourceMappingURL=ConfigureKeys.d.ts.map \ No newline at end of file diff --git a/docs/js/components/ConfigureKeys.d.ts.map b/docs/js/components/ConfigureKeys.d.ts.map new file mode 100644 index 0000000..bba01c2 --- /dev/null +++ b/docs/js/components/ConfigureKeys.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"ConfigureKeys.d.ts","sourceRoot":"","sources":["../../ts/components/ConfigureKeys.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAS,MAAM,EAA8B,MAAM,iBAAiB,CAAA;AAE3E,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAA;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAE9D,eAAO,MAAM,aAAa;cAMf,OAAO,aAAa,GAAG,SAAS,CAAC;YACnC,OAAO,MAAM,GAAG,SAAS,CAAC;aACzB,OAAO,OAAO,CAAC;eACb,OAAO,SAAS,CAAC;iBAwE5B,CAAA"} \ No newline at end of file diff --git a/docs/js/components/ConfigureKeys.js b/docs/js/components/ConfigureKeys.js new file mode 100644 index 0000000..646c53d --- /dev/null +++ b/docs/js/components/ConfigureKeys.js @@ -0,0 +1,54 @@ +import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime"; +import { batch, useSignal, useSignalEffect } from '@preact/signals'; +import { Wallet } from 'ethers'; +export const ConfigureKeys = ({ provider, bundle, signers, blockInfo, }) => { + const signerKeys = useSignal({}); + useSignalEffect(() => { + if (!bundle.value) + signerKeys.value = {}; + if (bundle.value && bundle.value.uniqueSigners.join() !== Object.keys(signerKeys.value).join()) { + signerKeys.value = bundle.value.uniqueSigners.reduce((curr, address) => { + curr[address] = { input: '', wallet: null }; + return curr; + }, {}); + } + }); + blockInfo.subscribe(() => { + if (provider.value && signers.value.burner) { + provider.value.provider.getBalance(signers.value.burner.address).then((balance) => (signers.value.burnerBalance = balance)); + } + }); + function tryUpdateSigners(address, privateKey) { + batch(() => { + try { + const wallet = new Wallet(privateKey); + signerKeys.value = { + ...signerKeys.peek(), + [address]: { + wallet: wallet.address === address ? wallet : null, + input: privateKey, + }, + }; + } + catch { + signerKeys.value = { + ...signerKeys.peek(), + [address]: { wallet: null, input: privateKey }, + }; + } + if (Object.values(signerKeys.value).filter(({ wallet }) => !wallet).length === 0) { + signers.value = { + ...signers.peek(), + bundleSigners: Object.values(signerKeys.peek()).reduce((acc, wallet) => { + if (wallet.wallet) { + acc[wallet.wallet.address] = wallet.wallet; + } + return acc; + }, {}), + }; + } + }); + } + return (_jsxs("div", { className: 'flex flex-col w-full gap-4', children: [_jsx("h3", { className: 'text-2xl font-semibold', children: "Enter Private Keys For Signing Accounts" }), Object.keys(signerKeys.value).map((address) => (_jsxs("div", { className: `flex flex-col justify-center border h-16 outline-none px-4 focus-within:bg-white/5 bg-transparent ${signerKeys.value[address].wallet ? 'border-green-400' : (signerKeys.peek()[address].input ? 'border-red-400' : 'border-white/50 focus-within:border-white/80')}`, children: [_jsx("span", { className: 'text-sm text-gray-500', children: address }), _jsx("input", { onInput: (e) => tryUpdateSigners(address, e.currentTarget.value), value: signerKeys.value[address].input, type: 'text', className: 'bg-transparent outline-none placeholder:text-gray-600', placeholder: `Enter private key for account` })] })))] })); +}; +//# sourceMappingURL=ConfigureKeys.js.map \ No newline at end of file diff --git a/docs/js/components/ConfigureKeys.js.map b/docs/js/components/ConfigureKeys.js.map new file mode 100644 index 0000000..3266505 --- /dev/null +++ b/docs/js/components/ConfigureKeys.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ConfigureKeys.js","sourceRoot":"","sources":["../../ts/components/ConfigureKeys.tsx"],"names":[],"mappings":"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","sourcesContent":["import { batch, Signal, useSignal, useSignalEffect } from '@preact/signals'\nimport { Wallet } from 'ethers'\nimport { JSX } from 'preact/jsx-runtime'\nimport { ProviderStore } from '../library/provider.js'\nimport { BlockInfo, Bundle, Signers } from '../types/types.js'\n\nexport const ConfigureKeys = ({\n\tprovider,\n\tbundle,\n\tsigners,\n\tblockInfo,\n}: {\n\tprovider: Signal\n\tbundle: Signal\n\tsigners: Signal\n\tblockInfo: Signal\n}) => {\n\tconst signerKeys = useSignal<{\n\t\t[address: string]: { input: string; wallet: Wallet | null }\n\t}>({})\n\n\tuseSignalEffect(() => {\n\t\tif (!bundle.value) signerKeys.value = {}\n\t\tif (bundle.value && bundle.value.uniqueSigners.join() !== Object.keys(signerKeys.value).join()) {\n\t\t\tsignerKeys.value = bundle.value.uniqueSigners.reduce((\n\t\t\t\t\tcurr: {\n\t\t\t\t\t\t[address: string]: { input: string; wallet: Wallet | null }\n\t\t\t\t\t},\n\t\t\t\t\taddress,\n\t\t\t\t) => {\n\t\t\t\t\tcurr[address] = { input: '', wallet: null }\n\t\t\t\t\treturn curr\n\t\t\t\t},\n\t\t\t\t{},\n\t\t\t)\n\t\t}\n\t})\n\n\tblockInfo.subscribe(() => {\n\t\tif (provider.value && signers.value.burner) {\n\t\t\tprovider.value.provider.getBalance(signers.value.burner.address).then((balance) => (signers.value.burnerBalance = balance))\n\t\t}\n\t})\n\n\tfunction tryUpdateSigners(address: string, privateKey: string) {\n\t\tbatch(() => {\n\t\t\ttry {\n\t\t\t\tconst wallet = new Wallet(privateKey)\n\n\t\t\t\tsignerKeys.value = {\n\t\t\t\t\t...signerKeys.peek(),\n\t\t\t\t\t[address]: {\n\t\t\t\t\t\twallet: wallet.address === address ? wallet : null,\n\t\t\t\t\t\tinput: privateKey,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\tsignerKeys.value = {\n\t\t\t\t\t...signerKeys.peek(),\n\t\t\t\t\t[address]: { wallet: null, input: privateKey },\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (Object.values(signerKeys.value).filter(({ wallet }) => !wallet).length === 0) {\n\t\t\t\tsigners.value = {\n\t\t\t\t\t...signers.peek(),\n\t\t\t\t\tbundleSigners: Object.values(signerKeys.peek()).reduce((acc: { [account: string]: Wallet }, wallet) => {\n\t\t\t\t\t\tif (wallet.wallet) {\n\t\t\t\t\t\t\tacc[wallet.wallet.address] = wallet.wallet\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn acc\n\t\t\t\t\t}, {}),\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n\n\treturn (\n\t\t
\n\t\t\t

Enter Private Keys For Signing Accounts

\n\t\t\t{Object.keys(signerKeys.value).map((address) => (\n\t\t\t\t
\n\t\t\t\t\t{address}\n\t\t\t\t\t) => tryUpdateSigners(address, e.currentTarget.value)} value={signerKeys.value[address].input} type='text' className='bg-transparent outline-none placeholder:text-gray-600' placeholder={`Enter private key for account`} />\n\t\t\t\t
\n\t\t\t))}\n\t\t
\n\t)\n}\n"]} \ No newline at end of file diff --git a/docs/js/components/Footer.d.ts b/docs/js/components/Footer.d.ts new file mode 100644 index 0000000..60f5dad --- /dev/null +++ b/docs/js/components/Footer.d.ts @@ -0,0 +1,2 @@ +export declare const Footer: () => import("preact").JSX.Element; +//# sourceMappingURL=Footer.d.ts.map \ No newline at end of file diff --git a/docs/js/components/Footer.d.ts.map b/docs/js/components/Footer.d.ts.map new file mode 100644 index 0000000..6339e61 --- /dev/null +++ b/docs/js/components/Footer.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"Footer.d.ts","sourceRoot":"","sources":["../../ts/components/Footer.tsx"],"names":[],"mappings":"AAAA,eAAO,MAAM,MAAM,oCAkBlB,CAAA"} \ No newline at end of file diff --git a/docs/js/components/Footer.js b/docs/js/components/Footer.js new file mode 100644 index 0000000..92e4db6 --- /dev/null +++ b/docs/js/components/Footer.js @@ -0,0 +1,3 @@ +import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime"; +export const Footer = () => (_jsxs("footer", { className: 'mt-auto w-full', children: [_jsxs("div", { className: 'flex flex-col sm:flex-row w-full gap-6 justify-around items-center', children: [_jsxs("div", { className: 'flex flex-col gap-2', children: [_jsx("h2", { className: 'font-semibold', children: "Contact" }), _jsx("a", { href: 'https://discord.com/invite/aCSKcvf5VW', target: '_blank', className: 'hover:underline text-gray-500', children: "Discord" }), _jsx("a", { href: 'https://twitter.com/DarkFlorist', target: '_blank', className: 'hover:underline text-gray-500', children: "Twitter" }), _jsx("a", { href: 'https://github.com/DarkFlorist/bouquet', target: '_blank', className: 'hover:underline text-gray-500', children: "Github" })] }), _jsxs("div", { className: 'flex flex-col gap-2', children: [_jsx("h2", { className: 'font-semibold', children: "Our other tools" }), _jsx("a", { href: 'https://dark.florist/', className: 'hover:underline text-gray-500', children: "The Interceptor" }), _jsx("a", { href: 'https://lunaria.dark.florist/', className: 'hover:underline text-gray-500', children: "Lunaria" }), _jsx("a", { href: 'https://nftsender.dark.florist/', className: 'hover:underline text-gray-500', children: "NFT Sender" })] })] }), _jsx("div", { className: 'font-xs py-8 sm:py-6 flex items-center justify-center', children: _jsxs("p", { className: 'hidden sm:block', children: ["\uD83D\uDC90 Bouquet by ", _jsx("a", { href: 'https://dark.florist', className: 'hover:underline', children: "DarkFlorist" })] }) })] })); +//# sourceMappingURL=Footer.js.map \ No newline at end of file diff --git a/docs/js/components/Footer.js.map b/docs/js/components/Footer.js.map new file mode 100644 index 0000000..ae3137b --- /dev/null +++ b/docs/js/components/Footer.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Footer.js","sourceRoot":"","sources":["../../ts/components/Footer.tsx"],"names":[],"mappings":";AAAA,MAAM,CAAC,MAAM,MAAM,GAAG,GAAG,EAAE,CAAC,CAC3B,kBAAQ,SAAS,EAAC,gBAAgB,aACjC,eAAK,SAAS,EAAC,oEAAoE,aAClF,eAAK,SAAS,EAAC,qBAAqB,aACnC,aAAI,SAAS,EAAC,eAAe,wBAAa,EAC1C,YAAG,IAAI,EAAC,uCAAuC,EAAC,MAAM,EAAC,QAAQ,EAAC,SAAS,EAAC,+BAA+B,wBAAY,EACrH,YAAG,IAAI,EAAC,iCAAiC,EAAC,MAAM,EAAC,QAAQ,EAAC,SAAS,EAAC,+BAA+B,wBAAY,EAC/G,YAAG,IAAI,EAAC,wCAAwC,EAAC,MAAM,EAAC,QAAQ,EAAC,SAAS,EAAC,+BAA+B,uBAAW,IAChH,EACN,eAAK,SAAS,EAAC,qBAAqB,aACnC,aAAI,SAAS,EAAC,eAAe,gCAAqB,EAClD,YAAG,IAAI,EAAC,uBAAuB,EAAC,SAAS,EAAC,+BAA+B,gCAAoB,EAC7F,YAAG,IAAI,EAAC,+BAA+B,EAAC,SAAS,EAAC,+BAA+B,wBAAY,EAC7F,YAAG,IAAI,EAAC,iCAAiC,EAAC,SAAS,EAAC,+BAA+B,2BAAe,IAC7F,IACD,EACN,cAAK,SAAS,EAAC,uDAAuD,YAAC,aAAG,SAAS,EAAC,iBAAiB,yCAAe,YAAG,IAAI,EAAC,sBAAsB,EAAC,SAAS,EAAC,iBAAiB,4BAAgB,IAAI,GAAM,IAChM,CACT,CAAA","sourcesContent":["export const Footer = () => (\n\t\n)\n"]} \ No newline at end of file diff --git a/docs/js/components/Import.d.ts b/docs/js/components/Import.d.ts new file mode 100644 index 0000000..64bcf81 --- /dev/null +++ b/docs/js/components/Import.d.ts @@ -0,0 +1,21 @@ +import { Signal } from '@preact/signals'; +import { ProviderStore } from '../library/provider.js'; +import { Bundle, Signers } from '../types/types.js'; +import { BouquetSettings } from '../types/bouquetTypes.js'; +export declare function importFromInterceptor(bundle: Signal, provider: Signal, blockInfo: Signal<{ + blockNumber: bigint; + baseFee: bigint; + priorityFee: bigint; +}>, signers: Signal | undefined, bouquetSettings: Signal): Promise; +export declare const Import: ({ bundle, provider, blockInfo, signers, bouquetSettings, }: { + bundle: Signal; + provider: Signal; + blockInfo: Signal<{ + blockNumber: bigint; + baseFee: bigint; + priorityFee: bigint; + }>; + signers: Signal; + bouquetSettings: Signal; +}) => import("preact").JSX.Element; +//# sourceMappingURL=Import.d.ts.map \ No newline at end of file diff --git a/docs/js/components/Import.d.ts.map b/docs/js/components/Import.d.ts.map new file mode 100644 index 0000000..9ea6466 --- /dev/null +++ b/docs/js/components/Import.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"Import.d.ts","sourceRoot":"","sources":["../../ts/components/Import.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAS,MAAM,EAAa,MAAM,iBAAiB,CAAA;AAG1D,OAAO,EAA0B,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAG9E,OAAO,EAAE,MAAM,EAAa,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAE9D,OAAO,EAAE,eAAe,EAAmB,MAAM,0BAA0B,CAAA;AAK3E,wBAAsB,qBAAqB,CAC1C,MAAM,EAAE,MAAM,CAAC,MAAM,GAAG,SAAS,CAAC,EAClC,QAAQ,EAAE,MAAM,CAAC,aAAa,GAAG,SAAS,CAAC,EAC3C,SAAS,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,MAAM,CAAA;CACnB,CAAC,EACF,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,SAAS,EACpC,eAAe,EAAE,MAAM,CAAC,eAAe,CAAC,iBA6ExC;AAED,eAAO,MAAM,MAAM;YAOV,OAAO,MAAM,GAAG,SAAS,CAAC;cACxB,OAAO,aAAa,GAAG,SAAS,CAAC;eAChC,OAAO;QACjB,WAAW,EAAE,MAAM,CAAA;QACnB,OAAO,EAAE,MAAM,CAAA;QACf,WAAW,EAAE,MAAM,CAAA;KACnB,CAAC;aACO,OAAO,OAAO,CAAC;qBACP,OAAO,eAAe,CAAC;kCAqDvC,CAAA"} \ No newline at end of file diff --git a/docs/js/components/Import.js b/docs/js/components/Import.js new file mode 100644 index 0000000..15db8e8 --- /dev/null +++ b/docs/js/components/Import.js @@ -0,0 +1,99 @@ +import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "preact/jsx-runtime"; +import { batch, useSignal } from '@preact/signals'; +import { useState } from 'preact/hooks'; +import { parseEther } from 'ethers'; +import { connectBrowserProvider } from '../library/provider.js'; +import { GetSimulationStackReply } from '../types/interceptorTypes.js'; +import { Button } from './Button.js'; +import { serialize } from '../types/types.js'; +import { TransactionList } from '../types/bouquetTypes.js'; +import { ImportModal } from './ImportModal.js'; +import { SingleNotice } from './Warns.js'; +import { addressString } from '../library/utils.js'; +export async function importFromInterceptor(bundle, provider, blockInfo, signers, bouquetSettings) { + if (!window.ethereum || !window.ethereum.request) + throw Error('No Ethereum wallet detected'); + connectBrowserProvider(provider, blockInfo, signers, bouquetSettings); + const { payload } = await window.ethereum + .request({ + method: 'interceptor_getSimulationStack', + params: ['1.0.0'], + }) + .catch((err) => { + if (err?.code === -32601) { + throw new Error('Wallet does not support returning simulations'); + } + else { + throw new Error(`Unknown Error: ${JSON.stringify(err)}`); + } + }); + const tryParse = GetSimulationStackReply.safeParse(payload); + if (!tryParse.success) + throw new Error('Wallet does not support returning simulations'); + if (tryParse.value.length === 0) + throw new Error('You have no transactions on your simulation'); + const converted = TransactionList.safeParse(serialize(GetSimulationStackReply, tryParse.value).map(({ from, to, value, input, gasLimit, chainId }) => ({ from, to, value, input, gasLimit, chainId }))); + if (!converted.success) + throw new Error('Malformed simulation stack'); + if (converted.value.length >= 2 && converted.value[0].to === converted.value[1].from && converted.value[0].value === parseEther('200000')) { + const fundingAddr = converted.value[0].from; + converted.value = converted.value.map(tx => tx.from === fundingAddr ? { ...tx, from: 'FUNDING' } : tx); + } + const uniqueToAddresses = [...new Set(converted.value.map(({ from }) => from))]; + const containsFundingTx = uniqueToAddresses.includes('FUNDING'); + const uniqueSigners = uniqueToAddresses.filter((address) => address !== 'FUNDING').map(address => addressString(address)); + const totalGas = converted.value.reduce((sum, tx) => tx.gasLimit + sum, 0n); + // Take addresses that recieved funding, determine spend deficit - gas fees + const fundingRecipients = new Set(converted.value.reduce((result, tx) => (tx.to && tx.from === 'FUNDING' ? [...result, tx.to] : result), [])); + const spenderDeficits = tryParse.value.reduce((amounts, tx) => { + if (!fundingRecipients.has(tx.from)) + return amounts; + const receipientBalanceChanges = tx.balanceChanges.filter(x => x.address === tx.from); + const consumed = tx.value; + // Rebate is the difference between balance change and consume amount (if there were any internal transactions sending ETH back), ignore gas fees + const balanceChange = receipientBalanceChanges.reduce((result, balanceChange) => result + balanceChange.after - balanceChange.before, 0n); + const rebate = balanceChange + consumed + tx.maxPriorityFeePerGas * tx.gasSpent; + // Calcuate current deficit + if (tx.from.toString() in amounts) { + // If credit, deduct current credit from new consumption, or cancel out new consumption and open credit - whichever is smaller + if (amounts[tx.from.toString()].credit > 0n) { + if (amounts[tx.from.toString()].credit <= consumed) { + amounts[tx.from.toString()].deficit += consumed - amounts[tx.from.toString()].credit; + amounts[tx.from.toString()].credit = rebate; + } + else { + // If consumed less than current rebates, deficit does not increase. + amounts[tx.from.toString()].credit += rebate - consumed; + } + } + } + else { + amounts[tx.from.toString()] = { deficit: consumed, credit: rebate }; + } + return amounts; + }, {}); + const inputValue = Object.values(spenderDeficits).reduce((sum, spender) => spender.deficit + sum, 0n); + // Copy value and set, input of funding to inputValue + const transactions = [...converted.value]; + if (containsFundingTx) { + transactions[0] = { ...transactions[0], value: inputValue }; + } + localStorage.setItem('payload', JSON.stringify(TransactionList.serialize(transactions))); + bundle.value = { transactions, containsFundingTx, uniqueSigners, totalGas, inputValue }; +} +export const Import = ({ bundle, provider, blockInfo, signers, bouquetSettings, }) => { + const showImportModal = useSignal(false); + const [error, setError] = useState(undefined); + const clearPayload = () => { + batch(() => { + bundle.value = undefined; + localStorage.removeItem('payload'); + signers.value.bundleSigners = {}; + setError(''); + // Keep burner wallet as long as it has funds, should clear is later if there is left over dust but not needed. + // if (fundingAccountBalance.value === 0n) signers.value.burner = undefined + }); + }; + return (_jsxs(_Fragment, { children: [showImportModal.value ? _jsx(ImportModal, { bundle: bundle, clearError: () => setError(''), display: showImportModal }) : null, _jsxs("h2", { className: 'font-bold text-2xl', children: [_jsx("span", { class: 'text-gray-500', children: "1." }), " Import"] }), _jsxs("div", { className: 'flex flex-col w-full gap-6', children: [_jsxs("div", { className: 'flex flex-col sm:flex-row gap-4', children: [_jsx(Button, { onClick: () => importFromInterceptor(bundle, provider, blockInfo, signers, bouquetSettings).then(() => setError(undefined)).catch((err) => setError(err.message)), children: "Import Payload from The Interceptor" }), _jsx(Button, { onClick: () => showImportModal.value = true, children: "Import From JSON" }), bundle.value ? (_jsx(Button, { variant: 'secondary', onClick: clearPayload, children: "Reset" })) : null] }), error ? _jsx(SingleNotice, { variant: 'error', title: 'Could Not Import Transactions', description: error }) : null, error && error === 'Wallet does not support returning simulations' ? (_jsxs("h3", { className: 'text-xl', children: ["Don't have The Interceptor Installed? Install it here", ' ', _jsx("a", { className: 'font-bold text-accent underline', href: 'https://dark.florist', children: "here" }), "."] })) : ('')] })] })); +}; +//# sourceMappingURL=Import.js.map \ No newline at end of file diff --git a/docs/js/components/Import.js.map b/docs/js/components/Import.js.map new file mode 100644 index 0000000..4d7a0a3 --- /dev/null +++ b/docs/js/components/Import.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Import.js","sourceRoot":"","sources":["../../ts/components/Import.tsx"],"names":[],"mappings":"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","sourcesContent":["import { batch, Signal, useSignal } from '@preact/signals'\nimport { useState } from 'preact/hooks'\nimport { parseEther } from 'ethers'\nimport { connectBrowserProvider, ProviderStore } from '../library/provider.js'\nimport { GetSimulationStackReply } from '../types/interceptorTypes.js'\nimport { Button } from './Button.js'\nimport { Bundle, serialize, Signers } from '../types/types.js'\nimport { EthereumAddress } from '../types/ethereumTypes.js'\nimport { BouquetSettings, TransactionList } from '../types/bouquetTypes.js'\nimport { ImportModal } from './ImportModal.js'\nimport { SingleNotice } from './Warns.js'\nimport { addressString } from '../library/utils.js'\n\nexport async function importFromInterceptor(\n\tbundle: Signal,\n\tprovider: Signal,\n\tblockInfo: Signal<{\n\t\tblockNumber: bigint\n\t\tbaseFee: bigint\n\t\tpriorityFee: bigint\n\t}>,\n\tsigners: Signal | undefined,\n\tbouquetSettings: Signal\n) {\n\tif (!window.ethereum || !window.ethereum.request) throw Error('No Ethereum wallet detected')\n\tconnectBrowserProvider(provider, blockInfo, signers, bouquetSettings)\n\n\tconst { payload } = await window.ethereum\n\t\t.request({\n\t\t\tmethod: 'interceptor_getSimulationStack',\n\t\t\tparams: ['1.0.0'],\n\t\t})\n\t\t.catch((err: { code: number }) => {\n\t\t\tif (err?.code === -32601) {\n\t\t\t\tthrow new Error('Wallet does not support returning simulations')\n\t\t\t} else {\n\t\t\t\tthrow new Error(`Unknown Error: ${JSON.stringify(err)}`)\n\t\t\t}\n\t\t})\n\n\tconst tryParse = GetSimulationStackReply.safeParse(payload)\n\tif (!tryParse.success) throw new Error('Wallet does not support returning simulations')\n\tif (tryParse.value.length === 0) throw new Error('You have no transactions on your simulation')\n\n\tconst converted = TransactionList.safeParse(serialize(GetSimulationStackReply, tryParse.value).map(({ from, to, value, input, gasLimit, chainId }) => ({ from, to, value, input, gasLimit, chainId })))\n\tif (!converted.success) throw new Error('Malformed simulation stack')\n\n\tif (converted.value.length >= 2 && converted.value[0].to === converted.value[1].from && converted.value[0].value === parseEther('200000')) {\n\t\tconst fundingAddr = converted.value[0].from\n\t\tconverted.value = converted.value.map(tx => tx.from === fundingAddr ? { ...tx, from: 'FUNDING' } : tx)\n\t}\n\n\tconst uniqueToAddresses = [...new Set(converted.value.map(({ from }) => from))]\n\tconst containsFundingTx = uniqueToAddresses.includes('FUNDING')\n\tconst uniqueSigners = uniqueToAddresses.filter((address): address is EthereumAddress => address !== 'FUNDING').map(address => addressString(address))\n\n\tconst totalGas = converted.value.reduce((sum, tx) => tx.gasLimit + sum, 0n)\n\n\t// Take addresses that recieved funding, determine spend deficit - gas fees\n\tconst fundingRecipients = new Set(converted.value.reduce((result: bigint[], tx) => (tx.to && tx.from === 'FUNDING' ? [...result, tx.to] : result), []))\n\n\tconst spenderDeficits = tryParse.value.reduce((amounts: { [account: string]: { deficit: bigint, credit: bigint } }, tx) => {\n\t\tif (!fundingRecipients.has(tx.from)) return amounts\n\t\tconst receipientBalanceChanges = tx.balanceChanges.filter(x => x.address === tx.from)\n\n\t\tconst consumed = tx.value\n\t\t// Rebate is the difference between balance change and consume amount (if there were any internal transactions sending ETH back), ignore gas fees\n\t\tconst balanceChange = receipientBalanceChanges.reduce((result: bigint, balanceChange) => result + balanceChange.after - balanceChange.before, 0n)\n\t\tconst rebate = balanceChange + consumed + tx.maxPriorityFeePerGas * tx.gasSpent\n\n\t\t// Calcuate current deficit\n\t\tif (tx.from.toString() in amounts) {\n\t\t\t// If credit, deduct current credit from new consumption, or cancel out new consumption and open credit - whichever is smaller\n\t\t\tif (amounts[tx.from.toString()].credit > 0n) {\n\t\t\t\tif (amounts[tx.from.toString()].credit <= consumed) {\n\t\t\t\t\tamounts[tx.from.toString()].deficit += consumed - amounts[tx.from.toString()].credit\n\t\t\t\t\tamounts[tx.from.toString()].credit = rebate\n\t\t\t\t} else {\n\t\t\t\t\t// If consumed less than current rebates, deficit does not increase.\n\t\t\t\t\tamounts[tx.from.toString()].credit += rebate - consumed\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tamounts[tx.from.toString()] = { deficit: consumed, credit: rebate }\n\t\t}\n\t\treturn amounts\n\n\t}, {})\n\n\tconst inputValue = Object.values(spenderDeficits).reduce((sum, spender) => spender.deficit + sum, 0n)\n\n\t// Copy value and set, input of funding to inputValue\n\tconst transactions = [...converted.value]\n\tif (containsFundingTx) {\n\t\ttransactions[0] = { ...transactions[0], value: inputValue }\n\t}\n\n\tlocalStorage.setItem('payload', JSON.stringify(TransactionList.serialize(transactions)))\n\tbundle.value = { transactions, containsFundingTx, uniqueSigners, totalGas, inputValue }\n}\n\nexport const Import = ({\n\tbundle,\n\tprovider,\n\tblockInfo,\n\tsigners,\n\tbouquetSettings,\n}: {\n\tbundle: Signal\n\tprovider: Signal\n\tblockInfo: Signal<{\n\t\tblockNumber: bigint\n\t\tbaseFee: bigint\n\t\tpriorityFee: bigint\n\t}>\n\tsigners: Signal\n\tbouquetSettings: Signal\n}) => {\n\tconst showImportModal = useSignal(false)\n\tconst [error, setError] = useState(undefined)\n\n\tconst clearPayload = () => {\n\t\tbatch(() => {\n\t\t\tbundle.value = undefined\n\t\t\tlocalStorage.removeItem('payload')\n\t\t\tsigners.value.bundleSigners = {}\n\t\t\tsetError('')\n\t\t\t// Keep burner wallet as long as it has funds, should clear is later if there is left over dust but not needed.\n\t\t\t// if (fundingAccountBalance.value === 0n) signers.value.burner = undefined\n\t\t})\n\t}\n\n\treturn (\n\t\t<>\n\t\t\t{showImportModal.value ? setError('')} display={showImportModal} /> : null}\n\t\t\t

1. Import

\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t importFromInterceptor(bundle, provider, blockInfo, signers, bouquetSettings).then(() => setError(undefined)).catch((err: Error) => setError(err.message))}\n\t\t\t\t\t>\n\t\t\t\t\t\tImport Payload from The Interceptor\n\t\t\t\t\t\n\t\t\t\t\t showImportModal.value = true}\n\t\t\t\t\t>\n\t\t\t\t\t\tImport From JSON\n\t\t\t\t\t\n\t\t\t\t\t{bundle.value ? (\n\t\t\t\t\t\t\n\t\t\t\t\t) : null}\n\t\t\t\t
\n\t\t\t\t{error ? : null}\n\t\t\t\t{error && error === 'Wallet does not support returning simulations' ? (\n\t\t\t\t\t

\n\t\t\t\t\t\tDon't have The Interceptor Installed? Install it here{' '}\n\t\t\t\t\t\t\n\t\t\t\t\t\t\there\n\t\t\t\t\t\t\n\t\t\t\t\t\t.\n\t\t\t\t\t

\n\t\t\t\t) : (\n\t\t\t\t\t''\n\t\t\t\t)}\n\t\t\t
\n\t\t\n\t)\n\t}\n"]} \ No newline at end of file diff --git a/docs/js/components/ImportModal.d.ts b/docs/js/components/ImportModal.d.ts new file mode 100644 index 0000000..f15840c --- /dev/null +++ b/docs/js/components/ImportModal.d.ts @@ -0,0 +1,9 @@ +import { Signal } from '@preact/signals'; +import { JSX } from 'preact/jsx-runtime'; +import { Bundle } from '../types/types.js'; +export declare const ImportModal: ({ display, bundle, clearError }: { + display: Signal; + clearError: () => void; + bundle: Signal; +}) => JSX.Element | null; +//# sourceMappingURL=ImportModal.d.ts.map \ No newline at end of file diff --git a/docs/js/components/ImportModal.d.ts.map b/docs/js/components/ImportModal.d.ts.map new file mode 100644 index 0000000..d4814b2 --- /dev/null +++ b/docs/js/components/ImportModal.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"ImportModal.d.ts","sourceRoot":"","sources":["../../ts/components/ImportModal.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAA0B,MAAM,iBAAiB,CAAA;AAChE,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAA;AAIxC,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAsB1C,eAAO,MAAM,WAAW;aAAgD,OAAO,OAAO,CAAC;gBAAc,MAAM,IAAI;YAAU,OAAO,MAAM,GAAG,SAAS,CAAC;wBAsDlJ,CAAA"} \ No newline at end of file diff --git a/docs/js/components/ImportModal.js b/docs/js/components/ImportModal.js new file mode 100644 index 0000000..47a00cd --- /dev/null +++ b/docs/js/components/ImportModal.js @@ -0,0 +1,57 @@ +import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime"; +import { useComputed, useSignal } from '@preact/signals'; +import { addressString } from '../library/utils.js'; +import { TransactionList } from '../types/bouquetTypes.js'; +import { Button } from './Button.js'; +const placeholder = `[ + { + "from": "0xb3cd36cfaa07652dbfecca76f438ff8998a4f539", + "to": "0xb4fbf271143f4fbf7b91a5ded31805e42b2208d6", + "value": "0x16345785d8a0000", + "input": "0xd0e30db0", + "chainId": "0x1", + "gasLimit": "0x15f90" + }, + { + "from": "0xb3cd36cfaa07652dbfecca76f438ff8998a4f539", + "to": "0xb4fbf271143f4fbf7b91a5ded31805e42b2208d6", + "value": "0x0", + "input": "0x2e1a7d4d000000000000000000000000000000000000000000000000016345785d8a0000", + "chainId": "0x1", + "gasLimit": "0x15f90" + } +]`; +export const ImportModal = ({ display, bundle, clearError }) => { + const jsonInput = useSignal(''); + const isValid = useComputed(() => { + if (!jsonInput.value) + return false; + try { + const { success } = TransactionList.safeParse(JSON.parse(jsonInput.value)); + return success; + } + catch { + return false; + } + }); + function importJson() { + if (!isValid.peek()) + return; + const txList = TransactionList.parse(JSON.parse(jsonInput.value)); + localStorage.setItem('payload', JSON.stringify(TransactionList.serialize(txList))); + const uniqueToAddresses = [...new Set(txList.map(({ from }) => from))]; + const containsFundingTx = uniqueToAddresses.includes('FUNDING'); + const uniqueSigners = uniqueToAddresses.filter((address) => address !== 'FUNDING').map(address => addressString(address)); + const totalGas = txList.reduce((sum, tx) => tx.gasLimit + sum, 0n); + const inputValue = txList.reduce((sum, tx) => (tx.from === 'FUNDING' ? tx.value : 0n) + sum, 0n); + bundle.value = { transactions: txList, containsFundingTx, uniqueSigners, totalGas, inputValue }; + clearError(); + close(); + } + function close() { + jsonInput.value = ''; + display.value = false; + } + return display.value ? (_jsx("div", { onClick: close, className: 'bg-white/10 w-full h-full inset-0 fixed p-4 flex flex-col items-center md:pt-24', children: _jsxs("div", { class: 'h-max px-8 py-4 flex flex-col gap-4 bg-black', onClick: (e) => e.stopPropagation(), children: [_jsx("h2", { className: 'text-xl font-semibold', children: "Import Transactions From JSON" }), _jsx("div", { children: _jsx("textarea", { placeholder: placeholder, onInput: (e) => jsonInput.value = e.currentTarget.value, value: jsonInput.value, type: 'url', className: `p-2 w-96 h-96 border ${jsonInput.value && isValid.value ? 'border-green-400 bg-green-200/10' : jsonInput.value && !isValid.value ? 'border-red-400 bg-red-200/10' : 'border-white/50 focus-within:border-white/90 bg-transparent focus-within:bg-white/5'} outline-none px-4` }) }), _jsx("div", { className: 'flex gap-2', children: _jsx(Button, { onClick: importJson, disabled: !isValid.value, variant: 'primary', children: "Import" }) })] }) })) : null; +}; +//# sourceMappingURL=ImportModal.js.map \ No newline at end of file diff --git a/docs/js/components/ImportModal.js.map b/docs/js/components/ImportModal.js.map new file mode 100644 index 0000000..b4ad727 --- /dev/null +++ b/docs/js/components/ImportModal.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ImportModal.js","sourceRoot":"","sources":["../../ts/components/ImportModal.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Signal, useComputed, useSignal } from '@preact/signals'\nimport { JSX } from 'preact/jsx-runtime'\nimport { addressString } from '../library/utils.js'\nimport { TransactionList } from '../types/bouquetTypes.js'\nimport { EthereumAddress } from '../types/ethereumTypes.js'\nimport { Bundle } from '../types/types.js'\nimport { Button } from './Button.js'\n\nconst placeholder = `[\n {\n \"from\": \"0xb3cd36cfaa07652dbfecca76f438ff8998a4f539\",\n \"to\": \"0xb4fbf271143f4fbf7b91a5ded31805e42b2208d6\",\n \"value\": \"0x16345785d8a0000\",\n \"input\": \"0xd0e30db0\",\n \"chainId\": \"0x1\",\n \"gasLimit\": \"0x15f90\"\n },\n {\n \"from\": \"0xb3cd36cfaa07652dbfecca76f438ff8998a4f539\",\n \"to\": \"0xb4fbf271143f4fbf7b91a5ded31805e42b2208d6\",\n \"value\": \"0x0\",\n \"input\": \"0x2e1a7d4d000000000000000000000000000000000000000000000000016345785d8a0000\",\n \"chainId\": \"0x1\",\n \"gasLimit\": \"0x15f90\"\n }\n]`\n\nexport const ImportModal = ({ display, bundle, clearError }: { display: Signal, clearError: () => void, bundle: Signal }) => {\n\tconst jsonInput = useSignal('')\n\n\tconst isValid = useComputed(() => {\n\t\tif (!jsonInput.value) return false\n\t\ttry {\n\t\t\tconst { success } = TransactionList.safeParse(JSON.parse(jsonInput.value))\n\t\t\treturn success\n\t\t} catch {\n\t\t\treturn false\n\t\t}\n\t})\n\n\tfunction importJson() {\n\t\tif (!isValid.peek()) return\n\t\tconst txList = TransactionList.parse(JSON.parse(jsonInput.value))\n\n\t\tlocalStorage.setItem('payload', JSON.stringify(TransactionList.serialize(txList)))\n\n\t\tconst uniqueToAddresses = [...new Set(txList.map(({ from }) => from))]\n\t\tconst containsFundingTx = uniqueToAddresses.includes('FUNDING')\n\t\tconst uniqueSigners = uniqueToAddresses.filter((address): address is EthereumAddress => address !== 'FUNDING').map(address => addressString(address))\n\n\t\tconst totalGas = txList.reduce((sum, tx) => tx.gasLimit + sum, 0n)\n\t\tconst inputValue = txList.reduce((sum, tx) => (tx.from === 'FUNDING' ? tx.value : 0n) + sum, 0n)\n\n\t\tbundle.value = { transactions: txList, containsFundingTx, uniqueSigners, totalGas, inputValue }\n\t\tclearError()\n\t\tclose()\n\t}\n\n\tfunction close() {\n\t\tjsonInput.value = ''\n\t\tdisplay.value = false\n\t}\n\treturn display.value ? (\n\t\t
\n\t\t\t
e.stopPropagation()}>\n\t\t\t\t

Import Transactions From JSON

\n\t\t\t\t
\n\t\t\t\t\t) => jsonInput.value = e.currentTarget.value}\n\t\t\t\t\t\tvalue={jsonInput.value}\n\t\t\t\t\t\ttype='url'\n\t\t\t\t\t\tclassName={`p-2 w-96 h-96 border ${jsonInput.value && isValid.value ? 'border-green-400 bg-green-200/10' : jsonInput.value && !isValid.value ? 'border-red-400 bg-red-200/10' : 'border-white/50 focus-within:border-white/90 bg-transparent focus-within:bg-white/5'} outline-none px-4`}\n\t\t\t\t\t/>\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t
\n\t\t
\n\t) : null\n}\n"]} \ No newline at end of file diff --git a/docs/js/components/Navbar.d.ts b/docs/js/components/Navbar.d.ts new file mode 100644 index 0000000..d769b03 --- /dev/null +++ b/docs/js/components/Navbar.d.ts @@ -0,0 +1,12 @@ +import { Signal } from '@preact/signals'; +import { ProviderStore } from '../library/provider.js'; +import { BlockInfo, Bundle, Signers } from '../types/types.js'; +import { BouquetSettings } from '../types/bouquetTypes.js'; +export declare const Navbar: ({ provider, bouquetSettings, blockInfo, bundle, signers }: { + provider: Signal; + blockInfo: Signal; + bouquetSettings: Signal; + bundle: Signal; + signers: Signal; +}) => import("preact").JSX.Element; +//# sourceMappingURL=Navbar.d.ts.map \ No newline at end of file diff --git a/docs/js/components/Navbar.d.ts.map b/docs/js/components/Navbar.d.ts.map new file mode 100644 index 0000000..f971fef --- /dev/null +++ b/docs/js/components/Navbar.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"Navbar.d.ts","sourceRoot":"","sources":["../../ts/components/Navbar.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAA0B,MAAM,iBAAiB,CAAA;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAEtD,OAAO,EAAE,SAAS,EAAE,MAAM,EAAa,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAKzE,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAG1D,eAAO,MAAM,MAAM;cAOR,OAAO,aAAa,GAAG,SAAS,CAAC;eAChC,OAAO,SAAS,CAAC;qBACX,OAAO,eAAe,CAAC;YAChC,OAAO,MAAM,GAAG,SAAS,CAAC;aACzB,OAAO,OAAO,CAAC;kCA+CxB,CAAA"} \ No newline at end of file diff --git a/docs/js/components/Navbar.js b/docs/js/components/Navbar.js new file mode 100644 index 0000000..6d17524 --- /dev/null +++ b/docs/js/components/Navbar.js @@ -0,0 +1,22 @@ +import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "preact/jsx-runtime"; +import { useComputed, useSignal } from '@preact/signals'; +import { EthereumAddress } from '../types/ethereumTypes.js'; +import { serialize } from '../types/types.js'; +import { Blockie } from './Blockie.js'; +import { SettingsIcon, SettingsModal } from './Settings.js'; +import { Button } from './Button.js'; +import { importFromInterceptor } from './Import.js'; +import { getNetwork } from '../constants.js'; +export const Navbar = ({ provider, bouquetSettings, blockInfo, bundle, signers }) => { + const switchNetwork = async (e) => { + const elm = e.target; + provider.peek()?.provider.send('wallet_switchEthereumChain', [{ chainId: `0x${BigInt(elm.value).toString(16)}` }]); + }; + const blockieScale = useSignal(5); + const showSettings = useSignal(false); + const walletAddress = useComputed(() => provider.value?.walletAddress ?? 0n); + const bouquetNetwork = useComputed(() => getNetwork(bouquetSettings.value, provider.value?.chainId || 1n)); + return (_jsxs("div", { className: 'flex flex-col w-full sm:flex-row items-center justify-between gap-4 border-slate-400/30 h-12', children: [_jsx("h1", { className: 'font-extrabold text-4xl', children: "\uD83D\uDC90" }), _jsxs("div", { className: 'flex gap-4 items-center justify-center w-min max-w-full px-4 sm:px-0 text-sm sm:text-md', children: [provider.value ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: 'flex flex-col items-end justify-around h-full w-full', children: [_jsx("p", { className: 'font-bold text-right w-min max-w-full truncate', children: serialize(EthereumAddress, provider.value.walletAddress) }), _jsxs("span", { className: 'text-gray-500 text-md w-max flex gap-1 items-center', children: [_jsx("svg", { width: '1em', height: '1em', viewBox: '0 0 48 48', xmlns: 'http://www.w3.org/2000/svg', className: 'inline-block', children: _jsx("path", { fill: 'currentColor', d: 'M44 32h-2v-8a2 2 0 0 0-2-2H26v-6h2a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2h-8a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h2v6H8a2 2 0 0 0-2 2v8H4a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2v-8a2 2 0 0 0-2-2h-2v-6h12v6h-2a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2v-8a2 2 0 0 0-2-2h-2v-6h12v6h-2a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2v-8a2 2 0 0 0-2-2Zm-34 8H6v-4h4ZM22 8h4v4h-4Zm4 32h-4v-4h4Zm16 0h-4v-4h4Z', "data-name": 'icons Q2' }) }), _jsxs("select", { value: provider.value.chainId.toString(), onChange: switchNetwork, className: 'px-2 py-1 bg-black', children: [bouquetSettings.value.map((network) => _jsx("option", { value: network.chainId.toString(), children: network.networkName })), bouquetSettings.value.find((network) => network.chainId === provider.value?.chainId) === undefined ? _jsx("option", { value: provider.value?.chainId.toString(), children: `ChainId: ${provider.value.chainId}` }) : _jsx(_Fragment, {})] })] })] }), _jsx(Blockie, { address: walletAddress, scale: blockieScale })] })) : (!provider.value && bundle.value ? _jsx("p", { className: 'w-max', children: " No Wallet Connected " }) : + _jsx("div", { className: 'w-max', children: _jsx(Button, { onClick: () => importFromInterceptor(bundle, provider, blockInfo, signers, bouquetSettings), children: "Connect Wallet" }) })), _jsx("button", { class: 'hover:rotate-45 duration-200 ml-2', onClick: () => (showSettings.value = true), children: _jsx(SettingsIcon, {}) })] }), _jsx(SettingsModal, { display: showSettings, bouquetNetwork: bouquetNetwork, bouquetSettings: bouquetSettings })] })); +}; +//# sourceMappingURL=Navbar.js.map \ No newline at end of file diff --git a/docs/js/components/Navbar.js.map b/docs/js/components/Navbar.js.map new file mode 100644 index 0000000..7650eb0 --- /dev/null +++ b/docs/js/components/Navbar.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Navbar.js","sourceRoot":"","sources":["../../ts/components/Navbar.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Signal, useComputed, useSignal } from '@preact/signals'\nimport { ProviderStore } from '../library/provider.js'\nimport { EthereumAddress } from '../types/ethereumTypes.js'\nimport { BlockInfo, Bundle, serialize, Signers } from '../types/types.js'\nimport { Blockie } from './Blockie.js'\nimport { SettingsIcon, SettingsModal } from './Settings.js'\nimport { Button } from './Button.js'\nimport { importFromInterceptor } from './Import.js'\nimport { BouquetSettings } from '../types/bouquetTypes.js'\nimport { getNetwork } from '../constants.js'\n\nexport const Navbar = ({\n\tprovider,\n\tbouquetSettings,\n\tblockInfo,\n\tbundle,\n\tsigners\n}: {\n\tprovider: Signal,\n\tblockInfo: Signal,\n\tbouquetSettings: Signal,\n\tbundle: Signal,\n\tsigners: Signal,\n}) => {\n\tconst switchNetwork = async (e: Event) => {\n\t\tconst elm = e.target as HTMLSelectElement\n\t\tprovider.peek()?.provider.send('wallet_switchEthereumChain', [{ chainId: `0x${ BigInt(elm.value).toString(16) }` }])\n\t}\n\n\tconst blockieScale = useSignal(5)\n\tconst showSettings = useSignal(false)\n\tconst walletAddress = useComputed(() => provider.value?.walletAddress ?? 0n)\n\tconst bouquetNetwork = useComputed(() => getNetwork(bouquetSettings.value, provider.value?.chainId || 1n))\n\treturn (\n\t\t
\n\t\t\t

💐

\n\t\t\t
\n\t\t\t\t{provider.value ? (\n\t\t\t\t\t<>\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

{serialize(EthereumAddress, provider.value.walletAddress)}

\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{bouquetSettings.value.map((network) => )}\n\t\t\t\t\t\t\t\t\t{bouquetSettings.value.find((network) => network.chainId === provider.value?.chainId) === undefined ? : <>}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t) : (!provider.value && bundle.value ?

No Wallet Connected

:\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t)}\n\t\t\t\t\n\t\t\t
\n\t\t\t\n\t\t
\n\t)\n}\n"]} \ No newline at end of file diff --git a/docs/js/components/Settings.d.ts b/docs/js/components/Settings.d.ts new file mode 100644 index 0000000..eabbdac --- /dev/null +++ b/docs/js/components/Settings.d.ts @@ -0,0 +1,10 @@ +import { ReadonlySignal, Signal } from '@preact/signals'; +import { JSX } from 'preact/jsx-runtime'; +import { BouquetNetwork, BouquetSettings } from '../types/bouquetTypes.js'; +export declare const SettingsIcon: () => JSX.Element; +export declare const SettingsModal: ({ display, bouquetNetwork, bouquetSettings }: { + display: Signal; + bouquetNetwork: ReadonlySignal; + bouquetSettings: Signal; +}) => JSX.Element | null; +//# sourceMappingURL=Settings.d.ts.map \ No newline at end of file diff --git a/docs/js/components/Settings.d.ts.map b/docs/js/components/Settings.d.ts.map new file mode 100644 index 0000000..2a60676 --- /dev/null +++ b/docs/js/components/Settings.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"Settings.d.ts","sourceRoot":"","sources":["../../ts/components/Settings.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAS,cAAc,EAAE,MAAM,EAA0B,MAAM,iBAAiB,CAAA;AAEvF,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAA;AAIxC,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAI1E,eAAO,MAAM,YAAY,mBAmBxB,CAAA;AAED,eAAO,MAAM,aAAa;aAA6D,OAAO,OAAO,CAAC;oBAAkB,eAAe,cAAc,CAAC;qBAAmB,OAAO,eAAe,CAAC;wBAgK/L,CAAA"} \ No newline at end of file diff --git a/docs/js/components/Settings.js b/docs/js/components/Settings.js new file mode 100644 index 0000000..767049b --- /dev/null +++ b/docs/js/components/Settings.js @@ -0,0 +1,129 @@ +import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "preact/jsx-runtime"; +import { batch, useComputed, useSignal } from '@preact/signals'; +import { formatUnits, parseUnits } from 'ethers'; +import { DEFAULT_NETWORKS } from '../constants.js'; +import { Button } from './Button.js'; +import { SingleNotice } from './Warns.js'; +import { BouquetSettings } from '../types/bouquetTypes.js'; +import { fetchSettingsFromStorage } from '../stores.js'; +import { useEffect } from 'preact/hooks'; +export const SettingsIcon = () => { + return (_jsxs("svg", { class: 'text-white h-8 w-8', "aria-hidden": 'true', fill: 'none', stroke: 'currentColor', strokeWidth: 1.5, viewBox: '0 0 24 24', xmlns: 'http://www.w3.org/2000/svg', children: [_jsx("path", { d: 'M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z', strokeLinecap: 'round', strokeLinejoin: 'round' }), _jsx("path", { d: 'M15 12a3 3 0 11-6 0 3 3 0 016 0z', strokeLinecap: 'round', strokeLinejoin: 'round' })] })); +}; +export const SettingsModal = ({ display, bouquetNetwork, bouquetSettings }) => { + const chainId = useSignal({ value: bouquetNetwork.peek().chainId, valid: true }); + const simulationRelayEndpointInput = useSignal({ value: bouquetNetwork.peek().simulationRelayEndpoint, valid: true }); + const submissionRelayEndpointInput = useSignal({ value: bouquetNetwork.peek().submissionRelayEndpoint, valid: true }); + const priorityFeeInput = useSignal({ value: formatUnits(bouquetNetwork.peek().priorityFee, 'gwei'), valid: true }); + const blocksInFutureInput = useSignal({ value: bouquetNetwork.peek().blocksInFuture.toString(10), valid: true }); + const mempoolSubmitRpcEndpoint = useSignal({ value: bouquetNetwork.peek().mempoolSubmitRpcEndpoint, valid: true }); + const mempoolSimulationRpcEndpoint = useSignal({ value: bouquetNetwork.peek().mempoolSimulationRpcEndpoint, valid: true }); + const relayMode = useSignal({ value: bouquetNetwork.peek().relayMode, valid: true }); + const loaded = useSignal(false); + useEffect(() => { + bringSettingsValues(); + loaded.value = display.value; + }, [display.value]); + const allValidInputs = useComputed(() => submissionRelayEndpointInput.value.valid && simulationRelayEndpointInput.value.valid && priorityFeeInput.value.valid && blocksInFutureInput.value.valid && mempoolSimulationRpcEndpoint.value.valid && mempoolSubmitRpcEndpoint.value.valid && mempoolSubmitRpcEndpoint.value.valid); + // https://urlregex.com/ + const uriMatcher = new RegExp('^(https?):\\/\\/' + // protocol + '((?:(?:[a-z\\d](?:[a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name + '(?:(?:\\d{1,3}\\.){3}\\d{1,3}))' + // OR IP (v4) address + '(?:\\:(\\d+))?' + // port + '((?:\\/[-a-z\\d%_.~+]*)*)' + // path + '(\\?[;&a-z\\d%_.~+=-]*)?' + // query string + '(\\#[-a-z\\d_]*)?$' // fragment locator + ); + function validateSimulationRelayEndpointInput(value) { + simulationRelayEndpointInput.value = { value, valid: uriMatcher.test(value) }; + } + function validateMempoolSubmitRpcEndpoint(value) { + mempoolSubmitRpcEndpoint.value = { value, valid: uriMatcher.test(value) }; + } + function validateMempoolSimulationRpcEndpointInput(value) { + mempoolSimulationRpcEndpoint.value = { value, valid: uriMatcher.test(value) }; + } + function validateAndSetSubmissionRelayEndpointInput(value) { + submissionRelayEndpointInput.value = { value, valid: uriMatcher.test(value) }; + } + function validateAndSetPriorityFeeInput(value) { + if (!value) + return priorityFeeInput.value = { value, valid: false }; + try { + parseUnits(String(Number(value)), 'gwei'); + return priorityFeeInput.value = { value, valid: true }; + } + catch { + return priorityFeeInput.value = { value, valid: false }; + } + } + function validateAndSetBlocksInFutureInput(value) { + if (!value) + return blocksInFutureInput.value = { value, valid: false }; + try { + BigInt(value); + return blocksInFutureInput.value = { value, valid: true }; + } + catch { + return blocksInFutureInput.value = { value, valid: false }; + } + } + function saveSettings() { + if (!allValidInputs.value) + return; + const newSettings = { + submissionRelayEndpoint: submissionRelayEndpointInput.value.value, + simulationRelayEndpoint: simulationRelayEndpointInput.value.value, + priorityFee: parseUnits(String(Number(priorityFeeInput.value.value)), 'gwei'), + blocksInFuture: BigInt(blocksInFutureInput.value.value), + mempoolSubmitRpcEndpoint: mempoolSubmitRpcEndpoint.value.value, + mempoolSimulationRpcEndpoint: mempoolSimulationRpcEndpoint.value.value, + relayMode: relayMode.value.value, + }; + const oldSettings = fetchSettingsFromStorage(); + const index = oldSettings.findIndex((item) => item.chainId === chainId.value.value); + if (index >= 0) { + localStorage.setItem('bouquetSettings', JSON.stringify(BouquetSettings.serialize(oldSettings.map((oldSetting) => { + if (oldSetting.chainId !== chainId.value.value) + return oldSetting; + return { ...oldSetting, ...newSettings }; + })))); + } + else { + localStorage.setItem('bouquetSettings', JSON.stringify(BouquetSettings.serialize([ + ...oldSettings, + { + ...newSettings, + chainId: chainId.value.value, + networkName: `ChainId: ${chainId.value.value}`, + blockExplorerApi: '', + blockExplorer: '', + } + ]))); + } + display.value = false; + bouquetSettings.value = fetchSettingsFromStorage(); + } + function bringSettingsValues() { + batch(() => { + chainId.value = { value: bouquetNetwork.peek().chainId, valid: true }; + simulationRelayEndpointInput.value = { value: bouquetNetwork.peek().simulationRelayEndpoint, valid: true }; + submissionRelayEndpointInput.value = { value: bouquetNetwork.peek().submissionRelayEndpoint, valid: true }; + priorityFeeInput.value = { value: formatUnits(bouquetNetwork.peek().priorityFee, 'gwei'), valid: true }; + blocksInFutureInput.value = { value: bouquetNetwork.peek().blocksInFuture.toString(10), valid: true }; + mempoolSimulationRpcEndpoint.value = { value: bouquetNetwork.peek().mempoolSimulationRpcEndpoint, valid: true }; + mempoolSubmitRpcEndpoint.value = { value: bouquetNetwork.peek().mempoolSubmitRpcEndpoint, valid: true }; + relayMode.value = { value: bouquetNetwork.peek().relayMode, valid: true }; + }); + } + function resetSettings() { + localStorage.setItem('bouquetSettings', JSON.stringify(BouquetSettings.serialize(DEFAULT_NETWORKS))); + bouquetSettings.value = fetchSettingsFromStorage(); + bringSettingsValues(); + } + function close() { + display.value = false; + } + return display.value && loaded.value ? (_jsx("div", { onClick: close, className: 'bg-white/10 w-full h-full inset-0 fixed p-4 flex flex-col items-center md:pt-24', children: _jsxs("div", { class: 'h-max px-8 py-4 w-full max-w-xl flex flex-col gap-4 bg-black', onClick: (e) => e.stopPropagation(), children: [_jsx("h2", { className: 'text-xl font-semibold', children: "App Settings" }), _jsxs("label", { class: 'toggle-switch', children: [_jsx("input", { type: 'checkbox', checked: relayMode.value.value === 'relay' ? false : true, onChange: (e) => { relayMode.value = { value: e.currentTarget.checked ? 'mempool' : 'relay', valid: true }; } }), _jsx("a", {}), _jsxs("span", { children: [_jsx("span", { class: 'left-span', children: "Relay" }), _jsx("span", { class: 'right-span', children: "Mempool" })] })] }), relayMode.value.value === 'mempool' ? _jsxs(_Fragment, { children: [_jsx(SingleNotice, { variant: 'warn', title: 'Mempool mode is dangerous', description: `When mempool mode is enabled. The transactions are sent as individual transactions to the below RPC URL. This means it's possible that only one of the transactions might end up on the chain. Use this mode only if a relay is not available for the network.` }), _jsxs("div", { className: `flex flex-col justify-center border h-16 outline-none px-4 focus-within:bg-white/5 bg-transparent ${!mempoolSimulationRpcEndpoint.value.valid ? 'border-red-400' : 'border-white/50 focus-within:border-white/80'}`, children: [_jsx("span", { className: 'text-sm text-gray-500', children: "Mempool Simulation RPC URL (a RPC with eth_simulateV1 support)" }), _jsx("input", { onInput: (e) => validateMempoolSimulationRpcEndpointInput(e.currentTarget.value), value: mempoolSimulationRpcEndpoint.value.value, type: 'text', className: 'bg-transparent outline-none placeholder:text-gray-600', placeholder: 'https://' })] }, 'mempoolSimulationRpcEndpoint'), _jsxs("div", { className: `flex flex-col justify-center border h-16 outline-none px-4 focus-within:bg-white/5 bg-transparent ${!mempoolSubmitRpcEndpoint.value.valid ? 'border-red-400' : 'border-white/50 focus-within:border-white/80'}`, children: [_jsx("span", { className: 'text-sm text-gray-500', children: "Mempool Submit RPC URL (a sequencer or similar)" }), _jsx("input", { onInput: (e) => validateMempoolSubmitRpcEndpoint(e.currentTarget.value), value: mempoolSubmitRpcEndpoint.value.value, type: 'text', className: 'bg-transparent outline-none placeholder:text-gray-600', placeholder: 'https://' })] }, 'mempoolSubmitRpcEndpoint')] }) : _jsxs(_Fragment, { children: [_jsxs("div", { className: `flex flex-col justify-center border h-16 outline-none px-4 focus-within:bg-white/5 bg-transparent ${!simulationRelayEndpointInput.value.valid ? 'border-red-400' : 'border-white/50 focus-within:border-white/80'}`, children: [_jsx("span", { className: 'text-sm text-gray-500', children: "Bundle Simulation Relay URL" }), _jsx("input", { onInput: (e) => validateSimulationRelayEndpointInput(e.currentTarget.value), value: simulationRelayEndpointInput.value.value, type: 'text', className: 'bg-transparent outline-none placeholder:text-gray-600', placeholder: 'https://' })] }, 'simulationRelayEndpointInput'), _jsxs("div", { className: `flex flex-col justify-center border h-16 outline-none px-4 focus-within:bg-white/5 bg-transparent ${!submissionRelayEndpointInput.value.valid ? 'border-red-400' : 'border-white/50 focus-within:border-white/80'}`, children: [_jsx("span", { className: 'text-sm text-gray-500', children: "Bundle Submission Relay URL" }), _jsx("input", { onInput: (e) => validateAndSetSubmissionRelayEndpointInput(e.currentTarget.value), value: submissionRelayEndpointInput.value.value, type: 'text', className: 'bg-transparent outline-none placeholder:text-gray-600', placeholder: 'https://' })] }, 'submissionRelayEndpointInput'), _jsxs("div", { className: `flex flex-col justify-center border h-16 outline-none px-4 focus-within:bg-white/5 bg-transparent ${!blocksInFutureInput.value.valid ? 'border-red-400' : 'border-white/50 focus-within:border-white/80'}`, children: [_jsx("span", { className: 'text-sm text-gray-500', children: "Target Blocks In Future For Bundle Confirmation" }), _jsx("input", { onInput: (e) => validateAndSetBlocksInFutureInput(e.currentTarget.value), value: blocksInFutureInput.value.value, type: 'number', className: 'bg-transparent outline-none placeholder:text-gray-600' })] }, 'blocksInFutureInput')] }), _jsxs("div", { className: `flex flex-col justify-center border h-16 outline-none px-4 focus-within:bg-white/5 bg-transparent ${!priorityFeeInput.value.valid ? 'border-red-400' : 'border-white/50 focus-within:border-white/80'}`, children: [_jsx("span", { className: 'text-sm text-gray-500', children: "Priority Fee (GWEI)" }), _jsx("input", { onInput: (e) => validateAndSetPriorityFeeInput(e.currentTarget.value), value: priorityFeeInput.value.value, type: 'number', className: 'bg-transparent outline-none placeholder:text-gray-600', placeholder: '0.1' })] }), _jsxs("div", { className: 'flex gap-2', children: [_jsx(Button, { onClick: saveSettings, disabled: !allValidInputs.value, variant: 'primary', children: "Save" }), _jsx(Button, { onClick: resetSettings, variant: 'secondary', children: "Reset" })] })] }) })) : null; +}; +//# sourceMappingURL=Settings.js.map \ No newline at end of file diff --git a/docs/js/components/Settings.js.map b/docs/js/components/Settings.js.map new file mode 100644 index 0000000..7c83cc1 --- /dev/null +++ b/docs/js/components/Settings.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Settings.js","sourceRoot":"","sources":["../../ts/components/Settings.tsx"],"names":[],"mappings":"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","sourcesContent":["import { batch, ReadonlySignal, Signal, useComputed, useSignal } from '@preact/signals'\nimport { formatUnits, parseUnits } from 'ethers'\nimport { JSX } from 'preact/jsx-runtime'\nimport { DEFAULT_NETWORKS } from '../constants.js'\nimport { Button } from './Button.js'\nimport { SingleNotice } from './Warns.js'\nimport { BouquetNetwork, BouquetSettings } from '../types/bouquetTypes.js'\nimport { fetchSettingsFromStorage } from '../stores.js'\nimport { useEffect } from 'preact/hooks'\n\nexport const SettingsIcon = () => {\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t)\n}\n\nexport const SettingsModal = ({ display, bouquetNetwork, bouquetSettings }: { display: Signal, bouquetNetwork: ReadonlySignal, bouquetSettings: Signal}) => {\n\tconst chainId = useSignal({ value: bouquetNetwork.peek().chainId, valid: true })\n\tconst simulationRelayEndpointInput = useSignal({ value: bouquetNetwork.peek().simulationRelayEndpoint, valid: true })\n\tconst submissionRelayEndpointInput = useSignal({ value: bouquetNetwork.peek().submissionRelayEndpoint, valid: true })\n\tconst priorityFeeInput = useSignal({ value: formatUnits(bouquetNetwork.peek().priorityFee, 'gwei'), valid: true })\n\tconst blocksInFutureInput = useSignal({ value: bouquetNetwork.peek().blocksInFuture.toString(10), valid: true })\n\tconst mempoolSubmitRpcEndpoint = useSignal({ value: bouquetNetwork.peek().mempoolSubmitRpcEndpoint, valid: true })\n\tconst mempoolSimulationRpcEndpoint = useSignal({ value: bouquetNetwork.peek().mempoolSimulationRpcEndpoint, valid: true })\n\tconst relayMode = useSignal({ value: bouquetNetwork.peek().relayMode, valid: true })\n\tconst loaded = useSignal(false)\n\n\tuseEffect(() => {\n\t\tbringSettingsValues()\n\t\tloaded.value = display.value\n\t}, [display.value])\n\tconst allValidInputs = useComputed(() => submissionRelayEndpointInput.value.valid && simulationRelayEndpointInput.value.valid && priorityFeeInput.value.valid && blocksInFutureInput.value.valid && mempoolSimulationRpcEndpoint.value.valid && mempoolSubmitRpcEndpoint.value.valid && mempoolSubmitRpcEndpoint.value.valid)\n\n\t// https://urlregex.com/\n\tconst uriMatcher = new RegExp(\n\t\t'^(https?):\\\\/\\\\/' + // protocol\n\t\t'((?:(?:[a-z\\\\d](?:[a-z\\\\d-]*[a-z\\\\d])*)\\\\.)+[a-z]{2,}|' + // domain name\n\t\t'(?:(?:\\\\d{1,3}\\\\.){3}\\\\d{1,3}))' + // OR IP (v4) address\n\t\t'(?:\\\\:(\\\\d+))?' + // port\n\t\t'((?:\\\\/[-a-z\\\\d%_.~+]*)*)' + // path\n\t\t'(\\\\?[;&a-z\\\\d%_.~+=-]*)?' + // query string\n\t\t'(\\\\#[-a-z\\\\d_]*)?$' // fragment locator\n\t)\n\tfunction validateSimulationRelayEndpointInput(value: string) {\n\t\tsimulationRelayEndpointInput.value = { value, valid: uriMatcher.test(value) }\n\t}\n\tfunction validateMempoolSubmitRpcEndpoint(value: string) {\n\t\tmempoolSubmitRpcEndpoint.value = { value, valid: uriMatcher.test(value) }\n\t}\n\tfunction validateMempoolSimulationRpcEndpointInput(value: string) {\n\t\tmempoolSimulationRpcEndpoint.value = { value, valid: uriMatcher.test(value) }\n\t}\n\tfunction validateAndSetSubmissionRelayEndpointInput(value: string) {\n\t\tsubmissionRelayEndpointInput.value = { value, valid: uriMatcher.test(value) }\n\t}\n\n\tfunction validateAndSetPriorityFeeInput(value: string) {\n\t\tif (!value) return priorityFeeInput.value = { value, valid: false }\n\t\ttry {\n\t\t\tparseUnits(String(Number(value)), 'gwei');\n\t\t\treturn priorityFeeInput.value = { value, valid: true }\n\t\t} catch {\n\t\t\treturn priorityFeeInput.value = { value, valid: false }\n\t\t}\n\t}\n\tfunction validateAndSetBlocksInFutureInput(value: string) {\n\t\tif (!value) return blocksInFutureInput.value = { value, valid: false }\n\t\ttry {\n\t\t\tBigInt(value)\n\t\t\treturn blocksInFutureInput.value = { value, valid: true }\n\t\t} catch {\n\t\t\treturn blocksInFutureInput.value = { value, valid: false }\n\t\t}\n\t}\n\tfunction saveSettings() {\n\t\tif (!allValidInputs.value) return\n\t\tconst newSettings = {\n\t\t\tsubmissionRelayEndpoint: submissionRelayEndpointInput.value.value,\n\t\t\tsimulationRelayEndpoint: simulationRelayEndpointInput.value.value,\n\t\t\tpriorityFee: parseUnits(String(Number(priorityFeeInput.value.value)), 'gwei'),\n\t\t\tblocksInFuture: BigInt(blocksInFutureInput.value.value),\n\t\t\tmempoolSubmitRpcEndpoint: mempoolSubmitRpcEndpoint.value.value,\n\t\t\tmempoolSimulationRpcEndpoint: mempoolSimulationRpcEndpoint.value.value,\n\t\t\trelayMode: relayMode.value.value,\n\t\t}\n\t\tconst oldSettings = fetchSettingsFromStorage()\n\t\tconst index = oldSettings.findIndex((item) => item.chainId === chainId.value.value)\n\t\tif (index >= 0) {\n\t\t\tlocalStorage.setItem('bouquetSettings', JSON.stringify(BouquetSettings.serialize(oldSettings.map((oldSetting) => {\n\t\t\t\tif (oldSetting.chainId !== chainId.value.value) return oldSetting\n\t\t\t\treturn { ...oldSetting, ...newSettings }\n\t\t\t}))))\n\t\t} else {\n\t\t\tlocalStorage.setItem('bouquetSettings', JSON.stringify(BouquetSettings.serialize([\n\t\t\t\t...oldSettings,\n\t\t\t\t{\n\t\t\t\t\t...newSettings,\n\t\t\t\t\tchainId: chainId.value.value,\n\t\t\t\t\tnetworkName: `ChainId: ${ chainId.value.value }`,\n\t\t\t\t\tblockExplorerApi: '',\n\t\t\t\t\tblockExplorer: '',\n\t\t\t\t}\n\t\t\t])))\n\t\t}\n\t\tdisplay.value = false\n\t\tbouquetSettings.value = fetchSettingsFromStorage()\n\t}\n\n\tfunction bringSettingsValues() {\n\t\tbatch(() => {\n\t\t\tchainId.value = { value: bouquetNetwork.peek().chainId, valid: true }\n\t\t\tsimulationRelayEndpointInput.value = { value: bouquetNetwork.peek().simulationRelayEndpoint, valid: true }\n\t\t\tsubmissionRelayEndpointInput.value = { value: bouquetNetwork.peek().submissionRelayEndpoint, valid: true }\n\t\t\tpriorityFeeInput.value = { value: formatUnits(bouquetNetwork.peek().priorityFee, 'gwei'), valid: true }\n\t\t\tblocksInFutureInput.value = { value: bouquetNetwork.peek().blocksInFuture.toString(10), valid: true }\n\t\t\tmempoolSimulationRpcEndpoint.value = { value: bouquetNetwork.peek().mempoolSimulationRpcEndpoint, valid: true }\n\t\t\tmempoolSubmitRpcEndpoint.value = { value: bouquetNetwork.peek().mempoolSubmitRpcEndpoint, valid: true }\n\t\t\trelayMode.value = { value: bouquetNetwork.peek().relayMode, valid: true }\n\t\t})\n\t}\n\n\tfunction resetSettings() {\n\t\tlocalStorage.setItem('bouquetSettings', JSON.stringify(BouquetSettings.serialize(DEFAULT_NETWORKS)))\n\t\tbouquetSettings.value = fetchSettingsFromStorage()\n\t\tbringSettingsValues()\n\t}\n\tfunction close() {\n\t\tdisplay.value = false\n\t}\n\treturn display.value && loaded.value ? (\n\t\t
\n\t\t\t
e.stopPropagation()}>\n\t\t\t\t

App Settings

\n\t\t\t\t\n\t\t\t\t{ relayMode.value.value === 'mempool' ? <>\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\tMempool Simulation RPC URL (a RPC with eth_simulateV1 support)\n\t\t\t\t\t\t) => validateMempoolSimulationRpcEndpointInput(e.currentTarget.value)} value={mempoolSimulationRpcEndpoint.value.value} type='text' className='bg-transparent outline-none placeholder:text-gray-600' placeholder='https://' />\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\tMempool Submit RPC URL (a sequencer or similar)\n\t\t\t\t\t\t) => validateMempoolSubmitRpcEndpoint(e.currentTarget.value)} value={mempoolSubmitRpcEndpoint.value.value} type='text' className='bg-transparent outline-none placeholder:text-gray-600' placeholder='https://' />\n\t\t\t\t\t
\n\t\t\t\t : <>\n\t\t\t\t\t
\n\t\t\t\t\t\tBundle Simulation Relay URL\n\t\t\t\t\t\t) => validateSimulationRelayEndpointInput(e.currentTarget.value)} value={simulationRelayEndpointInput.value.value} type='text' className='bg-transparent outline-none placeholder:text-gray-600' placeholder='https://' />\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\tBundle Submission Relay URL\n\t\t\t\t\t\t) => validateAndSetSubmissionRelayEndpointInput(e.currentTarget.value)} value={submissionRelayEndpointInput.value.value} type='text' className='bg-transparent outline-none placeholder:text-gray-600' placeholder='https://' />\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\tTarget Blocks In Future For Bundle Confirmation\n\t\t\t\t\t\t) => validateAndSetBlocksInFutureInput(e.currentTarget.value)} value={blocksInFutureInput.value.value} type='number' className='bg-transparent outline-none placeholder:text-gray-600' />\n\t\t\t\t\t
\n\t\t\t\t }\n\t\t\t\t
\n\t\t\t\t\tPriority Fee (GWEI)\n\t\t\t\t\t) => validateAndSetPriorityFeeInput(e.currentTarget.value)} value={priorityFeeInput.value.value} type='number' className='bg-transparent outline-none placeholder:text-gray-600' placeholder='0.1' />\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t
\n\t\t
\n\t) : null\n}\n"]} \ No newline at end of file diff --git a/docs/js/components/Submit.d.ts b/docs/js/components/Submit.d.ts new file mode 100644 index 0000000..5e66546 --- /dev/null +++ b/docs/js/components/Submit.d.ts @@ -0,0 +1,52 @@ +import { ReadonlySignal, Signal } from '@preact/signals'; +import { BlockInfo, Bundle, Signers } from '../types/types.js'; +import { ProviderStore } from '../library/provider.js'; +import { BouquetNetwork, BouquetSettings } from '../types/bouquetTypes.js'; +type PendingBundle = { + bundles: { + [bundleIdentifier: string]: { + targetBlock: bigint; + gas: { + priorityFee: bigint; + baseFee: bigint; + }; + transactions: { + signedTransaction: string; + hash: string; + account: string; + nonce: bigint; + }[]; + included: boolean; + }; + }; + error?: Error; + success?: { + targetBlock: bigint; + gas: { + priorityFee: bigint; + baseFee: bigint; + }; + transactions: { + signedTransaction: string; + hash: string; + account: string; + nonce: bigint; + }[]; + included: boolean; + includedInBlocks: bigint[]; + }; +}; +export declare const Bundles: ({ outstandingBundles, bouquetNetwork, }: { + outstandingBundles: Signal; + bouquetNetwork: Signal; +}) => import("preact").JSX.Element; +export declare const Submit: ({ provider, bundle, fundingAmountMin, signers, bouquetSettings, blockInfo, }: { + provider: Signal; + bundle: Signal; + signers: Signal; + fundingAmountMin: ReadonlySignal; + bouquetSettings: Signal; + blockInfo: Signal; +}) => import("preact").JSX.Element; +export {}; +//# sourceMappingURL=Submit.d.ts.map \ No newline at end of file diff --git a/docs/js/components/Submit.d.ts.map b/docs/js/components/Submit.d.ts.map new file mode 100644 index 0000000..f609414 --- /dev/null +++ b/docs/js/components/Submit.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"Submit.d.ts","sourceRoot":"","sources":["../../ts/components/Submit.tsx"],"names":[],"mappings":"AACA,OAAO,EAAS,cAAc,EAAE,MAAM,EAA2C,MAAM,iBAAiB,CAAA;AAGxG,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAKtD,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAG1E,KAAK,aAAa,GAAG;IACpB,OAAO,EAAE;QACR,CAAC,gBAAgB,EAAE,MAAM,GAAG;YAC3B,WAAW,EAAE,MAAM,CAAC;YACpB,GAAG,EAAE;gBAAE,WAAW,EAAE,MAAM,CAAC;gBAAC,OAAO,EAAE,MAAM,CAAA;aAAE,CAAA;YAC7C,YAAY,EAAE;gBAAE,iBAAiB,EAAE,MAAM,CAAC;gBAAC,IAAI,EAAE,MAAM,CAAC;gBAAC,OAAO,EAAE,MAAM,CAAC;gBAAC,KAAK,EAAE,MAAM,CAAA;aAAE,EAAE,CAAA;YAC3F,QAAQ,EAAE,OAAO,CAAA;SACjB,CAAA;KACD,CAAA;IACD,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,OAAO,CAAC,EAAE;QACT,WAAW,EAAE,MAAM,CAAC;QACpB,GAAG,EAAE;YAAE,WAAW,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAA;QAC7C,YAAY,EAAE;YAAE,iBAAiB,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,EAAE,CAAA;QAC3F,QAAQ,EAAE,OAAO,CAAA;QACjB,gBAAgB,EAAE,MAAM,EAAE,CAAA;KAC1B,CAAA;CACD,CAAA;AA6CD,eAAO,MAAM,OAAO;wBAIC,OAAO,aAAa,CAAC;oBACzB,OAAO,cAAc,CAAC;kCA4BtC,CAAA;AAED,eAAO,MAAM,MAAM;cAQR,OAAO,aAAa,GAAG,SAAS,CAAC;YACnC,OAAO,MAAM,GAAG,SAAS,CAAC;aACzB,OAAO,OAAO,CAAC;sBACN,eAAe,MAAM,CAAC;qBACvB,OAAO,eAAe,CAAC;eAC7B,OAAO,SAAS,CAAC;kCAkL5B,CAAA"} \ No newline at end of file diff --git a/docs/js/components/Submit.js b/docs/js/components/Submit.js new file mode 100644 index 0000000..3b183ef --- /dev/null +++ b/docs/js/components/Submit.js @@ -0,0 +1,148 @@ +import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "preact/jsx-runtime"; +import { EtherSymbol, formatEther, formatUnits } from 'ethers'; +import { batch, useComputed, useSignal, useSignalEffect } from '@preact/signals'; +import { getMaxBaseFeeInFutureBlock } from '../library/bundleUtils.js'; +import { Button } from './Button.js'; +import { SettingsModal } from './Settings.js'; +import { useAsyncState } from '../library/asyncState.js'; +import { simulateBundle, sendBundle, checkBundleInclusion } from '../library/flashbots.js'; +import { SingleNotice } from './Warns.js'; +import { getNetwork } from '../constants.js'; +const SimulationResult = ({ state }) => { + if (state.value.state === 'pending') + return _jsx("div", { children: "Simulating..." }); + if (state.value.state === 'resolved') { + return state.value.value.firstRevert ? + _jsx(SingleNotice, { variant: 'error', title: 'A Transaction Reverted During Simulation', description: _jsxs("div", { class: 'flex w-full min-h-[96px] border border-white/90 mt-4', children: [_jsx("div", { class: 'flex w-16 flex-col items-center justify-center text-white', children: _jsxs("span", { class: 'text-lg font-bold', children: ["#", state.value.value.results.findIndex((x) => 'error' in x)] }) }), _jsxs("div", { class: 'bg-gray-500/30 flex w-full justify-center flex-col gap-2 p-4 text-sm font-semibold', children: [_jsxs("div", { class: 'flex gap-2 items-center', children: [_jsx("span", { class: 'w-16 text-right', children: "From" }), _jsx("span", { class: 'bg-black px-2 py-1 font-mono font-medium', children: state.value.value.firstRevert.fromAddress })] }), _jsxs("div", { class: 'flex gap-2 items-center', children: [_jsx("span", { class: 'w-16 text-right', children: "To" }), _jsx("span", { class: 'bg-black px-2 py-1 font-mono font-medium', children: state.value.value.firstRevert.toAddress })] }), _jsxs("div", { class: 'flex gap-2 items-center', children: [_jsx("span", { class: 'w-16 text-right', children: "Gas Used" }), _jsxs("span", { class: 'bg-black px-2 py-1 font-mono font-medium', children: [state.value.value.firstRevert.gasUsed, " gas"] })] }), _jsxs("div", { class: 'flex gap-2 items-center', children: [_jsx("span", { class: 'w-16 text-right', children: "Error" }), _jsx("span", { class: 'bg-black px-2 py-1 font-mono font-medium', children: 'error' in state.value.value.firstRevert ? String(state.value.value.firstRevert.error) : 'Unknown' })] })] })] }) }) + : _jsx(SingleNotice, { variant: 'success', title: 'Simulation Succeeded', description: _jsxs("p", { children: [_jsx("b", { children: state.value.value.results.length }), " Transactions succeeded, consuming ", _jsx("b", { children: state.value.value.totalGasUsed }), " gas with a total fee of ", _jsxs("b", { children: [EtherSymbol, formatEther(state.value.value.gasFees)] }), "."] }) }); + } + if (state.value.state === 'rejected') { + return _jsx(SingleNotice, { variant: 'error', title: 'Simulation Failed', description: _jsx("p", { class: 'font-medium w-full break-all', children: state.value.error.message }) }); + } + return _jsx(_Fragment, {}); +}; +export const Bundles = ({ outstandingBundles, bouquetNetwork, }) => { + if (outstandingBundles.value.error) + return _jsx(SingleNotice, { variant: 'error', title: 'Error Sending Bundle', description: _jsx("p", { class: 'font-medium w-full break-all', children: outstandingBundles.value.error.message }) }); + const blockExplorerBaseUrl = bouquetNetwork.value !== undefined ? bouquetNetwork.value.blockExplorer : undefined; + return (_jsx("div", { class: 'flex flex-col-reverse gap-4', children: outstandingBundles.value.success + ? _jsx(SingleNotice, { variant: 'success', title: bouquetNetwork.value.relayMode === 'mempool' ? 'Transactions included!' : 'Bundle Included!', description: _jsxs("div", { children: [_jsxs("h3", { class: 'text-md', children: [_jsx("b", { children: outstandingBundles.value.success.transactions.length }), " ", `transactions were included in block${outstandingBundles.value.success.includedInBlocks.length > 1 ? 's' : ''}`, " ", _jsx("b", { children: outstandingBundles.value.success.includedInBlocks.join(',') })] }), _jsx("div", { class: 'flex flex-col gap-1 py-1', children: outstandingBundles.value.success.transactions.map((tx, index) => blockExplorerBaseUrl + ? _jsxs("p", { class: 'flex items-center gap-2', children: [_jsxs("b", { children: ["#", index] }), _jsxs("a", { class: 'underline text-white/50 flex items-center gap-2', href: `${blockExplorerBaseUrl}tx/${tx.hash}`, target: "_blank", children: [tx.hash, _jsxs("svg", { "aria-hidden": "true", class: 'h-6', fill: "none", stroke: "currentColor", "stroke-width": "1.5", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", children: [" ", _jsx("path", { d: "M13.5 6H5.25A2.25 2.25 0 003 8.25v10.5A2.25 2.25 0 005.25 21h10.5A2.25 2.25 0 0018 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25", "stroke-linecap": "round", "stroke-linejoin": "round" })] })] })] }) + : _jsxs("p", { children: [_jsxs("b", { children: ["#", index] }), " ", _jsx("span", { class: 'semibold text-white/50', children: tx.hash })] })) })] }) }) + : Object.values(outstandingBundles.value.bundles).map((bundle) => _jsxs("div", { class: 'flex items-center gap-2 text-white', children: [_jsxs("svg", { class: 'animate-spin h-4 w-4 text-white', xmlns: 'http://www.w3.org/2000/svg', fill: 'none', viewBox: '0 0 24 24', children: [_jsx("circle", { class: 'opacity-25', cx: '12', cy: '12', r: '10', stroke: 'currentColor', "stroke-width": '4' }), _jsx("path", { class: 'opacity-75', fill: 'currentColor', d: 'M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z' })] }), _jsxs("p", { children: ["Attempting to get ", bouquetNetwork.value.relayMode === 'mempool' ? 'transactions' : 'bundle', " included before block ", bundle.targetBlock.toString(10), " with max fee of ", Number(formatUnits(bundle.gas.baseFee + bundle.gas.priorityFee, 'gwei')).toPrecision(3), " gwei per gas"] })] })) })); +}; +export const Submit = ({ provider, bundle, fundingAmountMin, signers, bouquetSettings, blockInfo, }) => { + const bouquetNetwork = useComputed(() => getNetwork(bouquetSettings.value, provider.value?.chainId || 1n)); + // General component state + const showSettings = useSignal(false); + const missingRequirements = useComputed(() => { + if (!bundle.value) + return 'No transactions imported yet.'; + const missingSigners = bundle.value.uniqueSigners.length !== Object.keys(signers.value.bundleSigners).length; + const insufficientBalance = signers.value.burnerBalance < fundingAmountMin.value; + if (missingSigners && insufficientBalance) + return 'Missing private keys for signing accounts and funding wallet has insufficent balance.'; + if (missingSigners) + return 'Missing private keys for signing accounts.'; + if (insufficientBalance) + return 'Funding wallet has insufficent balance.'; + return false; + }); + // Simulations + const { value: simulationPromise, waitFor: waitForSimulation } = useAsyncState(); + async function simulateCallback() { + if (!provider.value) + throw 'User not connected'; + if (!bundle.value) + throw 'No imported bundle found'; + const simulationResult = await simulateBundle(bundle.value, fundingAmountMin.peek(), provider.value, signers.peek(), blockInfo.peek(), getNetwork(bouquetSettings.peek(), provider.value.chainId)); + if ('error' in simulationResult) + throw new Error(simulationResult.error.message); + else + return simulationResult; + } + // Submissions + const submissionStatus = useSignal({ active: false, lastBlock: 0n, timesSubmited: 0 }); + const outstandingBundles = useSignal({ bundles: {} }); + useSignalEffect(() => { + if (blockInfo.value.blockNumber > submissionStatus.value.lastBlock) { + bundleSubmission(blockInfo.value.blockNumber); + } + }); + async function bundleSubmission(blockNumber) { + submissionStatus.value = { ...submissionStatus.peek(), lastBlock: blockNumber }; + if (!provider.value) + throw new Error('User not connected'); + if (!bundle.value) + throw new Error('No imported bundle found'); + const providerStore = provider.value; + // Check status of current bundles + const checkedPending = await Promise.all(Object.keys(outstandingBundles.peek().bundles).map(bundleHash => checkBundleInclusion(outstandingBundles.peek().bundles[bundleHash].transactions, providerStore))); + const included = checkedPending.filter(checkedPending => checkedPending.included); + if (included.length > 0) { + // We done! + batch(() => { + const checkedBundles = Object.keys(outstandingBundles.peek().bundles).reduce((checked, current, index) => { + if (checkedPending[index].included) { + checked[current] = { ...outstandingBundles.peek().bundles[current], includedInBlocks: checkedPending[index].includedInBlocks }; + checked[current].included = checkedPending[index].included; + } + return checked; + }, {}); + outstandingBundles.value = { + error: outstandingBundles.peek().error, + bundles: checkedBundles, + success: Object.values(checkedBundles).find(x => x.included) + }; + submissionStatus.value = { active: false, lastBlock: blockNumber, timesSubmited: 0 }; + simulationPromise.value = { ...simulationPromise.value, state: 'inactive' }; + }); + } + else { + if (bouquetNetwork.peek().relayMode === 'mempool' && submissionStatus.peek().timesSubmited > 0) + return; // don't resubmit on mempool mode + // Remove old submissions + outstandingBundles.value = { + error: outstandingBundles.peek().error, + success: outstandingBundles.peek().success, + bundles: Object.keys(outstandingBundles.peek().bundles) + .filter(tx => outstandingBundles.peek().bundles[tx].targetBlock + 1n > blockNumber) + .reduce((obj, bundleHash) => { + obj[bundleHash] = outstandingBundles.peek().bundles[bundleHash]; + return obj; + }, {}) + }; + // Try Submit + if (submissionStatus.value.active && !outstandingBundles.value.success) { + submissionStatus.value = { ...submissionStatus.peek(), timesSubmited: submissionStatus.peek().timesSubmited + 1 }; + try { + const targetBlock = blockNumber + bouquetNetwork.peek().blocksInFuture; + const gas = blockInfo.peek(); + gas.priorityFee = bouquetNetwork.value.priorityFee; + const bundleRequest = await sendBundle(bundle.value, targetBlock, fundingAmountMin.peek(), provider.value, signers.peek(), blockInfo.peek(), bouquetNetwork.peek()); + if (!(bundleRequest.bundleIdentifier in outstandingBundles.peek().bundles)) { + outstandingBundles.value = { ...outstandingBundles.peek(), bundles: { ...outstandingBundles.peek().bundles, [bundleRequest.bundleIdentifier]: { targetBlock, gas, transactions: bundleRequest.bundleTransactions, included: false } } }; + } + } + catch (err) { + console.error('SendBundle error', err); + const error = err && typeof err === 'object' && 'message' in err && typeof err.message === 'string' ? new Error(err.message) : new Error('Unknown Error'); + batch(() => { + submissionStatus.value = { active: false, lastBlock: blockNumber, timesSubmited: submissionStatus.peek().timesSubmited }; + outstandingBundles.value = { ...outstandingBundles.peek(), error }; + }); + } + } + } + } + async function toggleSubmission() { + batch(() => { + simulationPromise.value = { ...simulationPromise.value, state: 'inactive' }; + outstandingBundles.value = { bundles: !outstandingBundles.peek().success ? outstandingBundles.peek().bundles : {}, error: undefined, success: submissionStatus.peek().active ? outstandingBundles.peek().success : undefined }; + submissionStatus.value = { ...submissionStatus.peek(), active: !submissionStatus.peek().active }; + }); + bundleSubmission(blockInfo.peek().blockNumber); + } + return (_jsxs(_Fragment, { children: [_jsxs("h2", { className: 'font-bold text-2xl', children: [_jsx("span", { class: 'text-gray-500', children: "3." }), " Submit"] }), _jsx(SettingsModal, { display: showSettings, bouquetNetwork: bouquetNetwork, bouquetSettings: bouquetSettings }), !outstandingBundles.value.success && missingRequirements.value ? (_jsx("p", { children: missingRequirements.peek() })) : (_jsxs("div", { className: 'flex flex-col w-full gap-4', children: [_jsxs("div", { children: [bouquetNetwork.value.relayMode === 'mempool' ? _jsxs(_Fragment, { children: [_jsx("div", { style: 'padding-bottom: 10px;', children: _jsx(SingleNotice, { variant: 'warn', title: 'Mempool mode is dangerous', description: `You are currently using Mempool mode. When mempool mode is enabled. The transactions are sent as individual transactions. This means it's possible that only one of the transactions might end up on the chain. Use this mode only if a relay is not available for the network.` }) }), _jsxs("p", { children: [_jsx("span", { className: 'font-bold', children: "Gas:" }), " ", formatUnits(getMaxBaseFeeInFutureBlock(blockInfo.value.baseFee, bouquetNetwork.value.blocksInFuture), 'gwei'), " gwei + ", formatUnits(bouquetNetwork.value.priorityFee.toString(), 'gwei'), " gwei priority"] }), _jsxs("p", { children: [_jsx("span", { className: 'font-bold', children: "Transaction Submit RPC:" }), " ", bouquetNetwork.value.mempoolSubmitRpcEndpoint] })] }) : _jsxs(_Fragment, { children: [_jsxs("p", { children: [_jsx("span", { className: 'font-bold', children: "Gas:" }), " ", formatUnits(getMaxBaseFeeInFutureBlock(blockInfo.value.baseFee, bouquetNetwork.value.blocksInFuture), 'gwei'), " gwei + ", formatUnits(bouquetNetwork.value.priorityFee.toString(), 'gwei'), " gwei priority"] }), _jsxs("p", { children: [_jsx("span", { className: 'font-bold', children: "Relays:" }), " simulation:", bouquetNetwork.value.simulationRelayEndpoint, ", submit:", bouquetNetwork.value.submissionRelayEndpoint, " (Block ", blockInfo.value.blockNumber.toString(), ")"] }), _jsxs("p", { children: ["Transactions will be attempt to be included in the block ", bouquetNetwork.value.blocksInFuture.toString(), " blocks from now."] })] }), _jsxs("p", { children: ["You can edit these settings ", _jsx("button", { className: 'font-bold underline', onClick: () => showSettings.value = true, children: "here" }), "."] })] }), _jsxs("div", { className: 'flex flex-row gap-6', children: [_jsx(Button, { onClick: () => waitForSimulation(simulateCallback), disabled: simulationPromise.value.state === 'pending', variant: 'secondary', children: "Simulate" }), _jsx(Button, { onClick: toggleSubmission, children: submissionStatus.value.active ? (bouquetNetwork.value.relayMode === 'relay' ? `Stop submitting to relay` : `Stop tracking the transactions`) : `Submit to ${bouquetNetwork.value.relayMode}` })] }), _jsx(SimulationResult, { state: simulationPromise }), _jsx(Bundles, { outstandingBundles: outstandingBundles, bouquetNetwork: bouquetNetwork })] }))] })); +}; +//# sourceMappingURL=Submit.js.map \ No newline at end of file diff --git a/docs/js/components/Submit.js.map b/docs/js/components/Submit.js.map new file mode 100644 index 0000000..ff5a982 --- /dev/null +++ b/docs/js/components/Submit.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Submit.js","sourceRoot":"","sources":["../../ts/components/Submit.tsx"],"names":[],"mappings":"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","sourcesContent":["import { EtherSymbol, formatEther, formatUnits } from 'ethers'\nimport { batch, ReadonlySignal, Signal, useComputed, useSignal, useSignalEffect } from '@preact/signals'\nimport { getMaxBaseFeeInFutureBlock } from '../library/bundleUtils.js'\nimport { Button } from './Button.js'\nimport { BlockInfo, Bundle, Signers } from '../types/types.js'\nimport { ProviderStore } from '../library/provider.js'\nimport { SettingsModal } from './Settings.js'\nimport { useAsyncState, AsyncProperty } from '../library/asyncState.js'\nimport { simulateBundle, sendBundle, checkBundleInclusion, RelayResponseError, SimulationResponseSuccess } from '../library/flashbots.js'\nimport { SingleNotice } from './Warns.js'\nimport { BouquetNetwork, BouquetSettings } from '../types/bouquetTypes.js'\nimport { getNetwork } from '../constants.js'\n\ntype PendingBundle = {\n\tbundles: {\n\t\t[bundleIdentifier: string]: {\n\t\t\ttargetBlock: bigint,\n\t\t\tgas: { priorityFee: bigint, baseFee: bigint }\n\t\t\ttransactions: { signedTransaction: string, hash: string, account: string, nonce: bigint }[]\n\t\t\tincluded: boolean\n\t\t}\n\t}\n\terror?: Error,\n\tsuccess?: {\n\t\ttargetBlock: bigint,\n\t\tgas: { priorityFee: bigint, baseFee: bigint }\n\t\ttransactions: { signedTransaction: string, hash: string, account: string, nonce: bigint }[]\n\t\tincluded: boolean\n\t\tincludedInBlocks: bigint[]\n\t}\n}\n\nconst SimulationResult = ({\n\tstate\n}: {\n\tstate: Signal>\n}) => {\n\tif (state.value.state === 'pending') return
Simulating...
\n\tif (state.value.state === 'resolved') {\n\t\treturn state.value.value.firstRevert ?\n\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t#{state.value.value.results.findIndex((x) => 'error' in x)}\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\tFrom\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{state.value.value.firstRevert.fromAddress}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\tTo\n\t\t\t\t\t\t\t{state.value.value.firstRevert.toAddress}\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\tGas Used\n\t\t\t\t\t\t\t{state.value.value.firstRevert.gasUsed} gas\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\tError\n\t\t\t\t\t\t\t{'error' in state.value.value.firstRevert ? String(state.value.value.firstRevert.error) : 'Unknown'}\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\n\t\t\t} />\n\t\t\t: {state.value.value.results.length} Transactions succeeded, consuming {state.value.value.totalGasUsed} gas with a total fee of {EtherSymbol}{formatEther(state.value.value.gasFees)}.

} />\n\t}\n\tif (state.value.state === 'rejected') {\n\t\treturn {state.value.error.message}

} />\n\t}\n\treturn <>\n}\n\nexport const Bundles = ({\n\toutstandingBundles,\n\tbouquetNetwork,\n}: {\n\toutstandingBundles: Signal,\n\tbouquetNetwork: Signal,\n}) => {\n\tif (outstandingBundles.value.error) return {outstandingBundles.value.error.message}

} />\n\n\tconst blockExplorerBaseUrl = bouquetNetwork.value !== undefined ? bouquetNetwork.value.blockExplorer : undefined\n\n\treturn (\n\t\t
\n\t\t\t{outstandingBundles.value.success\n\t\t\t\t? \n\t\t\t\t\t\t

{outstandingBundles.value.success.transactions.length} { `transactions were included in block${ outstandingBundles.value.success.includedInBlocks.length > 1 ? 's' : '' }` } { outstandingBundles.value.success.includedInBlocks.join(',') }

\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{outstandingBundles.value.success.transactions.map((tx, index) => blockExplorerBaseUrl\n\t\t\t\t\t\t\t\t?

#{index}{tx.hash}

\n\t\t\t\t\t\t\t\t:

#{index} {tx.hash}

\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t
\n\t\t\t\t\t
} />\n\t\t\t\t: Object.values(outstandingBundles.value.bundles).map((bundle) =>
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t

Attempting to get { bouquetNetwork.value.relayMode === 'mempool' ? 'transactions' : 'bundle' } included before block {bundle.targetBlock.toString(10)} with max fee of {Number(formatUnits(bundle.gas.baseFee + bundle.gas.priorityFee, 'gwei')).toPrecision(3)} gwei per gas

\n\t\t\t\t\t
\n\t\t\t)}\n\t\t\n\t)\n}\n\nexport const Submit = ({\n\tprovider,\n\tbundle,\n\tfundingAmountMin,\n\tsigners,\n\tbouquetSettings,\n\tblockInfo,\n}: {\n\tprovider: Signal\n\tbundle: Signal\n\tsigners: Signal\n\tfundingAmountMin: ReadonlySignal\n\tbouquetSettings: Signal\n\tblockInfo: Signal\n}) => {\n\tconst bouquetNetwork = useComputed(() => getNetwork(bouquetSettings.value, provider.value?.chainId || 1n))\n\n\t// General component state\n\tconst showSettings = useSignal(false)\n\n\tconst missingRequirements = useComputed(() => {\n\t\tif (!bundle.value) return 'No transactions imported yet.'\n\t\tconst missingSigners = bundle.value.uniqueSigners.length !== Object.keys(signers.value.bundleSigners).length\n\t\tconst insufficientBalance = signers.value.burnerBalance < fundingAmountMin.value\n\t\tif (missingSigners && insufficientBalance) return 'Missing private keys for signing accounts and funding wallet has insufficent balance.'\n\t\tif (missingSigners) return 'Missing private keys for signing accounts.'\n\t\tif (insufficientBalance) return 'Funding wallet has insufficent balance.'\n\t\treturn false\n\t})\n\n\t// Simulations\n\tconst { value: simulationPromise, waitFor: waitForSimulation } = useAsyncState()\n\n\tasync function simulateCallback() {\n\t\tif (!provider.value) throw 'User not connected'\n\t\tif (!bundle.value) throw 'No imported bundle found'\n\t\tconst simulationResult = await simulateBundle(\n\t\t\tbundle.value,\n\t\t\tfundingAmountMin.peek(),\n\t\t\tprovider.value,\n\t\t\tsigners.peek(),\n\t\t\tblockInfo.peek(),\n\t\t\tgetNetwork(bouquetSettings.peek(), provider.value.chainId)\n\t\t)\n\t\tif ('error' in simulationResult) throw new Error((simulationResult as RelayResponseError).error.message)\n\t\telse return simulationResult\n\t}\n\n\t// Submissions\n\tconst submissionStatus = useSignal<{ active: boolean, lastBlock: bigint, timesSubmited: number }>({ active: false, lastBlock: 0n, timesSubmited: 0 })\n\tconst outstandingBundles = useSignal({ bundles: {} })\n\n\tuseSignalEffect(() => {\n\t\tif (blockInfo.value.blockNumber > submissionStatus.value.lastBlock) {\n\t\t\tbundleSubmission(blockInfo.value.blockNumber)\n\t\t}\n\t})\n\n\tasync function bundleSubmission(blockNumber: bigint) {\n\t\tsubmissionStatus.value = { ...submissionStatus.peek(), lastBlock: blockNumber }\n\n\t\tif (!provider.value) throw new Error('User not connected')\n\t\tif (!bundle.value) throw new Error('No imported bundle found')\n\t\tconst providerStore = provider.value\n\n\t\t// Check status of current bundles\n\t\tconst checkedPending = await Promise.all(Object.keys(outstandingBundles.peek().bundles).map(bundleHash => checkBundleInclusion(outstandingBundles.peek().bundles[bundleHash].transactions, providerStore)))\n\t\tconst included = checkedPending.filter(checkedPending => checkedPending.included)\n\t\tif (included.length > 0) {\n\t\t\t// We done!\n\t\t\tbatch(() => {\n\t\t\t\tconst checkedBundles = Object.keys(outstandingBundles.peek().bundles).reduce((checked: {\n\t\t\t\t\t[bundleHash: string]: {\n\t\t\t\t\t\ttargetBlock: bigint,\n\t\t\t\t\t\tgas: { priorityFee: bigint, baseFee: bigint }\n\t\t\t\t\t\ttransactions: { signedTransaction: string, hash: string, account: string, nonce: bigint }[]\n\t\t\t\t\t\tincluded: boolean\n\t\t\t\t\t\tincludedInBlocks: bigint[]\n\t\t\t\t\t}\n\t\t\t\t}, current, index) => {\n\t\t\t\t\tif (checkedPending[index].included) {\n\t\t\t\t\t\tchecked[current] = { ...outstandingBundles.peek().bundles[current], includedInBlocks: checkedPending[index].includedInBlocks }\n\t\t\t\t\t\tchecked[current].included = checkedPending[index].included\n\t\t\t\t\t}\n\t\t\t\t\treturn checked\n\t\t\t\t}, {})\n\t\t\t\toutstandingBundles.value = {\n\t\t\t\t\terror: outstandingBundles.peek().error,\n\t\t\t\t\tbundles: checkedBundles,\n\t\t\t\t\tsuccess: Object.values(checkedBundles).find(x => x.included)\n\t\t\t\t}\n\t\t\t\tsubmissionStatus.value = { active: false, lastBlock: blockNumber, timesSubmited: 0 }\n\t\t\t\tsimulationPromise.value = { ...simulationPromise.value, state: 'inactive' }\n\t\t\t})\n\t\t} else {\n\t\t\tif (bouquetNetwork.peek().relayMode === 'mempool' && submissionStatus.peek().timesSubmited > 0) return // don't resubmit on mempool mode\n\t\t\t// Remove old submissions\n\t\t\toutstandingBundles.value = {\n\t\t\t\terror: outstandingBundles.peek().error,\n\t\t\t\tsuccess: outstandingBundles.peek().success,\n\t\t\t\tbundles: Object.keys(outstandingBundles.peek().bundles)\n\t\t\t\t\t.filter(tx => outstandingBundles.peek().bundles[tx].targetBlock + 1n > blockNumber)\n\t\t\t\t\t.reduce((obj: {\n\t\t\t\t\t\t[bundleHash: string]: {\n\t\t\t\t\t\t\ttargetBlock: bigint,\n\t\t\t\t\t\t\tgas: { priorityFee: bigint, baseFee: bigint }\n\t\t\t\t\t\t\ttransactions: { signedTransaction: string, hash: string, account: string, nonce: bigint }[]\n\t\t\t\t\t\t\tincluded: boolean\n\t\t\t\t\t\t}\n\t\t\t\t\t}, bundleHash) => {\n\t\t\t\t\t\tobj[bundleHash] = outstandingBundles.peek().bundles[bundleHash]\n\t\t\t\t\t\treturn obj\n\t\t\t\t\t}, {})\n\t\t\t}\n\n\t\t\t// Try Submit\n\t\t\tif (submissionStatus.value.active && !outstandingBundles.value.success) {\n\t\t\t\tsubmissionStatus.value = { ...submissionStatus.peek(), timesSubmited: submissionStatus.peek().timesSubmited + 1 }\n\t\t\t\ttry {\n\t\t\t\t\tconst targetBlock = blockNumber + bouquetNetwork.peek().blocksInFuture\n\t\t\t\t\tconst gas = blockInfo.peek()\n\t\t\t\t\tgas.priorityFee = bouquetNetwork.value.priorityFee\n\t\t\t\t\tconst bundleRequest = await sendBundle(\n\t\t\t\t\t\tbundle.value,\n\t\t\t\t\t\ttargetBlock,\n\t\t\t\t\t\tfundingAmountMin.peek(),\n\t\t\t\t\t\tprovider.value,\n\t\t\t\t\t\tsigners.peek(),\n\t\t\t\t\t\tblockInfo.peek(),\n\t\t\t\t\t\tbouquetNetwork.peek()\n\t\t\t\t\t)\n\n\t\t\t\t\tif (!(bundleRequest.bundleIdentifier in outstandingBundles.peek().bundles)) {\n\t\t\t\t\t\toutstandingBundles.value = { ...outstandingBundles.peek(), bundles: {...outstandingBundles.peek().bundles, [bundleRequest.bundleIdentifier]: { targetBlock, gas, transactions: bundleRequest.bundleTransactions, included: false } } }\n\t\t\t\t\t}\n\t\t\t\t} catch (err) {\n\t\t\t\t\tconsole.error('SendBundle error', err)\n\t\t\t\t\tconst error = err && typeof err === 'object' && 'message' in err && typeof err.message === 'string' ? new Error(err.message) : new Error('Unknown Error')\n\t\t\t\t\tbatch(() => {\n\t\t\t\t\t\tsubmissionStatus.value = { active: false, lastBlock: blockNumber, timesSubmited: submissionStatus.peek().timesSubmited }\n\t\t\t\t\t\toutstandingBundles.value = { ...outstandingBundles.peek(), error }\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tasync function toggleSubmission() {\n\t\tbatch(() => {\n\t\t\tsimulationPromise.value = { ...simulationPromise.value, state: 'inactive' }\n\t\t\toutstandingBundles.value = { bundles: !outstandingBundles.peek().success ? outstandingBundles.peek().bundles : {}, error: undefined, success: submissionStatus.peek().active ? outstandingBundles.peek().success : undefined }\n\t\t\tsubmissionStatus.value = { ...submissionStatus.peek(), active: !submissionStatus.peek().active }\n\t\t})\n\t\tbundleSubmission(blockInfo.peek().blockNumber)\n\t}\n\n\treturn (\n\t\t<>\n\t\t\t

3. Submit

\n\t\t\t\n\t\t\t{!outstandingBundles.value.success && missingRequirements.value ? (\n\t\t\t\t

{missingRequirements.peek()}

\n\t\t\t) : (\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t{ bouquetNetwork.value.relayMode === 'mempool' ? <>\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t

Gas: {formatUnits(getMaxBaseFeeInFutureBlock(blockInfo.value.baseFee, bouquetNetwork.value.blocksInFuture), 'gwei')} gwei + {formatUnits(bouquetNetwork.value.priorityFee.toString(), 'gwei')} gwei priority

\n\t\t\t\t\t\t\t\t

Transaction Submit RPC: { bouquetNetwork.value.mempoolSubmitRpcEndpoint }

\n\t\t\t\t\t\t\t : <>\n\t\t\t\t\t\t\t\t

Gas: {formatUnits(getMaxBaseFeeInFutureBlock(blockInfo.value.baseFee, bouquetNetwork.value.blocksInFuture), 'gwei')} gwei + {formatUnits(bouquetNetwork.value.priorityFee.toString(), 'gwei')} gwei priority

\n\t\t\t\t\t\t\t\t

Relays: simulation:{bouquetNetwork.value.simulationRelayEndpoint}, submit:{bouquetNetwork.value.submissionRelayEndpoint} (Block {blockInfo.value.blockNumber.toString()})

\n\t\t\t\t\t\t\t\t

Transactions will be attempt to be included in the block {bouquetNetwork.value.blocksInFuture.toString()} blocks from now.

\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t

You can edit these settings .

\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t)}\n\t\t\n\t)\n}\n"]} \ No newline at end of file diff --git a/docs/js/components/Transactions.d.ts b/docs/js/components/Transactions.d.ts new file mode 100644 index 0000000..2c4804e --- /dev/null +++ b/docs/js/components/Transactions.d.ts @@ -0,0 +1,14 @@ +import { ReadonlySignal, Signal } from '@preact/signals'; +import { JSXInternal } from 'preact/src/jsx.js'; +import { BlockInfo, Bundle, Signers } from '../types/types.js'; +import { ProviderStore } from '../library/provider.js'; +import { BouquetSettings } from '../types/bouquetTypes.js'; +export declare const Transactions: ({ provider, bundle, blockInfo, bouquetSettings, signers, }: { + provider: Signal; + bundle: Signal; + blockInfo: Signal; + signers: Signal; + bouquetSettings: Signal; + fundingAmountMin: ReadonlySignal; +}) => JSXInternal.Element; +//# sourceMappingURL=Transactions.d.ts.map \ No newline at end of file diff --git a/docs/js/components/Transactions.d.ts.map b/docs/js/components/Transactions.d.ts.map new file mode 100644 index 0000000..3ea96ce --- /dev/null +++ b/docs/js/components/Transactions.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"Transactions.d.ts","sourceRoot":"","sources":["../../ts/components/Transactions.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,EAA8B,MAAM,iBAAiB,CAAA;AAEpF,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,EAAa,OAAO,EAAE,MAAM,mBAAmB,CAAA;AACzE,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAGtD,OAAO,EAAE,eAAe,EAAmB,MAAM,0BAA0B,CAAA;AAoB3E,eAAO,MAAM,YAAY;cAOd,OAAO,aAAa,GAAG,SAAS,CAAC;YACnC,OAAO,MAAM,GAAG,SAAS,CAAC;eACvB,OAAO,SAAS,CAAC;aACnB,OAAO,OAAO,CAAC;qBACP,OAAO,eAAe,CAAC;sBACtB,eAAe,MAAM,CAAC;yBA4KxC,CAAA"} \ No newline at end of file diff --git a/docs/js/components/Transactions.js b/docs/js/components/Transactions.js new file mode 100644 index 0000000..5eb5ce0 --- /dev/null +++ b/docs/js/components/Transactions.js @@ -0,0 +1,126 @@ +import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime"; +import { useSignal, useSignalEffect } from '@preact/signals'; +import { EtherSymbol, formatEther, getAddress, Interface, parseEther } from 'ethers'; +import { serialize } from '../types/types.js'; +import { Button } from './Button.js'; +import { useAsyncState } from '../library/asyncState.js'; +import { TransactionList } from '../types/bouquetTypes.js'; +import { SingleNotice } from './Warns.js'; +import { GetSimulationStackReply } from '../types/interceptorTypes.js'; +import { addressString } from '../library/utils.js'; +import { importFromInterceptor } from './Import.js'; +import { EtherscanGetABIResult, EtherscanSourceCodeResult } from '../types/apiTypes.js'; +import { getNetwork } from '../constants.js'; +function formatTransactionDescription(tx) { + if (tx.fragment.inputs.length === 0) + return _jsx(_Fragment, { children: `${tx.name}()` }); + const params = tx.fragment.inputs.map((y, index) => _jsx("p", { class: 'pl-4', children: `${y.name}: ${tx.args[index].toString()}` })); + return (_jsxs(_Fragment, { children: [_jsx("p", { children: `${tx.name}(` }), params, _jsx("p", { children: ")" })] })); +} +export const Transactions = ({ provider, bundle, blockInfo, bouquetSettings, signers, }) => { + const interfaces = useSignal({}); + const decodedTransactions = useSignal([]); + const interceptorComparison = useSignal({ different: true }); + function copyTransactions() { + if (!bundle.value) + return; + const parsedList = TransactionList.safeSerialize(bundle.value.transactions); + if ('success' in parsedList && parsedList.success) + navigator.clipboard.writeText(JSON.stringify(parsedList.value, null, 2)); + } + const fetchingAbis = useAsyncState(); + async function fetchAbis() { + if (!bundle.value || !bundle.value.transactions) + return; + try { + const uniqueAddresses = [...new Set(bundle.value.transactions.map((tx) => tx.to ? addressString(tx.to) : null).filter(addr => addr))]; + const abis = []; + const network = getNetwork(bouquetSettings.value, provider.value?.chainId || 1n); + const requests = await Promise.all(uniqueAddresses.map((address) => fetch(`${network.blockExplorerApi}/api?module=contract&action=getsourcecode&address=${getAddress(address.toLowerCase())}&apiKey=PSW8C433Q667DVEX5BCRMGNAH9FSGFZ7Q8`))); + const sourcecodeResults = await Promise.all(requests.map((request) => request.json())); + const parsedSourceCode = sourcecodeResults.map(x => EtherscanSourceCodeResult.safeParse(x)); + // Extract ABI from getSourceCode request if not proxy, otherwise attempt to fetch ABI of implementation + for (const contract of parsedSourceCode) { + if (contract.success == false || contract.value.status !== '1') + abis.push(undefined); + else { + if (contract.value.result[0].Proxy === '1' && contract.value.result[0].Implementation !== '') { + const implReq = await fetch(`${network.blockExplorerApi}/api?module=contract&action=getabi&address=${addressString(contract.value.result[0].Implementation)}&apiKey=PSW8C433Q667DVEX5BCRMGNAH9FSGFZ7Q8`); + const implResult = EtherscanGetABIResult.safeParse(await implReq.json()); + abis.push(implResult.success && implResult.value.status === '1' ? implResult.value.result : undefined); + } + else + abis.push(contract.value.result[0].ABI && contract.value.result[0].ABI !== 'Contract source code not verified' ? contract.value.result[0].ABI : undefined); + } + } + interfaces.value = abis.reduce((acc, curr, index) => { + if (curr) + return { ...acc, [`${uniqueAddresses[index]}`]: new Interface(curr) }; + else + return acc; + }, {}); + } + catch (error) { + console.log('parseTransactionsCb Error:', error); + interfaces.value = {}; + } + } + useSignalEffect(() => { + if (interfaces.value && bundle.value) { + parseTransactions(); + compareWithInterceptor(); + } + if (provider.value && provider.value.isInterceptor && !interceptorComparison.value.intervalId) + createCompareInterval(); + }); + const parseTransactions = async () => { + if (!bundle.value) + return; + decodedTransactions.value = bundle.value.transactions.map((tx) => { + if (tx.to && tx.input && tx.input.length > 0) { + const contractAddr = addressString(tx.to); + const txDescription = interfaces.value[contractAddr] ? interfaces.value[contractAddr].parseTransaction({ value: tx.value ?? undefined, data: tx.input.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '0x') }) : null; + return txDescription ? formatTransactionDescription(txDescription) : null; + } + return null; + }); + }; + const compare = async () => { + if (!provider.value || !provider.value.isInterceptor || !bundle.value) + return false; + try { + // fetch stack from Interceptor + const { payload } = await provider.value.provider.send('interceptor_getSimulationStack', ['1.0.0']); + const tryParse = GetSimulationStackReply.safeParse(payload); + if (!tryParse.success) + return false; + let parsedInterceptorTransactions = TransactionList.parse(serialize(GetSimulationStackReply, tryParse.value).map(({ from, to, value, input, gasLimit, chainId }) => ({ from, to, value, input, gasLimit, chainId }))); + if (parsedInterceptorTransactions.length === 0) + return false; + // Detect 'make me rich' + if (parsedInterceptorTransactions.length >= 2 && parsedInterceptorTransactions[0].to === parsedInterceptorTransactions[1].from && parsedInterceptorTransactions[0].value === parseEther('200000')) { + const fundingAddrr = parsedInterceptorTransactions[0].from; + parsedInterceptorTransactions = parsedInterceptorTransactions.map(tx => tx.from === fundingAddrr ? { ...tx, from: 'FUNDING' } : tx); + } + // Compare + const interceptorValue = TransactionList.serialize(parsedInterceptorTransactions.filter(tx => tx.from !== 'FUNDING')); + const bouquetValue = TransactionList.serialize(bundle.value.transactions.filter(tx => tx.from !== 'FUNDING')); + return JSON.stringify(interceptorValue) !== JSON.stringify(bouquetValue); + } + catch { + return false; + } + }; + const compareWithInterceptor = async () => { + const different = await compare(); + interceptorComparison.value = { ...interceptorComparison.value, different }; + }; + async function createCompareInterval() { + if (!provider.value || !provider.value.isInterceptor) + return; + const different = await compare(); + interceptorComparison.value = { different, intervalId: setInterval(compareWithInterceptor, 20000) }; + } + return (_jsxs(_Fragment, { children: [_jsx("h2", { className: 'font-bold text-2xl', children: "Your Transactions" }), _jsxs("div", { className: 'flex flex-row gap-4', children: [_jsx(Button, { variant: 'secondary', disabled: fetchingAbis.value.value.state === 'pending', onClick: () => fetchingAbis.waitFor(fetchAbis), children: "Decode Transactions From Etherscan" }), _jsx(Button, { variant: 'secondary', onClick: copyTransactions, children: _jsxs(_Fragment, { children: ["Copy Transaction List", _jsx("svg", { className: 'h-8 inline-block', "aria-hidden": 'true', fill: 'none', stroke: 'currentColor', "stroke-width": '1.5', viewBox: '0 0 24 24', xmlns: 'http://www.w3.org/2000/svg', children: _jsx("path", { d: 'M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6', "stroke-linecap": 'round', "stroke-linejoin": 'round' }) })] }) })] }), interceptorComparison.value.different ? _jsx(SingleNotice, { variant: 'warn', title: 'Potentially Outdated Transaction List', description: _jsxs(_Fragment, { children: ["The transactions imported in Bouquet differ from the current simulation in The Interceptor extension. ", _jsx("button", { onClick: () => importFromInterceptor(bundle, provider, blockInfo, signers, bouquetSettings), class: 'underline text-white font-semibold', children: "Import From Interceptor" }), " "] }) }) : null, _jsx("div", { class: 'flex w-full flex-col gap-2', children: bundle.value?.transactions.map((tx, index) => (_jsxs("div", { class: 'flex w-full min-h-[96px] border border-white/90', children: [_jsx("div", { class: 'flex w-24 flex-col items-center justify-center text-white', children: _jsxs("span", { class: 'text-lg font-bold', children: ["#", index] }) }), _jsxs("div", { class: 'bg-gray-500/30 flex w-full justify-center flex-col gap-2 p-4 text-sm font-semibold', children: [_jsxs("div", { class: 'flex gap-2 items-center', children: [_jsx("span", { class: 'w-10 text-right', children: "From" }), _jsx("span", { class: 'bg-black px-2 py-1 font-mono font-medium', children: tx.from !== 'FUNDING' ? addressString(tx.from) : tx.from })] }), _jsxs("div", { class: 'flex gap-2 items-center', children: [_jsx("span", { class: 'w-10 text-right', children: "To" }), _jsx("span", { class: 'bg-black px-2 py-1 font-mono font-medium', children: tx.to ? addressString(tx.to) : 'Contract Deployment' })] }), _jsxs("div", { class: 'flex gap-2 items-center', children: [_jsx("span", { class: 'w-10 text-right', children: "Value" }), _jsxs("span", { class: 'bg-black px-2 py-1 font-mono font-medium', children: [EtherSymbol, formatEther(tx.value + (tx.from === 'FUNDING' && bundle.value && bundle.value.containsFundingTx ? bundle.value.totalGas * (blockInfo.value.baseFee + blockInfo.value.priorityFee) : 0n)), " + ", EtherSymbol, formatEther(tx.gasLimit * (blockInfo.value.baseFee + blockInfo.value.priorityFee)), " Gas Fee"] })] }), decodedTransactions.value[index] ? (_jsxs("div", { class: 'flex gap-2 items-center', children: [_jsx("span", { class: 'w-10 text-right', children: "Data" }), _jsx("span", { class: 'bg-black px-2 py-1 font-mono font-medium w-full break-all', children: decodedTransactions.value[index] })] })) : tx.input && tx.input.length > 0 ? (_jsxs("div", { class: 'flex gap-2 items-center', children: [_jsx("span", { class: 'w-10 text-right', children: "Data" }), _jsx("span", { class: 'bg-black px-2 py-1 font-mono font-medium w-full break-all', children: tx.input.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '0x') })] })) : null] })] }))) })] })); +}; +//# sourceMappingURL=Transactions.js.map \ No newline at end of file diff --git a/docs/js/components/Transactions.js.map b/docs/js/components/Transactions.js.map new file mode 100644 index 0000000..004a887 --- /dev/null +++ b/docs/js/components/Transactions.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Transactions.js","sourceRoot":"","sources":["../../ts/components/Transactions.tsx"],"names":[],"mappings":"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","sourcesContent":["import { ReadonlySignal, Signal, useSignal, useSignalEffect } from '@preact/signals'\nimport { EtherSymbol, formatEther, getAddress, Interface, parseEther, TransactionDescription } from 'ethers'\nimport { JSXInternal } from 'preact/src/jsx.js'\nimport { BlockInfo, Bundle, serialize, Signers } from '../types/types.js'\nimport { ProviderStore } from '../library/provider.js'\nimport { Button } from './Button.js'\nimport { useAsyncState } from '../library/asyncState.js'\nimport { BouquetSettings, TransactionList } from '../types/bouquetTypes.js'\nimport { SingleNotice } from './Warns.js'\nimport { GetSimulationStackReply } from '../types/interceptorTypes.js'\nimport { addressString } from '../library/utils.js'\nimport { importFromInterceptor } from './Import.js'\nimport { EtherscanGetABIResult, EtherscanSourceCodeResult } from '../types/apiTypes.js'\nimport { getNetwork } from '../constants.js'\n\nfunction formatTransactionDescription(tx: TransactionDescription) {\n\tif (tx.fragment.inputs.length === 0) return <>{`${tx.name}()`}\n\tconst params = tx.fragment.inputs.map((y, index) =>

{`${y.name}: ${tx.args[index].toString()}`}

)\n\treturn (\n\t\t<>\n\t\t\t

{`${tx.name}(`}

\n\t\t\t{params}\n\t\t\t

)

\n\t\t\n\t)\n}\n\nexport const Transactions = ({\n\tprovider,\n\tbundle,\n\tblockInfo,\n\tbouquetSettings,\n\tsigners,\n}: {\n\tprovider: Signal\n\tbundle: Signal\n\tblockInfo: Signal\n\tsigners: Signal\n\tbouquetSettings: Signal\n\tfundingAmountMin: ReadonlySignal\n}) => {\n\tconst interfaces = useSignal<{ [address: string]: Interface }>({})\n\tconst decodedTransactions = useSignal<(JSXInternal.Element | null)[]>([])\n\tconst interceptorComparison = useSignal<{ different: boolean, intervalId?: ReturnType }>({ different: true })\n\n\tfunction copyTransactions() {\n\t\tif (!bundle.value) return\n\t\tconst parsedList = TransactionList.safeSerialize(bundle.value.transactions)\n\t\tif ('success' in parsedList && parsedList.success) navigator.clipboard.writeText(JSON.stringify(parsedList.value, null, 2))\n\t}\n\n\tconst fetchingAbis = useAsyncState()\n\n\tasync function fetchAbis() {\n\t\tif (!bundle.value || !bundle.value.transactions) return\n\t\ttry {\n\t\t\tconst uniqueAddresses = [...new Set(bundle.value.transactions.map((tx) => tx.to ? addressString(tx.to) : null ).filter(addr => addr))] as string[]\n\t\t\tconst abis: (string | undefined)[] = []\n\t\t\t\n\t\t\tconst network = getNetwork(bouquetSettings.value, provider.value?.chainId || 1n)\n\t\t\tconst requests = await Promise.all(\n\t\t\t\tuniqueAddresses.map((address) =>\n\t\t\t\t\tfetch(\n\t\t\t\t\t\t`${ network.blockExplorerApi }/api?module=contract&action=getsourcecode&address=${getAddress(address.toLowerCase())}&apiKey=PSW8C433Q667DVEX5BCRMGNAH9FSGFZ7Q8`,\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t)\n\t\t\tconst sourcecodeResults = await Promise.all(requests.map((request) => request.json()))\n\t\t\tconst parsedSourceCode = sourcecodeResults.map(x => EtherscanSourceCodeResult.safeParse(x))\n\n\t\t\t// Extract ABI from getSourceCode request if not proxy, otherwise attempt to fetch ABI of implementation\n\t\t\tfor (const contract of parsedSourceCode) {\n\t\t\t\tif (contract.success == false || contract.value.status !== '1') abis.push(undefined)\n\t\t\t\telse {\n\t\t\t\t\tif (contract.value.result[0].Proxy === '1' && contract.value.result[0].Implementation !== '') {\n\t\t\t\t\t\tconst implReq = await fetch(`${ network.blockExplorerApi }/api?module=contract&action=getabi&address=${addressString(contract.value.result[0].Implementation)}&apiKey=PSW8C433Q667DVEX5BCRMGNAH9FSGFZ7Q8`)\n\t\t\t\t\t\tconst implResult = EtherscanGetABIResult.safeParse(await implReq.json())\n\t\t\t\t\t\tabis.push(implResult.success && implResult.value.status === '1' ? implResult.value.result : undefined)\n\t\t\t\t\t} else abis.push(contract.value.result[0].ABI && contract.value.result[0].ABI !== 'Contract source code not verified' ? contract.value.result[0].ABI : undefined)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tinterfaces.value = abis.reduce((acc, curr, index) => {\n\t\t\t\tif (curr) return { ...acc, [`${uniqueAddresses[index]}`]: new Interface(curr) }\n\t\t\t\telse return acc\n\t\t\t}, {})\n\t\t} catch (error) {\n\t\t\tconsole.log('parseTransactionsCb Error:', error)\n\t\t\tinterfaces.value = {}\n\t\t}\n\t}\n\n\tuseSignalEffect(() => {\n\t\tif (interfaces.value && bundle.value) {\n\t\t\tparseTransactions()\n\t\t\tcompareWithInterceptor()\n\t\t}\n\t\tif (provider.value && provider.value.isInterceptor && !interceptorComparison.value.intervalId) createCompareInterval()\n\t})\n\n\tconst parseTransactions = async () => {\n\t\tif (!bundle.value) return\n\t\tdecodedTransactions.value = bundle.value.transactions.map((tx) => {\n\t\t\tif (tx.to && tx.input && tx.input.length > 0) {\n\t\t\t\tconst contractAddr = addressString(tx.to)\n\t\t\t\tconst txDescription = interfaces.value[contractAddr] ? interfaces.value[contractAddr].parseTransaction({ value: tx.value ?? undefined, data: tx.input.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '0x') }) : null\n\t\t\t\treturn txDescription ? formatTransactionDescription(txDescription) : null\n\t\t\t}\n\t\t\treturn null\n\t\t})\n\t}\n\n\tconst compare = async () => {\n\t\tif (!provider.value || !provider.value.isInterceptor || !bundle.value) return false\n\t\ttry {\n\t\t\t// fetch stack from Interceptor\n\t\t\tconst { payload } = await provider.value.provider.send('interceptor_getSimulationStack', ['1.0.0'])\n\t\t\tconst tryParse = GetSimulationStackReply.safeParse(payload)\n\t\t\tif (!tryParse.success) return false\n\t\t\tlet parsedInterceptorTransactions = TransactionList.parse(serialize(GetSimulationStackReply, tryParse.value).map(({ from, to, value, input, gasLimit, chainId }) => ({ from, to, value, input, gasLimit, chainId })))\n\t\t\tif (parsedInterceptorTransactions.length === 0) return false\n\n\t\t\t// Detect 'make me rich'\n\t\t\tif (parsedInterceptorTransactions.length >= 2 && parsedInterceptorTransactions[0].to === parsedInterceptorTransactions[1].from && parsedInterceptorTransactions[0].value === parseEther('200000')) {\n\t\t\t\tconst fundingAddrr = parsedInterceptorTransactions[0].from\n\t\t\t\tparsedInterceptorTransactions = parsedInterceptorTransactions.map(tx => tx.from === fundingAddrr ? { ...tx, from: 'FUNDING' } : tx)\n\t\t\t}\n\n\t\t\t// Compare\n\t\t\tconst interceptorValue = TransactionList.serialize(parsedInterceptorTransactions.filter(tx => tx.from !== 'FUNDING'))\n\t\t\tconst bouquetValue = TransactionList.serialize(bundle.value.transactions.filter(tx => tx.from !== 'FUNDING'))\n\t\t\treturn JSON.stringify(interceptorValue) !== JSON.stringify(bouquetValue)\n\t\t} catch {\n\t\t\treturn false\n\t\t}\n\t}\n\n\tconst compareWithInterceptor = async () => {\n\t\tconst different = await compare()\n\t\tinterceptorComparison.value = { ...interceptorComparison.value, different }\n\t}\n\n\tasync function createCompareInterval() {\n\t\tif (!provider.value || !provider.value.isInterceptor) return;\n\t\tconst different = await compare()\n\t\tinterceptorComparison.value = { different, intervalId: setInterval(compareWithInterceptor, 20000)}\n\t}\n\n\treturn (\n\t\t<>\n\t\t\t

Your Transactions

\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\n\t\t\t
\n\t\t\t{interceptorComparison.value.different ? The transactions imported in Bouquet differ from the current simulation in The Interceptor extension. } /> : null}\n\t\t\t
\n\t\t\t\t{bundle.value?.transactions.map((tx, index) => (\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t#{index}\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\tFrom\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{tx.from !== 'FUNDING' ? addressString(tx.from) : tx.from}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\tTo\n\t\t\t\t\t\t\t\t{tx.to ? addressString(tx.to) : 'Contract Deployment'}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\tValue\n\t\t\t\t\t\t\t\t{EtherSymbol}{formatEther(tx.value + (tx.from === 'FUNDING' && bundle.value && bundle.value.containsFundingTx ? bundle.value.totalGas * (blockInfo.value.baseFee + blockInfo.value.priorityFee): 0n))} + {EtherSymbol}{formatEther(tx.gasLimit * (blockInfo.value.baseFee + blockInfo.value.priorityFee))} Gas Fee\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{decodedTransactions.value[index] ? (\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\tData\n\t\t\t\t\t\t\t\t\t{decodedTransactions.value[index]}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t) : tx.input && tx.input.length > 0 ? (\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\tData\n\t\t\t\t\t\t\t\t\t{tx.input.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '0x')}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t))}\n\t\t\t
\n\t\t\n\t)\n}\n"]} \ No newline at end of file diff --git a/docs/js/components/Warns.d.ts b/docs/js/components/Warns.d.ts new file mode 100644 index 0000000..a297519 --- /dev/null +++ b/docs/js/components/Warns.d.ts @@ -0,0 +1,7 @@ +import { JSX } from 'preact/jsx-runtime'; +export declare const SingleNotice: ({ variant, title, description }: { + variant: 'warn' | 'error' | 'success'; + title: string; + description?: string | JSX.Element | undefined; +}) => JSX.Element; +//# sourceMappingURL=Warns.d.ts.map \ No newline at end of file diff --git a/docs/js/components/Warns.d.ts.map b/docs/js/components/Warns.d.ts.map new file mode 100644 index 0000000..8db2f1f --- /dev/null +++ b/docs/js/components/Warns.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"Warns.d.ts","sourceRoot":"","sources":["../../ts/components/Warns.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAA;AAExC,eAAO,MAAM,YAAY;aAAgD,MAAM,GAAG,OAAO,GAAG,SAAS;WAAS,MAAM;;iBAmBnH,CAAA"} \ No newline at end of file diff --git a/docs/js/components/Warns.js b/docs/js/components/Warns.js new file mode 100644 index 0000000..405e251 --- /dev/null +++ b/docs/js/components/Warns.js @@ -0,0 +1,15 @@ +import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime"; +export const SingleNotice = ({ variant, title, description }) => { + const variantColors = { + warn: 'border-orange-400/50 bg-orange-400/10', + error: 'border-red-400/50 bg-red-400/10', + success: 'border-green-400/50 bg-green-400/10' + }; + const variantEmoji = { + warn: '⚠', + error: '🛑', + success: '🎉' + }; + return (_jsxs("div", { class: `flex items-center items-center border ${variantColors[variant]} px-4 py-2 gap-4`, children: [_jsx("span", { class: 'text-2xl', children: variantEmoji[variant] }), _jsxs("div", { class: 'py-3 flex-grow', children: [_jsx("h3", { class: 'font-lg font-semibold', children: title }), description ? (_jsx("div", { class: 'leading-tight text-white/75 break-word text-sm', children: description })) : null] })] })); +}; +//# sourceMappingURL=Warns.js.map \ No newline at end of file diff --git a/docs/js/components/Warns.js.map b/docs/js/components/Warns.js.map new file mode 100644 index 0000000..802f6af --- /dev/null +++ b/docs/js/components/Warns.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Warns.js","sourceRoot":"","sources":["../../ts/components/Warns.tsx"],"names":[],"mappings":";AAEA,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAgG,EAAE,EAAE;IAC7J,MAAM,aAAa,GAAG;QACrB,IAAI,EAAE,uCAAuC;QAC7C,KAAK,EAAE,iCAAiC;QACxC,OAAO,EAAE,qCAAqC;KAC9C,CAAA;IACD,MAAM,YAAY,GAAG;QACpB,IAAI,EAAE,GAAG;QACT,KAAK,EAAE,IAAI;QACX,OAAO,EAAE,IAAI;KACb,CAAA;IAED,OAAO,CAAC,eAAK,KAAK,EAAE,yCAAyC,aAAa,CAAC,OAAO,CAAC,kBAAkB,aACpG,eAAM,KAAK,EAAC,UAAU,YAAE,YAAY,CAAC,OAAO,CAAC,GAAQ,EACrD,eAAK,KAAK,EAAC,gBAAgB,aAC1B,aAAI,KAAK,EAAC,uBAAuB,YAAE,KAAK,GAAM,EAC7C,WAAW,CAAC,CAAC,CAAC,CAAC,cAAK,KAAK,EAAC,gDAAgD,YAAE,WAAW,GAAO,CAAC,CAAC,CAAC,CAAC,IAAI,IAClG,IACD,CAAC,CAAA;AACR,CAAC,CAAA","sourcesContent":["import { JSX } from 'preact/jsx-runtime'\n\nexport const SingleNotice = ({ variant, title, description }: { variant: 'warn' | 'error' | 'success', title: string, description?: string | JSX.Element }) => {\n\tconst variantColors = {\n\t\twarn: 'border-orange-400/50 bg-orange-400/10',\n\t\terror: 'border-red-400/50 bg-red-400/10',\n\t\tsuccess: 'border-green-400/50 bg-green-400/10'\n\t}\n\tconst variantEmoji = {\n\t\twarn: '⚠',\n\t\terror: '🛑',\n\t\tsuccess: '🎉'\n\t}\n\n\treturn (
\n\t\t{variantEmoji[variant]}\n\t\t
\n\t\t\t

{title}

\n\t\t\t{description ? (
{description}
) : null}\n\t\t
\n\t
)\n}\n"]} \ No newline at end of file diff --git a/docs/js/constants.d.ts b/docs/js/constants.d.ts new file mode 100644 index 0000000..f4d0db9 --- /dev/null +++ b/docs/js/constants.d.ts @@ -0,0 +1,29 @@ +import { BouquetNetwork } from './types/bouquetTypes.js'; +export declare const MAINNET: { + readonly networkName: "Mainnet"; + readonly simulationRelayEndpoint: "https://flashbots-cors-proxy.dark-florist.workers.dev/"; + readonly submissionRelayEndpoint: "https://rpc.titanbuilder.xyz"; + readonly blockExplorer: "https://etherscan.io/"; + readonly chainId: 1n; + readonly blockExplorerApi: "https://api.etherscan.io"; + readonly relayMode: "relay"; + readonly mempoolSubmitRpcEndpoint: undefined; + readonly mempoolSimulationRpcEndpoint: undefined; + readonly blocksInFuture: 3n; + readonly priorityFee: bigint; +}; +export declare const DEFAULT_NETWORKS: BouquetNetwork[]; +export declare const getNetwork: (networks: readonly { + chainId: bigint; + networkName: string; + relayMode: "relay" | "mempool"; + mempoolSubmitRpcEndpoint: string | undefined; + mempoolSimulationRpcEndpoint: string | undefined; + blocksInFuture: bigint; + priorityFee: bigint; + blockExplorerApi: string | undefined; + blockExplorer: string | undefined; + simulationRelayEndpoint: string | undefined; + submissionRelayEndpoint: string | undefined; +}[], chainId: bigint) => BouquetNetwork; +//# sourceMappingURL=constants.d.ts.map \ No newline at end of file diff --git a/docs/js/constants.d.ts.map b/docs/js/constants.d.ts.map new file mode 100644 index 0000000..07a3a02 --- /dev/null +++ b/docs/js/constants.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../ts/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAmB,MAAM,yBAAyB,CAAA;AAEzE,eAAO,MAAM,OAAO;;;;;;;;;;;;CAYV,CAAA;AAEV,eAAO,MAAM,gBAAgB,EAAE,cAAc,EA4B5C,CAAA;AAED,eAAO,MAAM,UAAU;;;;;;;;;;;;cAAwC,MAAM,KAAG,cAgBvE,CAAA"} \ No newline at end of file diff --git a/docs/js/constants.js b/docs/js/constants.js new file mode 100644 index 0000000..b24d597 --- /dev/null +++ b/docs/js/constants.js @@ -0,0 +1,61 @@ +export const MAINNET = { + networkName: 'Mainnet', + simulationRelayEndpoint: 'https://flashbots-cors-proxy.dark-florist.workers.dev/', + submissionRelayEndpoint: 'https://rpc.titanbuilder.xyz', + blockExplorer: 'https://etherscan.io/', + chainId: 1n, + blockExplorerApi: 'https://api.etherscan.io', + relayMode: 'relay', + mempoolSubmitRpcEndpoint: undefined, // don't set default for Mainnet as its not advisable to use it + mempoolSimulationRpcEndpoint: undefined, + blocksInFuture: 3n, + priorityFee: 10n ** 9n * 3n, +}; +export const DEFAULT_NETWORKS = [ + MAINNET, + { + networkName: 'Sepolia', + simulationRelayEndpoint: 'https://flashbots-sepolia-cors-proxy.dark-florist.workers.dev/', + submissionRelayEndpoint: 'https://flashbots-sepolia-cors-proxy.dark-florist.workers.dev/', + blockExplorer: 'https://sepolia.etherscan.io/', + chainId: 11155111n, + blockExplorerApi: 'https://sepolia.api.etherscan.io', + relayMode: 'relay', + mempoolSubmitRpcEndpoint: undefined, // don't set default for Sepolia as its not advisable to use it + mempoolSimulationRpcEndpoint: undefined, + blocksInFuture: 3n, + priorityFee: 10n ** 9n * 3n, + }, + { + networkName: 'Holesky', + simulationRelayEndpoint: undefined, + submissionRelayEndpoint: undefined, + blockExplorer: 'https://holesky.etherscan.io/', + chainId: 17000n, + blockExplorerApi: 'https://holesky.api.etherscan.io', + relayMode: 'mempool', + mempoolSubmitRpcEndpoint: 'https://holesky.dark.florist', + mempoolSimulationRpcEndpoint: 'https://holesky.dark.florist', + blocksInFuture: 3n, + priorityFee: 10n ** 9n * 3n, + } +]; +export const getNetwork = (networks, chainId) => { + const network = networks.find((network) => network.chainId === chainId); + if (network !== undefined) + return network; + return { + networkName: `Custom ChainId: ${chainId}`, + simulationRelayEndpoint: undefined, + submissionRelayEndpoint: undefined, + blockExplorer: undefined, + chainId, + blockExplorerApi: undefined, + relayMode: 'mempool', + mempoolSubmitRpcEndpoint: undefined, + mempoolSimulationRpcEndpoint: undefined, + blocksInFuture: 3n, + priorityFee: 10n ** 9n * 3n, + }; +}; +//# sourceMappingURL=constants.js.map \ No newline at end of file diff --git a/docs/js/constants.js.map b/docs/js/constants.js.map new file mode 100644 index 0000000..a1c6631 --- /dev/null +++ b/docs/js/constants.js.map @@ -0,0 +1 @@ +{"version":3,"file":"constants.js","sourceRoot":"","sources":["../ts/constants.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,OAAO,GAAG;IACtB,WAAW,EAAE,SAAS;IACtB,uBAAuB,EAAE,wDAAwD;IACjF,uBAAuB,EAAE,8BAA8B;IACvD,aAAa,EAAE,uBAAuB;IACtC,OAAO,EAAE,EAAE;IACX,gBAAgB,EAAE,0BAA0B;IAC5C,SAAS,EAAE,OAAO;IAClB,wBAAwB,EAAE,SAAS,EAAE,+DAA+D;IACpG,4BAA4B,EAAE,SAAS;IACvC,cAAc,EAAE,EAAE;IAClB,WAAW,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE;CAClB,CAAA;AAEV,MAAM,CAAC,MAAM,gBAAgB,GAAqB;IACjD,OAAO;IACP;QACC,WAAW,EAAE,SAAS;QACtB,uBAAuB,EAAE,gEAAgE;QACzF,uBAAuB,EAAE,gEAAgE;QACzF,aAAa,EAAE,+BAA+B;QAC9C,OAAO,EAAE,SAAS;QAClB,gBAAgB,EAAE,kCAAkC;QACpD,SAAS,EAAE,OAAO;QAClB,wBAAwB,EAAE,SAAS,EAAE,+DAA+D;QACpG,4BAA4B,EAAE,SAAS;QACvC,cAAc,EAAE,EAAE;QAClB,WAAW,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE;KAC3B;IACD;QACC,WAAW,EAAE,SAAS;QACtB,uBAAuB,EAAE,SAAS;QAClC,uBAAuB,EAAE,SAAS;QAClC,aAAa,EAAE,+BAA+B;QAC9C,OAAO,EAAE,MAAM;QACf,gBAAgB,EAAE,kCAAkC;QACpD,SAAS,EAAE,SAAS;QACpB,wBAAwB,EAAE,8BAA8B;QACxD,4BAA4B,EAAE,8BAA8B;QAC5D,cAAc,EAAE,EAAE;QAClB,WAAW,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE;KAC3B;CACD,CAAA;AAED,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,QAAyB,EAAE,OAAe,EAAkB,EAAE;IACxF,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,KAAK,OAAO,CAAC,CAAA;IACvE,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,OAAO,CAAA;IACzC,OAAO;QACN,WAAW,EAAE,mBAAoB,OAAQ,EAAE;QAC3C,uBAAuB,EAAE,SAAS;QAClC,uBAAuB,EAAE,SAAS;QAClC,aAAa,EAAE,SAAS;QACxB,OAAO;QACP,gBAAgB,EAAE,SAAS;QAC3B,SAAS,EAAE,SAAS;QACpB,wBAAwB,EAAE,SAAS;QACnC,4BAA4B,EAAE,SAAS;QACvC,cAAc,EAAE,EAAE;QAClB,WAAW,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE;KAC3B,CAAA;AACF,CAAC,CAAA","sourcesContent":["import { BouquetNetwork, BouquetSettings } from './types/bouquetTypes.js'\n\nexport const MAINNET = {\n\tnetworkName: 'Mainnet',\n\tsimulationRelayEndpoint: 'https://flashbots-cors-proxy.dark-florist.workers.dev/',\n\tsubmissionRelayEndpoint: 'https://rpc.titanbuilder.xyz',\n\tblockExplorer: 'https://etherscan.io/',\n\tchainId: 1n,\n\tblockExplorerApi: 'https://api.etherscan.io',\n\trelayMode: 'relay',\n\tmempoolSubmitRpcEndpoint: undefined, // don't set default for Mainnet as its not advisable to use it\n\tmempoolSimulationRpcEndpoint: undefined,\n\tblocksInFuture: 3n,\n\tpriorityFee: 10n ** 9n * 3n,\n} as const\n\nexport const DEFAULT_NETWORKS: BouquetNetwork[] = [\n\tMAINNET,\n\t{\n\t\tnetworkName: 'Sepolia',\n\t\tsimulationRelayEndpoint: 'https://flashbots-sepolia-cors-proxy.dark-florist.workers.dev/',\n\t\tsubmissionRelayEndpoint: 'https://flashbots-sepolia-cors-proxy.dark-florist.workers.dev/',\n\t\tblockExplorer: 'https://sepolia.etherscan.io/',\n\t\tchainId: 11155111n,\n\t\tblockExplorerApi: 'https://sepolia.api.etherscan.io',\n\t\trelayMode: 'relay',\n\t\tmempoolSubmitRpcEndpoint: undefined, // don't set default for Sepolia as its not advisable to use it\n\t\tmempoolSimulationRpcEndpoint: undefined,\n\t\tblocksInFuture: 3n,\n\t\tpriorityFee: 10n ** 9n * 3n,\n\t},\n\t{\n\t\tnetworkName: 'Holesky',\n\t\tsimulationRelayEndpoint: undefined,\n\t\tsubmissionRelayEndpoint: undefined,\n\t\tblockExplorer: 'https://holesky.etherscan.io/',\n\t\tchainId: 17000n,\n\t\tblockExplorerApi: 'https://holesky.api.etherscan.io',\n\t\trelayMode: 'mempool',\n\t\tmempoolSubmitRpcEndpoint: 'https://holesky.dark.florist',\n\t\tmempoolSimulationRpcEndpoint: 'https://holesky.dark.florist',\n\t\tblocksInFuture: 3n,\n\t\tpriorityFee: 10n ** 9n * 3n,\n\t}\n]\n\nexport const getNetwork = (networks: BouquetSettings, chainId: bigint): BouquetNetwork => {\n\tconst network = networks.find((network) => network.chainId === chainId)\n\tif (network !== undefined) return network\n\treturn {\n\t\tnetworkName: `Custom ChainId: ${ chainId }`,\n\t\tsimulationRelayEndpoint: undefined,\n\t\tsubmissionRelayEndpoint: undefined,\n\t\tblockExplorer: undefined,\n\t\tchainId,\n\t\tblockExplorerApi: undefined,\n\t\trelayMode: 'mempool',\n\t\tmempoolSubmitRpcEndpoint: undefined,\n\t\tmempoolSimulationRpcEndpoint: undefined,\n\t\tblocksInFuture: 3n,\n\t\tpriorityFee: 10n ** 9n * 3n,\n\t}\n}\n"]} \ No newline at end of file diff --git a/docs/js/index.d.ts b/docs/js/index.d.ts new file mode 100644 index 0000000..e26a57a --- /dev/null +++ b/docs/js/index.d.ts @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/docs/js/index.d.ts.map b/docs/js/index.d.ts.map new file mode 100644 index 0000000..87f1b8c --- /dev/null +++ b/docs/js/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../ts/index.tsx"],"names":[],"mappings":""} \ No newline at end of file diff --git a/docs/js/index.js b/docs/js/index.js new file mode 100644 index 0000000..8f37267 --- /dev/null +++ b/docs/js/index.js @@ -0,0 +1,5 @@ +import { jsx as _jsx } from "preact/jsx-runtime"; +import { render } from 'preact'; +import { App } from './components/App.js'; +render(_jsx(App, {}), document.body, document.querySelector('main')); +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/docs/js/index.js.map b/docs/js/index.js.map new file mode 100644 index 0000000..ba065d4 --- /dev/null +++ b/docs/js/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../ts/index.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAA;AAEzC,MAAM,CAAC,KAAC,GAAG,KAAG,EAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAgB,CAAC,CAAA","sourcesContent":["import { render } from 'preact'\nimport { App } from './components/App.js'\n\nrender(, document.body, document.querySelector('main') as HTMLElement)\n"]} \ No newline at end of file diff --git a/docs/js/library/DataURLCache.d.ts b/docs/js/library/DataURLCache.d.ts new file mode 100644 index 0000000..b03b3fb --- /dev/null +++ b/docs/js/library/DataURLCache.d.ts @@ -0,0 +1,7 @@ +export declare class DataURLCache { + private readonly dataURLs; + has: (key: string) => boolean; + get: (key: string) => string | undefined; + set: (image: string, key: string) => string; +} +//# sourceMappingURL=DataURLCache.d.ts.map \ No newline at end of file diff --git a/docs/js/library/DataURLCache.d.ts.map b/docs/js/library/DataURLCache.d.ts.map new file mode 100644 index 0000000..8770db6 --- /dev/null +++ b/docs/js/library/DataURLCache.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"DataURLCache.d.ts","sourceRoot":"","sources":["../../ts/library/DataURLCache.ts"],"names":[],"mappings":"AAEA,qBAAa,YAAY;IACxB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA4B;IAC9C,GAAG,QAAS,MAAM,aAA2B;IAC7C,GAAG,QAAS,MAAM,wBAA2B;IAC7C,GAAG,UAAW,MAAM,OAAO,MAAM,YAIvC;CACD"} \ No newline at end of file diff --git a/docs/js/library/DataURLCache.js b/docs/js/library/DataURLCache.js new file mode 100644 index 0000000..6e34024 --- /dev/null +++ b/docs/js/library/DataURLCache.js @@ -0,0 +1,15 @@ +const CACHE_SIZE = 100; +export class DataURLCache { + constructor() { + this.dataURLs = new Map(); + this.has = (key) => this.dataURLs.has(key); + this.get = (key) => this.dataURLs.get(key); + this.set = (image, key) => { + if (this.dataURLs.size > CACHE_SIZE) + this.dataURLs.delete(this.dataURLs.keys().next().value); + this.dataURLs.set(key, image); + return image; + }; + } +} +//# sourceMappingURL=DataURLCache.js.map \ No newline at end of file diff --git a/docs/js/library/DataURLCache.js.map b/docs/js/library/DataURLCache.js.map new file mode 100644 index 0000000..29b8651 --- /dev/null +++ b/docs/js/library/DataURLCache.js.map @@ -0,0 +1 @@ +{"version":3,"file":"DataURLCache.js","sourceRoot":"","sources":["../../ts/library/DataURLCache.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,GAAG,GAAG,CAAA;AAEtB,MAAM,OAAO,YAAY;IAAzB;QACkB,aAAQ,GAAG,IAAI,GAAG,EAAkB,CAAA;QAC9C,QAAG,GAAG,CAAC,GAAW,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QAC7C,QAAG,GAAG,CAAC,GAAW,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QAC7C,QAAG,GAAG,CAAC,KAAa,EAAE,GAAW,EAAE,EAAE;YAC3C,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,GAAG,UAAU;gBAAE,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAA;YAC5F,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;YAC7B,OAAO,KAAK,CAAA;QACb,CAAC,CAAA;IACF,CAAC;CAAA","sourcesContent":["const CACHE_SIZE = 100\n\nexport class DataURLCache {\n\tprivate readonly dataURLs = new Map()\n\tpublic has = (key: string) => this.dataURLs.has(key)\n\tpublic get = (key: string) => this.dataURLs.get(key)\n\tpublic set = (image: string, key: string) => {\n\t\tif (this.dataURLs.size > CACHE_SIZE) this.dataURLs.delete(this.dataURLs.keys().next().value)\n\t\tthis.dataURLs.set(key, image)\n\t\treturn image\n\t}\n}\n"]} \ No newline at end of file diff --git a/docs/js/library/asyncState.d.ts b/docs/js/library/asyncState.d.ts new file mode 100644 index 0000000..2439ed4 --- /dev/null +++ b/docs/js/library/asyncState.d.ts @@ -0,0 +1,23 @@ +import { Signal } from '@preact/signals'; +export type Inactive = { + state: 'inactive'; +}; +export type Pending = { + state: 'pending'; +}; +export type Resolved = { + state: 'resolved'; + value: T; +}; +export type Rejected = { + state: 'rejected'; + error: Error; +}; +export type AsyncProperty = Inactive | Pending | Resolved | Rejected; +export type AsyncState = { + value: Signal>; + waitFor: (resolver: () => Promise) => void; + reset: () => void; +}; +export declare function useAsyncState(): AsyncState; +//# sourceMappingURL=asyncState.d.ts.map \ No newline at end of file diff --git a/docs/js/library/asyncState.d.ts.map b/docs/js/library/asyncState.d.ts.map new file mode 100644 index 0000000..0fc3ed6 --- /dev/null +++ b/docs/js/library/asyncState.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"asyncState.d.ts","sourceRoot":"","sources":["../../ts/library/asyncState.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAa,MAAM,iBAAiB,CAAA;AACnD,MAAM,MAAM,QAAQ,GAAG;IAAE,KAAK,EAAE,UAAU,CAAA;CAAE,CAAA;AAC5C,MAAM,MAAM,OAAO,GAAG;IAAE,KAAK,EAAE,SAAS,CAAA;CAAE,CAAA;AAC1C,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,CAAA;AACzD,MAAM,MAAM,QAAQ,GAAG;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,KAAK,CAAA;CAAE,CAAA;AAC1D,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAA;AAC1E,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI;IAAE,KAAK,EAAE,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;IAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,IAAI,CAAA;CAAE,CAAA;AAEjI,wBAAgB,aAAa,CAAC,CAAC,KAAK,UAAU,CAAC,CAAC,CAAC,CAwChD"} \ No newline at end of file diff --git a/docs/js/library/asyncState.js b/docs/js/library/asyncState.js new file mode 100644 index 0000000..8bc4a49 --- /dev/null +++ b/docs/js/library/asyncState.js @@ -0,0 +1,42 @@ +import { useSignal } from '@preact/signals'; +export function useAsyncState() { + function getCaptureAndCancelOthers() { + // delete previously captured signal so any pending async work will no-op when they resolve + delete captureContainer.peek().result; + // capture the signal in a new object so we can delete it later if it is interrupted + captureContainer.value = { result }; + return captureContainer.peek(); + } + async function activate(resolver) { + const capture = getCaptureAndCancelOthers(); + // we need to read the property out of the capture every time we look at it, in case it is deleted asynchronously + function setCapturedResult(newResult) { + const result = capture.result; + if (result === undefined) + return; + result.value = newResult; + } + try { + const pendingState = { state: 'pending' }; + setCapturedResult(pendingState); + const resolvedValue = await resolver(); + const resolvedState = { state: 'resolved', value: resolvedValue }; + setCapturedResult(resolvedState); + } + catch (unknownError) { + const error = unknownError instanceof Error ? unknownError : typeof unknownError === 'string' ? new Error(unknownError) : new Error(`Unknown error occurred.\n${JSON.stringify(unknownError)}`); + const rejectedState = { state: 'rejected', error }; + setCapturedResult(rejectedState); + } + } + function reset() { + const result = getCaptureAndCancelOthers().result; + if (result === undefined) + return; + result.value = { state: 'inactive' }; + } + const result = useSignal({ state: 'inactive' }); + const captureContainer = useSignal({}); + return { value: result, waitFor: resolver => activate(resolver), reset }; +} +//# sourceMappingURL=asyncState.js.map \ No newline at end of file diff --git a/docs/js/library/asyncState.js.map b/docs/js/library/asyncState.js.map new file mode 100644 index 0000000..e889c00 --- /dev/null +++ b/docs/js/library/asyncState.js.map @@ -0,0 +1 @@ +{"version":3,"file":"asyncState.js","sourceRoot":"","sources":["../../ts/library/asyncState.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAQnD,MAAM,UAAU,aAAa;IAC5B,SAAS,yBAAyB;QACjC,2FAA2F;QAC3F,OAAO,gBAAgB,CAAC,IAAI,EAAE,CAAC,MAAM,CAAA;QACrC,oFAAoF;QACpF,gBAAgB,CAAC,KAAK,GAAG,EAAE,MAAM,EAAE,CAAA;QACnC,OAAO,gBAAgB,CAAC,IAAI,EAAE,CAAA;IAC/B,CAAC;IAED,KAAK,UAAU,QAAQ,CAAC,QAA0B;QACjD,MAAM,OAAO,GAAG,yBAAyB,EAAE,CAAA;QAC3C,iHAAiH;QACjH,SAAS,iBAAiB,CAAC,SAA2B;YACrD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAA;YAC7B,IAAI,MAAM,KAAK,SAAS;gBAAE,OAAM;YAChC,MAAM,CAAC,KAAK,GAAG,SAAS,CAAA;QACzB,CAAC;QACD,IAAI,CAAC;YACJ,MAAM,YAAY,GAAG,EAAE,KAAK,EAAE,SAAkB,EAAE,CAAA;YAClD,iBAAiB,CAAC,YAAY,CAAC,CAAA;YAC/B,MAAM,aAAa,GAAG,MAAM,QAAQ,EAAE,CAAA;YACtC,MAAM,aAAa,GAAG,EAAE,KAAK,EAAE,UAAmB,EAAE,KAAK,EAAE,aAAa,EAAE,CAAA;YAC1E,iBAAiB,CAAC,aAAa,CAAC,CAAA;QACjC,CAAC;QAAC,OAAO,YAAqB,EAAE,CAAC;YAChC,MAAM,KAAK,GAAG,YAAY,YAAY,KAAK,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,OAAO,YAAY,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,4BAA4B,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,EAAE,CAAC,CAAA;YAC/L,MAAM,aAAa,GAAG,EAAE,KAAK,EAAE,UAAmB,EAAE,KAAK,EAAE,CAAA;YAC3D,iBAAiB,CAAC,aAAa,CAAC,CAAA;QACjC,CAAC;IACF,CAAC;IAED,SAAS,KAAK;QACb,MAAM,MAAM,GAAG,yBAAyB,EAAE,CAAC,MAAM,CAAA;QACjD,IAAI,MAAM,KAAK,SAAS;YAAE,OAAM;QAChC,MAAM,CAAC,KAAK,GAAG,EAAE,KAAK,EAAE,UAAU,EAAE,CAAA;IACrC,CAAC;IAED,MAAM,MAAM,GAAG,SAAS,CAAmB,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAA;IACjE,MAAM,gBAAgB,GAAG,SAAS,CAAwC,EAAE,CAAC,CAAA;IAE7E,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,CAAA;AACzE,CAAC","sourcesContent":["import { Signal, useSignal } from '@preact/signals'\nexport type Inactive = { state: 'inactive' }\nexport type Pending = { state: 'pending' }\nexport type Resolved = { state: 'resolved'; value: T }\nexport type Rejected = { state: 'rejected'; error: Error }\nexport type AsyncProperty = Inactive | Pending | Resolved | Rejected\nexport type AsyncState = { value: Signal>; waitFor: (resolver: () => Promise) => void; reset: () => void }\n\nexport function useAsyncState(): AsyncState {\n\tfunction getCaptureAndCancelOthers() {\n\t\t// delete previously captured signal so any pending async work will no-op when they resolve\n\t\tdelete captureContainer.peek().result\n\t\t// capture the signal in a new object so we can delete it later if it is interrupted\n\t\tcaptureContainer.value = { result }\n\t\treturn captureContainer.peek()\n\t}\n\n\tasync function activate(resolver: () => Promise) {\n\t\tconst capture = getCaptureAndCancelOthers()\n\t\t// we need to read the property out of the capture every time we look at it, in case it is deleted asynchronously\n\t\tfunction setCapturedResult(newResult: AsyncProperty) {\n\t\t\tconst result = capture.result\n\t\t\tif (result === undefined) return\n\t\t\tresult.value = newResult\n\t\t}\n\t\ttry {\n\t\t\tconst pendingState = { state: 'pending' as const }\n\t\t\tsetCapturedResult(pendingState)\n\t\t\tconst resolvedValue = await resolver()\n\t\t\tconst resolvedState = { state: 'resolved' as const, value: resolvedValue }\n\t\t\tsetCapturedResult(resolvedState)\n\t\t} catch (unknownError: unknown) {\n\t\t\tconst error = unknownError instanceof Error ? unknownError : typeof unknownError === 'string' ? new Error(unknownError) : new Error(`Unknown error occurred.\\n${JSON.stringify(unknownError)}`)\n\t\t\tconst rejectedState = { state: 'rejected' as const, error }\n\t\t\tsetCapturedResult(rejectedState)\n\t\t}\n\t}\n\n\tfunction reset() {\n\t\tconst result = getCaptureAndCancelOthers().result\n\t\tif (result === undefined) return\n\t\tresult.value = { state: 'inactive' }\n\t}\n\n\tconst result = useSignal>({ state: 'inactive' })\n\tconst captureContainer = useSignal<{ result?: Signal> }>({})\n\n\treturn { value: result, waitFor: resolver => activate(resolver), reset }\n}\n"]} \ No newline at end of file diff --git a/docs/js/library/bundleUtils.d.ts b/docs/js/library/bundleUtils.d.ts new file mode 100644 index 0000000..da66c40 --- /dev/null +++ b/docs/js/library/bundleUtils.d.ts @@ -0,0 +1,13 @@ +import { BrowserProvider, Signer, TransactionRequest } from 'ethers'; +import { BlockInfo, Bundle, Signers } from '../types/types.js'; +export interface FlashbotsBundleTransaction { + transaction: TransactionRequest; + signer: Signer; +} +export declare const getMaxBaseFeeInFutureBlock: (baseFee: bigint, blocksInFuture: bigint) => bigint; +export declare const getRawTransactionsAndCalculateFeesAndNonces: (bundle: FlashbotsBundleTransaction[], provider: BrowserProvider, blockInfo: BlockInfo, maxBaseFee: bigint) => Promise<{ + rawTransaction: string; + transaction: TransactionRequest; +}[]>; +export declare const createBundleTransactions: (bundle: Bundle, signers: Signers, blockInfo: BlockInfo, blocksInFuture: bigint, fundingAmountMin: bigint) => Promise; +//# sourceMappingURL=bundleUtils.d.ts.map \ No newline at end of file diff --git a/docs/js/library/bundleUtils.d.ts.map b/docs/js/library/bundleUtils.d.ts.map new file mode 100644 index 0000000..0562801 --- /dev/null +++ b/docs/js/library/bundleUtils.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"bundleUtils.d.ts","sourceRoot":"","sources":["../../ts/library/bundleUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAc,MAAM,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAA;AAChF,OAAO,EAAE,SAAS,EAAE,MAAM,EAAa,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAIzE,MAAM,WAAW,0BAA0B;IAC1C,WAAW,EAAE,kBAAkB,CAAA;IAC/B,MAAM,EAAE,MAAM,CAAA;CACd;AAED,eAAO,MAAM,0BAA0B,YAAa,MAAM,kBAAkB,MAAM,WAGjF,CAAA;AAqBD,eAAO,MAAM,2CAA2C,WAAkB,0BAA0B,EAAE,YAAY,eAAe,aAAa,SAAS,cAAc,MAAM;oBACpI,MAAM;iBAAe,kBAAkB;IAoB7E,CAAA;AAED,eAAO,MAAM,wBAAwB,WAC5B,MAAM,WACL,OAAO,aACL,SAAS,kBACJ,MAAM,oBACJ,MAAM,KACtB,QAAQ,0BAA0B,EAAE,CAuCtC,CAAA"} \ No newline at end of file diff --git a/docs/js/library/bundleUtils.js b/docs/js/library/bundleUtils.js new file mode 100644 index 0000000..4114b96 --- /dev/null +++ b/docs/js/library/bundleUtils.js @@ -0,0 +1,96 @@ +import { getAddress } from 'ethers'; +import { serialize } from '../types/types.js'; +import { EthereumData } from '../types/ethereumTypes.js'; +import { addressString } from './utils.js'; +export const getMaxBaseFeeInFutureBlock = (baseFee, blocksInFuture) => { + if (blocksInFuture <= 0n) + throw new Error('blocksInFuture needs to be positive'); + return [...Array(blocksInFuture)].reduce((accumulator, _currentValue) => (accumulator * 1125n) / 1000n, baseFee) + 1n; +}; +async function getSimulatedCountsOnNetwork(provider) { + try { + const { payload } = await provider.send('interceptor_getSimulationStack', ['1.0.0']); + const result = payload.reduce((acc, curr) => { + curr.from = getAddress(curr.from); + if (curr.from in acc) + acc[curr.from] += 1; + else + acc[curr.from] = 1; + return acc; + }, {}); + return result; + } + catch (error) { + console.error('getSimulatedCountsOnNetwork error: ', error); + return {}; + } +} +export const getRawTransactionsAndCalculateFeesAndNonces = async (bundle, provider, blockInfo, maxBaseFee) => { + const transactions = []; + const inSimulation = await getSimulatedCountsOnNetwork(provider); + const accNonces = {}; + for (const tx of bundle) { + tx.transaction.maxPriorityFeePerGas = blockInfo.priorityFee; + tx.transaction.maxFeePerGas = blockInfo.priorityFee + maxBaseFee; + if (!tx.transaction.from) + throw new Error('BundleTransaction missing from address'); + if (!tx.transaction.chainId) + throw new Error('BundleTransaction missing chainId'); + // Fetch and increment nonces from network, reduce the fetch amount by amount of transactions made on the simulation stack + if (tx.transaction.from.toString() in accNonces) { + accNonces[tx.transaction.from.toString()] += 1; + } + else { + accNonces[tx.transaction.from.toString()] = await provider.getTransactionCount(tx.transaction.from, 'latest'); + if (tx.transaction.from.toString() in inSimulation) + accNonces[tx.transaction.from.toString()] -= inSimulation[tx.transaction.from.toString()]; + } + tx.transaction.nonce = accNonces[tx.transaction.from.toString()]; + const rawTransaction = await tx.signer.signTransaction({ ...tx.transaction }); + transactions.push({ rawTransaction, transaction: tx.transaction }); + } + return transactions; +}; +export const createBundleTransactions = async (bundle, signers, blockInfo, blocksInFuture, fundingAmountMin) => { + return await Promise.all(bundle.transactions.map(async ({ from, to, gasLimit, value, input, chainId }) => { + const gasOpts = { + maxPriorityFeePerGas: blockInfo.priorityFee, + type: 2, + maxFeePerGas: blockInfo.priorityFee + getMaxBaseFeeInFutureBlock(blockInfo.baseFee, blocksInFuture), + }; + if (from === 'FUNDING') { + if (!signers.burner) + throw new Error('No burner wallet provided'); + return { + signer: signers.burner, + transaction: { + from: signers.burner.address, + ...(bundle && bundle.transactions[0].to + ? { + to: addressString(bundle.transactions[0].to), + } + : {}), + value: fundingAmountMin - 21000n * (getMaxBaseFeeInFutureBlock(blockInfo.baseFee, blocksInFuture) + blockInfo.priorityFee), + data: '0x', + gasLimit: 21000n, + chainId: Number(chainId), + ...gasOpts, + }, + }; + } + else + return { + signer: signers.bundleSigners[addressString(from)], + transaction: { + from: addressString(from), + ...(to ? { to: addressString(to) } : {}), + gasLimit, + data: serialize(EthereumData, input), + value, + chainId: Number(chainId), + ...gasOpts, + }, + }; + })); +}; +//# sourceMappingURL=bundleUtils.js.map \ No newline at end of file diff --git a/docs/js/library/bundleUtils.js.map b/docs/js/library/bundleUtils.js.map new file mode 100644 index 0000000..20557b9 --- /dev/null +++ b/docs/js/library/bundleUtils.js.map @@ -0,0 +1 @@ +{"version":3,"file":"bundleUtils.js","sourceRoot":"","sources":["../../ts/library/bundleUtils.ts"],"names":[],"mappings":"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","sourcesContent":["import { BrowserProvider, getAddress, Signer, TransactionRequest } from 'ethers'\nimport { BlockInfo, Bundle, serialize, Signers } from '../types/types.js'\nimport { EthereumData } from '../types/ethereumTypes.js'\nimport { addressString } from './utils.js'\n\nexport interface FlashbotsBundleTransaction {\n\ttransaction: TransactionRequest\n\tsigner: Signer\n}\n\nexport const getMaxBaseFeeInFutureBlock = (baseFee: bigint, blocksInFuture: bigint) => {\n\tif (blocksInFuture <= 0n) throw new Error('blocksInFuture needs to be positive')\n\treturn [...Array(blocksInFuture)].reduce((accumulator, _currentValue) => (accumulator * 1125n) / 1000n, baseFee) + 1n\n}\n\nasync function getSimulatedCountsOnNetwork(provider: BrowserProvider): Promise<{ [address: string]: number }> {\n\ttry {\n\t\tconst { payload } = await provider.send(\n\t\t\t'interceptor_getSimulationStack',\n\t\t\t['1.0.0']\n\t\t)\n\t\tconst result = payload.reduce((acc: { [address: string]: number }, curr: { from: string }) => {\n\t\t\tcurr.from = getAddress(curr.from)\n\t\t\tif (curr.from in acc) acc[curr.from] += 1\n\t\t\telse acc[curr.from] = 1\n\t\t\treturn acc\n\t\t}, {})\n\t\treturn result\n\t} catch (error) {\n\t\tconsole.error('getSimulatedCountsOnNetwork error: ', error)\n\t\treturn {}\n\t}\n}\n\nexport const getRawTransactionsAndCalculateFeesAndNonces = async (bundle: FlashbotsBundleTransaction[], provider: BrowserProvider, blockInfo: BlockInfo, maxBaseFee: bigint) => {\n\tconst transactions: { rawTransaction: string, transaction: TransactionRequest } [] = []\n\tconst inSimulation = await getSimulatedCountsOnNetwork(provider)\n\tconst accNonces: { [address: string]: number } = {}\n\tfor (const tx of bundle) {\n\t\ttx.transaction.maxPriorityFeePerGas = blockInfo.priorityFee\n\t\ttx.transaction.maxFeePerGas = blockInfo.priorityFee + maxBaseFee\n\t\tif (!tx.transaction.from) throw new Error('BundleTransaction missing from address')\n\t\tif (!tx.transaction.chainId) throw new Error('BundleTransaction missing chainId')\n\t\t// Fetch and increment nonces from network, reduce the fetch amount by amount of transactions made on the simulation stack\n\t\tif (tx.transaction.from.toString() in accNonces) {\n\t\t\taccNonces[tx.transaction.from.toString()] += 1\n\t\t} else {\n\t\t\taccNonces[tx.transaction.from.toString()] = await provider.getTransactionCount(tx.transaction.from, 'latest')\n\t\t\tif (tx.transaction.from.toString() in inSimulation) accNonces[tx.transaction.from.toString()] -= inSimulation[tx.transaction.from.toString()]\n\t\t}\n\t\ttx.transaction.nonce = accNonces[tx.transaction.from.toString()]\n\t\tconst rawTransaction = await tx.signer.signTransaction({ ...tx.transaction })\n\t\ttransactions.push({ rawTransaction, transaction: tx.transaction })\n\t}\n\treturn transactions\n}\n\nexport const createBundleTransactions = async (\n\tbundle: Bundle,\n\tsigners: Signers,\n\tblockInfo: BlockInfo,\n\tblocksInFuture: bigint,\n\tfundingAmountMin: bigint,\n): Promise => {\n\treturn await Promise.all(bundle.transactions.map(async ({ from, to, gasLimit, value, input, chainId }) => {\n\t\tconst gasOpts = {\n\t\t\tmaxPriorityFeePerGas: blockInfo.priorityFee,\n\t\t\ttype: 2,\n\t\t\tmaxFeePerGas: blockInfo.priorityFee + getMaxBaseFeeInFutureBlock(blockInfo.baseFee, blocksInFuture),\n\t\t}\n\t\tif (from === 'FUNDING') {\n\t\t\tif (!signers.burner) throw new Error('No burner wallet provided')\n\t\t\treturn {\n\t\t\t\tsigner: signers.burner,\n\t\t\t\ttransaction: {\n\t\t\t\t\tfrom: signers.burner.address,\n\t\t\t\t\t...(bundle && bundle.transactions[0].to\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\tto: addressString(bundle.transactions[0].to),\n\t\t\t\t\t\t}\n\t\t\t\t\t\t: {}),\n\t\t\t\t\tvalue: fundingAmountMin - 21000n * (getMaxBaseFeeInFutureBlock(blockInfo.baseFee, blocksInFuture) + blockInfo.priorityFee),\n\t\t\t\t\tdata: '0x',\n\t\t\t\t\tgasLimit: 21000n,\n\t\t\t\t\tchainId: Number(chainId),\n\t\t\t\t\t...gasOpts,\n\t\t\t\t},\n\t\t\t}\n\t\t} else\n\t\t\treturn {\n\t\t\t\tsigner: signers.bundleSigners[addressString(from)],\n\t\t\t\ttransaction: {\n\t\t\t\t\tfrom: addressString(from),\n\t\t\t\t\t...(to ? { to: addressString(to) } : {}),\n\t\t\t\t\tgasLimit,\n\t\t\t\t\tdata: serialize(EthereumData, input),\n\t\t\t\t\tvalue,\n\t\t\t\t\tchainId: Number(chainId),\n\t\t\t\t\t...gasOpts,\n\t\t\t\t},\n\t\t\t}\n\t}))\n}\n"]} \ No newline at end of file diff --git a/docs/js/library/flashbots.d.ts b/docs/js/library/flashbots.d.ts new file mode 100644 index 0000000..11d486b --- /dev/null +++ b/docs/js/library/flashbots.d.ts @@ -0,0 +1,69 @@ +import { BlockInfo, Bundle, Signers } from '../types/types.js'; +import { ProviderStore } from './provider.js'; +import { BouquetNetwork } from '../types/bouquetTypes.js'; +import { EthSimulateV1CallResult, EthSimulateV1CallResults } from '../types/ethSimulateTypes.js'; +interface TransactionSimulationBase { + txHash: string; + gasUsed: number; + gasFees: string; + gasPrice: string; + toAddress: string; + fromAddress: string; + coinbaseDiff: string; +} +export interface TransactionSimulationSuccess extends TransactionSimulationBase { + value: string; + ethSentToCoinbase: string; +} +export interface TransactionSimulationRevert extends TransactionSimulationBase { + error: string; + revert: string; +} +export type TransactionSimulation = TransactionSimulationSuccess | TransactionSimulationRevert; +export interface RelayResponseError { + error: { + message: string; + code: number; + }; +} +export type SimulationResponseSuccess = { + bundleGasPrice: bigint; + bundleHash: string; + coinbaseDiff: bigint; + ethSentToCoinbase: bigint; + gasFees: bigint; + results: Array; + totalGasUsed: bigint; + stateBlockNumber: number; + firstRevert: TransactionSimulation | undefined; +} | { + totalGasUsed: bigint; + firstRevert: EthSimulateV1CallResult & { + toAddress: string; + fromAddress: string | undefined; + } | undefined; + results: EthSimulateV1CallResults; + gasFees: bigint; +}; +export type SimulationResponse = SimulationResponseSuccess | RelayResponseError; +export declare function simulateBundle(bundle: Bundle, fundingAmountMin: bigint, provider: ProviderStore, signers: Signers, blockInfo: BlockInfo, network: BouquetNetwork): Promise; +export declare function sendBundle(bundle: Bundle, targetBlock: bigint, fundingAmountMin: bigint, provider: ProviderStore, signers: Signers, blockInfo: BlockInfo, network: BouquetNetwork): Promise<{ + bundleTransactions: { + signedTransaction: string; + hash: string; + account: string; + nonce: bigint; + }[]; + bundleIdentifier: any; +}>; +export declare function checkBundleInclusion(transactions: { + hash: string; +}[], provider: ProviderStore): Promise<{ + transactions: { + hash: string; + }[]; + included: boolean; + includedInBlocks: bigint[]; +}>; +export {}; +//# sourceMappingURL=flashbots.d.ts.map \ No newline at end of file diff --git a/docs/js/library/flashbots.d.ts.map b/docs/js/library/flashbots.d.ts.map new file mode 100644 index 0000000..5e992c7 --- /dev/null +++ b/docs/js/library/flashbots.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"flashbots.d.ts","sourceRoot":"","sources":["../../ts/library/flashbots.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAE9D,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AACzD,OAAO,EAAE,uBAAuB,EAAE,wBAAwB,EAA8E,MAAM,8BAA8B,CAAA;AAI5K,UAAU,yBAAyB;IAClC,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,4BAA6B,SAAQ,yBAAyB;IAC9E,KAAK,EAAE,MAAM,CAAA;IACb,iBAAiB,EAAE,MAAM,CAAA;CACzB;AAED,MAAM,WAAW,2BAA4B,SAAQ,yBAAyB;IAC7E,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACd;AAED,MAAM,MAAM,qBAAqB,GAAG,4BAA4B,GAAG,2BAA2B,CAAA;AAE9F,MAAM,WAAW,kBAAkB;IAClC,KAAK,EAAE;QACN,OAAO,EAAE,MAAM,CAAA;QACf,IAAI,EAAE,MAAM,CAAA;KACZ,CAAA;CACD;AAED,MAAM,MAAM,yBAAyB,GAAG;IACvC,cAAc,EAAE,MAAM,CAAA;IACtB,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,MAAM,CAAA;IACpB,iBAAiB,EAAE,MAAM,CAAA;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,KAAK,CAAC,qBAAqB,CAAC,CAAA;IACrC,YAAY,EAAE,MAAM,CAAA;IACpB,gBAAgB,EAAE,MAAM,CAAA;IACxB,WAAW,EAAE,qBAAqB,GAAG,SAAS,CAAA;CAC9C,GAAG;IACH,YAAY,EAAE,MAAM,CAAA;IACpB,WAAW,EAAE,uBAAuB,GAAG;QACtC,SAAS,EAAE,MAAM,CAAA;QACjB,WAAW,EAAE,MAAM,GAAG,SAAS,CAAA;KAC/B,GAAG,SAAS,CAAA;IACb,OAAO,EAAE,wBAAwB,CAAA;IACjC,OAAO,EAAE,MAAM,CAAA;CACf,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG,yBAAyB,GAAG,kBAAkB,CAAA;AAE/E,wBAAsB,cAAc,CACnC,MAAM,EAAE,MAAM,EACd,gBAAgB,EAAE,MAAM,EACxB,QAAQ,EAAE,aAAa,EACvB,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,cAAc,GACrB,OAAO,CAAC,kBAAkB,CAAC,CAyF7B;AAGD,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,cAAc;;;;;;;;GA6EvL;AAED,wBAAsB,oBAAoB,CAAC,YAAY,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,EAAE,QAAQ,EAAE,aAAa;;cAAnC,MAAM;;;;GAItE"} \ No newline at end of file diff --git a/docs/js/library/flashbots.js b/docs/js/library/flashbots.js new file mode 100644 index 0000000..0a0c4c4 --- /dev/null +++ b/docs/js/library/flashbots.js @@ -0,0 +1,168 @@ +import { ethers, id, keccak256, toUtf8Bytes, Transaction } from 'ethers'; +import { createBundleTransactions, getMaxBaseFeeInFutureBlock, getRawTransactionsAndCalculateFeesAndNonces } from './bundleUtils.js'; +import { EthSimulateV1Params, EthSimulateV1Result, JsonRpcResponse, TransactionType } from '../types/ethSimulateTypes.js'; +import { serialize } from '../types/ethereumTypes.js'; +import { addressString, min } from './utils.js'; +export async function simulateBundle(bundle, fundingAmountMin, provider, signers, blockInfo, network) { + if (network.blocksInFuture <= 0n) + throw new Error('Blocks in future is negative or zero'); + const maxBaseFee = getMaxBaseFeeInFutureBlock(blockInfo.baseFee, network.blocksInFuture); + const bundleTransactions = await createBundleTransactions(bundle, signers, blockInfo, network.blocksInFuture, fundingAmountMin); + const txs = await getRawTransactionsAndCalculateFeesAndNonces(bundleTransactions, provider.provider, blockInfo, maxBaseFee); + const bigIntify = (ethersValue) => ethersValue ? BigInt(ethersValue.toString()) : undefined; + switch (network.relayMode) { + case 'mempool': { + if (network.mempoolSimulationRpcEndpoint === undefined) + throw new Error('mempoolSimulationRpcEndpoint is not defined'); + const data = { + method: 'eth_simulateV1', + params: [{ 'blockStateCalls': [{ calls: txs.map((tx) => ({ + type: TransactionType.parse(tx.transaction.type), + to: bigIntify(tx.transaction.to), + from: bigIntify(tx.transaction.from), + nonce: bigIntify(tx.transaction.nonce), + gas: bigIntify(tx.transaction.gasLimit), + gasPrice: tx.transaction.gasPrice, + maxPriorityFeePerGas: bigIntify(tx.transaction.maxPriorityFeePerGas), + maxFeePerGas: bigIntify(tx.transaction.maxFeePerGas), + data: tx.transaction.data, + value: bigIntify(tx.transaction.value), + chainId: bigIntify(tx.transaction.chainId), + accessList: [], + })) }], traceTransfers: false, validation: true }, 'latest'] + }; + const serialized = serialize(EthSimulateV1Params, data); + const request = await fetch(network.mempoolSimulationRpcEndpoint, { method: 'POST', body: JSON.stringify({ jsonrpc: '2.0', id: 0, ...serialized }), headers: { 'Content-Type': 'application/json' } }); + const response = JsonRpcResponse.parse(await request.json()); + if ('error' in response) { + console.log(response); + throw new Error(response.error.message); + } + const parsed = EthSimulateV1Result.parse(response.result); + const calls = parsed[0].calls; + return { + totalGasUsed: calls.reduce((a, b) => a + b.gasUsed, 0n), + firstRevert: calls.map((call, index) => { + const to = bigIntify(txs[index].transaction.to); + if (to === undefined) + throw new Error('to is undefined'); + const from = bigIntify(txs[index].transaction.from); + return { + ...call, + toAddress: addressString(to), + fromAddress: from !== undefined ? addressString(from) : undefined, + }; + }).find((txSim) => txSim.status === 'failure'), + results: calls, + gasFees: txs.reduce((totalFee, tx, currentIndex) => { + if (tx.transaction.gasPrice) + return totalFee + BigInt(tx.transaction.gasPrice) * calls[currentIndex].gasUsed; + return totalFee + min(parsed[0].baseFeePerGas + BigInt(tx.transaction.maxPriorityFeePerGas || 0n), BigInt(tx.transaction.maxFeePerGas || 0n)); + }, 0n), + }; + } + case 'relay': { + if (network.simulationRelayEndpoint === undefined) + throw new Error('simulationRelayEndpoint is not defined'); + const payload = JSON.stringify({ jsonrpc: '2.0', method: 'eth_callBundle', params: [{ txs: txs.map((x) => x.rawTransaction), blockNumber: `0x${blockInfo.blockNumber.toString(16)}`, stateBlockNumber: 'latest' }] }); + const flashbotsSig = `${await provider.authSigner.getAddress()}:${await provider.authSigner.signMessage(id(payload))}`; + const request = await fetch(network.simulationRelayEndpoint, { method: 'POST', body: payload, headers: { 'Content-Type': 'application/json', 'X-Flashbots-Signature': flashbotsSig } }); + const response = await request.json(); + if (response.error !== undefined && response.error !== null) { + return { + error: { + message: response.error.message, + code: response.error.code, + }, + }; + } + const callResult = response.result; + return { + bundleGasPrice: BigInt(callResult.bundleGasPrice), + bundleHash: callResult.bundleHash, + coinbaseDiff: BigInt(callResult.coinbaseDiff), + ethSentToCoinbase: BigInt(callResult.ethSentToCoinbase), + gasFees: BigInt(callResult.gasFees), + results: callResult.results, + stateBlockNumber: callResult.stateBlockNumber, + totalGasUsed: callResult.results.reduce((a, b) => a + BigInt(b.gasUsed), 0n), + firstRevert: callResult.results.find((txSim) => 'revert' in txSim || 'error' in txSim), + }; + } + } +} +let bundleId = 1; +export async function sendBundle(bundle, targetBlock, fundingAmountMin, provider, signers, blockInfo, network) { + if (network.blocksInFuture <= 0n) + throw new Error('Blocks in future is negative or zero'); + const maxBaseFee = getMaxBaseFeeInFutureBlock(blockInfo.baseFee, network.blocksInFuture); + const transactions = (await getRawTransactionsAndCalculateFeesAndNonces(await createBundleTransactions(bundle, signers, blockInfo, network.blocksInFuture, fundingAmountMin), provider.provider, blockInfo, maxBaseFee)).map((x) => x.rawTransaction); + switch (network.relayMode) { + case 'mempool': { + const payloads = transactions.map((transaction, index) => JSON.stringify({ + jsonrpc: '2.0', + method: 'eth_sendRawTransaction', + id: index, + params: [transaction] + })); + if (network.mempoolSubmitRpcEndpoint === undefined) + throw new Error('MemPool Submit Rpc Endpoint is not set'); + const requests = await Promise.all(payloads.map(async (payload) => { + if (network.mempoolSubmitRpcEndpoint === undefined) + throw new Error('MemPool Submit Rpc Endpoint is not set'); + return await fetch(network.mempoolSubmitRpcEndpoint, { method: 'POST', body: payload, headers: { 'Content-Type': 'application/json' } }); + })); + for (const request of requests) { + const response = await request.json(); + if (response.error !== undefined && response.error !== null) { + throw new Error(response.error.message); + } + } + const bundleTransactions = transactions.map((signedTransaction) => { + const transactionDetails = Transaction.from(signedTransaction); + return { + signedTransaction, + hash: keccak256(signedTransaction), + account: transactionDetails.from || '0x0', + nonce: BigInt(transactionDetails.nonce), + }; + }); + return { bundleTransactions, bundleIdentifier: ethers.keccak256(toUtf8Bytes(payloads.join('|'))) }; + } + case 'relay': { + const payload = JSON.stringify({ + jsonrpc: '2.0', + method: 'eth_sendBundle', + id: bundleId++, + params: [{ txs: transactions, blockNumber: `0x${targetBlock.toString(16)}`, revertingTxHashes: [] }] + }); + const flashbotsSig = `${await provider.authSigner.getAddress()}:${await provider.authSigner.signMessage(id(payload))}`; + if (network.submissionRelayEndpoint === undefined) + throw new Error('submissionRelayEndpoint is not defined'); + const request = await fetch(network.submissionRelayEndpoint, { method: 'POST', body: payload, headers: { 'Content-Type': 'application/json', 'X-Flashbots-Signature': flashbotsSig } }); + const response = await request.json(); + if (response.error !== undefined && response.error !== null) { + throw { + message: response.error.message, + code: response.error.code, + }; + } + const bundleTransactions = transactions.map((signedTransaction) => { + const transactionDetails = Transaction.from(signedTransaction); + return { + signedTransaction, + hash: keccak256(signedTransaction), + account: transactionDetails.from || '0x0', + nonce: BigInt(transactionDetails.nonce), + }; + }); + return { bundleTransactions, bundleIdentifier: response.result.bundleHash }; + } + } +} +export async function checkBundleInclusion(transactions, provider) { + const receipts = await Promise.all(transactions.map((tx) => provider.provider.getTransactionReceipt(tx.hash))); + const includedInBlocks = Array.from(new Set(receipts.filter((receipt) => receipt !== null).map((receipt) => BigInt(receipt.blockNumber)))); + return { transactions, included: receipts.filter(x => x === null).length === 0, includedInBlocks }; +} +//# sourceMappingURL=flashbots.js.map \ No newline at end of file diff --git a/docs/js/library/flashbots.js.map b/docs/js/library/flashbots.js.map new file mode 100644 index 0000000..0a6d921 --- /dev/null +++ b/docs/js/library/flashbots.js.map @@ -0,0 +1 @@ +{"version":3,"file":"flashbots.js","sourceRoot":"","sources":["../../ts/library/flashbots.ts"],"names":[],"mappings":"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","sourcesContent":["import { AddressLike, ethers, id, keccak256, toUtf8Bytes, Transaction } from 'ethers'\nimport { BlockInfo, Bundle, Signers } from '../types/types.js'\nimport { createBundleTransactions, getMaxBaseFeeInFutureBlock, getRawTransactionsAndCalculateFeesAndNonces } from './bundleUtils.js'\nimport { ProviderStore } from './provider.js'\nimport { BouquetNetwork } from '../types/bouquetTypes.js'\nimport { EthSimulateV1CallResult, EthSimulateV1CallResults, EthSimulateV1Params, EthSimulateV1Result, JsonRpcResponse, TransactionType } from '../types/ethSimulateTypes.js'\nimport { serialize } from '../types/ethereumTypes.js'\nimport { addressString, min } from './utils.js'\n\ninterface TransactionSimulationBase {\n\ttxHash: string\n\tgasUsed: number\n\tgasFees: string\n\tgasPrice: string\n\ttoAddress: string\n\tfromAddress: string\n\tcoinbaseDiff: string\n}\n\nexport interface TransactionSimulationSuccess extends TransactionSimulationBase {\n\tvalue: string\n\tethSentToCoinbase: string\n}\n\nexport interface TransactionSimulationRevert extends TransactionSimulationBase {\n\terror: string\n\trevert: string\n}\n\nexport type TransactionSimulation = TransactionSimulationSuccess | TransactionSimulationRevert\n\nexport interface RelayResponseError {\n\terror: {\n\t\tmessage: string\n\t\tcode: number\n\t}\n}\n\nexport type SimulationResponseSuccess = {\n\tbundleGasPrice: bigint\n\tbundleHash: string\n\tcoinbaseDiff: bigint\n\tethSentToCoinbase: bigint\n\tgasFees: bigint\n\tresults: Array\n\ttotalGasUsed: bigint\n\tstateBlockNumber: number\n\tfirstRevert: TransactionSimulation | undefined\n} | {\n\ttotalGasUsed: bigint\n\tfirstRevert: EthSimulateV1CallResult & {\n\t\ttoAddress: string\n\t\tfromAddress: string | undefined\n\t} | undefined\n\tresults: EthSimulateV1CallResults\n\tgasFees: bigint\n}\n\nexport type SimulationResponse = SimulationResponseSuccess | RelayResponseError\n\nexport async function simulateBundle(\n\tbundle: Bundle,\n\tfundingAmountMin: bigint,\n\tprovider: ProviderStore,\n\tsigners: Signers,\n\tblockInfo: BlockInfo,\n\tnetwork: BouquetNetwork\n): Promise {\n\tif (network.blocksInFuture <= 0n) throw new Error('Blocks in future is negative or zero')\n\tconst maxBaseFee = getMaxBaseFeeInFutureBlock(blockInfo.baseFee, network.blocksInFuture)\n\tconst bundleTransactions = await createBundleTransactions(bundle, signers, blockInfo, network.blocksInFuture, fundingAmountMin)\n\tconst txs = await getRawTransactionsAndCalculateFeesAndNonces(bundleTransactions, provider.provider, blockInfo, maxBaseFee)\n\n\tconst bigIntify = (ethersValue: ethers.BigNumberish | null | undefined | AddressLike) => ethersValue ? BigInt(ethersValue.toString()) : undefined\n\n\tswitch(network.relayMode) {\n\t\tcase 'mempool': {\n\t\t\tif (network.mempoolSimulationRpcEndpoint === undefined) throw new Error('mempoolSimulationRpcEndpoint is not defined')\n\t\t\tconst data: EthSimulateV1Params = {\n\t\t\t\tmethod: 'eth_simulateV1',\n\t\t\t\tparams: [ { 'blockStateCalls': [ { calls: txs.map((tx) => ({\n\t\t\t\t\ttype: TransactionType.parse(tx.transaction.type),\n\t\t\t\t\tto: bigIntify(tx.transaction.to),\n\t\t\t\t\tfrom: bigIntify(tx.transaction.from),\n\t\t\t\t\tnonce: bigIntify(tx.transaction.nonce),\n\t\t\t\t\tgas: bigIntify(tx.transaction.gasLimit),\n\t\t\t\t\tgasPrice: tx.transaction.gasPrice,\n\t\t\t\t\tmaxPriorityFeePerGas: bigIntify(tx.transaction.maxPriorityFeePerGas),\n\t\t\t\t\tmaxFeePerGas: bigIntify(tx.transaction.maxFeePerGas),\n\t\t\t\t\tdata: tx.transaction.data,\n\t\t\t\t\tvalue: bigIntify(tx.transaction.value),\n\t\t\t\t\tchainId: bigIntify(tx.transaction.chainId),\n\t\t\t\t\taccessList: [],\n\t\t\t\t})) } ], traceTransfers: false, validation: true }, 'latest' ]\n\t\t\t} as const\n\t\t\tconst serialized = serialize(EthSimulateV1Params, data)\n\t\t\tconst request = await fetch(network.mempoolSimulationRpcEndpoint, { method: 'POST', body: JSON.stringify({ jsonrpc: '2.0', id: 0, ...serialized }), headers: { 'Content-Type': 'application/json' } })\n\t\t\tconst response = JsonRpcResponse.parse(await request.json())\n\t\t\tif ('error' in response) {\n\t\t\t\tconsole.log(response)\n\t\t\t\tthrow new Error(response.error.message)\n\t\t\t}\n\t\t\tconst parsed = EthSimulateV1Result.parse(response.result)\n\t\t\tconst calls = parsed[0].calls\n\n\t\t\treturn {\n\t\t\t\ttotalGasUsed: calls.reduce((a, b) => a + b.gasUsed, 0n),\n\t\t\t\tfirstRevert: calls.map((call, index) => {\n\t\t\t\t\tconst to = bigIntify(txs[index].transaction.to)\n\t\t\t\t\tif (to === undefined) throw new Error('to is undefined')\n\t\t\t\t\tconst from = bigIntify(txs[index].transaction.from)\n\t\t\t\t\treturn {\n\t\t\t\t\t\t...call,\n\t\t\t\t\t\ttoAddress: addressString(to),\n\t\t\t\t\t\tfromAddress: from !== undefined ? addressString(from) : undefined,\n\t\t\t\t\t}\n\t\t\t\t}).find((txSim) => txSim.status === 'failure'),\n\t\t\t\tresults: calls,\n\t\t\t\tgasFees: txs.reduce((totalFee, tx, currentIndex) => {\n\t\t\t\t\tif (tx.transaction.gasPrice) return totalFee + BigInt(tx.transaction.gasPrice) * calls[currentIndex].gasUsed\n\t\t\t\t\treturn totalFee + min(parsed[0].baseFeePerGas + BigInt(tx.transaction.maxPriorityFeePerGas || 0n), BigInt(tx.transaction.maxFeePerGas || 0n))\n\t\t\t\t}, 0n),\n\t\t\t}\n\t\t}\n\t\tcase 'relay': {\n\t\t\tif (network.simulationRelayEndpoint === undefined) throw new Error('simulationRelayEndpoint is not defined')\n\t\t\tconst payload = JSON.stringify({ jsonrpc: '2.0', method: 'eth_callBundle', params: [{ txs: txs.map((x) => x.rawTransaction), blockNumber: `0x${blockInfo.blockNumber.toString(16)}`, stateBlockNumber: 'latest' }] })\n\t\t\tconst flashbotsSig = `${await provider.authSigner.getAddress()}:${await provider.authSigner.signMessage(id(payload))}`\n\t\t\tconst request = await fetch(network.simulationRelayEndpoint,\n\t\t\t\t{ method: 'POST', body: payload, headers: { 'Content-Type': 'application/json', 'X-Flashbots-Signature': flashbotsSig } }\n\t\t\t)\n\t\t\tconst response = await request.json()\n\n\t\t\tif (response.error !== undefined && response.error !== null) {\n\t\t\t\treturn {\n\t\t\t\t\terror: {\n\t\t\t\t\t\tmessage: response.error.message,\n\t\t\t\t\t\tcode: response.error.code,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst callResult = response.result\n\t\t\treturn {\n\t\t\t\tbundleGasPrice: BigInt(callResult.bundleGasPrice),\n\t\t\t\tbundleHash: callResult.bundleHash,\n\t\t\t\tcoinbaseDiff: BigInt(callResult.coinbaseDiff),\n\t\t\t\tethSentToCoinbase: BigInt(callResult.ethSentToCoinbase),\n\t\t\t\tgasFees: BigInt(callResult.gasFees),\n\t\t\t\tresults: callResult.results,\n\t\t\t\tstateBlockNumber: callResult.stateBlockNumber,\n\t\t\t\ttotalGasUsed: callResult.results.reduce((a: bigint, b: TransactionSimulation) => a + BigInt(b.gasUsed), 0n),\n\t\t\t\tfirstRevert: callResult.results.find((txSim: TransactionSimulation) => 'revert' in txSim || 'error' in txSim),\n\t\t\t}\n\t\t}\n\t}\n}\n\nlet bundleId = 1\nexport async function sendBundle(bundle: Bundle, targetBlock: bigint, fundingAmountMin: bigint, provider: ProviderStore, signers: Signers, blockInfo: BlockInfo, network: BouquetNetwork) {\n\tif (network.blocksInFuture <= 0n) throw new Error('Blocks in future is negative or zero')\n\tconst maxBaseFee = getMaxBaseFeeInFutureBlock(blockInfo.baseFee, network.blocksInFuture)\n\tconst transactions = (await getRawTransactionsAndCalculateFeesAndNonces(\n\t\tawait createBundleTransactions(bundle, signers, blockInfo, network.blocksInFuture, fundingAmountMin),\n\t\tprovider.provider,\n\t\tblockInfo,\n\t\tmaxBaseFee,\n\t)).map((x) => x.rawTransaction)\n\n\tswitch(network.relayMode) {\n\t\tcase 'mempool': {\n\t\t\tconst payloads = transactions.map((transaction, index) => JSON.stringify({\n\t\t\t\tjsonrpc: '2.0',\n\t\t\t\tmethod: 'eth_sendRawTransaction',\n\t\t\t\tid: index,\n\t\t\t\tparams: [transaction]\n\t\t\t}))\n\t\t\tif (network.mempoolSubmitRpcEndpoint === undefined) throw new Error('MemPool Submit Rpc Endpoint is not set')\n\t\t\tconst requests = await Promise.all(payloads.map(async (payload) => {\n\t\t\t\tif (network.mempoolSubmitRpcEndpoint === undefined) throw new Error('MemPool Submit Rpc Endpoint is not set')\n\t\t\t\treturn await fetch(network.mempoolSubmitRpcEndpoint, { method: 'POST', body: payload, headers: { 'Content-Type': 'application/json' } })\n\t\t\t}))\n\t\t\tfor (const request of requests) {\n\t\t\t\tconst response = await request.json()\n\t\t\t\tif (response.error !== undefined && response.error !== null) {\n\t\t\t\t\tthrow new Error(response.error.message)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst bundleTransactions = transactions.map((signedTransaction) => {\n\t\t\t\tconst transactionDetails = Transaction.from(signedTransaction)\n\t\t\t\treturn {\n\t\t\t\t\tsignedTransaction,\n\t\t\t\t\thash: keccak256(signedTransaction),\n\t\t\t\t\taccount: transactionDetails.from || '0x0',\n\t\t\t\t\tnonce: BigInt(transactionDetails.nonce),\n\t\t\t\t}\n\t\t\t})\n\n\t\t\treturn { bundleTransactions, bundleIdentifier: ethers.keccak256(toUtf8Bytes(payloads.join('|'))) }\n\t\t}\n\t\tcase 'relay': {\n\t\t\tconst payload = JSON.stringify({\n\t\t\t\tjsonrpc: '2.0',\n\t\t\t\tmethod: 'eth_sendBundle',\n\t\t\t\tid: bundleId++,\n\t\t\t\tparams: [{ txs: transactions, blockNumber: `0x${targetBlock.toString(16)}`, revertingTxHashes: [] }]\n\t\t\t})\n\t\t\tconst flashbotsSig = `${await provider.authSigner.getAddress()}:${await provider.authSigner.signMessage(id(payload))}`\n\n\t\t\tif (network.submissionRelayEndpoint === undefined) throw new Error('submissionRelayEndpoint is not defined')\n\t\t\tconst request = await fetch(network.submissionRelayEndpoint,\n\t\t\t\t{ method: 'POST', body: payload, headers: { 'Content-Type': 'application/json', 'X-Flashbots-Signature': flashbotsSig } }\n\t\t\t)\n\t\t\tconst response = await request.json()\n\n\t\t\tif (response.error !== undefined && response.error !== null) {\n\t\t\t\tthrow {\n\t\t\t\t\tmessage: response.error.message,\n\t\t\t\t\tcode: response.error.code,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst bundleTransactions = transactions.map((signedTransaction) => {\n\t\t\t\tconst transactionDetails = Transaction.from(signedTransaction)\n\t\t\t\treturn {\n\t\t\t\t\tsignedTransaction,\n\t\t\t\t\thash: keccak256(signedTransaction),\n\t\t\t\t\taccount: transactionDetails.from || '0x0',\n\t\t\t\t\tnonce: BigInt(transactionDetails.nonce),\n\t\t\t\t}\n\t\t\t})\n\n\t\t\treturn { bundleTransactions, bundleIdentifier: response.result.bundleHash }\n\t\t}\n\t}\n}\n\nexport async function checkBundleInclusion(transactions: { hash: string }[], provider: ProviderStore) {\n\tconst receipts = await Promise.all(transactions.map((tx) => provider.provider.getTransactionReceipt(tx.hash)))\n\tconst includedInBlocks = Array.from(new Set(receipts.filter((receipt): receipt is ethers.TransactionReceipt => receipt !== null).map((receipt) => BigInt(receipt.blockNumber))))\n\treturn { transactions, included: receipts.filter(x => x === null).length === 0, includedInBlocks }\n}\n"]} \ No newline at end of file diff --git a/docs/js/library/provider.d.ts b/docs/js/library/provider.d.ts new file mode 100644 index 0000000..a639047 --- /dev/null +++ b/docs/js/library/provider.d.ts @@ -0,0 +1,20 @@ +import { Signal } from '@preact/signals'; +import { Block, BrowserProvider, HDNodeWallet } from 'ethers'; +import { EthereumAddress } from '../types/ethereumTypes.js'; +import { BlockInfo, Signers } from '../types/types.js'; +import { BouquetSettings } from '../types/bouquetTypes.js'; +export type ProviderStore = { + provider: BrowserProvider; + _clearEvents: () => unknown; + authSigner: HDNodeWallet; + walletAddress: EthereumAddress; + chainId: bigint; + isInterceptor: boolean; +}; +export declare const connectBrowserProvider: (store: Signal, blockInfo: Signal<{ + blockNumber: bigint; + baseFee: bigint; + priorityFee: bigint; +}>, signers: Signal | undefined, bouquetSettings: Signal) => Promise; +export declare function updateLatestBlock(block: Block, provider: Signal, blockInfo: Signal, signers: Signal | undefined): Promise; +//# sourceMappingURL=provider.d.ts.map \ No newline at end of file diff --git a/docs/js/library/provider.d.ts.map b/docs/js/library/provider.d.ts.map new file mode 100644 index 0000000..daa7c33 --- /dev/null +++ b/docs/js/library/provider.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../ts/library/provider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAC/C,OAAO,EAAE,KAAK,EAAE,eAAe,EAAc,YAAY,EAAU,MAAM,QAAQ,CAAA;AACjF,OAAO,EAAiB,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAC1E,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAEtD,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAE1D,MAAM,MAAM,aAAa,GAAG;IAC3B,QAAQ,EAAE,eAAe,CAAA;IACzB,YAAY,EAAE,MAAM,OAAO,CAAA;IAC3B,UAAU,EAAE,YAAY,CAAC;IACzB,aAAa,EAAE,eAAe,CAAA;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,OAAO,CAAA;CACtB,CAAA;AA8BD,eAAO,MAAM,sBAAsB,UAC3B,OAAO,aAAa,GAAG,SAAS,CAAC,aAC7B,OAAO;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,MAAM,CAAA;CACnB,CAAC,WACO,OAAO,OAAO,CAAC,GAAG,SAAS,mBACnB,OAAO,eAAe,CAAC,kBAmExC,CAAA;AAED,wBAAsB,iBAAiB,CACtC,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,MAAM,CAAC,aAAa,GAAG,SAAS,CAAC,EAC3C,SAAS,EAAE,MAAM,CAAC,SAAS,CAAC,EAC5B,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,SAAS,iBAOpC"} \ No newline at end of file diff --git a/docs/js/library/provider.js b/docs/js/library/provider.js new file mode 100644 index 0000000..43b6635 --- /dev/null +++ b/docs/js/library/provider.js @@ -0,0 +1,100 @@ +import { batch } from '@preact/signals'; +import { BrowserProvider, getAddress, Wallet } from 'ethers'; +import { AddressParser } from '../types/ethereumTypes.js'; +import { fetchSettingsFromStorage } from '../stores.js'; +const addProvider = async (store, provider, clearEvents, isInterceptor) => { + const [signer, network] = await Promise.all([provider.getSigner(), provider.getNetwork()]); + const address = await signer.getAddress(); + if (store.peek()) + removeProvider(store); + const parsedAddress = AddressParser.parse(getAddress(address)); + if (!parsedAddress.success) + throw new Error('Provider provided invalid address!'); + store.value = { + provider, + authSigner: Wallet.createRandom(), + walletAddress: parsedAddress.value, + chainId: network.chainId, + _clearEvents: clearEvents, + isInterceptor + }; +}; +const removeProvider = async (store) => { + if (store.peek()) + store.peek()?._clearEvents(); + store.value = undefined; +}; +export const connectBrowserProvider = async (store, blockInfo, signers, bouquetSettings) => { + if (!window.ethereum || !window.ethereum.request) + throw new Error('No injected wallet detected'); + await window.ethereum.request({ method: 'eth_requestAccounts' }).catch((err) => { + if (err.code === 4001) { + throw new Error('Connect Wallet: Wallet connection rejected'); + } + else { + throw new Error(`Connect Wallet: ${JSON.stringify(err)}`); + } + }); + const provider = new BrowserProvider(window.ethereum, 'any'); + const blockCallback = async (blockNumber) => { + const block = await provider.getBlock(blockNumber); + if (block) + updateLatestBlock(block, store, blockInfo, signers); + }; + const disconnectEventCallback = () => { + removeProvider(store); + }; + const accountsChangedCallback = (accounts) => { + if (accounts.length === 0) { + removeProvider(store); + } + else { + const parsedAddress = AddressParser.parse(getAddress(accounts[0])); + if (!parsedAddress.success) + throw new Error('Provider provided invalid address!'); + store.value = store.value ? { ...store.value, walletAddress: parsedAddress.value } : undefined; + } + }; + const chainChangedCallback = async (chainId) => { + batch(() => { + bouquetSettings.value = fetchSettingsFromStorage(); + if (store.value) + store.value = { ...store.value, chainId: BigInt(chainId) }; + }); + const [accounts, block] = await Promise.all([provider.listAccounts(), provider.getBlock('latest')]); + if (accounts.length > 0 && window.ethereum) { + clearEvents(); + window.ethereum.on('disconnect', disconnectEventCallback); + window.ethereum.on('accountsChanged', accountsChangedCallback); + window.ethereum.on('chainChanged', chainChangedCallback); + provider.on('block', blockCallback); + } + accountsChangedCallback(await Promise.all(accounts.map(account => account.getAddress()))); + if (block) + updateLatestBlock(block, store, blockInfo, signers); + }; + const block = await provider.getBlock('latest'); + if (block) + await updateLatestBlock(block, store, blockInfo, signers); + window.ethereum.on('disconnect', disconnectEventCallback); + window.ethereum.on('accountsChanged', accountsChangedCallback); + window.ethereum.on('chainChanged', chainChangedCallback); + provider.on('block', blockCallback); + const clearEvents = () => { + window.ethereum?.removeListener('disconnect', disconnectEventCallback); + window.ethereum?.removeListener('accountsChanged', accountsChangedCallback); + window.ethereum?.removeListener('chainChanged', chainChangedCallback); + provider.removeListener('block', blockCallback); + }; + const [getSimulationStack] = await Promise.allSettled([window.ethereum.request({ method: 'interceptor_getSimulationStack', params: ['1.0.0'] })]); + const isInterceptor = getSimulationStack.status === 'fulfilled'; + addProvider(store, provider, clearEvents, isInterceptor); +}; +export async function updateLatestBlock(block, provider, blockInfo, signers) { + const baseFee = block.baseFeePerGas ? block.baseFeePerGas : 0n; + blockInfo.value = { ...blockInfo.value, blockNumber: BigInt(block.number ?? 0n), baseFee }; + if (provider.value && signers && signers.value.burner) { + provider.value.provider.getBalance(signers.value.burner.address).then((burnerBalance) => signers.value = { ...signers.value, burnerBalance }); + } +} +//# sourceMappingURL=provider.js.map \ No newline at end of file diff --git a/docs/js/library/provider.js.map b/docs/js/library/provider.js.map new file mode 100644 index 0000000..d854ee9 --- /dev/null +++ b/docs/js/library/provider.js.map @@ -0,0 +1 @@ +{"version":3,"file":"provider.js","sourceRoot":"","sources":["../../ts/library/provider.ts"],"names":[],"mappings":"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","sourcesContent":["import { batch, Signal } from '@preact/signals'\nimport { Block, BrowserProvider, getAddress, HDNodeWallet, Wallet } from 'ethers'\nimport { AddressParser, EthereumAddress } from '../types/ethereumTypes.js'\nimport { BlockInfo, Signers } from '../types/types.js'\nimport { fetchSettingsFromStorage } from '../stores.js'\nimport { BouquetSettings } from '../types/bouquetTypes.js'\n\nexport type ProviderStore = {\n\tprovider: BrowserProvider\n\t_clearEvents: () => unknown\n\tauthSigner: HDNodeWallet,\n\twalletAddress: EthereumAddress\n\tchainId: bigint,\n\tisInterceptor: boolean\n}\n\nconst addProvider = async (\n\tstore: Signal,\n\tprovider: BrowserProvider,\n\tclearEvents: () => unknown,\n\tisInterceptor: boolean\n) => {\n\tconst [signer, network] = await Promise.all([provider.getSigner(), provider.getNetwork()])\n\tconst address = await signer.getAddress()\n\tif (store.peek()) removeProvider(store)\n\n\tconst parsedAddress = AddressParser.parse(getAddress(address))\n\tif (!parsedAddress.success) throw new Error('Provider provided invalid address!')\n\n\tstore.value = {\n\t\tprovider,\n\t\tauthSigner: Wallet.createRandom(),\n\t\twalletAddress: parsedAddress.value,\n\t\tchainId: network.chainId,\n\t\t_clearEvents: clearEvents,\n\t\tisInterceptor\n\t}\n}\n\nconst removeProvider = async (store: Signal) => {\n\tif (store.peek()) store.peek()?._clearEvents()\n\tstore.value = undefined\n}\n\nexport const connectBrowserProvider = async (\n\tstore: Signal,\n\tblockInfo: Signal<{\n\t\tblockNumber: bigint\n\t\tbaseFee: bigint\n\t\tpriorityFee: bigint\n\t}>,\n\tsigners: Signal | undefined,\n\tbouquetSettings: Signal\n) => {\n\tif (!window.ethereum || !window.ethereum.request) throw new Error('No injected wallet detected')\n\tawait window.ethereum.request({ method: 'eth_requestAccounts' }).catch((err: { code: number }) => {\n\t\tif (err.code === 4001) {\n\t\t\tthrow new Error('Connect Wallet: Wallet connection rejected')\n\t\t} else {\n\t\t\tthrow new Error(`Connect Wallet: ${JSON.stringify(err)}`)\n\t\t}\n\t})\n\n\tconst provider = new BrowserProvider(window.ethereum, 'any')\n\n\tconst blockCallback = async (blockNumber: number) => {\n\t\tconst block = await provider.getBlock(blockNumber)\n\t\tif (block) updateLatestBlock(block, store, blockInfo, signers)\n\t}\n\n\tconst disconnectEventCallback = () => {\n\t\tremoveProvider(store)\n\t}\n\tconst accountsChangedCallback = (accounts: string[]) => {\n\t\tif (accounts.length === 0) {\n\t\t\tremoveProvider(store)\n\t\t} else {\n\t\t\tconst parsedAddress = AddressParser.parse(getAddress(accounts[0]))\n\t\t\tif (!parsedAddress.success) throw new Error('Provider provided invalid address!')\n\t\t\tstore.value = store.value ? { ...store.value, walletAddress: parsedAddress.value } : undefined\n\t\t}\n\t}\n\tconst chainChangedCallback = async (chainId: string) => {\n\t\tbatch(() => {\n\t\t\tbouquetSettings.value = fetchSettingsFromStorage()\n\t\t\tif (store.value) store.value = { ...store.value, chainId: BigInt(chainId) }\n\t\t})\n\n\t\tconst [accounts, block] = await Promise.all([provider.listAccounts(), provider.getBlock('latest')])\n\t\tif (accounts.length > 0 && window.ethereum) {\n\t\t\tclearEvents()\n\t\t\twindow.ethereum.on('disconnect', disconnectEventCallback)\n\t\t\twindow.ethereum.on('accountsChanged', accountsChangedCallback)\n\t\t\twindow.ethereum.on('chainChanged', chainChangedCallback)\n\t\t\tprovider.on('block', blockCallback)\n\t\t}\n\t\taccountsChangedCallback(await Promise.all(accounts.map(account => account.getAddress())))\n\t\tif (block) updateLatestBlock(block, store, blockInfo, signers)\n\t}\n\n\tconst block = await provider.getBlock('latest')\n\tif (block) await updateLatestBlock(block, store, blockInfo, signers)\n\n\twindow.ethereum.on('disconnect', disconnectEventCallback)\n\twindow.ethereum.on('accountsChanged', accountsChangedCallback)\n\twindow.ethereum.on('chainChanged', chainChangedCallback)\n\tprovider.on('block', blockCallback)\n\n\tconst clearEvents = () => {\n\t\twindow.ethereum?.removeListener('disconnect', disconnectEventCallback)\n\t\twindow.ethereum?.removeListener('accountsChanged', accountsChangedCallback)\n\t\twindow.ethereum?.removeListener('chainChanged', chainChangedCallback)\n\t\tprovider.removeListener('block', blockCallback)\n\t}\n\n\tconst [getSimulationStack] = await Promise.allSettled([window.ethereum.request({ method: 'interceptor_getSimulationStack', params: ['1.0.0'] })])\n\tconst isInterceptor = getSimulationStack.status === 'fulfilled'\n\n\taddProvider(store, provider, clearEvents, isInterceptor)\n}\n\nexport async function updateLatestBlock(\n\tblock: Block,\n\tprovider: Signal,\n\tblockInfo: Signal,\n\tsigners: Signal | undefined,\n) {\n\tconst baseFee = block.baseFeePerGas ? block.baseFeePerGas : 0n\n\tblockInfo.value = { ...blockInfo.value, blockNumber: BigInt(block.number ?? 0n), baseFee }\n\tif (provider.value && signers && signers.value.burner) {\n\t\tprovider.value.provider.getBalance(signers.value.burner.address).then((burnerBalance) => signers.value = { ...signers.value, burnerBalance})\n\t}\n}\n"]} \ No newline at end of file diff --git a/docs/js/library/utils.d.ts b/docs/js/library/utils.d.ts new file mode 100644 index 0000000..5d52099 --- /dev/null +++ b/docs/js/library/utils.d.ts @@ -0,0 +1,3 @@ +export declare function addressString(address: bigint): string; +export declare const min: (left: bigint, right: bigint) => bigint; +//# sourceMappingURL=utils.d.ts.map \ No newline at end of file diff --git a/docs/js/library/utils.d.ts.map b/docs/js/library/utils.d.ts.map new file mode 100644 index 0000000..a849a88 --- /dev/null +++ b/docs/js/library/utils.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../ts/library/utils.ts"],"names":[],"mappings":"AAIA,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,eAAO,MAAM,GAAG,SAAU,MAAM,SAAS,MAAM,WAAgC,CAAA"} \ No newline at end of file diff --git a/docs/js/library/utils.js b/docs/js/library/utils.js new file mode 100644 index 0000000..e63418e --- /dev/null +++ b/docs/js/library/utils.js @@ -0,0 +1,8 @@ +import { getAddress } from 'ethers'; +import { EthereumAddress } from '../types/ethereumTypes.js'; +import { serialize } from '../types/types.js'; +export function addressString(address) { + return getAddress(serialize(EthereumAddress, address)); +} +export const min = (left, right) => left < right ? left : right; +//# sourceMappingURL=utils.js.map \ No newline at end of file diff --git a/docs/js/library/utils.js.map b/docs/js/library/utils.js.map new file mode 100644 index 0000000..0a28d97 --- /dev/null +++ b/docs/js/library/utils.js.map @@ -0,0 +1 @@ +{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../ts/library/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AACnC,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAE7C,MAAM,UAAU,aAAa,CAAC,OAAe;IAC5C,OAAO,UAAU,CAAC,SAAS,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC,CAAA;AACvD,CAAC;AAED,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,IAAY,EAAE,KAAa,EAAE,EAAE,CAAC,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAA","sourcesContent":["import { getAddress } from 'ethers'\nimport { EthereumAddress } from '../types/ethereumTypes.js'\nimport { serialize } from '../types/types.js'\n\nexport function addressString(address: bigint): string {\n\treturn getAddress(serialize(EthereumAddress, address))\n}\n\nexport const min = (left: bigint, right: bigint) => left < right ? left : right\n"]} \ No newline at end of file diff --git a/docs/js/stores.d.ts b/docs/js/stores.d.ts new file mode 100644 index 0000000..b66e126 --- /dev/null +++ b/docs/js/stores.d.ts @@ -0,0 +1,36 @@ +import { ProviderStore } from './library/provider.js'; +import { BlockInfo, Bundle, Signers } from './types/types.js'; +export declare function fetchSettingsFromStorage(): readonly { + chainId: bigint; + networkName: string; + relayMode: "relay" | "mempool"; + mempoolSubmitRpcEndpoint: string | undefined; + mempoolSimulationRpcEndpoint: string | undefined; + blocksInFuture: bigint; + priorityFee: bigint; + blockExplorerApi: string | undefined; + blockExplorer: string | undefined; + simulationRelayEndpoint: string | undefined; + submissionRelayEndpoint: string | undefined; +}[]; +export declare function createGlobalState(): { + provider: import("@preact/signals-core").Signal; + blockInfo: import("@preact/signals-core").Signal; + bundle: import("@preact/signals-core").Signal; + bouquetSettings: import("@preact/signals-core").Signal; + signers: import("@preact/signals-core").Signal; + fundingAmountMin: import("@preact/signals-core").ReadonlySignal; +}; +//# sourceMappingURL=stores.d.ts.map \ No newline at end of file diff --git a/docs/js/stores.d.ts.map b/docs/js/stores.d.ts.map new file mode 100644 index 0000000..d699b28 --- /dev/null +++ b/docs/js/stores.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"stores.d.ts","sourceRoot":"","sources":["../ts/stores.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAiC7D,wBAAgB,wBAAwB;;;;;;;;;;;;IAMvC;AAED,wBAAgB,iBAAiB;;;;;;;;;;;;;;;;;;;EAsBhC"} \ No newline at end of file diff --git a/docs/js/stores.js b/docs/js/stores.js new file mode 100644 index 0000000..1ac914b --- /dev/null +++ b/docs/js/stores.js @@ -0,0 +1,66 @@ +import { useComputed, useSignal } from '@preact/signals'; +import { Wallet } from 'ethers'; +import { DEFAULT_NETWORKS, getNetwork } from './constants.js'; +import { getMaxBaseFeeInFutureBlock } from './library/bundleUtils.js'; +import { BouquetSettings, TransactionList } from './types/bouquetTypes.js'; +import { addressString } from './library/utils.js'; +function fetchBurnerWalletFromStorage() { + const burnerPrivateKey = localStorage.getItem('wallet'); + try { + return burnerPrivateKey ? new Wallet(burnerPrivateKey) : new Wallet(Wallet.createRandom().privateKey); + } + catch { + return new Wallet(Wallet.createRandom().privateKey); + } +} +function fetchBundleFromStorage() { + const payload = JSON.parse(localStorage.getItem('payload') ?? 'null'); + if (!payload) + return undefined; + const tryParse = TransactionList.safeParse(payload); + if (!tryParse.success) { + localStorage.removeItem('payload'); + return undefined; + } + const parsed = tryParse.value; + const uniqueToAddresses = [...new Set(parsed.map(({ from }) => from))]; + const containsFundingTx = uniqueToAddresses.includes('FUNDING'); + const uniqueSigners = uniqueToAddresses.filter((address) => address !== 'FUNDING').map(address => addressString(address)); + const totalGas = parsed.reduce((sum, tx) => tx.gasLimit + sum, 0n); + const inputValue = parsed.reduce((sum, tx) => tx.from === 'FUNDING' ? tx.value + sum : sum, 0n); + return { transactions: parsed, containsFundingTx, uniqueSigners, totalGas, inputValue }; +} +export function fetchSettingsFromStorage() { + const nonParsed = localStorage.getItem('bouquetSettings'); + if (nonParsed === null) + return DEFAULT_NETWORKS; + const settings = BouquetSettings.safeParse(JSON.parse(nonParsed)); + if (!settings.success) + return DEFAULT_NETWORKS; + return settings.value; +} +export function createGlobalState() { + const bouquetSettings = useSignal(fetchSettingsFromStorage()); + const provider = useSignal(undefined); + const blockInfo = useSignal({ blockNumber: 0n, baseFee: 0n, priorityFee: 10n ** 9n * 3n }); + const signers = useSignal({ burner: fetchBurnerWalletFromStorage(), burnerBalance: 0n, bundleSigners: {} }); + const bundle = useSignal(fetchBundleFromStorage()); + // Sync burnerWallet to localStorage + signers.subscribe(({ burner }) => { + if (burner) + localStorage.setItem('wallet', burner.privateKey); + else + localStorage.removeItem('wallet'); + }); + const fundingAmountMin = useComputed(() => { + if (!bundle.value) + return 0n; + if (!bundle.value.containsFundingTx) + return 0n; + const network = getNetwork(bouquetSettings.value, provider.value?.chainId || 1n); + const maxBaseFee = getMaxBaseFeeInFutureBlock(blockInfo.value.baseFee, network.blocksInFuture); + return bundle.value.totalGas * (blockInfo.value.priorityFee + maxBaseFee) + bundle.value.inputValue; + }); + return { provider, blockInfo, bundle, bouquetSettings, signers, fundingAmountMin }; +} +//# sourceMappingURL=stores.js.map \ No newline at end of file diff --git a/docs/js/stores.js.map b/docs/js/stores.js.map new file mode 100644 index 0000000..bf9967f --- /dev/null +++ b/docs/js/stores.js.map @@ -0,0 +1 @@ +{"version":3,"file":"stores.js","sourceRoot":"","sources":["../ts/stores.ts"],"names":[],"mappings":"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","sourcesContent":["import { useComputed, useSignal } from '@preact/signals'\nimport { Wallet } from 'ethers'\nimport { DEFAULT_NETWORKS, getNetwork } from './constants.js'\nimport { getMaxBaseFeeInFutureBlock } from './library/bundleUtils.js'\nimport { EthereumAddress } from './types/ethereumTypes.js'\nimport { ProviderStore } from './library/provider.js'\nimport { BlockInfo, Bundle, Signers } from './types/types.js'\nimport { BouquetSettings, TransactionList } from './types/bouquetTypes.js'\nimport { addressString } from './library/utils.js'\n\nfunction fetchBurnerWalletFromStorage(): Wallet {\n\tconst burnerPrivateKey = localStorage.getItem('wallet')\n\ttry {\n\t\treturn burnerPrivateKey ? new Wallet(burnerPrivateKey) : new Wallet(Wallet.createRandom().privateKey)\n\t} catch {\n\t\treturn new Wallet(Wallet.createRandom().privateKey)\n\t}\n}\n\nfunction fetchBundleFromStorage(): Bundle | undefined {\n\tconst payload = JSON.parse(localStorage.getItem('payload') ?? 'null')\n\tif (!payload) return undefined\n\tconst tryParse = TransactionList.safeParse(payload)\n\tif (!tryParse.success) {\n\t\tlocalStorage.removeItem('payload')\n\t\treturn undefined\n\t}\n\tconst parsed = tryParse.value\n\n\tconst uniqueToAddresses = [...new Set(parsed.map(({ from }) => from))]\n\tconst containsFundingTx = uniqueToAddresses.includes('FUNDING')\n\tconst uniqueSigners = uniqueToAddresses.filter((address): address is EthereumAddress => address !== 'FUNDING').map(address => addressString(address))\n\n\tconst totalGas = parsed.reduce((sum, tx) => tx.gasLimit + sum, 0n)\n\tconst inputValue = parsed.reduce((sum, tx) => tx.from === 'FUNDING' ? tx.value + sum : sum, 0n)\n\n\treturn { transactions: parsed, containsFundingTx, uniqueSigners, totalGas, inputValue }\n}\n\nexport function fetchSettingsFromStorage() {\n\tconst nonParsed = localStorage.getItem('bouquetSettings')\n\tif (nonParsed === null) return DEFAULT_NETWORKS\n\tconst settings = BouquetSettings.safeParse(JSON.parse(nonParsed))\n\tif (!settings.success) return DEFAULT_NETWORKS\n\treturn settings.value\n}\n\nexport function createGlobalState() {\n\tconst bouquetSettings = useSignal(fetchSettingsFromStorage())\n\tconst provider = useSignal(undefined)\n\tconst blockInfo = useSignal({ blockNumber: 0n, baseFee: 0n, priorityFee: 10n ** 9n * 3n })\n\tconst signers = useSignal({ burner: fetchBurnerWalletFromStorage(), burnerBalance: 0n, bundleSigners: {} })\n\tconst bundle = useSignal(fetchBundleFromStorage())\n\n\t// Sync burnerWallet to localStorage\n\tsigners.subscribe(({ burner }) => {\n\t\tif (burner) localStorage.setItem('wallet', burner.privateKey)\n\t\telse localStorage.removeItem('wallet')\n\t})\n\t\n\tconst fundingAmountMin = useComputed(() => {\n\t\tif (!bundle.value) return 0n\n\t\tif (!bundle.value.containsFundingTx) return 0n\n\t\tconst network = getNetwork(bouquetSettings.value, provider.value?.chainId || 1n)\n\t\tconst maxBaseFee = getMaxBaseFeeInFutureBlock(blockInfo.value.baseFee, network.blocksInFuture)\n\t\treturn bundle.value.totalGas * (blockInfo.value.priorityFee + maxBaseFee) + bundle.value.inputValue\n\t})\n\n\treturn { provider, blockInfo, bundle, bouquetSettings, signers, fundingAmountMin }\n}\n"]} \ No newline at end of file diff --git a/docs/js/types/apiTypes.d.ts b/docs/js/types/apiTypes.d.ts new file mode 100644 index 0000000..e6464a1 --- /dev/null +++ b/docs/js/types/apiTypes.d.ts @@ -0,0 +1,16 @@ +import * as t from 'funtypes'; +export type EtherscanSourceCodeResult = t.Static; +export declare const EtherscanSourceCodeResult: t.Object<{ + status: t.Union<[t.Literal<"1">, t.Literal<"0">]>; + result: t.ReadonlyTuple<[t.Object<{ + ABI: t.String; + Proxy: t.Union<[t.Literal<"1">, t.Literal<"0">]>; + Implementation: t.Union<[t.Literal<"">, t.ParsedValue]>; + }, false>]>; +}, true>; +export type EtherscanGetABIResult = t.Static; +export declare const EtherscanGetABIResult: t.Object<{ + status: t.Union<[t.Literal<"1">, t.Literal<"0">]>; + result: t.String; +}, true>; +//# sourceMappingURL=apiTypes.d.ts.map \ No newline at end of file diff --git a/docs/js/types/apiTypes.d.ts.map b/docs/js/types/apiTypes.d.ts.map new file mode 100644 index 0000000..8779a5e --- /dev/null +++ b/docs/js/types/apiTypes.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"apiTypes.d.ts","sourceRoot":"","sources":["../../ts/types/apiTypes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,UAAU,CAAA;AAK7B,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,yBAAyB,CAAC,CAAA;AAClF,eAAO,MAAM,yBAAyB;;;;;;;QAOvB,CAAA;AAKf,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,qBAAqB,CAAC,CAAA;AAC1E,eAAO,MAAM,qBAAqB;;;QAGnB,CAAA"} \ No newline at end of file diff --git a/docs/js/types/apiTypes.js b/docs/js/types/apiTypes.js new file mode 100644 index 0000000..da55055 --- /dev/null +++ b/docs/js/types/apiTypes.js @@ -0,0 +1,15 @@ +import * as t from 'funtypes'; +import { EthereumAddress } from './ethereumTypes.js'; +export const EtherscanSourceCodeResult = t.Object({ + status: t.Union(t.Literal('1'), t.Literal('0')), + result: t.ReadonlyTuple(t.Object({ + ABI: t.String, + Proxy: t.Union(t.Literal('1'), t.Literal('0')), + Implementation: t.Union(t.Literal(''), EthereumAddress) + })) +}).asReadonly(); +export const EtherscanGetABIResult = t.Object({ + status: t.Union(t.Literal('1'), t.Literal('0')), + result: t.String +}).asReadonly(); +//# sourceMappingURL=apiTypes.js.map \ No newline at end of file diff --git a/docs/js/types/apiTypes.js.map b/docs/js/types/apiTypes.js.map new file mode 100644 index 0000000..46190d0 --- /dev/null +++ b/docs/js/types/apiTypes.js.map @@ -0,0 +1 @@ +{"version":3,"file":"apiTypes.js","sourceRoot":"","sources":["../../ts/types/apiTypes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,UAAU,CAAA;AAC7B,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AAKpD,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC,MAAM,CAAC;IACjD,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC/C,MAAM,EAAE,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC;QAChC,GAAG,EAAE,CAAC,CAAC,MAAM;QACb,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC9C,cAAc,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,eAAe,CAAC;KACvD,CAAC,CAAC;CACH,CAAC,CAAC,UAAU,EAAE,CAAA;AAMf,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7C,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC/C,MAAM,EAAE,CAAC,CAAC,MAAM;CAChB,CAAC,CAAC,UAAU,EAAE,CAAA","sourcesContent":["import * as t from 'funtypes'\nimport { EthereumAddress } from './ethereumTypes.js'\n\n// Not full result definition, only entries that we consume\n// https://docs.etherscan.io/api-endpoints/contracts#get-contract-source-code-for-verified-contract-source-codes\nexport type EtherscanSourceCodeResult = t.Static\nexport const EtherscanSourceCodeResult = t.Object({\n\tstatus: t.Union(t.Literal('1'), t.Literal('0')),\n\tresult: t.ReadonlyTuple(t.Object({\n\t\tABI: t.String,\n\t\tProxy: t.Union(t.Literal('1'), t.Literal('0')),\n\t\tImplementation: t.Union(t.Literal(''), EthereumAddress)\n\t}))\n}).asReadonly()\n\n\n// Not full result definition, only entries that we consume\n// https://docs.etherscan.io/api-endpoints/contracts#get-contract-abi-for-verified-contract-source-codes\nexport type EtherscanGetABIResult = t.Static\nexport const EtherscanGetABIResult = t.Object({\n\tstatus: t.Union(t.Literal('1'), t.Literal('0')),\n\tresult: t.String\n}).asReadonly()\n\n"]} \ No newline at end of file diff --git a/docs/js/types/bouquetTypes.d.ts b/docs/js/types/bouquetTypes.d.ts new file mode 100644 index 0000000..bdf9d43 --- /dev/null +++ b/docs/js/types/bouquetTypes.d.ts @@ -0,0 +1,51 @@ +import * as funtypes from 'funtypes'; +export type TransactionList = funtypes.Static; +export declare const TransactionList: funtypes.ReadonlyArray, funtypes.Literal<"FUNDING">]>; + to: funtypes.Union<[funtypes.ParsedValue, funtypes.Literal]>; + value: funtypes.ParsedValue; + input: funtypes.ParsedValue]>, Uint8Array>; + chainId: funtypes.ParsedValue; + gasLimit: funtypes.ParsedValue; +}, true>>; +export type PopulatedTransactionList = funtypes.Static; +export declare const PopulatedTransactionList: funtypes.ReadonlyArray; + to: funtypes.Union<[funtypes.ParsedValue, funtypes.Literal]>; + value: funtypes.ParsedValue; + input: funtypes.ParsedValue]>, Uint8Array>; + chainId: funtypes.ParsedValue; + gasLimit: funtypes.ParsedValue; + nonce: funtypes.ParsedValue; + maxFeePerGas: funtypes.ParsedValue; + maxPriorityFeePerGas: funtypes.ParsedValue; +}, true>>; +export type BouquetNetwork = funtypes.Static; +export declare const BouquetNetwork: funtypes.Object<{ + chainId: funtypes.ParsedValue; + networkName: funtypes.String; + relayMode: funtypes.Union<[funtypes.Literal<"relay">, funtypes.Literal<"mempool">]>; + mempoolSubmitRpcEndpoint: funtypes.Union<[funtypes.String, funtypes.Literal]>; + mempoolSimulationRpcEndpoint: funtypes.Union<[funtypes.String, funtypes.Literal]>; + blocksInFuture: funtypes.ParsedValue; + priorityFee: funtypes.ParsedValue; + blockExplorerApi: funtypes.Union<[funtypes.String, funtypes.Literal]>; + blockExplorer: funtypes.Union<[funtypes.String, funtypes.Literal]>; + simulationRelayEndpoint: funtypes.Union<[funtypes.String, funtypes.Literal]>; + submissionRelayEndpoint: funtypes.Union<[funtypes.String, funtypes.Literal]>; +}, false>; +export type BouquetSettings = funtypes.Static; +export declare const BouquetSettings: funtypes.ReadonlyArray; + networkName: funtypes.String; + relayMode: funtypes.Union<[funtypes.Literal<"relay">, funtypes.Literal<"mempool">]>; + mempoolSubmitRpcEndpoint: funtypes.Union<[funtypes.String, funtypes.Literal]>; + mempoolSimulationRpcEndpoint: funtypes.Union<[funtypes.String, funtypes.Literal]>; + blocksInFuture: funtypes.ParsedValue; + priorityFee: funtypes.ParsedValue; + blockExplorerApi: funtypes.Union<[funtypes.String, funtypes.Literal]>; + blockExplorer: funtypes.Union<[funtypes.String, funtypes.Literal]>; + simulationRelayEndpoint: funtypes.Union<[funtypes.String, funtypes.Literal]>; + submissionRelayEndpoint: funtypes.Union<[funtypes.String, funtypes.Literal]>; +}, false>>; +//# sourceMappingURL=bouquetTypes.d.ts.map \ No newline at end of file diff --git a/docs/js/types/bouquetTypes.d.ts.map b/docs/js/types/bouquetTypes.d.ts.map new file mode 100644 index 0000000..699b528 --- /dev/null +++ b/docs/js/types/bouquetTypes.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"bouquetTypes.d.ts","sourceRoot":"","sources":["../../ts/types/bouquetTypes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,UAAU,CAAA;AAGpC,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO,eAAe,CAAC,CAAA;AACrE,eAAO,MAAM,eAAe;;;;;;;SAOZ,CAAA;AAEhB,MAAM,MAAM,wBAAwB,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO,wBAAwB,CAAC,CAAA;AACvF,eAAO,MAAM,wBAAwB;;;;;;;;;;SAUrB,CAAA;AAEhB,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO,cAAc,CAAC,CAAA;AACnE,eAAO,MAAM,cAAc;;;;;;;;;;;;SAYzB,CAAA;AAEF,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO,eAAe,CAAC,CAAA;AACrE,eAAO,MAAM,eAAe;;;;;;;;;;;;UAAyC,CAAA"} \ No newline at end of file diff --git a/docs/js/types/bouquetTypes.js b/docs/js/types/bouquetTypes.js new file mode 100644 index 0000000..8cdaecf --- /dev/null +++ b/docs/js/types/bouquetTypes.js @@ -0,0 +1,36 @@ +import * as funtypes from 'funtypes'; +import { EthereumAddress, EthereumInput, EthereumQuantity } from './ethereumTypes.js'; +export const TransactionList = funtypes.ReadonlyArray(funtypes.Object({ + from: funtypes.Union(EthereumAddress, funtypes.Literal('FUNDING')), + to: funtypes.Union(EthereumAddress, funtypes.Null), + value: EthereumQuantity, + input: EthereumInput, + chainId: EthereumQuantity, + gasLimit: EthereumQuantity +}).asReadonly()); +export const PopulatedTransactionList = funtypes.ReadonlyArray(funtypes.Object({ + from: EthereumAddress, + to: funtypes.Union(EthereumAddress, funtypes.Null), + value: EthereumQuantity, + input: EthereumInput, + chainId: EthereumQuantity, + gasLimit: EthereumQuantity, + nonce: EthereumQuantity, + maxFeePerGas: EthereumQuantity, + maxPriorityFeePerGas: EthereumQuantity +}).asReadonly()); +export const BouquetNetwork = funtypes.Object({ + chainId: EthereumQuantity, + networkName: funtypes.String, + relayMode: funtypes.Union(funtypes.Literal('relay'), funtypes.Literal('mempool')), + mempoolSubmitRpcEndpoint: funtypes.Union(funtypes.String, funtypes.Undefined), + mempoolSimulationRpcEndpoint: funtypes.Union(funtypes.String, funtypes.Undefined), + blocksInFuture: EthereumQuantity, + priorityFee: EthereumQuantity, + blockExplorerApi: funtypes.Union(funtypes.String, funtypes.Undefined), + blockExplorer: funtypes.Union(funtypes.String, funtypes.Undefined), + simulationRelayEndpoint: funtypes.Union(funtypes.String, funtypes.Undefined), + submissionRelayEndpoint: funtypes.Union(funtypes.String, funtypes.Undefined) +}); +export const BouquetSettings = funtypes.ReadonlyArray(BouquetNetwork); +//# sourceMappingURL=bouquetTypes.js.map \ No newline at end of file diff --git a/docs/js/types/bouquetTypes.js.map b/docs/js/types/bouquetTypes.js.map new file mode 100644 index 0000000..51d5ba3 --- /dev/null +++ b/docs/js/types/bouquetTypes.js.map @@ -0,0 +1 @@ +{"version":3,"file":"bouquetTypes.js","sourceRoot":"","sources":["../../ts/types/bouquetTypes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,UAAU,CAAA;AACpC,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAGrF,MAAM,CAAC,MAAM,eAAe,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,MAAM,CAAC;IACrE,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,eAAe,EAAE,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAClE,EAAE,EAAE,QAAQ,CAAC,KAAK,CAAC,eAAe,EAAE,QAAQ,CAAC,IAAI,CAAC;IAClD,KAAK,EAAE,gBAAgB;IACvB,KAAK,EAAE,aAAa;IACpB,OAAO,EAAE,gBAAgB;IACzB,QAAQ,EAAE,gBAAgB;CAC1B,CAAC,CAAC,UAAU,EAAE,CAAC,CAAA;AAGhB,MAAM,CAAC,MAAM,wBAAwB,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,MAAM,CAAC;IAC9E,IAAI,EAAE,eAAe;IACrB,EAAE,EAAE,QAAQ,CAAC,KAAK,CAAC,eAAe,EAAE,QAAQ,CAAC,IAAI,CAAC;IAClD,KAAK,EAAE,gBAAgB;IACvB,KAAK,EAAE,aAAa;IACpB,OAAO,EAAE,gBAAgB;IACzB,QAAQ,EAAE,gBAAgB;IAC1B,KAAK,EAAE,gBAAgB;IACvB,YAAY,EAAE,gBAAgB;IAC9B,oBAAoB,EAAE,gBAAgB;CACtC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAA;AAGhB,MAAM,CAAC,MAAM,cAAc,GAAG,QAAQ,CAAC,MAAM,CAAC;IAC7C,OAAO,EAAE,gBAAgB;IACzB,WAAW,EAAE,QAAQ,CAAC,MAAM;IAC5B,SAAS,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACjF,wBAAwB,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,SAAS,CAAC;IAC7E,4BAA4B,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,SAAS,CAAC;IACjF,cAAc,EAAE,gBAAgB;IAChC,WAAW,EAAE,gBAAgB;IAC7B,gBAAgB,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,SAAS,CAAC;IACrE,aAAa,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,SAAS,CAAC;IAClE,uBAAuB,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,SAAS,CAAC;IAC5E,uBAAuB,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,SAAS,CAAC;CAC5E,CAAC,CAAA;AAGF,MAAM,CAAC,MAAM,eAAe,GAAG,QAAQ,CAAC,aAAa,CAAC,cAAc,CAAC,CAAA","sourcesContent":["import * as funtypes from 'funtypes'\nimport { EthereumAddress, EthereumInput, EthereumQuantity } from './ethereumTypes.js'\n\nexport type TransactionList = funtypes.Static\nexport const TransactionList = funtypes.ReadonlyArray(funtypes.Object({\n\tfrom: funtypes.Union(EthereumAddress, funtypes.Literal('FUNDING')),\n\tto: funtypes.Union(EthereumAddress, funtypes.Null),\n\tvalue: EthereumQuantity,\n\tinput: EthereumInput,\n\tchainId: EthereumQuantity,\n\tgasLimit: EthereumQuantity\n}).asReadonly())\n\nexport type PopulatedTransactionList = funtypes.Static\nexport const PopulatedTransactionList = funtypes.ReadonlyArray(funtypes.Object({\n\tfrom: EthereumAddress,\n\tto: funtypes.Union(EthereumAddress, funtypes.Null),\n\tvalue: EthereumQuantity,\n\tinput: EthereumInput,\n\tchainId: EthereumQuantity,\n\tgasLimit: EthereumQuantity,\n\tnonce: EthereumQuantity,\n\tmaxFeePerGas: EthereumQuantity,\n\tmaxPriorityFeePerGas: EthereumQuantity\n}).asReadonly())\n\nexport type BouquetNetwork = funtypes.Static\nexport const BouquetNetwork = funtypes.Object({\n\tchainId: EthereumQuantity,\n\tnetworkName: funtypes.String,\n\trelayMode: funtypes.Union(funtypes.Literal('relay'), funtypes.Literal('mempool')),\n\tmempoolSubmitRpcEndpoint: funtypes.Union(funtypes.String, funtypes.Undefined),\n\tmempoolSimulationRpcEndpoint: funtypes.Union(funtypes.String, funtypes.Undefined),\n\tblocksInFuture: EthereumQuantity,\n\tpriorityFee: EthereumQuantity,\n\tblockExplorerApi: funtypes.Union(funtypes.String, funtypes.Undefined),\n\tblockExplorer: funtypes.Union(funtypes.String, funtypes.Undefined),\n\tsimulationRelayEndpoint: funtypes.Union(funtypes.String, funtypes.Undefined),\n\tsubmissionRelayEndpoint: funtypes.Union(funtypes.String, funtypes.Undefined)\n})\n\nexport type BouquetSettings = funtypes.Static\nexport const BouquetSettings = funtypes.ReadonlyArray(BouquetNetwork)\n"]} \ No newline at end of file diff --git a/docs/js/types/ethSimulateTypes.d.ts b/docs/js/types/ethSimulateTypes.d.ts new file mode 100644 index 0000000..1e9b994 --- /dev/null +++ b/docs/js/types/ethSimulateTypes.d.ts @@ -0,0 +1,249 @@ +import * as funtypes from 'funtypes'; +export type TransactionType = funtypes.Static; +export declare const TransactionType: funtypes.Union<[funtypes.ParsedValue, "legacy">, funtypes.ParsedValue, "legacy">, funtypes.ParsedValue, "legacy">, funtypes.ParsedValue, "2930">, funtypes.ParsedValue, "1559">, funtypes.ParsedValue, "4844">]>; +export type StateOverrides = funtypes.Static; +export declare const StateOverrides: funtypes.ReadonlyRecord>; + stateDiff: funtypes.ReadonlyRecord>; + nonce: funtypes.ParsedValue; + balance: funtypes.ParsedValue; + code: funtypes.ParsedValue; + movePrecompileToAddress: funtypes.ParsedValue; +}, true>>; +export type BlockCalls = funtypes.Static; +export declare const BlockCalls: funtypes.Intersect<[funtypes.Object<{ + calls: funtypes.ReadonlyArray, "legacy">, funtypes.ParsedValue, "legacy">, funtypes.ParsedValue, "legacy">, funtypes.ParsedValue, "2930">, funtypes.ParsedValue, "1559">, funtypes.ParsedValue, "4844">]>; + from: funtypes.ParsedValue; + nonce: funtypes.ParsedValue; + maxFeePerGas: funtypes.ParsedValue; + maxPriorityFeePerGas: funtypes.ParsedValue; + gas: funtypes.ParsedValue; + to: funtypes.Union<[funtypes.ParsedValue, funtypes.Literal]>; + value: funtypes.ParsedValue; + input: funtypes.ParsedValue]>, Uint8Array>; + chainId: funtypes.ParsedValue; + accessList: funtypes.ReadonlyArray; + storageKeys: funtypes.ReadonlyArray>; + }, true>>; + }, false>>; +}, true>, funtypes.Partial<{ + stateOverrides: funtypes.ReadonlyRecord>; + stateDiff: funtypes.ReadonlyRecord>; + nonce: funtypes.ParsedValue; + balance: funtypes.ParsedValue; + code: funtypes.ParsedValue; + movePrecompileToAddress: funtypes.ParsedValue; + }, true>>; + blockOverrides: funtypes.Partial<{ + number: funtypes.ParsedValue; + prevRandao: funtypes.ParsedValue; + time: funtypes.ParsedValue; + gasLimit: funtypes.ParsedValue; + feeRecipient: funtypes.ParsedValue; + baseFeePerGas: funtypes.ParsedValue; + }, true>; +}, true>]>; +export type ethSimulateV1ParamObject = funtypes.Static; +declare const ethSimulateV1ParamObject: funtypes.Object<{ + blockStateCalls: funtypes.ReadonlyArray, "legacy">, funtypes.ParsedValue, "legacy">, funtypes.ParsedValue, "legacy">, funtypes.ParsedValue, "2930">, funtypes.ParsedValue, "1559">, funtypes.ParsedValue, "4844">]>; + from: funtypes.ParsedValue; + nonce: funtypes.ParsedValue; + maxFeePerGas: funtypes.ParsedValue; + maxPriorityFeePerGas: funtypes.ParsedValue; + gas: funtypes.ParsedValue; + to: funtypes.Union<[funtypes.ParsedValue, funtypes.Literal]>; + value: funtypes.ParsedValue; + input: funtypes.ParsedValue]>, Uint8Array>; + chainId: funtypes.ParsedValue; + accessList: funtypes.ReadonlyArray; + storageKeys: funtypes.ReadonlyArray>; + }, true>>; + }, false>>; + }, true>, funtypes.Partial<{ + stateOverrides: funtypes.ReadonlyRecord>; + stateDiff: funtypes.ReadonlyRecord>; + nonce: funtypes.ParsedValue; + balance: funtypes.ParsedValue; + code: funtypes.ParsedValue; + movePrecompileToAddress: funtypes.ParsedValue; + }, true>>; + blockOverrides: funtypes.Partial<{ + number: funtypes.ParsedValue; + prevRandao: funtypes.ParsedValue; + time: funtypes.ParsedValue; + gasLimit: funtypes.ParsedValue; + feeRecipient: funtypes.ParsedValue; + baseFeePerGas: funtypes.ParsedValue; + }, true>; + }, true>]>>; + traceTransfers: funtypes.Boolean; + validation: funtypes.Boolean; +}, true>; +export type EthSimulateV1Params = funtypes.Static; +export declare const EthSimulateV1Params: funtypes.Object<{ + method: funtypes.Literal<"eth_simulateV1">; + params: funtypes.ReadonlyTuple<[funtypes.Object<{ + blockStateCalls: funtypes.ReadonlyArray, "legacy">, funtypes.ParsedValue, "legacy">, funtypes.ParsedValue, "legacy">, funtypes.ParsedValue, "2930">, funtypes.ParsedValue, "1559">, funtypes.ParsedValue, "4844">]>; + from: funtypes.ParsedValue; + nonce: funtypes.ParsedValue; + maxFeePerGas: funtypes.ParsedValue; + maxPriorityFeePerGas: funtypes.ParsedValue; + gas: funtypes.ParsedValue; + to: funtypes.Union<[funtypes.ParsedValue, funtypes.Literal]>; + value: funtypes.ParsedValue; + input: funtypes.ParsedValue]>, Uint8Array>; + chainId: funtypes.ParsedValue; + accessList: funtypes.ReadonlyArray; + storageKeys: funtypes.ReadonlyArray>; + }, true>>; + }, false>>; + }, true>, funtypes.Partial<{ + stateOverrides: funtypes.ReadonlyRecord>; + stateDiff: funtypes.ReadonlyRecord>; + nonce: funtypes.ParsedValue; + balance: funtypes.ParsedValue; + code: funtypes.ParsedValue; + movePrecompileToAddress: funtypes.ParsedValue; + }, true>>; + blockOverrides: funtypes.Partial<{ + number: funtypes.ParsedValue; + prevRandao: funtypes.ParsedValue; + time: funtypes.ParsedValue; + gasLimit: funtypes.ParsedValue; + feeRecipient: funtypes.ParsedValue; + baseFeePerGas: funtypes.ParsedValue; + }, true>; + }, true>]>>; + traceTransfers: funtypes.Boolean; + validation: funtypes.Boolean; + }, true>, funtypes.Union<[funtypes.ParsedValue, funtypes.ParsedValue, funtypes.Literal<"latest">, funtypes.Literal<"pending">]>]>; +}, true>; +export type EthereumEvent = funtypes.Static; +export declare const EthereumEvent: funtypes.Object<{ + address: funtypes.ParsedValue; + data: funtypes.ParsedValue]>, Uint8Array>; + topics: funtypes.ReadonlyArray>; +}, true>; +export type EthSimulateV1CallResult = funtypes.Static; +export declare const EthSimulateV1CallResult: funtypes.Union<[funtypes.Object<{ + status: funtypes.ParsedValue, "failure">; + returnData: funtypes.ParsedValue; + gasUsed: funtypes.ParsedValue; + error: funtypes.Object<{ + code: funtypes.Number; + message: funtypes.String; + }, true>; +}, true>, funtypes.Object<{ + returnData: funtypes.ParsedValue; + gasUsed: funtypes.ParsedValue; + status: funtypes.ParsedValue, "success">; + logs: funtypes.ReadonlyArray; + data: funtypes.ParsedValue]>, Uint8Array>; + topics: funtypes.ReadonlyArray>; + }, true>, funtypes.Object<{ + logIndex: funtypes.ParsedValue; + blockHash: funtypes.ParsedValue; + blockNumber: funtypes.ParsedValue; + }, true>, funtypes.Partial<{ + transactionHash: funtypes.ParsedValue; + transactionIndex: funtypes.ParsedValue; + }, true>]>>; +}, true>]>; +export type EthSimulateV1CallResults = funtypes.Static; +export declare const EthSimulateV1CallResults: funtypes.ReadonlyArray, "failure">; + returnData: funtypes.ParsedValue; + gasUsed: funtypes.ParsedValue; + error: funtypes.Object<{ + code: funtypes.Number; + message: funtypes.String; + }, true>; +}, true>, funtypes.Object<{ + returnData: funtypes.ParsedValue; + gasUsed: funtypes.ParsedValue; + status: funtypes.ParsedValue, "success">; + logs: funtypes.ReadonlyArray; + data: funtypes.ParsedValue]>, Uint8Array>; + topics: funtypes.ReadonlyArray>; + }, true>, funtypes.Object<{ + logIndex: funtypes.ParsedValue; + blockHash: funtypes.ParsedValue; + blockNumber: funtypes.ParsedValue; + }, true>, funtypes.Partial<{ + transactionHash: funtypes.ParsedValue; + transactionIndex: funtypes.ParsedValue; + }, true>]>>; +}, true>]>>; +export type EthSimulateV1Result = funtypes.Static; +export declare const EthSimulateV1Result: funtypes.ReadonlyArray; + hash: funtypes.ParsedValue; + timestamp: funtypes.ParsedValue; + gasLimit: funtypes.ParsedValue; + gasUsed: funtypes.ParsedValue; + baseFeePerGas: funtypes.ParsedValue; + calls: funtypes.ReadonlyArray, "failure">; + returnData: funtypes.ParsedValue; + gasUsed: funtypes.ParsedValue; + error: funtypes.Object<{ + code: funtypes.Number; + message: funtypes.String; + }, true>; + }, true>, funtypes.Object<{ + returnData: funtypes.ParsedValue; + gasUsed: funtypes.ParsedValue; + status: funtypes.ParsedValue, "success">; + logs: funtypes.ReadonlyArray; + data: funtypes.ParsedValue]>, Uint8Array>; + topics: funtypes.ReadonlyArray>; + }, true>, funtypes.Object<{ + logIndex: funtypes.ParsedValue; + blockHash: funtypes.ParsedValue; + blockNumber: funtypes.ParsedValue; + }, true>, funtypes.Partial<{ + transactionHash: funtypes.ParsedValue; + transactionIndex: funtypes.ParsedValue; + }, true>]>>; + }, true>]>>; +}, true>>; +export type JsonRpcErrorResponse = funtypes.Static; +export declare const JsonRpcErrorResponse: funtypes.Object<{ + jsonrpc: funtypes.Literal<"2.0">; + id: funtypes.Union<[funtypes.String, funtypes.Number]>; + error: funtypes.Object<{ + code: funtypes.Number; + message: funtypes.String; + data: funtypes.Unknown; + }, true>; +}, true>; +export type JsonRpcResponse = funtypes.Static; +export declare const JsonRpcResponse: funtypes.Union<[funtypes.Object<{ + jsonrpc: funtypes.Literal<"2.0">; + id: funtypes.Union<[funtypes.String, funtypes.Number]>; + error: funtypes.Object<{ + code: funtypes.Number; + message: funtypes.String; + data: funtypes.Unknown; + }, true>; +}, true>, funtypes.Object<{ + jsonrpc: funtypes.Literal<"2.0">; + id: funtypes.Union<[funtypes.String, funtypes.Number]>; + result: funtypes.Unknown; +}, true>]>; +export {}; +//# sourceMappingURL=ethSimulateTypes.d.ts.map \ No newline at end of file diff --git a/docs/js/types/ethSimulateTypes.d.ts.map b/docs/js/types/ethSimulateTypes.d.ts.map new file mode 100644 index 0000000..ed072b5 --- /dev/null +++ b/docs/js/types/ethSimulateTypes.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"ethSimulateTypes.d.ts","sourceRoot":"","sources":["../../ts/types/ethSimulateTypes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,UAAU,CAAA;AAuBpC,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO,eAAe,CAAC,CAAA;AACrE,eAAO,MAAM,eAAe,qVAO3B,CAAA;AAiBD,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO,cAAc,CAAC,CAAA;AACnE,eAAO,MAAM,cAAc;;;;;;;SAA4D,CAAA;AAEvF,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO,UAAU,CAAC,CAAA;AAC3D,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAQtB,CAAA;AAED,MAAM,MAAM,wBAAwB,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO,wBAAwB,CAAC,CAAA;AACvF,QAAA,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAI5B,CAAA;AAEF,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAC7E,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAG9B,CAAA;AAEF,MAAM,MAAM,aAAa,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO,aAAa,CAAC,CAAA;AACjE,eAAO,MAAM,aAAa;;;;QAIX,CAAA;AAsCf,MAAM,MAAM,uBAAuB,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO,uBAAuB,CAAC,CAAA;AACrF,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;UAA6E,CAAA;AAEjH,MAAM,MAAM,wBAAwB,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO,wBAAwB,CAAC,CAAA;AACvF,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;WAAkD,CAAA;AAavF,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAC7E,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;SAAmD,CAAA;AASnF,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO,oBAAoB,CAAC,CAAA;AAC/E,eAAO,MAAM,oBAAoB;;;;;;;;QAQlB,CAAA;AAEf,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO,eAAe,CAAC,CAAA;AACrE,eAAO,MAAM,eAAe;;;;;;;;;;;;UAA+D,CAAA"} \ No newline at end of file diff --git a/docs/js/types/ethSimulateTypes.js b/docs/js/types/ethSimulateTypes.js new file mode 100644 index 0000000..7b84c0d --- /dev/null +++ b/docs/js/types/ethSimulateTypes.js @@ -0,0 +1,105 @@ +import * as funtypes from 'funtypes'; +import { EthereumAccessList, EthereumAddress, EthereumBlockTag, EthereumBytes32, EthereumData, EthereumInput, EthereumQuantity, EthereumQuantitySmall, EthereumTimestamp, LiteralConverterParserFactory } from './ethereumTypes.js'; +const AccountOverride = funtypes.ReadonlyPartial({ + state: funtypes.ReadonlyRecord(funtypes.String, EthereumBytes32), + stateDiff: funtypes.ReadonlyRecord(funtypes.String, EthereumBytes32), + nonce: EthereumQuantitySmall, + balance: EthereumQuantity, + code: EthereumData, + movePrecompileToAddress: EthereumAddress, +}); +const BlockOverride = funtypes.Partial({ + number: EthereumQuantity, + prevRandao: EthereumQuantity, + time: EthereumTimestamp, + gasLimit: EthereumQuantitySmall, + feeRecipient: EthereumAddress, + baseFeePerGas: EthereumQuantity, +}).asReadonly(); +export const TransactionType = funtypes.Union(funtypes.Literal(0).withParser(LiteralConverterParserFactory(0, 'legacy')), funtypes.Literal(null).withParser(LiteralConverterParserFactory(null, 'legacy')), funtypes.Literal(undefined).withParser(LiteralConverterParserFactory(undefined, 'legacy')), funtypes.Literal(1).withParser(LiteralConverterParserFactory(1, '2930')), funtypes.Literal(2).withParser(LiteralConverterParserFactory(2, '1559')), funtypes.Literal(3).withParser(LiteralConverterParserFactory(3, '4844'))); +const BlockCall = funtypes.Partial({ + type: TransactionType, + from: EthereumAddress, + nonce: EthereumQuantity, + maxFeePerGas: EthereumQuantity, + maxPriorityFeePerGas: EthereumQuantity, + gas: EthereumQuantity, + to: funtypes.Union(EthereumAddress, funtypes.Null), + value: EthereumQuantity, + input: EthereumInput, + chainId: EthereumQuantity, + accessList: EthereumAccessList, +}); +export const StateOverrides = funtypes.ReadonlyRecord(funtypes.String, AccountOverride); +export const BlockCalls = funtypes.Intersect(funtypes.ReadonlyObject({ + calls: funtypes.ReadonlyArray(BlockCall), +}), funtypes.ReadonlyPartial({ + stateOverrides: StateOverrides, + blockOverrides: BlockOverride, +})); +const ethSimulateV1ParamObject = funtypes.ReadonlyObject({ + blockStateCalls: funtypes.ReadonlyArray(BlockCalls), + traceTransfers: funtypes.Boolean, + validation: funtypes.Boolean, +}); +export const EthSimulateV1Params = funtypes.ReadonlyObject({ + method: funtypes.Literal('eth_simulateV1'), + params: funtypes.ReadonlyTuple(ethSimulateV1ParamObject, EthereumBlockTag), +}); +export const EthereumEvent = funtypes.ReadonlyObject({ + address: EthereumAddress, + data: EthereumInput, + topics: funtypes.ReadonlyArray(EthereumBytes32), +}).asReadonly(); +const CallResultLog = funtypes.Intersect(EthereumEvent, funtypes.ReadonlyObject({ + logIndex: EthereumQuantity, + blockHash: EthereumBytes32, + blockNumber: EthereumQuantity, +}), funtypes.ReadonlyPartial({ + transactionHash: EthereumBytes32, + transactionIndex: EthereumQuantity, +})); +const CallResultLogs = funtypes.ReadonlyArray(CallResultLog); +const EthSimulateCallResultFailure = funtypes.ReadonlyObject({ + status: funtypes.Literal('0x0').withParser(LiteralConverterParserFactory('0x0', 'failure')), + returnData: EthereumData, + gasUsed: EthereumQuantitySmall, + error: funtypes.ReadonlyObject({ + code: funtypes.Number, + message: funtypes.String + }) +}); +const EthSimulateCallResultSuccess = funtypes.ReadonlyObject({ + returnData: EthereumData, + gasUsed: EthereumQuantitySmall, + status: funtypes.Literal('0x1').withParser(LiteralConverterParserFactory('0x1', 'success')), + logs: CallResultLogs +}); +export const EthSimulateV1CallResult = funtypes.Union(EthSimulateCallResultFailure, EthSimulateCallResultSuccess); +export const EthSimulateV1CallResults = funtypes.ReadonlyArray(EthSimulateV1CallResult); +const EthSimulateV1BlockResult = funtypes.ReadonlyObject({ + number: EthereumQuantity, + hash: EthereumBytes32, + timestamp: EthereumQuantity, + gasLimit: EthereumQuantitySmall, + gasUsed: EthereumQuantitySmall, + baseFeePerGas: EthereumQuantity, + calls: EthSimulateV1CallResults, +}); +export const EthSimulateV1Result = funtypes.ReadonlyArray(EthSimulateV1BlockResult); +const JsonRpcSuccessResponse = funtypes.ReadonlyObject({ + jsonrpc: funtypes.Literal('2.0'), + id: funtypes.Union(funtypes.String, funtypes.Number), + result: funtypes.Unknown, +}).asReadonly(); +export const JsonRpcErrorResponse = funtypes.ReadonlyObject({ + jsonrpc: funtypes.Literal('2.0'), + id: funtypes.Union(funtypes.String, funtypes.Number), + error: funtypes.ReadonlyObject({ + code: funtypes.Number, + message: funtypes.String, + data: funtypes.Unknown, + }).asReadonly(), +}).asReadonly(); +export const JsonRpcResponse = funtypes.Union(JsonRpcErrorResponse, JsonRpcSuccessResponse); +//# sourceMappingURL=ethSimulateTypes.js.map \ No newline at end of file diff --git a/docs/js/types/ethSimulateTypes.js.map b/docs/js/types/ethSimulateTypes.js.map new file mode 100644 index 0000000..540897a --- /dev/null +++ b/docs/js/types/ethSimulateTypes.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ethSimulateTypes.js","sourceRoot":"","sources":["../../ts/types/ethSimulateTypes.ts"],"names":[],"mappings":"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","sourcesContent":["import * as funtypes from 'funtypes'\nimport { EthereumAccessList, EthereumAddress, EthereumBlockTag, EthereumBytes32, EthereumData, EthereumInput, EthereumQuantity, EthereumQuantitySmall, EthereumTimestamp, LiteralConverterParserFactory } from './ethereumTypes.js'\n\ntype AccountOverride = funtypes.Static\nconst AccountOverride = funtypes.ReadonlyPartial({\n\tstate: funtypes.ReadonlyRecord(funtypes.String, EthereumBytes32),\n\tstateDiff: funtypes.ReadonlyRecord(funtypes.String, EthereumBytes32),\n\tnonce: EthereumQuantitySmall,\n\tbalance: EthereumQuantity,\n\tcode: EthereumData,\n\tmovePrecompileToAddress: EthereumAddress,\n})\n\ntype BlockOverride = funtypes.Static\nconst BlockOverride = funtypes.Partial({\n number: EthereumQuantity,\n prevRandao: EthereumQuantity,\n time: EthereumTimestamp,\n gasLimit: EthereumQuantitySmall,\n feeRecipient: EthereumAddress,\n baseFeePerGas: EthereumQuantity,\n}).asReadonly()\n\nexport type TransactionType = funtypes.Static\nexport const TransactionType = funtypes.Union(\n\tfuntypes.Literal(0).withParser(LiteralConverterParserFactory(0, 'legacy' as const)),\n\tfuntypes.Literal(null).withParser(LiteralConverterParserFactory(null, 'legacy' as const)),\n\tfuntypes.Literal(undefined).withParser(LiteralConverterParserFactory(undefined, 'legacy' as const)),\n\tfuntypes.Literal(1).withParser(LiteralConverterParserFactory(1, '2930' as const)),\n\tfuntypes.Literal(2).withParser(LiteralConverterParserFactory(2, '1559' as const)),\n\tfuntypes.Literal(3).withParser(LiteralConverterParserFactory(3, '4844' as const)),\n)\n\ntype BlockCall = funtypes.Static\nconst BlockCall = funtypes.Partial({\n\ttype: TransactionType,\n\tfrom: EthereumAddress,\n\tnonce: EthereumQuantity,\n\tmaxFeePerGas: EthereumQuantity,\n\tmaxPriorityFeePerGas: EthereumQuantity,\n\tgas: EthereumQuantity,\n\tto: funtypes.Union(EthereumAddress, funtypes.Null),\n\tvalue: EthereumQuantity,\n\tinput: EthereumInput,\n\tchainId: EthereumQuantity,\n\taccessList: EthereumAccessList,\n})\n\nexport type StateOverrides = funtypes.Static\nexport const StateOverrides = funtypes.ReadonlyRecord(funtypes.String, AccountOverride)\n\nexport type BlockCalls = funtypes.Static\nexport const BlockCalls = funtypes.Intersect(\n\tfuntypes.ReadonlyObject({\n\t\tcalls: funtypes.ReadonlyArray(BlockCall),\n\t}),\n\tfuntypes.ReadonlyPartial({\n\t\tstateOverrides: StateOverrides,\n\t\tblockOverrides: BlockOverride,\n\t})\n)\n\nexport type ethSimulateV1ParamObject = funtypes.Static\nconst ethSimulateV1ParamObject = funtypes.ReadonlyObject({\n\tblockStateCalls: funtypes.ReadonlyArray(BlockCalls),\n\ttraceTransfers: funtypes.Boolean,\n\tvalidation: funtypes.Boolean,\n})\n\nexport type EthSimulateV1Params = funtypes.Static\nexport const EthSimulateV1Params = funtypes.ReadonlyObject({\n\tmethod: funtypes.Literal('eth_simulateV1'),\n\tparams: funtypes.ReadonlyTuple(ethSimulateV1ParamObject, EthereumBlockTag),\n})\n\nexport type EthereumEvent = funtypes.Static\nexport const EthereumEvent = funtypes.ReadonlyObject({\n\taddress: EthereumAddress,\n\tdata: EthereumInput,\n\ttopics: funtypes.ReadonlyArray(EthereumBytes32),\n}).asReadonly()\n\ntype CallResultLog = funtypes.Static\nconst CallResultLog = funtypes.Intersect(\n\tEthereumEvent,\n\tfuntypes.ReadonlyObject({\n\t\tlogIndex: EthereumQuantity,\n\t\tblockHash: EthereumBytes32,\n\t\tblockNumber: EthereumQuantity,\n\t}),\n\tfuntypes.ReadonlyPartial({ // these are not optional in the eth_simulateV1 spec, but they are not standard for logs\n\t\ttransactionHash: EthereumBytes32,\n\t\ttransactionIndex: EthereumQuantity,\n\t})\n)\n\ntype CallResultLogs = funtypes.Static\nconst CallResultLogs = funtypes.ReadonlyArray(CallResultLog)\n\ntype EthSimulateCallResultFailure = funtypes.Static\nconst EthSimulateCallResultFailure = funtypes.ReadonlyObject({\n\t status: funtypes.Literal('0x0').withParser(LiteralConverterParserFactory('0x0', 'failure' as const)),\n\t returnData: EthereumData,\n\t gasUsed: EthereumQuantitySmall,\n\t error: funtypes.ReadonlyObject({\n\t\t code: funtypes.Number,\n\t\t message: funtypes.String\n\t })\n})\n\ntype EthSimulateCallResultSuccess = funtypes.Static\nconst EthSimulateCallResultSuccess = funtypes.ReadonlyObject({\n\treturnData: EthereumData,\n\tgasUsed: EthereumQuantitySmall,\n\tstatus: funtypes.Literal('0x1').withParser(LiteralConverterParserFactory('0x1', 'success' as const)),\n\tlogs: CallResultLogs\n})\n\nexport type EthSimulateV1CallResult = funtypes.Static\nexport const EthSimulateV1CallResult = funtypes.Union(EthSimulateCallResultFailure, EthSimulateCallResultSuccess)\n\nexport type EthSimulateV1CallResults = funtypes.Static\nexport const EthSimulateV1CallResults = funtypes.ReadonlyArray(EthSimulateV1CallResult)\n\ntype EthSimulateV1BlockResult = funtypes.Static\nconst EthSimulateV1BlockResult = funtypes.ReadonlyObject({\n number: EthereumQuantity,\n hash: EthereumBytes32,\n timestamp: EthereumQuantity,\n gasLimit: EthereumQuantitySmall,\n gasUsed: EthereumQuantitySmall,\n baseFeePerGas: EthereumQuantity,\n calls: EthSimulateV1CallResults,\n})\n\nexport type EthSimulateV1Result = funtypes.Static\nexport const EthSimulateV1Result = funtypes.ReadonlyArray(EthSimulateV1BlockResult)\n\ntype JsonRpcSuccessResponse = funtypes.Static\nconst JsonRpcSuccessResponse = funtypes.ReadonlyObject({\n\tjsonrpc: funtypes.Literal('2.0'),\n\tid: funtypes.Union(funtypes.String, funtypes.Number),\n\tresult: funtypes.Unknown,\n}).asReadonly()\n\nexport type JsonRpcErrorResponse = funtypes.Static\nexport const JsonRpcErrorResponse = funtypes.ReadonlyObject({\n\tjsonrpc: funtypes.Literal('2.0'),\n\tid: funtypes.Union(funtypes.String, funtypes.Number),\n\terror: funtypes.ReadonlyObject({\n\t\tcode: funtypes.Number,\n\t\tmessage: funtypes.String,\n\t\tdata: funtypes.Unknown,\n\t}).asReadonly(),\n}).asReadonly()\n\nexport type JsonRpcResponse = funtypes.Static\nexport const JsonRpcResponse = funtypes.Union(JsonRpcErrorResponse, JsonRpcSuccessResponse)\n"]} \ No newline at end of file diff --git a/docs/js/types/ethereumTypes.d.ts b/docs/js/types/ethereumTypes.d.ts new file mode 100644 index 0000000..813e5ed --- /dev/null +++ b/docs/js/types/ethereumTypes.d.ts @@ -0,0 +1,46 @@ +import * as t from 'funtypes'; +export declare const AddressParser: t.ParsedValue['config']; +export declare const BytesParser: t.ParsedValue['config']; +export declare const LiteralConverterParserFactory: (input: TInput, output: TOutput) => t.ParsedValue, TOutput>['config']; +export declare const EthereumQuantity: t.ParsedValue; +export type EthereumQuantity = t.Static; +export declare const EthereumData: t.ParsedValue; +export type EthereumData = t.Static; +export declare const EthereumAddress: t.ParsedValue; +export type EthereumAddress = t.Static; +export declare const EthereumBytes32: t.ParsedValue; +export type EthereumBytes32 = t.Static; +export declare const EthereumInput: t.ParsedValue]>, Uint8Array>; +export type EthereumInput = t.Static; +export declare const EthereumQuantitySmall: t.ParsedValue; +export type EthereumQuantitySmall = t.Static; +export declare const EthereumTimestamp: t.ParsedValue; +export type EthereumTimestamp = t.Static; +export declare const EthereumAccessList: t.ReadonlyArray; + storageKeys: t.ReadonlyArray>; +}, true>>; +export type EthereumAccessList = t.Static; +export declare const EthereumBlockTag: t.Union<[t.ParsedValue, t.ParsedValue, t.Literal<"latest">, t.Literal<"pending">]>; +export type EthereumBlockTag = t.Static; +export type UnionToIntersection = (T extends unknown ? (k: T) => void : never) extends (k: infer I) => void ? I : never; +type ToWireType = T extends t.Intersect ? UnionToIntersection<{ + [I in keyof U]: ToWireType; +}[number]> : T extends t.Union ? { + [I in keyof U]: ToWireType; +}[number] : T extends t.Record ? Record, ToWireType> : T extends t.Partial ? V extends true ? { + readonly [K in keyof U]?: ToWireType; +} : { + [K in keyof U]?: ToWireType; +} : T extends t.Object ? V extends true ? { + readonly [K in keyof U]: ToWireType; +} : { + [K in keyof U]: ToWireType; +} : T extends t.Readonly> ? { + readonly [P in keyof U]: ToWireType; +} : T extends t.Tuple ? { + [P in keyof U]: ToWireType; +} : T extends t.ReadonlyArray ? readonly ToWireType[] : T extends t.Array ? ToWireType[] : T extends t.ParsedValue ? ToWireType : T extends t.Codec ? U : never; +export declare function serialize>(funtype: U, value: T): ToWireType; +export {}; +//# sourceMappingURL=ethereumTypes.d.ts.map \ No newline at end of file diff --git a/docs/js/types/ethereumTypes.d.ts.map b/docs/js/types/ethereumTypes.d.ts.map new file mode 100644 index 0000000..c41ffa3 --- /dev/null +++ b/docs/js/types/ethereumTypes.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"ethereumTypes.d.ts","sourceRoot":"","sources":["../../ts/types/ethereumTypes.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/docs/js/types/ethereumTypes.js b/docs/js/types/ethereumTypes.js new file mode 100644 index 0000000..52823c7 --- /dev/null +++ b/docs/js/types/ethereumTypes.js @@ -0,0 +1,133 @@ +import * as t from 'funtypes'; +const BigIntParser = { + parse: (value) => { + if (!/^0x([a-fA-F0-9]{1,64})$/.test(value)) + return { + success: false, + message: `${value} is not a hex string encoded number.`, + }; + else + return { success: true, value: BigInt(value) }; + }, + serialize: (value) => { + if (typeof value !== 'bigint') + return { success: false, message: `${typeof value} is not a bigint.` }; + return { success: true, value: `0x${value.toString(16)}` }; + }, +}; +export const AddressParser = { + parse: (value) => { + if (!/^0x([a-fA-F0-9]{40})$/.test(value)) + return { + success: false, + message: `${value} is not a hex string encoded address.`, + }; + else + return { success: true, value: BigInt(value) }; + }, + serialize: (value) => { + if (typeof value !== 'bigint') + return { success: false, message: `${typeof value} is not a bigint.` }; + return { success: true, value: `0x${value.toString(16).padStart(40, '0')}` }; + }, +}; +const Bytes32Parser = { + parse: (value) => { + if (!/^0x([a-fA-F0-9]{64})$/.test(value)) + return { + success: false, + message: `${value} is not a hex string encoded 32 byte value.`, + }; + else + return { success: true, value: BigInt(value) }; + }, + serialize: (value) => { + if (typeof value !== 'bigint') + return { success: false, message: `${typeof value} is not a bigint.` }; + return { success: true, value: `0x${value.toString(16).padStart(64, '0')}` }; + }, +}; +export const BytesParser = { + parse: (value) => { + const match = /^(?:0x)?([a-fA-F0-9]*)$/.exec(value); + if (match === null) + return { + success: false, + message: `Expected a hex string encoded byte array with an optional '0x' prefix but received ${value}`, + }; + const normalized = match[1]; + if (normalized.length % 2) + return { + success: false, + message: `Hex string encoded byte array must be an even number of charcaters long.`, + }; + const bytes = new Uint8Array(normalized.length / 2); + for (let i = 0; i < normalized.length; i += 2) { + bytes[i / 2] = Number.parseInt(`${normalized[i]}${normalized[i + 1]}`, 16); + } + return { success: true, value: new Uint8Array(bytes) }; + }, + serialize: (value) => { + if (!(value instanceof Uint8Array)) + return { success: false, message: `${typeof value} is not a Uint8Array.` }; + let result = ''; + for (let i = 0; i < value.length; ++i) { + result += ('0' + value[i].toString(16)).slice(-2); + } + return { success: true, value: `0x${result}` }; + }, +}; +const OptionalBytesParser = { + parse: (value) => BytesParser.parse(value || '0x'), + serialize: (value) => BytesParser.serialize(value || new Uint8Array()), +}; +export const LiteralConverterParserFactory = (input, output) => { + return { + parse: (value) => (value === input ? { success: true, value: output } : { success: false, message: `${value} was expected to be literal.` }), + serialize: (value) => (value === output ? { success: true, value: input } : { success: false, message: `${value} was expected to be literal.` }), + }; +}; +const SmallIntParser = { + parse: value => { + if (!/^0x([a-fA-F0-9]{1,64})$/.test(value)) + return { success: false, message: `${value} is not a hex string encoded number.` }; + if (BigInt(value) >= 2n ** 64n) + return { success: false, message: `${value} must be smaller than 2^64.` }; + return { success: true, value: BigInt(value) }; + }, + serialize: value => { + if (value >= 2n ** 64n) + return { success: false, message: `${value} must be smaller than 2^64.` }; + if (typeof value !== 'bigint') + return { success: false, message: `${typeof value} is not a bigint.` }; + return { success: true, value: `0x${value.toString(16)}` }; + }, +}; +const TimestampParser = { + parse: value => { + if (!/^0x([a-fA-F0-9]{0,8})$/.test(value)) + return { success: false, message: `${value} is not a hex string encoded timestamp.` }; + return { success: true, value: new Date(Number.parseInt(value, 16) * 1000) }; + }, + serialize: value => { + if (!(value instanceof Date)) + return { success: false, message: `${typeof value} is not a Date.` }; + return { success: true, value: `0x${Math.floor(value.valueOf() / 1000).toString(16)}` }; + }, +}; +export const EthereumQuantity = t.String.withParser(BigIntParser); +export const EthereumData = t.String.withParser(BytesParser); +export const EthereumAddress = t.String.withParser(AddressParser); +export const EthereumBytes32 = t.String.withParser(Bytes32Parser); +export const EthereumInput = t.Union(t.String, t.Undefined).withParser(OptionalBytesParser); +export const EthereumQuantitySmall = t.String.withParser(SmallIntParser); +export const EthereumTimestamp = t.String.withParser(TimestampParser); +export const EthereumAccessList = t.ReadonlyArray(t.ReadonlyObject({ + address: EthereumAddress, + storageKeys: t.ReadonlyArray(EthereumBytes32) +}).asReadonly()); +export const EthereumBlockTag = t.Union(EthereumQuantitySmall, EthereumBytes32, t.Literal('latest'), t.Literal('pending')); +export function serialize(funtype, value) { + return funtype.serialize(value); +} +//# sourceMappingURL=ethereumTypes.js.map \ No newline at end of file diff --git a/docs/js/types/ethereumTypes.js.map b/docs/js/types/ethereumTypes.js.map new file mode 100644 index 0000000..9b01ed7 --- /dev/null +++ b/docs/js/types/ethereumTypes.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ethereumTypes.js","sourceRoot":"","sources":["../../ts/types/ethereumTypes.ts"],"names":[],"mappings":"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","sourcesContent":["import * as t from 'funtypes'\n\nconst BigIntParser: t.ParsedValue['config'] = {\n\tparse: (value) => {\n\t\tif (!/^0x([a-fA-F0-9]{1,64})$/.test(value))\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\tmessage: `${value} is not a hex string encoded number.`,\n\t\t\t}\n\t\telse return { success: true, value: BigInt(value) }\n\t},\n\tserialize: (value) => {\n\t\tif (typeof value !== 'bigint') return { success: false, message: `${typeof value} is not a bigint.` }\n\t\treturn { success: true, value: `0x${value.toString(16)}` }\n\t},\n}\n\nexport const AddressParser: t.ParsedValue['config'] = {\n\tparse: (value) => {\n\t\tif (!/^0x([a-fA-F0-9]{40})$/.test(value))\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\tmessage: `${value} is not a hex string encoded address.`,\n\t\t\t}\n\t\telse return { success: true, value: BigInt(value) }\n\t},\n\tserialize: (value) => {\n\t\tif (typeof value !== 'bigint') return { success: false, message: `${typeof value} is not a bigint.` }\n\t\treturn { success: true, value: `0x${value.toString(16).padStart(40, '0')}` }\n\t},\n}\n\nconst Bytes32Parser: t.ParsedValue['config'] = {\n\tparse: (value) => {\n\t\tif (!/^0x([a-fA-F0-9]{64})$/.test(value))\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\tmessage: `${value} is not a hex string encoded 32 byte value.`,\n\t\t\t}\n\t\telse return { success: true, value: BigInt(value) }\n\t},\n\tserialize: (value) => {\n\t\tif (typeof value !== 'bigint') return { success: false, message: `${typeof value} is not a bigint.` }\n\t\treturn { success: true, value: `0x${value.toString(16).padStart(64, '0')}` }\n\t},\n}\n\nexport const BytesParser: t.ParsedValue['config'] = {\n\tparse: (value) => {\n\t\tconst match = /^(?:0x)?([a-fA-F0-9]*)$/.exec(value)\n\t\tif (match === null)\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\tmessage: `Expected a hex string encoded byte array with an optional '0x' prefix but received ${value}`,\n\t\t\t}\n\t\tconst normalized = match[1]\n\t\tif (normalized.length % 2)\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\tmessage: `Hex string encoded byte array must be an even number of charcaters long.`,\n\t\t\t}\n\t\tconst bytes = new Uint8Array(normalized.length / 2)\n\t\tfor (let i = 0; i < normalized.length; i += 2) {\n\t\t\tbytes[i / 2] = Number.parseInt(`${normalized[i]}${normalized[i + 1]}`, 16)\n\t\t}\n\t\treturn { success: true, value: new Uint8Array(bytes) }\n\t},\n\tserialize: (value) => {\n\t\tif (!(value instanceof Uint8Array)) return { success: false, message: `${typeof value} is not a Uint8Array.` }\n\t\tlet result = ''\n\t\tfor (let i = 0; i < value.length; ++i) {\n\t\t\tresult += ('0' + value[i].toString(16)).slice(-2)\n\t\t}\n\t\treturn { success: true, value: `0x${result}` }\n\t},\n}\n\nconst OptionalBytesParser: t.ParsedValue]>, Uint8Array>['config'] = {\n\tparse: (value) => BytesParser.parse(value || '0x'),\n\tserialize: (value) => BytesParser.serialize!(value || new Uint8Array()),\n}\n\nexport const LiteralConverterParserFactory: (input: TInput, output: TOutput) => t.ParsedValue, TOutput>['config'] = (\n\tinput,\n\toutput,\n) => {\n\treturn {\n\t\tparse: (value) => (value === input ? { success: true, value: output } : { success: false, message: `${value} was expected to be literal.` }),\n\t\tserialize: (value) => (value === output ? { success: true, value: input } : { success: false, message: `${value} was expected to be literal.` }),\n\t}\n}\n\nconst SmallIntParser: t.ParsedValue['config'] = {\n\tparse: value => {\n\t\tif (!/^0x([a-fA-F0-9]{1,64})$/.test(value)) return { success: false, message: `${value} is not a hex string encoded number.` }\n\t\tif (BigInt(value) >= 2n**64n) return { success: false, message: `${value} must be smaller than 2^64.` }\n\t\treturn { success: true, value: BigInt(value) }\n\t},\n\tserialize: value => {\n\t\tif (value >= 2n**64n) return { success: false, message: `${value} must be smaller than 2^64.` }\n\t\tif (typeof value !== 'bigint') return { success: false, message: `${typeof value} is not a bigint.`}\n\t\treturn { success: true, value: `0x${value.toString(16)}` }\n\t},\n}\n\nconst TimestampParser: t.ParsedValue['config'] = {\n\tparse: value => {\n\t\tif (!/^0x([a-fA-F0-9]{0,8})$/.test(value)) return { success: false, message: `${value} is not a hex string encoded timestamp.` }\n\t\treturn { success: true, value: new Date(Number.parseInt(value, 16) * 1000) }\n\t},\n\tserialize: value => {\n\t\tif (!(value instanceof Date)) return { success: false, message: `${typeof value} is not a Date.`}\n\t\treturn { success: true, value: `0x${Math.floor(value.valueOf() / 1000).toString(16)}` }\n\t},\n}\n\nexport const EthereumQuantity = t.String.withParser(BigIntParser)\nexport type EthereumQuantity = t.Static\n\nexport const EthereumData = t.String.withParser(BytesParser)\nexport type EthereumData = t.Static\n\nexport const EthereumAddress = t.String.withParser(AddressParser)\nexport type EthereumAddress = t.Static\n\nexport const EthereumBytes32 = t.String.withParser(Bytes32Parser)\nexport type EthereumBytes32 = t.Static\n\nexport const EthereumInput = t.Union(t.String, t.Undefined).withParser(OptionalBytesParser)\nexport type EthereumInput = t.Static\n\nexport const EthereumQuantitySmall = t.String.withParser(SmallIntParser)\nexport type EthereumQuantitySmall = t.Static\n\nexport const EthereumTimestamp = t.String.withParser(TimestampParser)\nexport type EthereumTimestamp = t.Static\n\nexport const EthereumAccessList = t.ReadonlyArray(\n\tt.ReadonlyObject({\n\t\taddress: EthereumAddress,\n\t\tstorageKeys: t.ReadonlyArray(EthereumBytes32)\n\t}).asReadonly()\n)\nexport type EthereumAccessList = t.Static\n\nexport const EthereumBlockTag = t.Union(EthereumQuantitySmall, EthereumBytes32, t.Literal('latest'), t.Literal('pending'))\nexport type EthereumBlockTag = t.Static\n\nexport type UnionToIntersection = (T extends unknown ? (k: T) => void : never) extends (k: infer I) => void ? I : never\n\ntype ToWireType =\n\tT extends t.Intersect ? UnionToIntersection<{ [I in keyof U]: ToWireType }[number]>\n\t: T extends t.Union ? { [I in keyof U]: ToWireType }[number]\n\t: T extends t.Record ? Record, ToWireType>\n\t: T extends t.Partial ? V extends true ? { readonly [K in keyof U]?: ToWireType } : { [K in keyof U]?: ToWireType }\n\t: T extends t.Object ? V extends true ? { readonly [K in keyof U]: ToWireType } : { [K in keyof U]: ToWireType }\n\t: T extends t.Readonly> ? { readonly [P in keyof U]: ToWireType}\n\t: T extends t.Tuple ? { [P in keyof U]: ToWireType}\n\t: T extends t.ReadonlyArray ? readonly ToWireType[]\n\t: T extends t.Array ? ToWireType[]\n\t: T extends t.ParsedValue ? ToWireType\n\t: T extends t.Codec ? U\n\t: never\n\nexport function serialize>(funtype: U, value: T) {\n\treturn funtype.serialize(value) as ToWireType\n}\n"]} \ No newline at end of file diff --git a/docs/js/types/interceptorTypes.d.ts b/docs/js/types/interceptorTypes.d.ts new file mode 100644 index 0000000..99d5982 --- /dev/null +++ b/docs/js/types/interceptorTypes.d.ts @@ -0,0 +1,74 @@ +import * as t from 'funtypes'; +export type GetSimulationStackReply = t.Static; +export declare const GetSimulationStackReply: t.ReadonlyArray, "legacy">, t.ParsedValue, "legacy">]>; + from: t.ParsedValue; + nonce: t.ParsedValue; + gasPrice: t.ParsedValue; + gas: t.ParsedValue; + to: t.Union<[t.ParsedValue, t.Literal]>; + value: t.ParsedValue; + input: t.ParsedValue]>, Uint8Array>; +}, true>, t.Partial<{ + chainId: t.ParsedValue; +}, true>]>, t.Intersect<[t.Object<{ + type: t.ParsedValue, "2930">; + from: t.ParsedValue; + nonce: t.ParsedValue; + gasPrice: t.ParsedValue; + gas: t.ParsedValue; + to: t.Union<[t.ParsedValue, t.Literal]>; + value: t.ParsedValue; + input: t.ParsedValue]>, Uint8Array>; + chainId: t.ParsedValue; +}, true>, t.Partial<{ + accessList: t.ReadonlyArray; + storageKeys: t.ReadonlyArray>; + }, true>>; +}, false>]>, t.Intersect<[t.Object<{ + type: t.ParsedValue, "1559">; + from: t.ParsedValue; + nonce: t.ParsedValue; + maxFeePerGas: t.ParsedValue; + maxPriorityFeePerGas: t.ParsedValue; + gas: t.ParsedValue; + to: t.Union<[t.ParsedValue, t.Literal]>; + value: t.ParsedValue; + input: t.ParsedValue]>, Uint8Array>; + chainId: t.ParsedValue; +}, true>, t.Partial<{ + accessList: t.ReadonlyArray; + storageKeys: t.ReadonlyArray>; + }, true>>; +}, false>]>]>, t.Union<[t.Object<{ + statusCode: t.ParsedValue, "success">; + gasSpent: t.ParsedValue; + returnValue: t.ParsedValue; + events: t.ReadonlyArray; + data: t.ParsedValue]>, Uint8Array>; + topics: t.ReadonlyArray>; + }, true>>; + balanceChanges: t.ReadonlyArray; + before: t.ParsedValue; + after: t.ParsedValue; + }, true>>; +}, true>, t.Object<{ + statusCode: t.ParsedValue, "failure">; + gasSpent: t.ParsedValue; + error: t.ParsedValue; + returnValue: t.ParsedValue; +}, true>]>, t.Object<{ + realizedGasPrice: t.ParsedValue; + gasLimit: t.ParsedValue; + maxPriorityFeePerGas: t.ParsedValue; + balanceChanges: t.ReadonlyArray; + before: t.ParsedValue; + after: t.ParsedValue; + }, true>>; +}, true>]>>; +//# sourceMappingURL=interceptorTypes.d.ts.map \ No newline at end of file diff --git a/docs/js/types/interceptorTypes.d.ts.map b/docs/js/types/interceptorTypes.d.ts.map new file mode 100644 index 0000000..284b761 --- /dev/null +++ b/docs/js/types/interceptorTypes.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"interceptorTypes.d.ts","sourceRoot":"","sources":["../../ts/types/interceptorTypes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,UAAU,CAAA;AA4H7B,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,uBAAuB,CAAC,CAAA;AAC9E,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAWnC,CAAA"} \ No newline at end of file diff --git a/docs/js/types/interceptorTypes.js b/docs/js/types/interceptorTypes.js new file mode 100644 index 0000000..736b6d1 --- /dev/null +++ b/docs/js/types/interceptorTypes.js @@ -0,0 +1,96 @@ +import * as t from 'funtypes'; +import { EthereumAddress, EthereumQuantity, EthereumData, EthereumBytes32, LiteralConverterParserFactory, EthereumInput, BytesParser } from './ethereumTypes.js'; +const EthereumAccessList = t.ReadonlyArray(t + .Object({ + address: EthereumAddress, + storageKeys: t.ReadonlyArray(EthereumBytes32), +}) + .asReadonly()); +const EthereumUnsignedTransactionLegacy = t.Intersect(t.Object({ + type: t.Union(t.Literal('0x0').withParser(LiteralConverterParserFactory('0x0', 'legacy')), t.Literal(undefined).withParser(LiteralConverterParserFactory(undefined, 'legacy'))), + from: EthereumAddress, + nonce: EthereumQuantity, + gasPrice: EthereumQuantity, + gas: EthereumQuantity, + to: t.Union(EthereumAddress, t.Null), + value: EthereumQuantity, + input: EthereumInput, +}).asReadonly(), t.Partial({ + chainId: EthereumQuantity, +}).asReadonly()); +const EthereumUnsignedTransaction2930 = t.Intersect(t.Object({ + type: t.Literal('0x1').withParser(LiteralConverterParserFactory('0x1', '2930')), + from: EthereumAddress, + nonce: EthereumQuantity, + gasPrice: EthereumQuantity, + gas: EthereumQuantity, + to: t.Union(EthereumAddress, t.Null), + value: EthereumQuantity, + input: EthereumInput, + chainId: EthereumQuantity, +}).asReadonly(), t.Partial({ + accessList: EthereumAccessList, +})); +const EthereumUnsignedTransaction1559 = t.Intersect(t.Object({ + type: t.Literal('0x2').withParser(LiteralConverterParserFactory('0x2', '1559')), + from: EthereumAddress, + nonce: EthereumQuantity, + maxFeePerGas: EthereumQuantity, + maxPriorityFeePerGas: EthereumQuantity, + gas: EthereumQuantity, + to: t.Union(EthereumAddress, t.Null), + value: EthereumQuantity, + input: EthereumInput, + chainId: EthereumQuantity, +}).asReadonly(), t.Partial({ + accessList: EthereumAccessList, +})); +const EthereumUnsignedTransaction = t.Union(EthereumUnsignedTransactionLegacy, EthereumUnsignedTransaction2930, EthereumUnsignedTransaction1559); +const RevertErrorParser = { + parse: (value) => { + if (!value.startsWith('Reverted ')) + return { success: true, value }; + const parseResult = BytesParser.parse(value.slice('Reverted '.length)); + if (!parseResult.success) + return parseResult; + const decoded = new TextDecoder().decode(parseResult.value); + return { success: true, value: decoded }; + }, + serialize: (value) => { + const encoded = new TextEncoder().encode(value); + const serializationResult = BytesParser.serialize(encoded); + if (!serializationResult.success) + return serializationResult; + return { success: true, value: `Reverted ${serializationResult.value}` }; + } +}; +const MulticallResponseEventLog = t.Object({ + loggersAddress: EthereumAddress, + data: EthereumInput, + topics: t.ReadonlyArray(EthereumBytes32), +}).asReadonly(); +const MulticallResponseEventLogs = t.ReadonlyArray(MulticallResponseEventLog); +const EthBalanceChanges = t.ReadonlyArray(t.Object({ + address: EthereumAddress, + before: EthereumQuantity, + after: EthereumQuantity, +}).asReadonly()); +const SingleMulticallResponse = t.Union(t.Object({ + statusCode: t.Literal(1).withParser(LiteralConverterParserFactory(1, 'success')), + gasSpent: EthereumQuantity, + returnValue: EthereumData, + events: MulticallResponseEventLogs, + balanceChanges: EthBalanceChanges, +}).asReadonly(), t.Object({ + statusCode: t.Literal(0).withParser(LiteralConverterParserFactory(0, 'failure')), + gasSpent: EthereumQuantity, + error: t.String.withParser(RevertErrorParser), + returnValue: EthereumData, +}).asReadonly()); +export const GetSimulationStackReply = t.ReadonlyArray(t.Intersect(EthereumUnsignedTransaction, SingleMulticallResponse, t.Object({ + realizedGasPrice: EthereumQuantity, + gasLimit: EthereumQuantity, + maxPriorityFeePerGas: EthereumQuantity, + balanceChanges: EthBalanceChanges +}).asReadonly())); +//# sourceMappingURL=interceptorTypes.js.map \ No newline at end of file diff --git a/docs/js/types/interceptorTypes.js.map b/docs/js/types/interceptorTypes.js.map new file mode 100644 index 0000000..cc420a9 --- /dev/null +++ b/docs/js/types/interceptorTypes.js.map @@ -0,0 +1 @@ +{"version":3,"file":"interceptorTypes.js","sourceRoot":"","sources":["../../ts/types/interceptorTypes.ts"],"names":[],"mappings":"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","sourcesContent":["import * as t from 'funtypes'\nimport { EthereumAddress, EthereumQuantity, EthereumData, EthereumBytes32, LiteralConverterParserFactory, EthereumInput, BytesParser } from './ethereumTypes.js'\n\nconst EthereumAccessList = t.ReadonlyArray(\n\tt\n\t\t.Object({\n\t\t\taddress: EthereumAddress,\n\t\t\tstorageKeys: t.ReadonlyArray(EthereumBytes32),\n\t\t})\n\t\t.asReadonly(),\n)\ntype EthereumAccessList = t.Static\n\ntype EthereumUnsignedTransactionLegacy = t.Static\nconst EthereumUnsignedTransactionLegacy = t.Intersect(\n\tt.Object({\n\t\ttype: t.Union(\n\t\t\tt.Literal('0x0').withParser(LiteralConverterParserFactory('0x0', 'legacy' as const)),\n\t\t\tt.Literal(undefined).withParser(LiteralConverterParserFactory(undefined, 'legacy' as const)),\n\t\t),\n\t\tfrom: EthereumAddress,\n\t\tnonce: EthereumQuantity,\n\t\tgasPrice: EthereumQuantity,\n\t\tgas: EthereumQuantity,\n\t\tto: t.Union(EthereumAddress, t.Null),\n\t\tvalue: EthereumQuantity,\n\t\tinput: EthereumInput,\n\t}).asReadonly(),\n\tt.Partial({\n\t\tchainId: EthereumQuantity,\n\t}).asReadonly()\n)\n\ntype EthereumUnsignedTransaction2930 = t.Static\nconst EthereumUnsignedTransaction2930 = t.Intersect(\n\tt.Object({\n\t\ttype: t.Literal('0x1').withParser(LiteralConverterParserFactory('0x1', '2930' as const)),\n\t\tfrom: EthereumAddress,\n\t\tnonce: EthereumQuantity,\n\t\tgasPrice: EthereumQuantity,\n\t\tgas: EthereumQuantity,\n\t\tto: t.Union(EthereumAddress, t.Null),\n\t\tvalue: EthereumQuantity,\n\t\tinput: EthereumInput,\n\t\tchainId: EthereumQuantity,\n\t}).asReadonly(),\n\tt.Partial({\n\t\taccessList: EthereumAccessList,\n\t})\n)\n\ntype EthereumUnsignedTransaction1559 = t.Static\nconst EthereumUnsignedTransaction1559 = t.Intersect(\n\tt.Object({\n\t\ttype: t.Literal('0x2').withParser(LiteralConverterParserFactory('0x2', '1559' as const)),\n\t\tfrom: EthereumAddress,\n\t\tnonce: EthereumQuantity,\n\t\tmaxFeePerGas: EthereumQuantity,\n\t\tmaxPriorityFeePerGas: EthereumQuantity,\n\t\tgas: EthereumQuantity,\n\t\tto: t.Union(EthereumAddress, t.Null),\n\t\tvalue: EthereumQuantity,\n\t\tinput: EthereumInput,\n\t\tchainId: EthereumQuantity,\n\t}).asReadonly(),\n\tt.Partial({\n\t\taccessList: EthereumAccessList,\n\t})\n)\ntype EthereumUnsignedTransaction = t.Static\nconst EthereumUnsignedTransaction = t.Union(EthereumUnsignedTransactionLegacy, EthereumUnsignedTransaction2930, EthereumUnsignedTransaction1559)\n\nconst RevertErrorParser: t.ParsedValue['config'] = {\n\tparse: (value) => {\n\t\tif (!value.startsWith('Reverted ')) return { success: true, value }\n\t\tconst parseResult = BytesParser.parse(value.slice('Reverted '.length))\n\t\tif (!parseResult.success) return parseResult\n\t\tconst decoded = new TextDecoder().decode(parseResult.value)\n\t\treturn { success: true, value: decoded }\n\t},\n\tserialize: (value) => {\n\t\tconst encoded = new TextEncoder().encode(value)\n\t\tconst serializationResult = BytesParser.serialize!(encoded)\n\t\tif (!serializationResult.success) return serializationResult\n\t\treturn { success: true, value: `Reverted ${serializationResult.value}` }\n\t}\n}\n\ntype MulticallResponseEventLog = t.Static\nconst MulticallResponseEventLog = t.Object({\n\tloggersAddress: EthereumAddress,\n\tdata: EthereumInput,\n\ttopics: t.ReadonlyArray(EthereumBytes32),\n}).asReadonly()\n\ntype MulticallResponseEventLogs = t.Static\nconst MulticallResponseEventLogs = t.ReadonlyArray(MulticallResponseEventLog)\n\ntype EthBalanceChanges = t.Static\nconst EthBalanceChanges = t.ReadonlyArray(\n\tt.Object({\n\t\taddress: EthereumAddress,\n\t\tbefore: EthereumQuantity,\n\t\tafter: EthereumQuantity,\n\t}).asReadonly()\n)\n\ntype SingleMulticallResponse = t.Static\nconst SingleMulticallResponse = t.Union(\n\tt.Object({\n\t\tstatusCode: t.Literal(1).withParser(LiteralConverterParserFactory(1, 'success' as const)),\n\t\tgasSpent: EthereumQuantity,\n\t\treturnValue: EthereumData,\n\t\tevents: MulticallResponseEventLogs,\n\t\tbalanceChanges: EthBalanceChanges,\n\t}).asReadonly(),\n\tt.Object({\n\t\tstatusCode: t.Literal(0).withParser(LiteralConverterParserFactory(0, 'failure' as const)),\n\t\tgasSpent: EthereumQuantity,\n\t\terror: t.String.withParser(RevertErrorParser),\n\t\treturnValue: EthereumData,\n\t}).asReadonly()\n)\n\nexport type GetSimulationStackReply = t.Static\nexport const GetSimulationStackReply = t.ReadonlyArray(\n\tt.Intersect(\n\t\tEthereumUnsignedTransaction,\n\t\tSingleMulticallResponse,\n\t\tt.Object({\n\t\t\trealizedGasPrice: EthereumQuantity,\n\t\t\tgasLimit: EthereumQuantity,\n\t\t\tmaxPriorityFeePerGas: EthereumQuantity,\n\t\t\tbalanceChanges: EthBalanceChanges\n\t\t}).asReadonly()\n\t)\n)\n"]} \ No newline at end of file diff --git a/docs/js/types/types.d.ts b/docs/js/types/types.d.ts new file mode 100644 index 0000000..4787181 --- /dev/null +++ b/docs/js/types/types.d.ts @@ -0,0 +1,63 @@ +import { Wallet } from 'ethers'; +import * as t from 'funtypes'; +import { TransactionList } from './bouquetTypes.js'; +export declare function serialize>(funtype: U, value: T): ToWireType; +export type UnionToIntersection = (T extends unknown ? (k: T) => void : never) extends (k: infer I) => void ? I : never; +export type ToWireType = T extends t.Intersect ? UnionToIntersection<{ + [I in keyof U]: ToWireType; +}[number]> : T extends t.Union ? { + [I in keyof U]: ToWireType; +}[number] : T extends t.Record ? Record, ToWireType> : T extends t.Partial ? V extends true ? { + readonly [K in keyof U]?: ToWireType; +} : { + [K in keyof U]?: ToWireType; +} : T extends t.Object ? V extends true ? { + readonly [K in keyof U]: ToWireType; +} : { + [K in keyof U]: ToWireType; +} : T extends t.Readonly> ? { + readonly [P in keyof U]: ToWireType; +} : T extends t.Tuple ? { + [P in keyof U]: ToWireType; +} : T extends t.ReadonlyArray ? readonly ToWireType[] : T extends t.Array ? ToWireType[] : T extends t.ParsedValue ? ToWireType : T extends t.Codec ? U : never; +export type HexString = `0x${string}`; +interface Eip1193Provider { + request(request: { + method: string; + params?: Array | Record; + }): Promise; + on(eventName: string | symbol, listener: (...args: any[]) => void): this; + removeListener(eventName: string | symbol, listener: (...args: any[]) => void): this; +} +declare global { + interface Window { + ethereum?: Eip1193Provider; + } +} +export type BlockInfo = { + blockNumber: bigint; + baseFee: bigint; + priorityFee: bigint; +}; +export type Bundle = { + transactions: TransactionList; + containsFundingTx: boolean; + totalGas: bigint; + inputValue: bigint; + uniqueSigners: string[]; +}; +export type Signers = { + burner: Wallet | undefined; + burnerBalance: bigint; + bundleSigners: { + [account: string]: Wallet; + }; +}; +export type PromiseState = 'pending' | 'resolved' | 'rejected'; +export type BundleInfo = { + hash: string; + state: PromiseState; + details: string; +}; +export {}; +//# sourceMappingURL=types.d.ts.map \ No newline at end of file diff --git a/docs/js/types/types.d.ts.map b/docs/js/types/types.d.ts.map new file mode 100644 index 0000000..55d1e6b --- /dev/null +++ b/docs/js/types/types.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../ts/types/types.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/docs/js/types/types.js b/docs/js/types/types.js new file mode 100644 index 0000000..89c67b8 --- /dev/null +++ b/docs/js/types/types.js @@ -0,0 +1,4 @@ +export function serialize(funtype, value) { + return funtype.serialize(value); +} +//# sourceMappingURL=types.js.map \ No newline at end of file diff --git a/docs/js/types/types.js.map b/docs/js/types/types.js.map new file mode 100644 index 0000000..1062577 --- /dev/null +++ b/docs/js/types/types.js.map @@ -0,0 +1 @@ +{"version":3,"file":"types.js","sourceRoot":"","sources":["../../ts/types/types.ts"],"names":[],"mappings":"AAIA,MAAM,UAAU,SAAS,CAA0B,OAAU,EAAE,KAAQ;IACtE,OAAO,OAAO,CAAC,SAAS,CAAC,KAAK,CAAkB,CAAA;AACjD,CAAC","sourcesContent":["import { Wallet } from 'ethers'\nimport * as t from 'funtypes'\nimport { TransactionList } from './bouquetTypes.js'\n\nexport function serialize>(funtype: U, value: T) {\n\treturn funtype.serialize(value) as ToWireType\n}\n\nexport type UnionToIntersection = (T extends unknown ? (k: T) => void : never) extends (k: infer I) => void ? I : never\n\nexport type ToWireType = T extends t.Intersect\n\t? UnionToIntersection<{ [I in keyof U]: ToWireType }[number]>\n\t: T extends t.Union\n\t? { [I in keyof U]: ToWireType }[number]\n\t: T extends t.Record\n\t? Record, ToWireType>\n\t: T extends t.Partial\n\t? V extends true\n\t? { readonly [K in keyof U]?: ToWireType }\n\t: { [K in keyof U]?: ToWireType }\n\t: T extends t.Object\n\t? V extends true\n\t? { readonly [K in keyof U]: ToWireType }\n\t: { [K in keyof U]: ToWireType }\n\t: T extends t.Readonly>\n\t? { readonly [P in keyof U]: ToWireType }\n\t: T extends t.Tuple\n\t? { [P in keyof U]: ToWireType }\n\t: T extends t.ReadonlyArray\n\t? readonly ToWireType[]\n\t: T extends t.Array\n\t? ToWireType[]\n\t: T extends t.ParsedValue\n\t? ToWireType\n\t: T extends t.Codec\n\t? U\n\t: never\n\nexport type HexString = `0x${string}`\n\ninterface Eip1193Provider {\n\trequest(request: { method: string; params?: Array | Record }): Promise\n\ton(eventName: string | symbol, listener: (...args: any[]) => void): this\n\tremoveListener(eventName: string | symbol, listener: (...args: any[]) => void): this\n}\n\ndeclare global {\n\tinterface Window {\n\t\tethereum?: Eip1193Provider\n\t}\n}\n\nexport type BlockInfo = { blockNumber: bigint; baseFee: bigint; priorityFee: bigint }\nexport type Bundle = { transactions: TransactionList; containsFundingTx: boolean; totalGas: bigint; inputValue: bigint; uniqueSigners: string[] }\nexport type Signers = { burner: Wallet | undefined; burnerBalance: bigint; bundleSigners: { [account: string]: Wallet } }\n\nexport type PromiseState = 'pending' | 'resolved' | 'rejected'\nexport type BundleInfo = { hash: string; state: PromiseState; details: string }\n"]} \ No newline at end of file diff --git a/docs/ts/components/App.tsx b/docs/ts/components/App.tsx new file mode 100644 index 0000000..1f8a1fa --- /dev/null +++ b/docs/ts/components/App.tsx @@ -0,0 +1,41 @@ +import { Import } from './Import.js' +import { Submit } from './Submit.js' +import { Button } from './Button.js' +import { Transactions } from './Transactions.js' +import { connectBrowserProvider } from '../library/provider.js' +import { Navbar } from './Navbar.js' +import { createGlobalState } from '../stores.js' +import { Footer } from './Footer.js' +import { ConfigureKeys } from './ConfigureKeys.js' +import { ConfigureFunding } from './ConfigureFunding.js' + +export function App() { + const state = createGlobalState() + + return ( +
+ +
+ {!state.provider.value && state.bundle.value ? ( +
+

Welcome Back

+ +
+ ) : ( + <> + + {state.bundle.value ? : null} +

2. Configure

+ {state.bundle.value ? (<>) :

No transactions imported yet.

} + + + )} +
+
+
+ ) +} diff --git a/docs/ts/components/Blockie.tsx b/docs/ts/components/Blockie.tsx new file mode 100644 index 0000000..4ae3eae --- /dev/null +++ b/docs/ts/components/Blockie.tsx @@ -0,0 +1,197 @@ +import { JSX } from 'preact/jsx-runtime' +import { ReadonlySignal, Signal, useSignalEffect } from '@preact/signals' +import { useAsyncState } from '../library/asyncState.js' +import { DataURLCache } from '../library/DataURLCache.js' + +const dataURLCache = new DataURLCache() + +class Future implements PromiseLike { + private promise: Promise + private resolveFunction: (value: T | PromiseLike) => void + private rejectFunction: (reason: Error) => void + + constructor() { + let resolveFunction: (value: T | PromiseLike) => void + let rejectFunction: (reason: Error) => void + this.promise = new Promise((resolve: (value: T | PromiseLike) => void, reject: (reason: Error) => void) => { + resolveFunction = resolve + rejectFunction = reject + }) + // the function passed to the Promise constructor is called before the constructor returns, so we can be sure the resolve and reject functions have been set by here even if the compiler can't verify + this.resolveFunction = resolveFunction! + this.rejectFunction = rejectFunction! + } + + public get asPromise() { return this.promise } + + public readonly then = ( + onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, + onrejected?: ((reason: Error) => TResult2 | PromiseLike) | undefined | null + ): PromiseLike => { + return this.promise.then(onfulfilled, onrejected) + } + + public readonly resolve = (value: T | PromiseLike) => { + this.resolveFunction!(value) + } + + public readonly reject = (reason: Error) => { + this.rejectFunction!(reason) + } +} + +function addressString(address: bigint) { + return `0x${address.toString(16).padStart(40, '0')}` +} + +interface BlockieProps { + address: Signal | ReadonlySignal, + scale?: Signal, + style?: JSX.CSSProperties +} + +function generateIdenticon(address: bigint, scale: number, canvasRef: HTMLCanvasElement) { + // NOTE -- Majority of this code is referenced from: https://github.com/alexvandesande/blockies + // Mostly to ensure congruence to Ethereum Mist's Identicons + + // The random number is a js implementation of the Xorshift PRNG + const randseed = new Array(4) // Xorshift: [x, y, z, w] 32 bit values + + function seedrand(seed: string) { + for (let i = 0; i < randseed.length; i++) { + randseed[i] = 0 + } + for (let i = 0; i < seed.length; i++) { + randseed[i % 4] = ((randseed[i % 4] << 5) - randseed[i % 4]) + seed.charCodeAt(i) + } + } + + function rand() { + // based on Java's String.hashCode(), expanded to 4 32bit values + const t = randseed[0] ^ (randseed[0] << 11) + + randseed[0] = randseed[1] + randseed[1] = randseed[2] + randseed[2] = randseed[3] + randseed[3] = (randseed[3] ^ (randseed[3] >> 19) ^ t ^ (t >> 8)) + + return (randseed[3] >>> 0) / ((1 << 31) >>> 0) + } + + function createColor() { + // saturation is the whole color spectrum + const h = Math.floor(rand() * 360) + // saturation goes from 40 to 100, it avoids greyish colors + const s = ((rand() * 60) + 40) + '%' + // lightness can be anything from 0 to 100, but probabilities are a bell curve around 50% + const l = ((rand() + rand() + rand() + rand()) * 25) + '%' + + const color = 'hsl(' + h + ',' + s + ',' + l + ')' + return color + } + + function createImageData(size: number) { + const width = size // Only support square icons for now + const height = size + + const dataWidth = Math.ceil(width / 2) + const mirrorWidth = width - dataWidth + + const data = [] + for (let y = 0; y < height; y++) { + let row = [] + for (let x = 0; x < dataWidth; x++) { + // this makes foreground and background color to have a 43% (1/2.3) probability + // spot color has 13% chance + row[x] = Math.floor(rand() * 2.3) + } + const r = row.slice(0, mirrorWidth) + r.reverse() + row = row.concat(r) + + for (let i = 0; i < row.length; i++) { + data.push(row[i]) + } + } + + return data + } + + function setCanvas(identicon: HTMLCanvasElement, imageData: number[], color: string, scale: number, bgcolor: string, spotcolor: string) { + const width = Math.sqrt(imageData.length) + const size = width * scale + + identicon.width = size + identicon.style.width = `${size}px` + + identicon.height = size + identicon.style.height = `${size}px` + + const cc = identicon.getContext('2d') + cc!.fillStyle = bgcolor + cc!.fillRect(0, 0, identicon.width, identicon.height) + cc!.fillStyle = color + + for (let i = 0; i < imageData.length; i++) { + // if data is 2, choose spot color, if 1 choose foreground + cc!.fillStyle = (imageData[i] === 1) ? color : spotcolor + + // if data is 0, leave the background + if (imageData[i]) { + const row = Math.floor(i / width) + const col = i % width + + cc!.fillRect(col * scale, row * scale, scale, scale) + } + } + } + + const seed = addressString(address) + + seedrand(seed) + + const color = createColor() + const bgcolor = createColor() + const spotcolor = createColor() + const imageData = createImageData(8) + const canvas = setCanvas(canvasRef, imageData, color, scale, bgcolor, spotcolor) + + return canvas +} + +async function renderBlockieToUrl(address: Signal, scale: Signal | undefined) { + const key = `${address.value}!${scale?.value || 4}` + const cacheResult = dataURLCache.get(key) + if (cacheResult !== undefined) return cacheResult + const future = new Future() + const element = document.createElement('canvas') + generateIdenticon(address.value, scale?.value || 4, element) + element.toBlob((blob) => { + if (!blob) return + const dataUrl = URL.createObjectURL(blob) + dataURLCache.set(dataUrl, key) + future.resolve(dataUrl) + }) + return await future +} + +export function Blockie(props: BlockieProps) { + const dimension = 8 * (props.scale?.value || 4) + const { value: dataURL, waitFor } = useAsyncState() + useSignalEffect(() => { + props.address.value + waitFor(async () => renderBlockieToUrl(props.address, props.scale)) + }) + return unknown +}) => { + return ( + + ) +} diff --git a/docs/ts/components/ConfigureFunding.tsx b/docs/ts/components/ConfigureFunding.tsx new file mode 100644 index 0000000..ff1ec57 --- /dev/null +++ b/docs/ts/components/ConfigureFunding.tsx @@ -0,0 +1,207 @@ +import { batch, ReadonlySignal, Signal, useComputed, useSignal, useSignalEffect } from '@preact/signals' +import { EtherSymbol, formatEther, getAddress, JsonRpcProvider, Wallet } from 'ethers' +import { JSX } from 'preact/jsx-runtime' +import { useAsyncState } from '../library/asyncState.js' +import { getMaxBaseFeeInFutureBlock } from '../library/bundleUtils.js' +import { ProviderStore } from '../library/provider.js' +import { addressString } from '../library/utils.js' +import { EthereumAddress } from '../types/ethereumTypes.js' +import { BlockInfo, Bundle, Signers } from '../types/types.js' +import { Button } from './Button.js' +import { SingleNotice } from './Warns.js' +import { BouquetNetwork, BouquetSettings } from '../types/bouquetTypes.js' +import { getNetwork } from '../constants.js' + +export const ConfigureFunding = ({ + provider, + bouquetSettings, + bundle, + fundingAmountMin, + signers, + blockInfo, +}: { + provider: Signal + bundle: Signal + signers: Signal + fundingAmountMin: ReadonlySignal + blockInfo: Signal, + bouquetSettings: Signal +}) => { + const signerKeys = useSignal<{ + [address: string]: { input: string; wallet: Wallet | null } + }>({}) + + const bouquetNetwork = useComputed(() => getNetwork(bouquetSettings.value, provider.value?.chainId || 1n)) + + useSignalEffect(() => { + if (!bundle.value) signerKeys.value = {} + }) + + if (bundle.peek() && Object.keys(signerKeys.peek()).length === 0) { + signerKeys.value = + bundle.value && Object.keys(signerKeys.peek()).length === 0 + ? bundle.value.uniqueSigners.reduce( + ( + curr: { + [address: string]: { input: string; wallet: Wallet | null } + }, + address, + ) => { + curr[getAddress(address)] = { input: '', wallet: null } + return curr + }, + {}, + ) + : {} + } + + blockInfo.subscribe(() => { + if (provider.value && signers.value.burner) { + provider.value.provider.getBalance(signers.value.burner.address).then((balance) => (signers.value.burnerBalance = balance)) + } + }) + + function copyBurnerToClipboard() { + if (!signers.value.burner) return + navigator.clipboard.writeText(signers.value.burner.address) + } + + const showWithdrawModal = useSignal(false) + + function openWithdrawModal() { + showWithdrawModal.value = true + } + + return ( + <> + + {bundle.value && bundle.value.containsFundingTx && signers.value.burner ? ( +
+

Deposit To Funding Account

+

This is a temporary account, send only enough needed plus a tiny bit to account for possible rising gas price changes.

+
+ + +
+

+ Wallet Balance: {EtherSymbol}{formatEther(signers.value.burnerBalance)} +
+ Minimum Required Balance: {EtherSymbol}{formatEther(fundingAmountMin.value)} +

+
+ ) : null} + + ) +} + +const WithdrawModal = ({ display, blockInfo, signers, provider, bouquetNetwork }: { display: Signal, provider: Signal, signers: Signal, blockInfo: Signal, bouquetNetwork: Signal }) => { + if (!display.value) return null + + const recipientAddress = useSignal<{ input: string, address?: EthereumAddress }>({ input: '' }) + const inputStyle = useComputed(() => `flex flex-col justify-center border h-16 outline-none px-4 focus-within:bg-white/5 bg-transparent ${recipientAddress.value.address ? 'border-green-400' : (recipientAddress.value.input ? 'border-red-400' : 'border-white/50 focus-within:border-white/80')}`) + function parseInput(input: string) { + const address = EthereumAddress.safeParse(input) + recipientAddress.value = { input, address: address.success ? address.value : undefined } + } + + const withdrawAmount = useComputed(() => { + let maxFeePerGas = getMaxBaseFeeInFutureBlock(blockInfo.value.baseFee, 5n) + blockInfo.value.priorityFee; + let fee = maxFeePerGas * 21000n + let amount = signers.value.burnerBalance - fee + return { amount, fee, maxFeePerGas } + }) + + const { value: signedMessage, waitFor } = useAsyncState() + + // Default check if we know the network, can also switch to true if sending to known RPC fails + const useBrowserProvider = useSignal(false) + useSignalEffect(() => { useBrowserProvider.value = Boolean(provider.value && bouquetNetwork.value.mempoolSubmitRpcEndpoint === undefined) }) + + function withdraw() { + waitFor(async () => { + if (withdrawAmount.value.amount <= 0n) throw 'Funding account\'s balance is to small to withdraw' + if (!signers.value.burner) throw 'No funding account found' + if (!provider.value) throw 'User not connected' + if (!recipientAddress.value.address) throw 'No recipient provided' + + if (useBrowserProvider.value === true) { + try { + const burnerWithBrowserProvider = signers.value.burner.connect(provider.value.provider) + const txInput = await burnerWithBrowserProvider.populateTransaction({ chainId: provider.value.chainId, from: signers.value.burner.address, to: addressString(recipientAddress.value.address), gasLimit: 21000, type: 2, value: withdrawAmount.value.amount, maxFeePerGas: withdrawAmount.value.maxFeePerGas }) + const tx = await burnerWithBrowserProvider.signTransaction(txInput) + const txHash = await provider.value.provider.send('eth_sendRawTransaction', [tx]) + return txHash as string + } catch (error) { + throw error + } + } + if (bouquetNetwork.value.mempoolSubmitRpcEndpoint === undefined) throw new Error('No RPC URL set and not connected to wallet') + const fundingWithProvider = signers.value.burner.connect(new JsonRpcProvider(bouquetNetwork.value.mempoolSubmitRpcEndpoint)) + try { + const tx = await fundingWithProvider.sendTransaction({ chainId: provider.value.chainId, from: signers.value.burner.address, to: addressString(recipientAddress.value.address), gasLimit: 21000, type: 2, value: withdrawAmount.value.amount, maxFeePerGas: withdrawAmount.value.maxFeePerGas }) + fundingWithProvider.provider?.destroy() + return tx.hash + } catch (error) { + console.warn('Error sending burner withdraw tx to known RPC:', bouquetNetwork.value.mempoolSubmitRpcEndpoint, ' error:', error) + fundingWithProvider.provider?.destroy() + useBrowserProvider.value = true + throw 'Unknown network! If you have Interceptor installed and simulation mode on please switch to signing mode and try again.' + } + }) + } + + function close() { + batch(() => { + display.value = false + recipientAddress.value = { input: '' } + signedMessage.value.state = 'inactive' + }) + } + + return ( +
+
e.stopPropagation()}> +

Withdraw From Funding Account

+
+ ETH Recipient + ) => parseInput(e.currentTarget.value)} type='text' className='bg-transparent outline-none placeholder:text-gray-600' placeholder='0x...' /> +
+ {withdrawAmount.value.amount > 0n + ? (

Withdraw {EtherSymbol} {formatEther(withdrawAmount.value.amount)} + {EtherSymbol} {formatEther(withdrawAmount.value.fee)} fee

) + : (

Transfer fee ({EtherSymbol} {formatEther(withdrawAmount.value.fee)}) is more than funding account balance

)} +
+ +
+

{signedMessage.value.state === 'rejected' ? : ''}

+

{signedMessage.value.state === 'resolved' ? Transaction submitted with TX Hash {signedMessage.value.value} : Transaction submitted with TX Hash {signedMessage.value.value}} title='Transaction Submitted' /> : ''}

+
+
+ ) +} + diff --git a/docs/ts/components/ConfigureKeys.tsx b/docs/ts/components/ConfigureKeys.tsx new file mode 100644 index 0000000..4eaeffa --- /dev/null +++ b/docs/ts/components/ConfigureKeys.tsx @@ -0,0 +1,88 @@ +import { batch, Signal, useSignal, useSignalEffect } from '@preact/signals' +import { Wallet } from 'ethers' +import { JSX } from 'preact/jsx-runtime' +import { ProviderStore } from '../library/provider.js' +import { BlockInfo, Bundle, Signers } from '../types/types.js' + +export const ConfigureKeys = ({ + provider, + bundle, + signers, + blockInfo, +}: { + provider: Signal + bundle: Signal + signers: Signal + blockInfo: Signal +}) => { + const signerKeys = useSignal<{ + [address: string]: { input: string; wallet: Wallet | null } + }>({}) + + useSignalEffect(() => { + if (!bundle.value) signerKeys.value = {} + if (bundle.value && bundle.value.uniqueSigners.join() !== Object.keys(signerKeys.value).join()) { + signerKeys.value = bundle.value.uniqueSigners.reduce(( + curr: { + [address: string]: { input: string; wallet: Wallet | null } + }, + address, + ) => { + curr[address] = { input: '', wallet: null } + return curr + }, + {}, + ) + } + }) + + blockInfo.subscribe(() => { + if (provider.value && signers.value.burner) { + provider.value.provider.getBalance(signers.value.burner.address).then((balance) => (signers.value.burnerBalance = balance)) + } + }) + + function tryUpdateSigners(address: string, privateKey: string) { + batch(() => { + try { + const wallet = new Wallet(privateKey) + + signerKeys.value = { + ...signerKeys.peek(), + [address]: { + wallet: wallet.address === address ? wallet : null, + input: privateKey, + }, + } + } catch { + signerKeys.value = { + ...signerKeys.peek(), + [address]: { wallet: null, input: privateKey }, + } + } + if (Object.values(signerKeys.value).filter(({ wallet }) => !wallet).length === 0) { + signers.value = { + ...signers.peek(), + bundleSigners: Object.values(signerKeys.peek()).reduce((acc: { [account: string]: Wallet }, wallet) => { + if (wallet.wallet) { + acc[wallet.wallet.address] = wallet.wallet + } + return acc + }, {}), + } + } + }) + } + + return ( +
+

Enter Private Keys For Signing Accounts

+ {Object.keys(signerKeys.value).map((address) => ( +
+ {address} + ) => tryUpdateSigners(address, e.currentTarget.value)} value={signerKeys.value[address].input} type='text' className='bg-transparent outline-none placeholder:text-gray-600' placeholder={`Enter private key for account`} /> +
+ ))} +
+ ) +} diff --git a/docs/ts/components/Footer.tsx b/docs/ts/components/Footer.tsx new file mode 100644 index 0000000..4ddf032 --- /dev/null +++ b/docs/ts/components/Footer.tsx @@ -0,0 +1,19 @@ +export const Footer = () => ( + +) diff --git a/docs/ts/components/Import.tsx b/docs/ts/components/Import.tsx new file mode 100644 index 0000000..48a5fb9 --- /dev/null +++ b/docs/ts/components/Import.tsx @@ -0,0 +1,170 @@ +import { batch, Signal, useSignal } from '@preact/signals' +import { useState } from 'preact/hooks' +import { parseEther } from 'ethers' +import { connectBrowserProvider, ProviderStore } from '../library/provider.js' +import { GetSimulationStackReply } from '../types/interceptorTypes.js' +import { Button } from './Button.js' +import { Bundle, serialize, Signers } from '../types/types.js' +import { EthereumAddress } from '../types/ethereumTypes.js' +import { BouquetSettings, TransactionList } from '../types/bouquetTypes.js' +import { ImportModal } from './ImportModal.js' +import { SingleNotice } from './Warns.js' +import { addressString } from '../library/utils.js' + +export async function importFromInterceptor( + bundle: Signal, + provider: Signal, + blockInfo: Signal<{ + blockNumber: bigint + baseFee: bigint + priorityFee: bigint + }>, + signers: Signal | undefined, + bouquetSettings: Signal +) { + if (!window.ethereum || !window.ethereum.request) throw Error('No Ethereum wallet detected') + connectBrowserProvider(provider, blockInfo, signers, bouquetSettings) + + const { payload } = await window.ethereum + .request({ + method: 'interceptor_getSimulationStack', + params: ['1.0.0'], + }) + .catch((err: { code: number }) => { + if (err?.code === -32601) { + throw new Error('Wallet does not support returning simulations') + } else { + throw new Error(`Unknown Error: ${JSON.stringify(err)}`) + } + }) + + const tryParse = GetSimulationStackReply.safeParse(payload) + if (!tryParse.success) throw new Error('Wallet does not support returning simulations') + if (tryParse.value.length === 0) throw new Error('You have no transactions on your simulation') + + const converted = TransactionList.safeParse(serialize(GetSimulationStackReply, tryParse.value).map(({ from, to, value, input, gasLimit, chainId }) => ({ from, to, value, input, gasLimit, chainId }))) + if (!converted.success) throw new Error('Malformed simulation stack') + + if (converted.value.length >= 2 && converted.value[0].to === converted.value[1].from && converted.value[0].value === parseEther('200000')) { + const fundingAddr = converted.value[0].from + converted.value = converted.value.map(tx => tx.from === fundingAddr ? { ...tx, from: 'FUNDING' } : tx) + } + + const uniqueToAddresses = [...new Set(converted.value.map(({ from }) => from))] + const containsFundingTx = uniqueToAddresses.includes('FUNDING') + const uniqueSigners = uniqueToAddresses.filter((address): address is EthereumAddress => address !== 'FUNDING').map(address => addressString(address)) + + const totalGas = converted.value.reduce((sum, tx) => tx.gasLimit + sum, 0n) + + // Take addresses that recieved funding, determine spend deficit - gas fees + const fundingRecipients = new Set(converted.value.reduce((result: bigint[], tx) => (tx.to && tx.from === 'FUNDING' ? [...result, tx.to] : result), [])) + + const spenderDeficits = tryParse.value.reduce((amounts: { [account: string]: { deficit: bigint, credit: bigint } }, tx) => { + if (!fundingRecipients.has(tx.from)) return amounts + const receipientBalanceChanges = tx.balanceChanges.filter(x => x.address === tx.from) + + const consumed = tx.value + // Rebate is the difference between balance change and consume amount (if there were any internal transactions sending ETH back), ignore gas fees + const balanceChange = receipientBalanceChanges.reduce((result: bigint, balanceChange) => result + balanceChange.after - balanceChange.before, 0n) + const rebate = balanceChange + consumed + tx.maxPriorityFeePerGas * tx.gasSpent + + // Calcuate current deficit + if (tx.from.toString() in amounts) { + // If credit, deduct current credit from new consumption, or cancel out new consumption and open credit - whichever is smaller + if (amounts[tx.from.toString()].credit > 0n) { + if (amounts[tx.from.toString()].credit <= consumed) { + amounts[tx.from.toString()].deficit += consumed - amounts[tx.from.toString()].credit + amounts[tx.from.toString()].credit = rebate + } else { + // If consumed less than current rebates, deficit does not increase. + amounts[tx.from.toString()].credit += rebate - consumed + } + } + } else { + amounts[tx.from.toString()] = { deficit: consumed, credit: rebate } + } + return amounts + + }, {}) + + const inputValue = Object.values(spenderDeficits).reduce((sum, spender) => spender.deficit + sum, 0n) + + // Copy value and set, input of funding to inputValue + const transactions = [...converted.value] + if (containsFundingTx) { + transactions[0] = { ...transactions[0], value: inputValue } + } + + localStorage.setItem('payload', JSON.stringify(TransactionList.serialize(transactions))) + bundle.value = { transactions, containsFundingTx, uniqueSigners, totalGas, inputValue } +} + +export const Import = ({ + bundle, + provider, + blockInfo, + signers, + bouquetSettings, +}: { + bundle: Signal + provider: Signal + blockInfo: Signal<{ + blockNumber: bigint + baseFee: bigint + priorityFee: bigint + }> + signers: Signal + bouquetSettings: Signal +}) => { + const showImportModal = useSignal(false) + const [error, setError] = useState(undefined) + + const clearPayload = () => { + batch(() => { + bundle.value = undefined + localStorage.removeItem('payload') + signers.value.bundleSigners = {} + setError('') + // Keep burner wallet as long as it has funds, should clear is later if there is left over dust but not needed. + // if (fundingAccountBalance.value === 0n) signers.value.burner = undefined + }) + } + + return ( + <> + {showImportModal.value ? setError('')} display={showImportModal} /> : null} +

1. Import

+
+
+ + + {bundle.value ? ( + + ) : null} +
+ {error ? : null} + {error && error === 'Wallet does not support returning simulations' ? ( +

+ Don't have The Interceptor Installed? Install it here{' '} + + here + + . +

+ ) : ( + '' + )} +
+ + ) + } diff --git a/docs/ts/components/ImportModal.tsx b/docs/ts/components/ImportModal.tsx new file mode 100644 index 0000000..75d83a3 --- /dev/null +++ b/docs/ts/components/ImportModal.tsx @@ -0,0 +1,82 @@ +import { Signal, useComputed, useSignal } from '@preact/signals' +import { JSX } from 'preact/jsx-runtime' +import { addressString } from '../library/utils.js' +import { TransactionList } from '../types/bouquetTypes.js' +import { EthereumAddress } from '../types/ethereumTypes.js' +import { Bundle } from '../types/types.js' +import { Button } from './Button.js' + +const placeholder = `[ + { + "from": "0xb3cd36cfaa07652dbfecca76f438ff8998a4f539", + "to": "0xb4fbf271143f4fbf7b91a5ded31805e42b2208d6", + "value": "0x16345785d8a0000", + "input": "0xd0e30db0", + "chainId": "0x1", + "gasLimit": "0x15f90" + }, + { + "from": "0xb3cd36cfaa07652dbfecca76f438ff8998a4f539", + "to": "0xb4fbf271143f4fbf7b91a5ded31805e42b2208d6", + "value": "0x0", + "input": "0x2e1a7d4d000000000000000000000000000000000000000000000000016345785d8a0000", + "chainId": "0x1", + "gasLimit": "0x15f90" + } +]` + +export const ImportModal = ({ display, bundle, clearError }: { display: Signal, clearError: () => void, bundle: Signal }) => { + const jsonInput = useSignal('') + + const isValid = useComputed(() => { + if (!jsonInput.value) return false + try { + const { success } = TransactionList.safeParse(JSON.parse(jsonInput.value)) + return success + } catch { + return false + } + }) + + function importJson() { + if (!isValid.peek()) return + const txList = TransactionList.parse(JSON.parse(jsonInput.value)) + + localStorage.setItem('payload', JSON.stringify(TransactionList.serialize(txList))) + + const uniqueToAddresses = [...new Set(txList.map(({ from }) => from))] + const containsFundingTx = uniqueToAddresses.includes('FUNDING') + const uniqueSigners = uniqueToAddresses.filter((address): address is EthereumAddress => address !== 'FUNDING').map(address => addressString(address)) + + const totalGas = txList.reduce((sum, tx) => tx.gasLimit + sum, 0n) + const inputValue = txList.reduce((sum, tx) => (tx.from === 'FUNDING' ? tx.value : 0n) + sum, 0n) + + bundle.value = { transactions: txList, containsFundingTx, uniqueSigners, totalGas, inputValue } + clearError() + close() + } + + function close() { + jsonInput.value = '' + display.value = false + } + return display.value ? ( +
+
e.stopPropagation()}> +

Import Transactions From JSON

+
+