Compare commits

20 Commits

Author SHA1 Message Date
tim
ed03740852 fixed weight slider bounds 2025-05-19 16:02:01 -04:00
tim
a3c1dfad2d put app back on app.dexorder.com and corp site on dexorder.com with www redirecting to apex 2025-05-19 15:19:20 -04:00
tim
9b410ace09 mobile screen alert 2025-05-08 17:34:05 -04:00
tim
72b061749d withdraw fix; wrap still broken 2025-05-07 21:02:05 -04:00
tim
a42f228984 support chat 2025-05-06 22:27:30 -04:00
tim
47b33a152d crisp for the dotcom 2025-05-06 18:47:34 -04:00
tim
ecffd976ac dotcom 2025-05-06 15:28:19 -04:00
tim
f0f431b34e dotcom 2025-05-06 13:56:05 -04:00
tim
b2457f0617 tab switch bugfix; remove floating div 2025-05-01 11:16:45 -04:00
tim
148b37dca3 removed vite url debug 2025-04-28 16:24:49 -04:00
tim
1b83dad6b3 spam log removal; mobile share fixes 2025-04-26 17:52:04 -04:00
tim
a1bbb17b5d spam log removal; mobile share fixes 2025-04-26 17:51:20 -04:00
tim
916c23e092 mobile fixes 2025-04-26 15:00:12 -04:00
tim
2c5116c5de mobile fixes 2025-04-26 14:56:55 -04:00
tim
441a514acc short share urls 2025-04-23 12:55:49 -04:00
tim
6f7388bb10 share.dexorder.trade instead of ws 2025-04-22 17:43:16 -04:00
tim
fd7b9713ea share landing page bugfixes 2025-04-22 17:21:12 -04:00
tim
7b5421e6e7 share tracking 2025-04-22 16:53:26 -04:00
tim
21f324aa12 better unlisted token handling 2025-04-22 16:45:44 -04:00
tim
eeee9d9853 order sharing 2025-04-22 16:18:32 -04:00
70 changed files with 1497 additions and 456 deletions

395
.dependency-cruiser.cjs Normal file
View File

@@ -0,0 +1,395 @@
/** @type {import('dependency-cruiser').IConfiguration} */
module.exports = {
forbidden: [
{
name: 'no-circular',
severity: 'warn',
comment:
'This dependency is part of a circular relationship. You might want to revise ' +
'your solution (i.e. use dependency inversion, make sure the modules have a single responsibility) ',
from: {},
to: {
circular: true
}
},
{
name: 'no-orphans',
comment:
"This is an orphan module - it's likely not used (anymore?). Either use it or " +
"remove it. If it's logical this module is an orphan (i.e. it's a config file), " +
"add an exception for it in your dependency-cruiser configuration. By default " +
"this rule does not scrutinize dot-files (e.g. .eslintrc.js), TypeScript declaration " +
"files (.d.ts), tsconfig.json and some of the babel and webpack configs.",
severity: 'warn',
from: {
orphan: true,
pathNot: [
'(^|/)[.][^/]+[.](?:js|cjs|mjs|ts|cts|mts|json)$', // dot files
'[.]d[.]ts$', // TypeScript declaration files
'(^|/)tsconfig[.]json$', // TypeScript config
'(^|/)(?:babel|webpack)[.]config[.](?:js|cjs|mjs|ts|cts|mts|json)$' // other configs
]
},
to: {},
},
{
name: 'no-deprecated-core',
comment:
'A module depends on a node core module that has been deprecated. Find an alternative - these are ' +
"bound to exist - node doesn't deprecate lightly.",
severity: 'warn',
from: {},
to: {
dependencyTypes: [
'core'
],
path: [
'^v8/tools/codemap$',
'^v8/tools/consarray$',
'^v8/tools/csvparser$',
'^v8/tools/logreader$',
'^v8/tools/profile_view$',
'^v8/tools/profile$',
'^v8/tools/SourceMap$',
'^v8/tools/splaytree$',
'^v8/tools/tickprocessor-driver$',
'^v8/tools/tickprocessor$',
'^node-inspect/lib/_inspect$',
'^node-inspect/lib/internal/inspect_client$',
'^node-inspect/lib/internal/inspect_repl$',
'^async_hooks$',
'^punycode$',
'^domain$',
'^constants$',
'^sys$',
'^_linklist$',
'^_stream_wrap$'
],
}
},
{
name: 'not-to-deprecated',
comment:
'This module uses a (version of an) npm module that has been deprecated. Either upgrade to a later ' +
'version of that module, or find an alternative. Deprecated modules are a security risk.',
severity: 'warn',
from: {},
to: {
dependencyTypes: [
'deprecated'
]
}
},
{
name: 'no-non-package-json',
severity: 'error',
comment:
"This module depends on an npm package that isn't in the 'dependencies' section of your package.json. " +
"That's problematic as the package either (1) won't be available on live (2 - worse) will be " +
"available on live with an non-guaranteed version. Fix it by adding the package to the dependencies " +
"in your package.json.",
from: {},
to: {
dependencyTypes: [
'npm-no-pkg',
'npm-unknown'
]
}
},
{
name: 'not-to-unresolvable',
comment:
"This module depends on a module that cannot be found ('resolved to disk'). If it's an npm " +
'module: add it to your package.json. In all other cases you likely already know what to do.',
severity: 'error',
from: {},
to: {
couldNotResolve: true
}
},
{
name: 'no-duplicate-dep-types',
comment:
"Likely this module depends on an external ('npm') package that occurs more than once " +
"in your package.json i.e. bot as a devDependencies and in dependencies. This will cause " +
"maintenance problems later on.",
severity: 'warn',
from: {},
to: {
moreThanOneDependencyType: true,
// as it's pretty common to have a type import be a type only import
// _and_ (e.g.) a devDependency - don't consider type-only dependency
// types for this rule
dependencyTypesNot: ["type-only"]
}
},
/* rules you might want to tweak for your specific situation: */
{
name: 'not-to-spec',
comment:
'This module depends on a spec (test) file. The sole responsibility of a spec file is to test code. ' +
"If there's something in a spec that's of use to other modules, it doesn't have that single " +
'responsibility anymore. Factor it out into (e.g.) a separate utility/ helper or a mock.',
severity: 'error',
from: {},
to: {
path: '[.](?:spec|test)[.](?:js|mjs|cjs|jsx|ts|mts|cts|tsx)$'
}
},
{
name: 'not-to-dev-dep',
severity: 'error',
comment:
"This module depends on an npm package from the 'devDependencies' section of your " +
'package.json. It looks like something that ships to production, though. To prevent problems ' +
"with npm packages that aren't there on production declare it (only!) in the 'dependencies'" +
'section of your package.json. If this module is development only - add it to the ' +
'from.pathNot re of the not-to-dev-dep rule in the dependency-cruiser configuration',
from: {
path: '^(src)',
pathNot: '[.](?:spec|test)[.](?:js|mjs|cjs|jsx|ts|mts|cts|tsx)$'
},
to: {
dependencyTypes: [
'npm-dev',
],
// type only dependencies are not a problem as they don't end up in the
// production code or are ignored by the runtime.
dependencyTypesNot: [
'type-only'
],
pathNot: [
'node_modules/@types/'
]
}
},
{
name: 'optional-deps-used',
severity: 'info',
comment:
"This module depends on an npm package that is declared as an optional dependency " +
"in your package.json. As this makes sense in limited situations only, it's flagged here. " +
"If you're using an optional dependency here by design - add an exception to your" +
"dependency-cruiser configuration.",
from: {},
to: {
dependencyTypes: [
'npm-optional'
]
}
},
{
name: 'peer-deps-used',
comment:
"This module depends on an npm package that is declared as a peer dependency " +
"in your package.json. This makes sense if your package is e.g. a plugin, but in " +
"other cases - maybe not so much. If the use of a peer dependency is intentional " +
"add an exception to your dependency-cruiser configuration.",
severity: 'warn',
from: {},
to: {
dependencyTypes: [
'npm-peer'
]
}
}
],
options: {
/* Which modules not to follow further when encountered */
doNotFollow: {
/* path: an array of regular expressions in strings to match against */
path: ['node_modules']
},
/* Which modules to exclude */
// exclude : {
// /* path: an array of regular expressions in strings to match against */
// path: '',
// },
/* Which modules to exclusively include (array of regular expressions in strings)
dependency-cruiser will skip everything not matching this pattern
*/
// includeOnly : [''],
/* List of module systems to cruise.
When left out dependency-cruiser will fall back to the list of _all_
module systems it knows of. It's the default because it's the safe option
It might come at a performance penalty, though.
moduleSystems: ['amd', 'cjs', 'es6', 'tsd']
As in practice only commonjs ('cjs') and ecmascript modules ('es6')
are widely used, you can limit the moduleSystems to those.
*/
// moduleSystems: ['cjs', 'es6'],
/*
false: don't look at JSDoc imports (the default)
true: dependency-cruiser will detect dependencies in JSDoc-style
import statements. Implies "parser": "tsc", so the dependency-cruiser
will use the typescript parser for JavaScript files.
For this to work the typescript compiler will need to be installed in the
same spot as you're running dependency-cruiser from.
*/
// detectJSDocImports: true,
/* prefix for links in html and svg output (e.g. 'https://github.com/you/yourrepo/blob/main/'
to open it on your online repo or `vscode://file/${process.cwd()}/` to
open it in visual studio code),
*/
// prefix: `vscode://file/${process.cwd()}/`,
/* false (the default): ignore dependencies that only exist before typescript-to-javascript compilation
true: also detect dependencies that only exist before typescript-to-javascript compilation
"specify": for each dependency identify whether it only exists before compilation or also after
*/
// tsPreCompilationDeps: false,
/* list of extensions to scan that aren't javascript or compile-to-javascript.
Empty by default. Only put extensions in here that you want to take into
account that are _not_ parsable.
*/
// extraExtensionsToScan: [".json", ".jpg", ".png", ".svg", ".webp"],
/* if true combines the package.jsons found from the module up to the base
folder the cruise is initiated from. Useful for how (some) mono-repos
manage dependencies & dependency definitions.
*/
// combinedDependencies: false,
/* if true leave symlinks untouched, otherwise use the realpath */
// preserveSymlinks: false,
/* TypeScript project file ('tsconfig.json') to use for
(1) compilation and
(2) resolution (e.g. with the paths property)
The (optional) fileName attribute specifies which file to take (relative to
dependency-cruiser's current working directory). When not provided
defaults to './tsconfig.json'.
*/
tsConfig: {
fileName: 'jsconfig.json'
},
/* Webpack configuration to use to get resolve options from.
The (optional) fileName attribute specifies which file to take (relative
to dependency-cruiser's current working directory. When not provided defaults
to './webpack.conf.js'.
The (optional) `env` and `arguments` attributes contain the parameters
to be passed if your webpack config is a function and takes them (see
webpack documentation for details)
*/
// webpackConfig: {
// fileName: 'webpack.config.js',
// env: {},
// arguments: {}
// },
/* Babel config ('.babelrc', '.babelrc.json', '.babelrc.json5', ...) to use
for compilation
*/
// babelConfig: {
// fileName: '.babelrc',
// },
/* List of strings you have in use in addition to cjs/ es6 requires
& imports to declare module dependencies. Use this e.g. if you've
re-declared require, use a require-wrapper or use window.require as
a hack.
*/
// exoticRequireStrings: [],
/* options to pass on to enhanced-resolve, the package dependency-cruiser
uses to resolve module references to disk. The values below should be
suitable for most situations
If you use webpack: you can also set these in webpack.conf.js. The set
there will override the ones specified here.
*/
enhancedResolveOptions: {
/* What to consider as an 'exports' field in package.jsons */
exportsFields: ["exports"],
/* List of conditions to check for in the exports field.
Only works when the 'exportsFields' array is non-empty.
*/
conditionNames: ["import", "require", "node", "default", "types"],
/* The extensions, by default are the same as the ones dependency-cruiser
can access (run `npx depcruise --info` to see which ones that are in
_your_ environment). If that list is larger than you need you can pass
the extensions you actually use (e.g. [".js", ".jsx"]). This can speed
up module resolution, which is the most expensive step.
*/
// extensions: [".js", ".jsx", ".ts", ".tsx", ".d.ts"],
/* What to consider a 'main' field in package.json */
mainFields: ["module", "main", "types", "typings"],
/* A list of alias fields in package.jsons
See [this specification](https://github.com/defunctzombie/package-browser-field-spec) and
the webpack [resolve.alias](https://webpack.js.org/configuration/resolve/#resolvealiasfields)
documentation.
Defaults to an empty array (= don't use alias fields).
*/
// aliasFields: ["browser"],
},
/* skipAnalysisNotInRules will make dependency-cruiser execute
analysis strictly necessary for checking the rule set only.
See https://github.com/sverweij/dependency-cruiser/blob/main/doc/options-reference.md#skipanalysisnotinrules
for details
*/
skipAnalysisNotInRules: true,
reporterOptions: {
dot: {
/* pattern of modules that can be consolidated in the detailed
graphical dependency graph. The default pattern in this configuration
collapses everything in node_modules to one folder deep so you see
the external modules, but their innards.
*/
collapsePattern: 'node_modules/(?:@[^/]+/[^/]+|[^/]+)',
/* Options to tweak the appearance of your graph.See
https://github.com/sverweij/dependency-cruiser/blob/main/doc/options-reference.md#reporteroptions
for details and some examples. If you don't specify a theme
dependency-cruiser falls back to a built-in one.
*/
// theme: {
// graph: {
// /* splines: "ortho" gives straight lines, but is slow on big graphs
// splines: "true" gives bezier curves (fast, not as nice as ortho)
// */
// splines: "true"
// },
// }
},
archi: {
/* pattern of modules that can be consolidated in the high level
graphical dependency graph. If you use the high level graphical
dependency graph reporter (`archi`) you probably want to tweak
this collapsePattern to your situation.
*/
collapsePattern: '^(?:packages|src|lib(s?)|app(s?)|bin|test(s?)|spec(s?))/[^/]+|node_modules/(?:@[^/]+/[^/]+|[^/]+)',
/* Options to tweak the appearance of your graph. If you don't specify a
theme for 'archi' dependency-cruiser will use the one specified in the
dot section above and otherwise use the default one.
*/
// theme: { },
},
"text": {
"highlightFocused": true
},
}
}
};
// generated: dependency-cruiser@16.10.1 on 2025-04-24T20:50:32.854Z

View File

@@ -1 +1,2 @@
VITE_WS_URL=wss://ws.dexorder.trade VITE_WS_URL=wss://ws.dexorder.com
VITE_SHARE_URL=https://app.dexorder.com

View File

@@ -1,3 +1,3 @@
VITE_WS_URL=ws://localhost:3001 VITE_WS_URL=ws://localhost:3001
VITE_SNAPSHOT_URL=http://localhost:3001/snapshot VITE_SHARE_URL=http://localhost:3001
REQUIRE_AUTH=NOAUTH VITE_REQUIRE_APPROVAL=NO

2
bin/depcruise Executable file
View File

@@ -0,0 +1,2 @@
#!/bin/bash
npx depcruise src

View File

@@ -21,11 +21,11 @@
"flexsearch": "^0.7.43", "flexsearch": "^0.7.43",
"lru-cache": "^11.0.2", "lru-cache": "^11.0.2",
"luxon": "^3.4.4", "luxon": "^3.4.4",
"lz-string": "^1.5.0",
"pinia": "2.1.6", "pinia": "2.1.6",
"pinia-plugin-persistedstate": "^4.1.3", "pinia-plugin-persistedstate": "^4.1.3",
"roboto-fontface": "*", "roboto-fontface": "*",
"socket.io-client": "^4.7.2", "socket.io-client": "^4.7.2",
"uuid": "^11.1.0",
"vue": "^3.2.0", "vue": "^3.2.0",
"vue-router": "^4.0.0", "vue-router": "^4.0.0",
"vue-scroll-picker": "^1.2.2", "vue-scroll-picker": "^1.2.2",
@@ -34,6 +34,7 @@
}, },
"devDependencies": { "devDependencies": {
"@vitejs/plugin-vue": "^4.0.0", "@vitejs/plugin-vue": "^4.0.0",
"dependency-cruiser": "^16.10.1",
"eslint": "^8.37.0", "eslint": "^8.37.0",
"eslint-plugin-vue": "^9.3.0", "eslint-plugin-vue": "^9.3.0",
"sass": "^1.60.0", "sass": "^1.60.0",

View File

@@ -1,28 +0,0 @@
console.log('snapshot.js')
async function getSnapCode() {
const formData = new FormData;
formData.append('language', 'en');
formData.append('timezone', 'Etc/UTC');
formData.append('symbol', 'BTC/USD');
formData.append('preparedImage', window.snapshotImage, 'blob');
try {
const response = await fetch('https://tradingview.com/snapshot/', {
method: 'POST',
body: formData,
credentials: "same-origin",
});
if (!response.ok) {
console.error('Failed to upload snapshot:', response.status, response);
return null;
}
return await response.text();
} catch (error) {
console.error('Error uploading snapshot:', error);
return null
}
}
getSnapCode().then((code)=>console.log('snapshot code', code)).catch((e)=>console.error('snapshot error', e))

View File

@@ -6,12 +6,9 @@
<script setup> <script setup>
import SupportChat from "@/components/SupportChat.vue"; import SupportChat from "@/components/SupportChat.vue";
import {detectChain} from "@/blockchain/wallet.js";
import WelcomeDialog from "@/components/WelcomeDialog.vue"; import WelcomeDialog from "@/components/WelcomeDialog.vue";
import {usePrefStore} from "@/store/store.js"; import {usePrefStore} from "@/store/store.js";
detectChain()
const prefs = usePrefStore() const prefs = usePrefStore()
</script> </script>

View File

@@ -1,6 +1,6 @@
import {provider as walletProvider} from "@/blockchain/provider.js";
import {ethers} from "ethers"; import {ethers} from "ethers";
import {AbiURLCache} from "../common.js"; import {AbiURLCache} from "../common.js";
import {provider as walletProvider} from "@/blockchain/wallet.js";
export const abiCache = new AbiURLCache('/contract/out/') export const abiCache = new AbiURLCache('/contract/out/')

View File

@@ -10,7 +10,7 @@ export function subOHLC( chainId, pool, period ) {
// console.log('subOHLC', chainId, pool, period, ckey, ohlcSubCounts[ckey]) // console.log('subOHLC', chainId, pool, period, ckey, ohlcSubCounts[ckey])
if (!(ckey in ohlcSubCounts) || ohlcSubCounts[ckey] === 0) { if (!(ckey in ohlcSubCounts) || ohlcSubCounts[ckey] === 0) {
ohlcSubCounts[ckey] = 1 ohlcSubCounts[ckey] = 1
console.log('subscribing OHLCs', chainId, key) // console.log('subscribing OHLCs', chainId, key)
socket.emit('subOHLCs', chainId, [key]) socket.emit('subOHLCs', chainId, [key])
} else } else
ohlcSubCounts[ckey]++ ohlcSubCounts[ckey]++
@@ -30,7 +30,7 @@ export function refreshOHLCSubs() {
} }
keys.push(`${pool}|${period}`) keys.push(`${pool}|${period}`)
} }
console.log('refreshing OHLC subs', keys) // console.log('refreshing OHLC subs', keys)
socket.emit('subOHLCs', chainId, keys) socket.emit('subOHLCs', chainId, keys)
} }
@@ -45,7 +45,7 @@ export function unsubOHLC( chainId, pool, period ) {
} else { } else {
ohlcSubCounts[ckey]-- ohlcSubCounts[ckey]--
if (ohlcSubCounts[ckey] === 0) { if (ohlcSubCounts[ckey] === 0) {
console.log('unsubscribing OHLCs', chainId, key) // console.log('unsubscribing OHLCs', chainId, key)
// noinspection JSCheckFunctionSignatures // noinspection JSCheckFunctionSignatures
socket.emit('unsubOHLCs', chainId, [key]) socket.emit('unsubOHLCs', chainId, [key])
} }

View File

@@ -1,6 +1,6 @@
import {uint32max, uint64max} from "@/misc.js"; import {uint32max, uint64max} from "@/misc.js";
import {encodeIEE754} from "@/common.js"; import {encodeIEE754} from "@/common.js";
import {DEFAULT_SLIPPAGE, MIN_SLIPPAGE} from "@/orderbuild.js";
export const MAX_FRACTION = 65535; export const MAX_FRACTION = 65535;
export const NO_CONDITIONAL_ORDER = uint64max; export const NO_CONDITIONAL_ORDER = uint64max;
@@ -8,6 +8,10 @@ export const NO_OCO = uint64max;
export const DISTANT_PAST = 0 export const DISTANT_PAST = 0
export const DISTANT_FUTURE = uint32max export const DISTANT_FUTURE = uint32max
export const MIN_EXECUTION_TIME = 60 // give at least one full minute for each tranche to trigger
export const DEFAULT_SLIPPAGE = 0.0030;
export const MIN_SLIPPAGE = 0.0001;
// struct SwapOrder { // struct SwapOrder {
// address tokenIn; // address tokenIn;
// address tokenOut; // address tokenOut;
@@ -140,7 +144,7 @@ export function parseElaboratedOrderStatus(chainId, status) {
export function parseOrderStatus(chainId, status) { export function parseOrderStatus(chainId, status) {
console.log('parseOrderStatus', status) // console.log('parseOrderStatus', status)
let [ let [
order, order,
fillFeeHalfBps, fillFeeHalfBps,
@@ -161,7 +165,7 @@ export function parseOrderStatus(chainId, status) {
chainId, order, fillFeeHalfBps, state, startTime, startPrice, ocoGroup, chainId, order, fillFeeHalfBps, state, startTime, startPrice, ocoGroup,
filledIn, filledOut, filled, trancheStatus, filledIn, filledOut, filled, trancheStatus,
}; };
console.log('SwapOrderStatus', result) // console.log('SwapOrderStatus', result)
return result return result
} }
@@ -253,4 +257,3 @@ export function parseFeeSchedule(sched) {
fillFee: fillFeeHalfBps/1_000_000 // fillFee is a multiplier on the filled volume. 0.0001 = 0.1% of the output token taken as a fee fillFee: fillFeeHalfBps/1_000_000 // fillFee is a multiplier on the filled volume. 0.0001 = 0.1% of the output token taken as a fee
} }
} }

View File

@@ -1,10 +1,10 @@
import {socket} from "@/socket.js";
import {useStore} from "@/store/store.js"; import {useStore} from "@/store/store.js";
import {Exchange} from "@/blockchain/orderlib.js"; import {Exchange} from "@/blockchain/orderlib.js";
import {uniswapV3PoolAddress} from "@/blockchain/uniswap.js"; import {uniswapV3PoolAddress} from "@/blockchain/uniswap.js";
import {FixedNumber} from "ethers"; import {FixedNumber} from "ethers";
import {provider} from "@/blockchain/wallet.js";
import {newContract} from "@/blockchain/contract.js"; import {newContract} from "@/blockchain/contract.js";
import {provider} from "@/blockchain/provider.js";
import {socket} from "@/socket.js";
const subscriptionCounts = {} // key is route and value is a subscription counter const subscriptionCounts = {} // key is route and value is a subscription counter
export const WIDE_PRICE_FORMAT = {decimals:38, width:512, signed:false}; // 38 decimals is 127 bits export const WIDE_PRICE_FORMAT = {decimals:38, width:512, signed:false}; // 38 decimals is 127 bits

View File

@@ -0,0 +1,3 @@
export let provider = null
export function setProvider(p) {provider = p}

View File

@@ -2,7 +2,8 @@ import {Exchange} from "@/blockchain/orderlib.js";
import {useOrderStore, useStore} from "@/store/store.js"; import {useOrderStore, useStore} from "@/store/store.js";
import {queryHelperContract} from "@/blockchain/contract.js"; import {queryHelperContract} from "@/blockchain/contract.js";
import {SingletonCoroutine} from "@/misc.js"; import {SingletonCoroutine} from "@/misc.js";
import {provider} from "@/blockchain/wallet.js";
import {provider} from "@/blockchain/provider.js";
export async function findRoute(helper, chainId, tokenA, tokenB) { export async function findRoute(helper, chainId, tokenA, tokenB) {

View File

@@ -1,8 +1,8 @@
import {socket} from "@/socket.js";
import {useStore} from "@/store/store.js"; import {useStore} from "@/store/store.js";
import {metadataMap} from "@/version.js"; import {metadataMap} from "@/version.js";
import {provider} from "@/blockchain/wallet.js";
import {newContract} from "@/blockchain/contract.js"; import {newContract} from "@/blockchain/contract.js";
import {provider} from "@/blockchain/provider.js";
import {socket} from "@/socket.js";
// synchronous version may return null but will trigger a lookup // synchronous version may return null but will trigger a lookup
@@ -31,7 +31,16 @@ export async function getToken(chainId, addr) {
return found return found
if (!(addr in s.tokens)) if (!(addr in s.tokens))
await addExtraToken(chainId, addr) await addExtraToken(chainId, addr)
return s.tokens[addr] let result = s.tokens[addr]
if (!result) {
result = {
n: addr,
a: addr,
s: addr,
d: 0,
}
}
return result
} }

View File

@@ -1,28 +1,13 @@
import {nav, sleep, uuid} from "@/misc.js"; import {provider} from "@/blockchain/provider.js";
import {TransactionState, TransactionType} from "@/blockchain/transactionDecl.js";
import {sleep, uuid} from "@/misc.js";
import {vaultContract} from "@/blockchain/contract.js"; import {vaultContract} from "@/blockchain/contract.js";
import {ensureVault, provider, switchChain, useWalletStore} from "@/blockchain/wallet.js"; import {switchChain, useWalletStore} from "@/blockchain/wallet.js";
import {toRaw} from "vue"; import {toRaw} from "vue";
import {useChartOrderStore} from "@/orderbuild.js"; import {useChartOrderStore} from "@/orderbuild.js";
import {placementFee} from "@/fees.js"; import {placementFee} from "@/fees.js";
import {router} from "@/router/router.js";
export const TransactionState = {
Created: 0, // user requested a transaction
Proposed: 1, // tx is sent to the wallet
Signed: 2, // tx is awaiting blockchain mining
Rejected: 3, // user refused to sign the tx
Error: 3, // unknown error sending the tx to the wallet
Mined: 4, // transaction has been confirmed on-chain
}
export const TransactionType = {
PlaceOrder: 1,
CancelOrder: 2,
CancelAll: 3,
Wrap: 4,
Unwrap: 5,
WithdrawNative: 6,
Withdraw: 7,
}
export class Transaction { export class Transaction {
constructor(chainId, type) { constructor(chainId, type) {
@@ -37,6 +22,7 @@ export class Transaction {
} }
submit() { submit() {
console.log('submitting transaction', this.type)
const ws = useWalletStore(); const ws = useWalletStore();
if ( ws.transaction !== null ) { if ( ws.transaction !== null ) {
console.error('Transaction already in progress', ws.transaction) console.error('Transaction already in progress', ws.transaction)
@@ -47,6 +33,7 @@ export class Transaction {
// "propose" means attach the transaction to a specific vault // "propose" means attach the transaction to a specific vault
propose(owner, vault) { propose(owner, vault) {
console.log('transaction bind', owner, vault)
if (this.vault !== null && this.vault !== vault) { if (this.vault !== null && this.vault !== vault) {
this.failed('proposed vault did not match withdrawl vault', vault, this.vault) this.failed('proposed vault did not match withdrawl vault', vault, this.vault)
return return
@@ -195,7 +182,8 @@ export class PlaceOrderTransaction extends Transaction {
super.end(state) super.end(state)
if (state === TransactionState.Mined) { if (state === TransactionState.Mined) {
useChartOrderStore().resetOrders() useChartOrderStore().resetOrders()
nav('Status') // noinspection JSIgnoredPromiseFromCall
router.push({name: 'Status'})
} }
} }
@@ -280,4 +268,3 @@ export class UnwrapTransaction extends Transaction {
return await vaultContract.unwrap(this.amount) return await vaultContract.unwrap(this.amount)
} }
} }

View File

@@ -0,0 +1,17 @@
export const TransactionState = {
Created: 0, // user requested a transaction
Proposed: 1, // tx is sent to the wallet
Signed: 2, // tx is awaiting blockchain mining
Rejected: 3, // user refused to sign the tx
Error: 3, // unknown error sending the tx to the wallet
Mined: 4, // transaction has been confirmed on-chain
}
export const TransactionType = {
PlaceOrder: 1,
CancelOrder: 2,
CancelAll: 3,
Wrap: 4,
Unwrap: 5,
WithdrawNative: 6,
Withdraw: 7,
}

View File

@@ -1,16 +1,14 @@
import {provider, setProvider} from "@/blockchain/provider.js";
import {BrowserProvider, ethers} from "ethers"; import {BrowserProvider, ethers} from "ethers";
import {useStore} from "@/store/store"; import {useStore} from "@/store/store";
import {socket} from "@/socket.js";
import {errorSuggestsMissingVault, SingletonCoroutine} from "@/misc.js"; import {errorSuggestsMissingVault, SingletonCoroutine} from "@/misc.js";
import {newContract, vaultAddress, vaultContract} from "@/blockchain/contract.js"; import {newContract, vaultAddress, vaultContract} from "@/blockchain/contract.js";
import {defineStore} from "pinia"; import {defineStore} from "pinia";
import {computed, ref} from "vue"; import {computed, ref} from "vue";
import {metadataMap, version} from "@/version.js"; import {metadataMap, version} from "@/version.js";
import {CancelAllTransaction, TransactionState, TransactionType} from "@/blockchain/transaction.js"; import {TransactionState, TransactionType} from "@/blockchain/transactionDecl.js";
import {track} from "@/track.js"; import {track} from "@/track.js";
import {socket} from "@/socket.js";
export let provider = null
export const useWalletStore = defineStore('wallet', ()=>{ export const useWalletStore = defineStore('wallet', ()=>{
@@ -35,12 +33,14 @@ export const useWalletStore = defineStore('wallet', ()=>{
set(v) { set(v) {
_tx.value = v; _tx.value = v;
if (v===null) { if (v===null) {
console.log('clear transaction')
if (progressionInvoker!==null) { if (progressionInvoker!==null) {
clearTimeout(progressionInvoker) clearTimeout(progressionInvoker)
progressionInvoker = null progressionInvoker = null
} }
} }
else { else {
console.log('set transaction', v)
transactionProgressor.invoke(); transactionProgressor.invoke();
if (progressionInvoker===null) if (progressionInvoker===null)
progressionInvoker = setInterval(()=>transactionProgressor.invoke(), 1000) progressionInvoker = setInterval(()=>transactionProgressor.invoke(), 1000)
@@ -67,7 +67,7 @@ export function onChainChanged(chainId) {
console.log('app chain changed', chainId) console.log('app chain changed', chainId)
store.chainId = chainId store.chainId = chainId
store.account = null store.account = null
provider = new BrowserProvider(window.ethereum, chainId) setProvider(new BrowserProvider(window.ethereum, chainId))
updateAccounts(chainId, provider) updateAccounts(chainId, provider)
} }
else { else {
@@ -121,17 +121,17 @@ export function detectChain() {
try { try {
window.ethereum.on('chainChanged', onChainChanged); window.ethereum.on('chainChanged', onChainChanged);
window.ethereum.on('accountsChanged', onAccountsChanged); window.ethereum.on('accountsChanged', onAccountsChanged);
new ethers.BrowserProvider(window.ethereum).getNetwork().then((network)=>{
const chainId = network.chainId
onChainChanged(chainId)
})
} }
catch (e) { catch (e) {
console.log('Could not connect change hooks to wallet', e) console.log('Could not connect change hooks to wallet', e)
return
} }
new ethers.BrowserProvider(window.ethereum).getNetwork().then((network)=>{
const chainId = network.chainId
onChainChanged(chainId)
})
} }
detectChain()
const errorHandlingProxy = { const errorHandlingProxy = {
get(target, prop, proxy) { get(target, prop, proxy) {
@@ -244,7 +244,7 @@ async function _discoverVaults(owner) {
if( useWalletStore().transaction ) { if( useWalletStore().transaction ) {
const num = 0 // todo multiple vaults const num = 0 // todo multiple vaults
if (result.length) if (result.length)
flushOrders(s.chainId, owner, num, result[0]) flushWalletTransactions(s.chainId, owner, num, result[0])
else else
ensureVault2(s.chainId, owner, num) ensureVault2(s.chainId, owner, num)
} }
@@ -286,7 +286,7 @@ async function doEnsureVault(chainId, owner, num) {
if (s.vaults.length <= num) if (s.vaults.length <= num)
await _discoverVaults(owner) await _discoverVaults(owner)
if( s.vaults[num] ) if( s.vaults[num] )
flushOrders(chainId, owner, num, s.vaults[num]) flushWalletTransactions(chainId, owner, num, s.vaults[num])
else { else {
console.log(`requesting vault ${owner} ${num}`) console.log(`requesting vault ${owner} ${num}`)
socket.emit('ensureVault', chainId, owner, num) socket.emit('ensureVault', chainId, owner, num)
@@ -308,23 +308,20 @@ export async function cancelOrder(vault, orderIndex) {
}) })
} }
export async function cancelAll(vault) {
new CancelAllTransaction(useStore().chainId, vault).submit()
}
async function progressTransactions() { async function progressTransactions() {
const s = useStore() const s = useStore()
const ws = useWalletStore(); const ws = useWalletStore();
console.log('progressTransactions', ws.transaction)
if( ws.transaction===null ) if( ws.transaction===null )
return return
if( s.account === null ) { if( s.account === null ) {
let signer = null let signer = null
try { try {
console.log('account is null. requesting sign-in.')
signer = await provider.getSigner() signer = await provider.getSigner()
} }
catch (e) { catch (e) {
console.log('signer error', e.code, e.info.error.code) console.log('signer error', e.code, e.info?.error?.code)
if (e?.info?.error?.code === 4001) { if (e?.info?.error?.code === 4001) {
console.log('signer rejected') console.log('signer rejected')
signer = null signer = null
@@ -340,11 +337,27 @@ async function progressTransactions() {
} }
} }
if( s.vault === null ) { if( s.vault === null ) {
console.log('vault is null. requesting vault creation.')
ensureVault() ensureVault()
return return
} }
if( ws.transaction.state < TransactionState.Proposed )
ws.transaction.propose(s.account, s.vault)
if( ws.transaction.type === TransactionType.PlaceOrder ) { if( ws.transaction.type === TransactionType.PlaceOrder ) {
flushOrders(s.chainId, s.account, 0, s.vault) flushWalletTransactions(s.chainId, s.account, 0, s.vault)
}
else {
console.log('flushing transaction', ws.transaction.type)
if (ws.transaction.state < TransactionState.Proposed) {
pendTransaction(async (signer) => {
if (signer.address !== ws.transaction.owner) {
console.error('signer address does not match transaction owner', signer.address, ws.transaction.owner)
return
}
const contract = await vaultContract(ws.transaction.vault, signer)
return await ws.transaction.createTx(contract)
})
}
} }
} }
@@ -353,12 +366,10 @@ const transactionProgressor = new SingletonCoroutine(progressTransactions, 10)
let progressionInvoker = null let progressionInvoker = null
export function flushOrders(chainId, owner, num, vault) { export function flushWalletTransactions(chainId, owner, num, vault) {
const ws = useWalletStore(); const ws = useWalletStore();
console.log('flushOrders', chainId, owner, num, vault) console.log('flushWalletTransactions', chainId, owner, num, vault)
if (ws.transaction!==null && ws.transaction.type === TransactionType.PlaceOrder && ws.transaction.state < TransactionState.Proposed) let needsFlush = ws.transaction !== null && ws.transaction.type !== TransactionType.PlaceOrder
ws.transaction.propose(owner, vault)
let needsFlush = false
for( const pend of ws.pendingOrders ) { for( const pend of ws.pendingOrders ) {
if (pend.vault === null) if (pend.vault === null)
pend.vault = vault pend.vault = vault
@@ -427,6 +438,7 @@ function pendOrderAsTransaction(pend) {
export function pendTransaction(sender, errHandler) { export function pendTransaction(sender, errHandler) {
console.log('pendTransaction')
const s = useStore() const s = useStore()
s.transactionSenders.push([sender,errHandler]) s.transactionSenders.push([sender,errHandler])
flushTransactions() flushTransactions()
@@ -445,6 +457,7 @@ async function asyncFlushTransactions() {
console.log('flushTransactions', ws.transaction, s.vault) console.log('flushTransactions', ws.transaction, s.vault)
if (ws.transaction !== null) { if (ws.transaction !== null) {
if (s.vault === null) { if (s.vault === null) {
console.log('transaction doesn\'t have a vault. creating one.')
await ensureVault() await ensureVault()
if (s.vault === null) { if (s.vault === null) {
console.error('vault could not be created') console.error('vault could not be created')
@@ -462,8 +475,10 @@ async function asyncFlushTransactions() {
return return
} }
const senders = s.transactionSenders const senders = s.transactionSenders
if (!senders.length) if (!senders.length) {
console.log('no transactionSenders!')
return return
}
console.log(`flushing ${senders.length} transactions`) console.log(`flushing ${senders.length} transactions`)
let signer let signer
try { try {
@@ -579,7 +594,7 @@ const _chainInfos = {
1337: { 1337: {
"chainId": "0x539", "chainId": "0x539",
"chainName": "Dexorder Alpha Testnet", "chainName": "Dexorder Alpha Testnet",
"rpcUrls": ["https://rpc.alpha.dexorder.trade"], "rpcUrls": ["https://rpc.alpha.dexorder.com"],
"nativeCurrency": { "nativeCurrency": {
"name": "Test Ethereum", "name": "Test Ethereum",
"symbol": "TETH", "symbol": "TETH",
@@ -643,4 +658,4 @@ export async function addNetworkAndConnectWallet(chainId) {
if (e.code !== 4001) if (e.code !== 4001)
console.log('connectWallet() failed', e) console.log('connectWallet() failed', e)
} }
} }

View File

@@ -27,11 +27,11 @@ export function removeSymbolChangedCallback(cb) {
function symbolChanged(symbol) { function symbolChanged(symbol) {
const info = symbol===null ? (defaultSymbol===null?'default':defaultSymbol) : lookupSymbol(symbol.ticker) const info = symbol===null ? (defaultSymbol===null?'default':defaultSymbol) : lookupSymbol(symbol.ticker)
co.selectedSymbol = info co.selectedSymbol = info
console.log('setting prefs ticker', info.ticker) // console.log('setting prefs ticker', info.ticker)
prefs.selectedTicker = info.ticker prefs.selectedTicker = info.ticker
symbolChangedCbs.forEach((cb) => cb(info)) symbolChangedCbs.forEach((cb) => cb(info))
updateFeeDropdown() updateFeeDropdown()
console.log('symbol changed', info) // console.log('symbol changed', info)
} }
@@ -74,7 +74,7 @@ export function changeIntervalSecs(secs) {
function dataLoaded() { function dataLoaded() {
const range = chartMeanRange() const range = chartMeanRange()
console.log('new mean range', range,) // console.log('new mean range', range,)
co.meanRange = range co.meanRange = range
} }
@@ -158,6 +158,7 @@ export function initTVButtons() {
} }
export function initWidget(el) { export function initWidget(el) {
getAllSymbols()
const symbol = prefs.selectedTicker === null ? 'default' : prefs.selectedTicker const symbol = prefs.selectedTicker === null ? 'default' : prefs.selectedTicker
const interval = prefs.selectedTimeframe === null ? '15' : prefs.selectedTimeframe const interval = prefs.selectedTimeframe === null ? '15' : prefs.selectedTimeframe
widget = window.tvWidget = new TradingView.widget({ widget = window.tvWidget = new TradingView.widget({
@@ -212,7 +213,7 @@ let chartInitCbs = []
function initChart() { function initChart() {
console.log('init chart') // console.log('init chart')
chart = widget.activeChart() chart = widget.activeChart()
const themeName = useStore().theme; const themeName = useStore().theme;
widget.changeTheme(themeName).catch((e)=>console.warn(`Could not change theme to ${themeName}`, e)) widget.changeTheme(themeName).catch((e)=>console.warn(`Could not change theme to ${themeName}`, e))
@@ -240,7 +241,7 @@ function initChart() {
cb(widget, chart) cb(widget, chart)
chartInitCbs = [] chartInitCbs = []
}, 1) }, 1)
console.log('chart ready') // console.log('chart ready')
} }
@@ -285,7 +286,7 @@ let drawingCallbacks = null
export function drawShape(shapeType, ...callbacks) { export function drawShape(shapeType, ...callbacks) {
// puts the chart into a line-drawing mode for a new shape // puts the chart into a line-drawing mode for a new shape
console.log('drawShape', callbacks, shapeType.name, shapeType.code) // console.log('drawShape', callbacks, shapeType.name, shapeType.code)
if( drawingCallbacks ) if( drawingCallbacks )
invokeCallbacks(drawingCallbacks, 'onUndraw') invokeCallbacks(drawingCallbacks, 'onUndraw')
drawingCallbacks = callbacks drawingCallbacks = callbacks
@@ -351,7 +352,7 @@ const shapeCallbacks = {}
function onSelectedLineToolChanged() { function onSelectedLineToolChanged() {
const tool = widget.selectedLineTool(); const tool = widget.selectedLineTool();
console.log('line tool changed', tool) // console.log('line tool changed', tool)
if (drawingTool===null) if (drawingTool===null)
drawingTool = tool drawingTool = tool
else if (tool!==drawingTool && co.drawing) else if (tool!==drawingTool && co.drawing)

View File

@@ -1,3 +1,4 @@
import {provider} from "@/blockchain/provider.js";
import {convertTvResolution, loadOHLC} from './ohlc.js'; import {convertTvResolution, loadOHLC} from './ohlc.js';
import {metadata} from "@/version.js"; import {metadata} from "@/version.js";
import FlexSearch from "flexsearch"; import FlexSearch from "flexsearch";
@@ -5,12 +6,11 @@ import {useChartOrderStore} from "@/orderbuild.js";
import {useStore} from "@/store/store.js"; import {useStore} from "@/store/store.js";
import {subOHLC, unsubOHLC} from "@/blockchain/ohlcs.js"; import {subOHLC, unsubOHLC} from "@/blockchain/ohlcs.js";
import {ohlcStart} from "@/charts/chart-misc.js"; import {ohlcStart} from "@/charts/chart-misc.js";
import {timestamp, withTimeout} from "@/common.js";
import {timestamp, USD_FIAT} from "@/common.js";
import {erc20Contract} from "@/blockchain/contract.js"; import {erc20Contract} from "@/blockchain/contract.js";
import {provider} from "@/blockchain/wallet.js";
import {track} from "@/track.js"; import {track} from "@/track.js";
const DEBUG_LOGGING = false const DEBUG_LOGGING = false
const log = DEBUG_LOGGING ? console.log : ()=>{} const log = DEBUG_LOGGING ? console.log : ()=>{}
@@ -335,6 +335,14 @@ class RealtimeSubscription {
} }
async function getLiquidities(markToken, symbolItem) {
const token = await erc20Contract(markToken.a, provider)
const liquidities = await Promise.all(symbolItem.feeGroup.map(
async ([addr, fee]) => await token.balanceOf(addr)
))
return liquidities;
}
export const DataFeed = { export const DataFeed = {
onReady(callback) { onReady(callback) {
log('[onReady]: Method call'); log('[onReady]: Method call');
@@ -381,11 +389,14 @@ export const DataFeed = {
onResolveErrorCallback, onResolveErrorCallback,
extension extension
) { ) {
log('[resolveSymbol]: Method call', symbolName); console.log('resolveSymbol', symbolName);
const symbols = getAllSymbols(); const symbols = getAllSymbols();
const symbolItem = symbolName === 'default' ? defaultSymbol : symbols[symbolName] const symbolItem = symbolName === 'default' ? defaultSymbol : symbols[symbolName]
if (symbolName==='default') {
console.log('using default symbol', defaultSymbol)
}
if (!symbolItem) { if (!symbolItem) {
log('[resolveSymbol]: Cannot resolve symbol', symbolName); console.log('[resolveSymbol]: Cannot resolve symbol', symbolName);
onResolveErrorCallback('cannot resolve symbol'); onResolveErrorCallback('cannot resolve symbol');
return; return;
} }
@@ -399,10 +410,11 @@ export const DataFeed = {
const inv = invertedDefault(symbolItem.base.a, symbolItem.quote.a) const inv = invertedDefault(symbolItem.base.a, symbolItem.quote.a)
const markToken = inv ? symbolItem.base : symbolItem.quote const markToken = inv ? symbolItem.base : symbolItem.quote
const mark = useStore().markPrice(markToken.a) const mark = useStore().markPrice(markToken.a)
const token = await erc20Contract(markToken.a, provider) const liquidities = await withTimeout(
const liquidities = await Promise.all(symbolItem.feeGroup.map( getLiquidities(markToken, symbolItem),
async ([addr, fee]) => await token.balanceOf(addr) 3000,
)) 'liquidity fetch timeout'
)
symbolItem.liquidities = liquidities.map(l => Number(l / 10n ** BigInt(markToken.d))) symbolItem.liquidities = liquidities.map(l => Number(l / 10n ** BigInt(markToken.d)))
if (mark) { if (mark) {
symbolItem.liquidities = symbolItem.liquidities.map(l => l * mark) symbolItem.liquidities = symbolItem.liquidities.map(l => l * mark)
@@ -451,7 +463,7 @@ export const DataFeed = {
// volume_precision: 2, // volume_precision: 2,
data_status: 'streaming', data_status: 'streaming',
}; };
log('[resolveSymbol]: Symbol resolved', symbolName); // console.log('[resolveSymbol]: Symbol resolved', symbolName);
onSymbolResolvedCallback(symbolInfo) onSymbolResolvedCallback(symbolInfo)
}, },

View File

@@ -52,7 +52,7 @@ function addDay(timestamp) {
function addMonth(timestamp) { function addMonth(timestamp) {
const date = new Date(timestamp*1000) const date = new Date(timestamp*1000)
const result = Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate()) / 1000 const result = Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate()) / 1000
console.log('addMonth', timestamp, result, new Date(timestamp*1000), new Date(result*1000)) // console.log('addMonth', timestamp, result, new Date(timestamp*1000), new Date(result*1000))
return result return result
} }
@@ -137,7 +137,7 @@ async function getUrl(url) {
export async function loadOHLC (symbol, contract, from, to, tvRes) { export async function loadOHLC (symbol, contract, from, to, tvRes) {
console.log('loadOHLC', tvRes, new Date(1000*from), new Date(1000*to), symbol, contract); // console.log('loadOHLC', tvRes, new Date(1000*from), new Date(1000*to), symbol, contract);
let chainId let chainId
let bars = []; let bars = [];
let inverted = false; let inverted = false;
@@ -179,7 +179,7 @@ export async function loadOHLC (symbol, contract, from, to, tvRes) {
if (response.length) { if (response.length) {
const [start,end,price] = response.split(',') const [start,end,price] = response.split(',')
seriesStarts[baseUrl] = parseInt(start) seriesStarts[baseUrl] = parseInt(start)
console.log(`Series ${baseUrl} starts at ${new Date(start*1000)}`) // console.log(`Series ${baseUrl} starts at ${new Date(start*1000)}`)
} }
else { else {
console.error(`Bad response while fetching ${baseUrl+'quote.csv'}`) console.error(`Bad response while fetching ${baseUrl+'quote.csv'}`)

View File

@@ -158,3 +158,33 @@ export function dateString(datetime) {
export function logicalXOR(a, b) { export function logicalXOR(a, b) {
return (a || b) && !(a && b) return (a || b) && !(a && b)
} }
// Base62
// base62.js
const base62charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
export function encodeBase62(num) {
if (num === 0) return base62charset[0];
let encoded = "";
while (num > 0) {
encoded = base62charset[num % 62] + encoded;
num = Math.floor(num / 62);
}
return encoded;
}
export function decodeBase62(str) {
return str.split('').reverse().reduce((acc, char, i) =>
acc + base62charset.indexOf(char) * Math.pow(62, i), 0);
}
export function withTimeout(promise, timeoutDuration, fallbackErrorMessage) {
return Promise.race([
promise,
new Promise((_, reject) =>
setTimeout(() => reject(new Error(fallbackErrorMessage)), timeoutDuration)
),
]);
}

View File

@@ -1,4 +1,5 @@
<template> <template>
<v-alert class="d-sm-none" type="info" title="Mobile Screen" text="Dexorder is designed for desktop. Mobile coming soon!" rounded="0"/>
<v-alert v-if='!s.connected' icon="mdi-wifi-off" type="error" title="Not Connected" class="mb-3" rounded="0" density="compact"/> <v-alert v-if='!s.connected' icon="mdi-wifi-off" type="error" title="Not Connected" class="mb-3" rounded="0" density="compact"/>
<v-alert v-for="e in s.errors" icon="mdi-alert" type="error" :title="e.title" :text="e.text" class="mb-3" :closable="e.closeable" rounded="0"/> <v-alert v-for="e in s.errors" icon="mdi-alert" type="error" :title="e.title" :text="e.text" class="mb-3" :closable="e.closeable" rounded="0"/>
</template> </template>

View File

@@ -8,8 +8,8 @@
<script setup> <script setup>
import {useStore} from "@/store/store"; import {useStore} from "@/store/store";
import {socket} from "@/socket.js";
import {useRoute} from "vue-router"; import {useRoute} from "vue-router";
import {socket} from "@/socket.js";
const s = useStore() const s = useStore()

View File

@@ -0,0 +1,65 @@
<template>
<div class="debug-console">
<div v-for="(entry, idx) in logs" :key="idx" :class="['log-entry', entry.type]">
[{{ entry.type }}] {{ entry.message }}
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue';
const logs = ref([]);
function stringify(args) {
return args.map(arg => {
if (typeof arg === 'object') {
try { return JSON.stringify(arg); } catch { return String(arg); }
}
return String(arg);
}).join(' ');
}
let original = {};
onMounted(() => {
['log', 'warn', 'error', 'info'].forEach(type => {
// Save the original method
original[type] = console[type];
console[type] = function(...args) {
logs.value.push({
type,
message: stringify(args)
});
// Optionally limit log length:
if (logs.value.length > 200) logs.value.shift();
original[type].apply(console, args);
};
});
});
onBeforeUnmount(() => {
// Restore the original methods
Object.keys(original).forEach(type => {
if (original[type]) console[type] = original[type];
});
});
</script>
<style scoped>
.debug-console {
background: #1a1a1a;
color: #0f0;
font-family: monospace;
font-size: 14px;
padding: 10px;
max-height: 260px;
overflow-y: auto;
border-radius: 5px;
margin: 8px 0;
}
.log-entry { margin-bottom: 2px; }
.log-entry.warn { color: #ff0; }
.log-entry.error { color: #f55; }
.log-entry.info { color: #0cf; }
</style>

View File

@@ -36,8 +36,8 @@
import {useStore} from "@/store/store"; import {useStore} from "@/store/store";
import {computed, ref} from "vue"; import {computed, ref} from "vue";
import Btn from "@/components/Btn.vue"; import Btn from "@/components/Btn.vue";
import {socket} from "@/socket.js";
import {metadata} from "@/version.js"; import {metadata} from "@/version.js";
import {socket} from "@/socket.js";
const DISABLED_DURATION = 60_000; const DISABLED_DURATION = 60_000;

View File

@@ -0,0 +1,142 @@
<template>
<div
ref="floatingDiv"
:style="divStyle"
@mousedown="startDrag"
>
<slot></slot>
</div>
</template>
<script setup>
import { ref, reactive, onMounted, onBeforeUnmount } from 'vue';
const props = defineProps({
id: {
type: String,
required: false,
}
});
const floatingDiv = ref(null);
const position = reactive({ x: 0, y: 0 });
const isDragging = ref(false);
const isFloating = ref(false);
const dragStartOffset = reactive({ x: 0, y: 0 });
const divStyle = reactive({
position: "",
top: "",
left: "",
zIndex: "",
cursor: "default",
});
let activeComponent = null;
function positionKey() {
return props.id ? `floating-div-pos:${props.id}` : null;
}
function savePosition() {
if (props.id) {
localStorage.setItem(
positionKey(),
JSON.stringify({ x: position.x, y: position.y })
);
}
}
function loadPosition() {
if (props.id) {
const data = localStorage.getItem(positionKey());
if (data) {
try {
const { x, y } = JSON.parse(data);
position.x = x;
position.y = y;
divStyle.position = "fixed";
divStyle.top = `${position.y}px`;
divStyle.left = `${position.x}px`;
divStyle.zIndex = 9999;
isFloating.value = true;
} catch {
// Ignore corrupted data
}
}
}
}
const startDrag = (event) => {
if (event.shiftKey || event.ctrlKey) {
if (!isFloating.value) {
const rect = floatingDiv.value.getBoundingClientRect();
position.x = rect.left;
position.y = rect.top;
divStyle.position = "fixed";
divStyle.top = `${position.y}px`;
divStyle.left = `${position.x}px`;
divStyle.zIndex = 9999;
isFloating.value = true;
}
isDragging.value = true;
dragStartOffset.x = event.clientX - position.x;
dragStartOffset.y = event.clientY - position.y;
divStyle.cursor = "move";
document.body.style.userSelect = "none";
activeComponent = floatingDiv;
window.addEventListener("mousemove", handleDrag);
window.addEventListener("mouseup", stopDrag);
}
};
const handleDrag = (event) => {
if (isDragging.value && activeComponent === floatingDiv) {
position.x = event.clientX - dragStartOffset.x;
position.y = event.clientY - dragStartOffset.y;
divStyle.top = `${position.y}px`;
divStyle.left = `${position.x}px`;
}
};
const stopDrag = () => {
if (isDragging.value && activeComponent === floatingDiv) {
isDragging.value = false;
divStyle.cursor = "default";
document.body.style.userSelect = "auto";
activeComponent = null;
savePosition();
window.removeEventListener("mousemove", handleDrag);
window.removeEventListener("mouseup", stopDrag);
}
};
const handleOutsideDrag = (event) => {
if (event.key === "Shift" || event.key === "Control") {
stopDrag();
}
};
onMounted(() => {
document.addEventListener("keyup", handleOutsideDrag);
loadPosition();
});
onBeforeUnmount(() => {
document.removeEventListener("keyup", handleOutsideDrag);
window.removeEventListener("mousemove", handleDrag);
window.removeEventListener("mouseup", stopDrag);
});
</script>
<style>
div[ref="floatingDiv"] {
background: rgba(255, 255, 255, 0.8);
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
}
</style>

View File

@@ -15,7 +15,7 @@
<script setup> <script setup>
import {useStore} from "@/store/store"; import {useStore} from "@/store/store";
import router from "@/router/index.js"; import {router} from "@/router/router.js";
const s = useStore() const s = useStore()
function nav(path) { function nav(path) {

View File

@@ -11,7 +11,8 @@
</div> </div>
</v-card-item> </v-card-item>
<v-card-actions class="d-flex justify-space-evenly mb-4"> <v-card-actions class="d-flex justify-space-evenly mb-4">
<v-btn variant="outlined" color="red" @click="nav('Assets')">Cancel</v-btn> <v-btn variant="outlined" color="red" @click="// noinspection JSIgnoredPromiseFromCall
router.push({name: 'Assets'})">Cancel</v-btn>
<v-btn variant="flat" color="green" :disabled="!valid()" @click="placeOrder">Place Dexorder</v-btn> <v-btn variant="flat" color="green" :disabled="!valid()" @click="placeOrder">Place Dexorder</v-btn>
</v-card-actions> </v-card-actions>
</phone-card> </phone-card>
@@ -23,12 +24,13 @@ import {useOrderStore, useStore} from "@/store/store";
import PhoneCard from "@/components/PhoneCard.vue"; import PhoneCard from "@/components/PhoneCard.vue";
import Amount from "@/components/Amount.vue" import Amount from "@/components/Amount.vue"
// noinspection ES6UnusedImports // noinspection ES6UnusedImports
import {nav, SingletonCoroutine, vAutoSelect} from "@/misc.js"; import {SingletonCoroutine, vAutoSelect} from "@/misc.js";
import {newOrder} from "@/blockchain/orderlib.js"; import {newOrder} from "@/blockchain/orderlib.js";
import {FixedNumber} from "ethers"; import {FixedNumber} from "ethers";
import PairChoice from "@/components/PairChoice.vue"; import PairChoice from "@/components/PairChoice.vue";
import NeedsSigner from "@/components/NeedsSigner.vue"; import NeedsSigner from "@/components/NeedsSigner.vue";
import {useChartOrderStore} from "@/orderbuild.js"; import {useChartOrderStore} from "@/orderbuild.js";
import {router} from "@/router/router.js";
const s = useStore() const s = useStore()
const os = useOrderStore() const os = useOrderStore()

View File

@@ -3,6 +3,6 @@
<script setup> <script setup>
if(window.location.hostname !== 'localhost') { if(window.location.hostname !== 'localhost') {
window.$crisp = [];window.CRISP_WEBSITE_ID = "b153f30a-4b0b-49cc-8a38-989409a73acb";(function () {const d = document;const s = d.createElement("script");s.src = "https://client.crisp.chat/l.js";s.async = 1;d.getElementsByTagName("head")[0].appendChild(s);})(); window.$crisp=[];window.CRISP_WEBSITE_ID="a88f707f-adee-4960-b6de-c328c9d86945";(function(){const d=document;const s=d.createElement("script");s.src="https://client.crisp.chat/l.js";s.async=1;d.getElementsByTagName("head")[0].appendChild(s);})();
} }
</script> </script>

View File

@@ -22,8 +22,8 @@
import {usePrefStore} from "@/store/store.js"; import {usePrefStore} from "@/store/store.js";
import {computed, ref, watch} from "vue"; import {computed, ref, watch} from "vue";
import {socket} from "@/socket.js";
import TosCard from "@/components/TosCard.vue"; import TosCard from "@/components/TosCard.vue";
import {socket} from "@/socket.js";
// UPDATE THIS WHEN CHANGING THE TOS // UPDATE THIS WHEN CHANGING THE TOS
const CURRENT_VERSION='2024-11-18' // this must be a parseable date const CURRENT_VERSION='2024-11-18' // this must be a parseable date

View File

@@ -5,10 +5,10 @@
<v-card-text class="text-center">Last Updated November 18, 2024</v-card-text> <v-card-text class="text-center">Last Updated November 18, 2024</v-card-text>
<v-card-text> <v-card-text>
Please read these Terms of Service (the <b>Terms</b>) and our <a href="https://dexorder.trade/privacy-policy" target="dexorder">Privacy Policy</a> carefully because they govern your Please read these Terms of Service (the <b>Terms</b>) and our <a href="https://dexorder.com/privacy-policy" target="dexorder">Privacy Policy</a> carefully because they govern your
use of the use of the
website (and all subdomains and subpages thereon) located at dexorder.trade, including without limitation the website (and all subdomains and subpages thereon) located at dexorder.com, including without limitation the
subdomains app.dexorder.trade and www.dexorder.trade (collectively, the <b>Site</b>), and the Dexorder web subdomains app.dexorder.com and www.dexorder.com (collectively, the <b>Site</b>), and the Dexorder web
application graphical user interface and any other services accessible via the Site (together with the Site, web application graphical user interface and any other services accessible via the Site (together with the Site, web
application, and other services, collectively, the <b>Dexorder Service</b>) offered by Dexorder Trading Services application, and other services, collectively, the <b>Dexorder Service</b>) offered by Dexorder Trading Services
Ltd. Ltd.
@@ -205,7 +205,7 @@
(i) Subject to your compliance with these Terms, Dexorder will use its commercially (i) Subject to your compliance with these Terms, Dexorder will use its commercially
reasonable efforts to provide you with access to the Dexorder Service and to cause your Interactions to be reasonable efforts to provide you with access to the Dexorder Service and to cause your Interactions to be
executed on the applicable DEX in accordance with Dexorders Execution Policy located at executed on the applicable DEX in accordance with Dexorders Execution Policy located at
<a href="https://dexorder.trade/execution-policy">https://dexorder.trade/execution-policy</a> <a href="https://dexorder.com/execution-policy">https://dexorder.com/execution-policy</a>
(<b>Execution Policy</b>), however from time to time the Site and the Dexorder Service may be inaccessible or (<b>Execution Policy</b>), however from time to time the Site and the Dexorder Service may be inaccessible or
inoperable for any inoperable for any
reason, including, without limitation: (a) if an Interaction repeatedly fails to be executed (such as due to an reason, including, without limitation: (a) if an Interaction repeatedly fails to be executed (such as due to an
@@ -690,7 +690,7 @@
<v-card-title>18. Contact Information</v-card-title> <v-card-title>18. Contact Information</v-card-title>
<v-card-text> <v-card-text>
If you have any questions about these Terms or the Dexorder Service, please contact Dexorder If you have any questions about these Terms or the Dexorder Service, please contact Dexorder
at <a href="mailto:legal@dexorder.trade">legal@dexorder.trade</a> or <a href="mailto:support@dexorder.trade">support@dexorder.trade</a>. at <a href="mailto:legal@dexorder.com">legal@dexorder.com</a> or <a href="mailto:support@dexorder.com">support@dexorder.com</a>.
</v-card-text> </v-card-text>

View File

@@ -34,10 +34,10 @@
<v-card-item v-if="!empty"> <v-card-item v-if="!empty">
<v-table> <v-table>
<tbody> <tbody>
<native-row v-if="nativeBalance" :chain-id="s.chainId" :addr="s.vault" :amount="nativeBalance" <native-row v-if="nativeBalance && BigInt(nativeBalance)>0n" :chain-id="s.chainId" :addr="s.vault" :amount="nativeBalance"
:on-withdraw="onWithdrawNative" :on-wrap="()=>wrapShow=true"/> :on-withdraw="onWithdrawNative" :on-wrap="()=>wrapShow=true"/>
<suspense v-for="(amount,addr) of balances"> <suspense v-for="(amount,addr) of balances">
<token-row v-if="BigInt(amount)!==0n" :chain-id="s.chainId" :addr="addr" :amount="amount" :onWithdraw="onWithdraw"/> <token-row v-if="amount && BigInt(amount)>0n" :chain-id="s.chainId" :addr="addr" :amount="amount" :onWithdraw="onWithdraw"/>
</suspense> </suspense>
</tbody> </tbody>
</v-table> </v-table>
@@ -60,12 +60,13 @@
import {useStore} from "@/store/store.js"; import {useStore} from "@/store/store.js";
import {computed, defineAsyncComponent, onUnmounted, ref, watchEffect} from "vue"; import {computed, defineAsyncComponent, onUnmounted, ref, watchEffect} from "vue";
import {vaultAddress} from "@/blockchain/contract.js"; import {vaultAddress} from "@/blockchain/contract.js";
import {ensureVault, provider} from "@/blockchain/wallet.js"; import {ensureVault} from "@/blockchain/wallet.js";
import CopyButton from "@/components/CopyButton.vue"; import CopyButton from "@/components/CopyButton.vue";
import Withdraw from "@/components/Withdraw.vue"; import Withdraw from "@/components/Withdraw.vue";
import NativeRow from "@/components/NativeRow.vue"; import NativeRow from "@/components/NativeRow.vue";
import NativeWrap from "@/components/NativeWrap.vue"; import NativeWrap from "@/components/NativeWrap.vue";
import WithdrawNative from "@/components/WithdrawNative.vue"; import WithdrawNative from "@/components/WithdrawNative.vue";
import {provider} from "@/blockchain/provider.js";
const TokenRow = defineAsyncComponent(()=>import('./TokenRow.vue')) const TokenRow = defineAsyncComponent(()=>import('./TokenRow.vue'))
const s = useStore() const s = useStore()
@@ -89,7 +90,7 @@ const hasVault = computed(()=>s.vault!==null)
const withdrawToken = ref(null) const withdrawToken = ref(null)
const withdrawShow = ref(false) const withdrawShow = ref(false)
async function onWithdraw(token) { async function onWithdraw(token) {
console.log('withdraw', addr, token) console.log('withdraw', addr.value, token)
withdrawToken.value = token withdrawToken.value = token
withdrawShow.value = true withdrawShow.value = true
} }

View File

@@ -59,7 +59,7 @@ function tryIt() {
function learnMore() { function learnMore() {
track('learn-more') track('learn-more')
window.open('https://dexorder.trade/introduction.html', 'dexordertrade') window.open('https://dexorder.com/introduction.html', 'dexorderwww')
modelValue.value = false modelValue.value = false
} }

View File

@@ -11,7 +11,7 @@
<v-btn variant="text" text="max" @click="floatAmount=balanceFloat"/> <v-btn variant="text" text="max" @click="floatAmount=balanceFloat"/>
</template> </template>
<template v-slot:append-inner> <template v-slot:append-inner>
<span>{{ token.s }}</span> <span style="max-width: 6em">{{ token.s }}</span>
</template> </template>
</v-text-field> </v-text-field>
<v-card-actions> <v-card-actions>
@@ -36,11 +36,17 @@ const s = useStore()
const props = defineProps(['modelValue', 'vault', 'token']) const props = defineProps(['modelValue', 'vault', 'token'])
const emit = defineEmits(['update:modelValue']) const emit = defineEmits(['update:modelValue'])
const balance = computed(() => { const balance = computed(() => {
console.log('balance', props.vault, props.token, s.vaultBalances) const b = s.getBalance(props.token?.a)
const b = s.vaultBalances[props.vault][props.token.address]; console.log('balance', b)
// const b = s.vaultBalances[props.vault][props.token.address];
return b === undefined ? 0n : BigInt(b) return b === undefined ? 0n : BigInt(b)
}) })
const balanceFloat = computed(() => tokenFloat(props.token, balance.value)) const balanceFloat = computed(() => {
let balance = tokenFloat(props.token, s.getBalance(props.token?.a))
balance /= 10**props.token.d
console.log('balanceFloat', balance, s.getBalance(props.token?.a), props.token)
return balance
})
const floatAmount = ref(0) const floatAmount = ref(0)
function withdraw() { function withdraw() {

View File

@@ -85,7 +85,6 @@ const tokenOut = computed(()=>props.order.buy ? co.baseToken : co.quoteToken)
const builtAny = ref(false) const builtAny = ref(false)
console.log('order', props.order)
function build(order, component, options={}) { function build(order, component, options={}) {
track('build', {builder:component}) track('build', {builder:component})
builtAny.value = true builtAny.value = true

View File

@@ -10,8 +10,8 @@
:disabled="!orderChanged" @click="resetOrder">Reset</v-btn> :disabled="!orderChanged" @click="resetOrder">Reset</v-btn>
<v-btn id="share-btn" variant="text" prepend-icon="mdi-share" v-if="co.orders.length>0" <v-btn id="share-btn" variant="text" prepend-icon="mdi-share" v-if="co.orders.length>0"
:disabled="sharing" :disabled="sharing"
@click="shareOrder">{{sharing?'Preparing...':'Share'}}</v-btn> @click="shareOrder">{{sharing?'Preparing...':'Share Order'}}</v-btn>
<v-tooltip activator="#share-btn" text="Copied share link!" v-model="showSharedTooltip" <v-tooltip activator="#share-btn" :text="shareError?'Error copying share link :(':'Copied share link!'" v-model="showSharedTooltip"
:open-on-hover="false" :open-on-click="false" :open-on-hover="false" :open-on-click="false"
/> />
</template> </template>
@@ -80,7 +80,7 @@ const os = useOrderStore()
const ws = useWalletStore() const ws = useWalletStore()
function changeSymbol(symbol) { function changeSymbol(symbol) {
console.log('changeSymbol', symbol) // console.log('changeSymbol', symbol)
os.tokenA = symbol.base os.tokenA = symbol.base
os.tokenB = symbol.quote os.tokenB = symbol.quote
// routeFinder.invoke() // routeFinder.invoke()
@@ -205,12 +205,20 @@ async function doPlaceOrder() {
const sharing = ref(false) const sharing = ref(false)
const showSharedTooltip = ref(false) const showSharedTooltip = ref(false)
const shareError = ref(false)
const showShareDialog = ref(false) const showShareDialog = ref(false)
const shareUrl = ref(null) const shareUrl = ref(null)
function shareOrder() { function shareOrder() {
sharing.value = true sharing.value = true
track('share')
getShareUrl().then(url => { getShareUrl().then(url => {
shareError.value = url === null
if (url === null) {
sharing.value = false
return
}
console.log('share url', url)
shareUrl.value = url shareUrl.value = url
sharing.value = false sharing.value = false
navigator.permissions.query({name: "clipboard-write"}).then((permission) => { navigator.permissions.query({name: "clipboard-write"}).then((permission) => {

View File

@@ -2,7 +2,9 @@
<toolbar-pane title="Status" icon="mdi-format-list-bulleted-square"> <toolbar-pane title="Status" icon="mdi-format-list-bulleted-square">
<template #toolbar> <template #toolbar>
<v-btn variant="text" v-if="s.vault" prepend-icon="mdi-delete-alert" color="red" <v-btn variant="text" v-if="s.vault" prepend-icon="mdi-delete-alert" color="red"
@click="cancelAll(s.vault)" :disabled="!anyOrdersOpen" @click="(async function (vault){
new CancelAllTransaction(useStore().chainId, vault).submit()
})(s.vault)" :disabled="!anyOrdersOpen"
text="Cancel All"/> text="Cancel All"/>
</template> </template>
<needs-signer> <needs-signer>
@@ -16,10 +18,10 @@
import ToolbarPane from "@/components/chart/ToolbarPane.vue"; import ToolbarPane from "@/components/chart/ToolbarPane.vue";
import Orders from "@/components/Status.vue"; import Orders from "@/components/Status.vue";
import NeedsSigner from "@/components/NeedsSigner.vue"; import NeedsSigner from "@/components/NeedsSigner.vue";
import {cancelAll} from "@/blockchain/wallet.js";
import {useStore} from "@/store/store.js"; import {useStore} from "@/store/store.js";
import {computed} from "vue"; import {computed} from "vue";
import {isOpen} from "@/blockchain/orderlib.js"; import {isOpen} from "@/blockchain/orderlib.js";
import {CancelAllTransaction} from "@/blockchain/transaction.js";
const s = useStore() const s = useStore()

View File

@@ -46,7 +46,7 @@
</template> </template>
<script setup> <script setup>
import {builderDefaults, DEFAULT_SLIPPAGE, useChartOrderStore} from "@/orderbuild.js"; import {builderDefaults, useChartOrderStore} from "@/orderbuild.js";
import {allocationText, ShapeType} from "@/charts/shape.js"; import {allocationText, ShapeType} from "@/charts/shape.js";
import {sideColor, SingletonCoroutine, toPrecision, vAutoSelect} from "@/misc.js"; import {sideColor, SingletonCoroutine, toPrecision, vAutoSelect} from "@/misc.js";
import {useStore} from "@/store/store.js"; import {useStore} from "@/store/store.js";
@@ -57,7 +57,7 @@ import BuilderPanel from "@/components/chart/BuilderPanel.vue";
import {ohlcStart} from "@/charts/chart-misc.js"; import {ohlcStart} from "@/charts/chart-misc.js";
import Color from "color"; import Color from "color";
import OrderAmount from "@/components/chart/OrderAmount.vue"; import OrderAmount from "@/components/chart/OrderAmount.vue";
import {MAX_FRACTION, newTranche} from "@/blockchain/orderlib.js"; import {DEFAULT_SLIPPAGE, MAX_FRACTION, newTranche} from "@/blockchain/orderlib.js";
import {getFeeSchedule} from "@/fees.js"; import {getFeeSchedule} from "@/fees.js";
import {NATIVE_TOKEN} from "@/common.js"; import {NATIVE_TOKEN} from "@/common.js";

View File

@@ -39,11 +39,11 @@
</template> </template>
<script setup> <script setup>
import {builderDefaults, DEFAULT_SLIPPAGE, MIN_EXECUTION_TIME, useChartOrderStore} from "@/orderbuild.js"; import {builderDefaults, useChartOrderStore} from "@/orderbuild.js";
import {allocationText, VLine} from "@/charts/shape.js"; import {allocationText, VLine} from "@/charts/shape.js";
import {sideColor} from "@/misc.js"; import {sideColor} from "@/misc.js";
import {useOrderStore, usePrefStore, useStore} from "@/store/store.js"; import {useOrderStore, usePrefStore, useStore} from "@/store/store.js";
import {DISTANT_FUTURE, MAX_FRACTION, newTranche} from "@/blockchain/orderlib.js"; import {DEFAULT_SLIPPAGE, DISTANT_FUTURE, MAX_FRACTION, MIN_EXECUTION_TIME, newTranche} from "@/blockchain/orderlib.js";
import RungBuilder from "@/components/chart/RungBuilder.vue"; import RungBuilder from "@/components/chart/RungBuilder.vue";
import {computed, ref, watchEffect} from "vue"; import {computed, ref, watchEffect} from "vue";
import {chart, dragging} from "@/charts/chart.js"; import {chart, dragging} from "@/charts/chart.js";

View File

@@ -18,9 +18,9 @@
</template> </template>
<script setup> <script setup>
import {builderDefaults, builderFuncs, DEFAULT_SLIPPAGE, useChartOrderStore} from "@/orderbuild.js"; import {builderDefaults, builderFuncs, useChartOrderStore} from "@/orderbuild.js";
import {computed, onMounted, onUnmounted} from "vue"; import {computed, onMounted, onUnmounted} from "vue";
import {newTranche} from "@/blockchain/orderlib.js"; import {DEFAULT_SLIPPAGE, newTranche} from "@/blockchain/orderlib.js";
const co = useChartOrderStore() const co = useChartOrderStore()
const props = defineProps(['order', 'builder']) const props = defineProps(['order', 'builder'])

View File

@@ -55,7 +55,7 @@ const s = useStore()
const co = useChartOrderStore() const co = useChartOrderStore()
const theme = useTheme().current const theme = useTheme().current
console.log('theme', theme.value) // console.log('theme', theme.value)
const sideColor = computed(()=>new Color(props.order.buy?theme.value.colors.success:theme.value.colors.error).darken(0.2).string()) const sideColor = computed(()=>new Color(props.order.buy?theme.value.colors.success:theme.value.colors.error).darken(0.2).string())
const switchModel = computed({ const switchModel = computed({
get() {return !props.order.buy}, get() {return !props.order.buy},

View File

@@ -9,6 +9,9 @@
@click="()=>{if (props.builder.breakout!==undefined) props.builder.breakout=!props.builder.breakout}">{{ name }}</v-btn> @click="()=>{if (props.builder.breakout!==undefined) props.builder.breakout=!props.builder.breakout}">{{ name }}</v-btn>
<div class="description w-100 text-center">{{description}}</div> <div class="description w-100 text-center">{{description}}</div>
</div> </div>
-->
<!--
<floating-div id="rungs" v-if="endpoints[0]">
--> -->
<v-text-field type="number" v-model="rungs" <v-text-field type="number" v-model="rungs"
density="compact" hide-details class="mx-1 my-2" variant="outlined" density="compact" hide-details class="mx-1 my-2" variant="outlined"
@@ -16,9 +19,11 @@
:color="color" :color="color"
min="1" :max="MAX_RUNGS" min="1" :max="MAX_RUNGS"
:disabled="rungsDisabled" :disabled="rungsDisabled"
style="width: 6.6em; max-height: 2.5em; height: 2.5em"
id="rungs" id="rungs"
/> />
<!--
</floating-div>
-->
<one-time-hint name="rungs" activator="#rungs" after="choose-builder" <one-time-hint name="rungs" activator="#rungs" after="choose-builder"
text="↓ Try increasing rungs!" location="top" text="↓ Try increasing rungs!" location="top"
:when="rungs===1&&endpoints[0]!==null" :when="rungs===1&&endpoints[0]!==null"
@@ -50,10 +55,16 @@
</div> </div>
<div v-if="rungs>1" class="mx-2 d-flex justify-start"> <div v-if="rungs>1" class="mx-2 d-flex justify-start">
<div class="d-flex align-center mt-2"> <div class="d-flex align-center mt-2">
<!--
<floating-div id="slider" v-if="endpoints[0]">
-->
<div id="balance-slider"> <div id="balance-slider">
<v-slider v-if="rungs>1" :direction="orientation?'vertical':'horizontal'" min="-100" max="100" v-model="balance100" <v-slider v-if="rungs>1" :direction="orientation?'vertical':'horizontal'" min="-100" max="100" v-model="balance100"
class="no-slider-bg ml-2 mr-4" hide-details/> class="no-slider-bg ml-2 mr-4" hide-details/>
</div> </div>
<!--
</floating-div>
-->
<one-time-hint name="balance-slider" activator="#balance-slider" after="rungs" <one-time-hint name="balance-slider" activator="#balance-slider" after="rungs"
text="↓ Slide the amount balance ↓" location="top" text="↓ Slide the amount balance ↓" location="top"
:when="balance100===0" :when="balance100===0"
@@ -93,6 +104,7 @@ import {
import {logicalXOR} from "@/common.js"; import {logicalXOR} from "@/common.js";
import OneTimeHint from "@/components/OneTimeHint.vue"; import OneTimeHint from "@/components/OneTimeHint.vue";
import {track} from "@/track.js"; import {track} from "@/track.js";
import FloatingDiv from "@/components/FloatingDiv.vue";
const co = useChartOrderStore() const co = useChartOrderStore()
const endpoints = defineModel('modelValue') // 2-item list of points/values const endpoints = defineModel('modelValue') // 2-item list of points/values
@@ -119,7 +131,9 @@ const flippedSign = computed(()=>props.flip?-1:1)
const balance100 = computed( { const balance100 = computed( {
get() {return flippedSign.value*props.builder.balance*100}, get() {return flippedSign.value*props.builder.balance*100},
set(v) {props.builder.balance = flippedSign.value*v/100; } set(v) {
// if (v<-60) v = -60;
props.builder.balance = flippedSign.value*v/100; }
} ) } )
// validity checks // validity checks
@@ -236,11 +250,7 @@ const values = computed(()=>{
const weights = computed(() => { const weights = computed(() => {
// const balance = props.flip ? -props.builder.balance : props.builder.balance // const balance = props.flip ? -props.builder.balance : props.builder.balance
const most = 0.998 const most = 0.998
let balance = -props.builder.balance let balance = Math.min(most, Math.max(-most, -props.builder.balance))
if (balance <= -1)
balance = -most
else if (balance >= 1)
balance = most
const ws = linearWeights(props.builder.rungs, balance) const ws = linearWeights(props.builder.rungs, balance)
if (props.setWeights) if (props.setWeights)
props.setWeights(ws) props.setWeights(ws)

View File

@@ -1,14 +1,28 @@
<template> <template>
<h2>Shared Order</h2>
<p>Loading the shared order into the app...</p>
</template> </template>
<script setup> <script setup>
import {loadShareUrl} from "@/share.js"; import {loadShareUrl} from "@/share.js";
import router from "@/router/index.js"; import {router} from "@/router/router.js";
import {useRoute} from "vue-router";
loadShareUrl() const route = useRoute()
router.replace('/order') const code = route.params.code
loadShareUrl(code).then((ok)=> {
if (ok) {
console.log('loaded share data',code)
router.replace('/order');
}
else {
console.log('failed to load share data',code)
}
}).catch((e)=> {
console.error(e)
})
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
</style> </style>

View File

@@ -1,6 +1,7 @@
<template> <template>
<div class="d-flex mb-1 align-center w-100"> <div class="d-flex mb-1 align-center w-100">
<logo class="d-flex align-end clickable logo-large ml-1" @click="nav('Order')" :show-tag="true" max-height="32"/> <logo class="d-flex align-end clickable logo-large ml-1" @click="// noinspection JSIgnoredPromiseFromCall
router.push({name: 'Order'})" :show-tag="true" max-height="32"/>
<slot/> <slot/>
<div class="ml-auto d-flex align-center"> <div class="ml-auto d-flex align-center">
<span class="title mr-4">{{title}}</span> <span class="title mr-4">{{title}}</span>
@@ -9,7 +10,7 @@
<toolbar-button tooltip="Assets" icon="mdi-currency-btc" route="Assets"/> <toolbar-button tooltip="Assets" icon="mdi-currency-btc" route="Assets"/>
<!-- mdi-format-list-checks mdi-format-list-bulleted-square --> <!-- mdi-format-list-checks mdi-format-list-bulleted-square -->
<toolbar-button tooltip="Status" icon="mdi-format-list-checks" route="Status"/> <toolbar-button tooltip="Status" icon="mdi-format-list-checks" route="Status"/>
<toolbar-button tooltip="About" icon="mdi-information-outline" href="https://dexorder.trade/" target="dexorder"/> <toolbar-button tooltip="About" icon="mdi-information-outline" href="https://dexorder.com/" target="dexorderwww"/>
</div> </div>
</div> </div>
</template> </template>
@@ -17,7 +18,7 @@
<script setup> <script setup>
import ToolbarButton from "@/components/chart/ToolbarButton.vue"; import ToolbarButton from "@/components/chart/ToolbarButton.vue";
import Logo from "@/components/Logo.vue"; import Logo from "@/components/Logo.vue";
import {nav} from "@/misc.js"; import {router} from "@/router/router.js";
const props = defineProps(['title', 'icon']) const props = defineProps(['title', 'icon'])

View File

@@ -9,11 +9,9 @@
<script setup> <script setup>
import {computed} from "vue"; import {computed} from "vue";
import {useRoute} from "vue-router"; import {router} from "@/router/router.js";
import {nav} from "/src/misc.js"
const props = defineProps(['icon', 'route', 'tooltip', 'href', 'target']) const props = defineProps(['icon', 'route', 'tooltip', 'href', 'target'])
const router = useRoute();
const isCurrent = computed(() => router.name === props.route) const isCurrent = computed(() => router.name === props.route)
function click() { function click() {
@@ -29,7 +27,8 @@ function click() {
} }
else else
nav(props.route) // noinspection JSIgnoredPromiseFromCall
router.push({name: props.route})
} }
</script> </script>

View File

@@ -4,7 +4,7 @@
<script setup> <script setup>
function openApp() { function openApp() {
window.open('https://app.dexorder.trade/', 'dexorderapp') window.open('https://app.dexorder.com/', 'dexorderapp')
} }
</script> </script>

View File

@@ -26,7 +26,8 @@
</v-card-text> </v-card-text>
<div class="w-100 d-flex justify-center my-6 actions"> <div class="w-100 d-flex justify-center my-6 actions">
<app-btn/> <app-btn/>
<v-btn prepend-icon="mdi-information-outline" variant="flat" text="How It Works" @click="nav('HowItWorks')"/> <v-btn prepend-icon="mdi-information-outline" variant="flat" text="How It Works" @click="// noinspection JSIgnoredPromiseFromCall
router.push({name: 'HowItWorks'})"/>
</div> </div>
</v-card> </v-card>
</template> </template>
@@ -36,10 +37,8 @@ import beta from "@/components/Beta.vue";
import Soon from "@/components/Soon.vue"; import Soon from "@/components/Soon.vue";
import UniswapLogo from "@/corp/UniswapLogo.vue"; import UniswapLogo from "@/corp/UniswapLogo.vue";
import Logo from "@/components/Logo.vue"; import Logo from "@/components/Logo.vue";
import {nav} from "@/misc.js";
import AppBtn from "@/corp/AppBtn.vue"; import AppBtn from "@/corp/AppBtn.vue";
import Social from "@/components/Social.vue"; import {router} from "@/router/router.js";
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -49,7 +49,8 @@
</v-card-text> </v-card-text>
<v-card-actions class="justify-center my-6 actions"> <v-card-actions class="justify-center my-6 actions">
<app-btn/> <app-btn/>
<v-btn prepend-icon="mdi-home-outline" variant="flat" text="Home" @click="nav('Home')"/> <v-btn prepend-icon="mdi-home-outline" variant="flat" text="Home" @click="// noinspection JSIgnoredPromiseFromCall
router.push({name: 'Home'})"/>
</v-card-actions> </v-card-actions>
</v-card> </v-card>
</template> </template>
@@ -57,8 +58,8 @@
<script setup> <script setup>
import UniswapLogo from "@/corp/UniswapLogo.vue"; import UniswapLogo from "@/corp/UniswapLogo.vue";
import {nav} from "@/misc.js";
import AppBtn from "@/corp/AppBtn.vue"; import AppBtn from "@/corp/AppBtn.vue";
import {router} from "@/router/router.js";
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -3,11 +3,13 @@
<!-- <v-app-bar-nav-icon @click="s.nav=!s.nav" icon="mdi-plus"/>--> <!-- <v-app-bar-nav-icon @click="s.nav=!s.nav" icon="mdi-plus"/>-->
<v-app-bar-title> <v-app-bar-title>
<logo @click="nav('Home')" class="clickable"/> <logo @click="// noinspection JSIgnoredPromiseFromCall
router.push({name: 'Home'})" class="clickable"/>
<social class="d-inline" size="small"/> <social class="d-inline" size="small"/>
</v-app-bar-title> </v-app-bar-title>
<v-btn @click="nav('HowItWorks')" prepend-icon="mdi-information-outline" text="How It Works"/> <v-btn @click="// noinspection JSIgnoredPromiseFromCall
router.push({name: 'HowItWorks'})" prepend-icon="mdi-information-outline" text="How It Works"/>
<v-btn prepend-icon="mdi-arrow-up-bold" variant="tonal" color="primary" @click="openApp" text="Launch App"/> <v-btn prepend-icon="mdi-arrow-up-bold" variant="tonal" color="primary" @click="openApp" text="Launch App"/>
</v-app-bar> </v-app-bar>
@@ -16,13 +18,13 @@
<script setup> <script setup>
import {useTheme} from "vuetify"; import {useTheme} from "vuetify";
import Logo from "@/components/Logo.vue"; import Logo from "@/components/Logo.vue";
import {nav} from "@/misc.js";
import Social from "@/components/Social.vue"; import Social from "@/components/Social.vue";
import {router} from "@/router/router.js";
const theme = useTheme().current const theme = useTheme().current
function openApp() { function openApp() {
window.open('https://app.dexorder.trade/', 'dexorderapp') window.open('https://app.dexorder.com/', 'dexorderapp')
} }
</script> </script>

47
src/debug_console.js Normal file
View File

@@ -0,0 +1,47 @@
(function() {
let debugDiv = document.createElement('div');
debugDiv.id = 'debug-log';
debugDiv.setAttribute(
'style',
`
background:#222 !important;
color:#0f0 !important;
padding:8px !important;
font-family:monospace !important;
font-size:14px !important;
position:fixed !important;
left:0 !important; right:0 !important;
bottom:0 !important;
max-height:35vh !important;
width:100vw !important;
overflow-y:auto !important;
z-index:9999 !important;
pointer-events:auto !important;
box-shadow:0 0 8px #000 !important;
touch-action: auto !important;
-webkit-overflow-scrolling: touch !important;
white-space: pre-wrap !important;
word-break: break-word !important;
`
);
document.body.appendChild(debugDiv);
function printLog(type, args) {
let msg = Array.from(args).map(a => {
try { return typeof a === 'object' ? JSON.stringify(a, null, 2) : String(a); }
catch { return String(a); }
}).join(' ');
let line = document.createElement('div');
line.textContent = `[${type}] ${msg}`;
debugDiv.appendChild(line);
debugDiv.scrollTop = debugDiv.scrollHeight;
}
['trace', 'log', 'info', 'warn', 'error'].forEach(type => {
let orig = console[type];
console[type] = function(...args) {
printLog(type, args);
orig.apply(console, args);
}
});
})();

View File

@@ -1,7 +1,7 @@
import {newContract, vaultContract} from "@/blockchain/contract.js"; import {newContract, vaultContract} from "@/blockchain/contract.js";
import {provider} from "@/blockchain/wallet.js";
import {timestamp} from "@/common.js"; import {timestamp} from "@/common.js";
import TTLCache from "@isaacs/ttlcache"; import TTLCache from "@isaacs/ttlcache";
import {provider} from "@/blockchain/provider.js";
async function getFeeManagerContract(vaultContract) { async function getFeeManagerContract(vaultContract) {

View File

@@ -19,9 +19,9 @@ import MainView from './MainView.vue'
import {useStore} from "@/store/store.js"; import {useStore} from "@/store/store.js";
import {computed} from "vue"; import {computed} from "vue";
import {useWalletStore} from "@/blockchain/wallet.js"; import {useWalletStore} from "@/blockchain/wallet.js";
import {TransactionState, TransactionType} from "@/blockchain/transaction.js";
import {FixedNumber} from "ethers"; import {FixedNumber} from "ethers";
import PoolSelectionDialog from "@/components/PoolSelectionDialog.vue"; import PoolSelectionDialog from "@/components/PoolSelectionDialog.vue";
import {TransactionState, TransactionType} from "@/blockchain/transactionDecl.js";
const s = useStore() const s = useStore()
const ws = useWalletStore() const ws = useWalletStore()

View File

@@ -13,7 +13,7 @@ import { createApp } from 'vue'
// Plugins // Plugins
import { registerPlugins } from '@/plugins' import { registerPlugins } from '@/plugins'
import '@/styles/style.scss' import '@/styles/style.scss'
import "./socket.js" import "./socketInit.js"
import "./version.js" import "./version.js"
BigInt.prototype.toJSON = function() { return this.toString() } BigInt.prototype.toJSON = function() { return this.toString() }

View File

@@ -3,13 +3,8 @@ import {usePrefStore, useStore} from "@/store/store.js";
import {token} from "@/blockchain/token.js"; import {token} from "@/blockchain/token.js";
import Color from "color"; import Color from "color";
import {DateTime} from "luxon"; import {DateTime} from "luxon";
import router from "@/router/index.js";
import {dateString} from "@/common.js"; import {dateString} from "@/common.js";
import { v4 as uuidv4 } from 'uuid';
export function nav(name) {
// noinspection JSIgnoredPromiseFromCall
router.push({name})
}
const QUOTE_SYMBOLS = [ const QUOTE_SYMBOLS = [
// in order of preference // in order of preference
@@ -71,7 +66,9 @@ export const uint32max = 4294967295
export const uint64max = 18446744073709551615n export const uint64max = 18446744073709551615n
export function tokenNumber(token, balance) { export function tokenNumber(token, balance) {
return FixedNumber.fromValue(balance, token.decimals, {decimals: token.decimals, width: 256}) const dec = token ? token.decimals : 0
console.log('token dec', dec, balance)
return FixedNumber.fromValue(balance, dec, {decimals: dec, width: 256})
} }
export function tokenFloat(token, balance) { export function tokenFloat(token, balance) {
@@ -153,10 +150,7 @@ export function inversionPreference(chainId, base, quote) {
export const sleep = ms => new Promise(r => setTimeout(r, ms)) export const sleep = ms => new Promise(r => setTimeout(r, ms))
export function uuid() { export function uuid() {return uuidv4()}
// noinspection JSUnresolvedReference
return crypto.randomUUID();
}
export function lightenColor(color, lightness = 85, alpha = null) { export function lightenColor(color, lightness = 85, alpha = null) {
let c = new Color(color).hsl() let c = new Color(color).hsl()

View File

@@ -1,20 +1,20 @@
import {getToken} from "@/blockchain/token.js"; import {getToken} from "@/blockchain/token.js";
let native = false // whether native browser notifications are allowed let notificationsAllowed = false // whether native browser notifications are allowed
Notification.requestPermission() if ('Notification' in window) {
.then(permission => { Notification.requestPermission()
console.log(`notification permission: ${permission}`); .then(permission => {
native = permission === 'granted' notificationsAllowed = permission === 'granted'
}) if (!notificationsAllowed)
.catch(error => { console.log(`notification permission denied: ${permission}`);
console.error(`notification permission error: ${error}`); })
native = false; .catch(error => {console.error(`notification permission error: ${error}`);});
}); }
export function notify(title, message=null) { export function notify(title, message=null) {
if (native) { if (notificationsAllowed) {
const options = { const options = {
renotify: true, renotify: true,
tag: title, tag: title,
@@ -44,4 +44,4 @@ export async function notifyFillEvent(chainId, status, trancheIndex, fill) {
` for ${quoteAmount.toPrecision(5)} ${quote.s} average ${average.toPrecision(5)} ${base.s}/${quote.s}}`; ` for ${quoteAmount.toPrecision(5)} ${quote.s} average ${average.toPrecision(5)} ${base.s}/${quote.s}}`;
// todo buy/sell arrow icon // todo buy/sell arrow icon
notify(title) notify(title)
} }

View File

@@ -7,11 +7,6 @@ import {computed, ref} from "vue";
import Color from "color"; import Color from "color";
export const MIN_EXECUTION_TIME = 60 // give at least one full minute for each tranche to trigger
export const DEFAULT_SLIPPAGE = 0.0030;
export const MIN_SLIPPAGE = 0.0001;
// Builders are data objects which store a configuration state // Builders are data objects which store a configuration state
// the component name must match a corresponding Vue component in the BuilderFactory.vue component, which is responsible // the component name must match a corresponding Vue component in the BuilderFactory.vue component, which is responsible
// for instantiating the UI component for a given builder dictionary, based on its builder.component field. // for instantiating the UI component for a given builder dictionary, based on its builder.component field.

View File

@@ -8,11 +8,15 @@
import { loadFonts } from './webfontloader' import { loadFonts } from './webfontloader'
import vuetify from './vuetify' import vuetify from './vuetify'
import {pinia} from '../store/pinia.js' import {pinia} from '../store/pinia.js'
import router from '../router' import {setRouter} from '../router/router.js'
import {newRouter} from '../router/newRouter.js'
import vsp from "vue-scroll-picker"; import vsp from "vue-scroll-picker";
import "vue-scroll-picker/lib/style.css"; import "vue-scroll-picker/lib/style.css";
const router = newRouter();
setRouter(router)
export function registerPlugins (app) { export function registerPlugins (app) {
loadFonts().catch((e)=>console.error('Could not load fonts!',e)) loadFonts().catch((e)=>console.error('Could not load fonts!',e))
app app

View File

@@ -53,7 +53,7 @@ function makeColors(isLight) {
"on-warning": ink, "on-warning": ink,
"on-error": ink, "on-error": ink,
} }
console.log('colors', isLight?'light':'dark', colors) // console.log('colors', isLight?'light':'dark', colors)
return colors; return colors;
} }

View File

@@ -1,55 +0,0 @@
// Composables
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{
path: '/',
component: () => import('@/layouts/chart/ChartLayout.vue'),
children: [
{
name: 'App',
path: '/',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('@/components/chart/ChartPlaceOrder.vue'),
},
{
name: 'Shared',
path: '/shared',
component: ()=> import('@/components/chart/Shared.vue')
},
{
name: 'Order',
path: '/order',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('@/components/chart/ChartPlaceOrder.vue'),
},
{
name: 'Assets',
path: '/assets',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('@/components/chart/ChartVault.vue'),
},
{
name: 'Status',
path: '/status',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('@/components/chart/ChartStatus.vue'),
},
],
},
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes,
})
export default router

10
src/router/newRouter.js Normal file
View File

@@ -0,0 +1,10 @@
// Composables
import {createRouter, createWebHistory} from 'vue-router'
import {routes} from "@/router/routes.js";
export function newRouter() {
return createRouter({
history: createWebHistory(process.env.BASE_URL),
routes,
})
}

3
src/router/router.js Normal file
View File

@@ -0,0 +1,3 @@
export let router = null
export function setRouter (r) { router = r }

45
src/router/routes.js Normal file
View File

@@ -0,0 +1,45 @@
export const routes = [
{
path: '/',
component: () => import('@/layouts/chart/ChartLayout.vue'),
children: [
{
name: 'App',
path: '/',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('@/components/chart/ChartPlaceOrder.vue'),
},
{
name: 'Shared',
path: '/shared/:code',
component: () => import('@/components/chart/Shared.vue')
},
{
name: 'Order',
path: '/order',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('@/components/chart/ChartPlaceOrder.vue'),
},
{
name: 'Assets',
path: '/assets',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('@/components/chart/ChartVault.vue'),
},
{
name: 'Status',
path: '/status',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('@/components/chart/ChartStatus.vue'),
},
],
},
]

View File

@@ -1,8 +1,10 @@
import { compressToEncodedURIComponent, decompressFromEncodedURIComponent } from 'lz-string';
import {useChartOrderStore} from "@/orderbuild.js"; import {useChartOrderStore} from "@/orderbuild.js";
import {changeIntervalSecs, onChartReady, setSymbol, widget} from "@/charts/chart.js"; import {changeIntervalSecs, onChartReady, setSymbol, widget} from "@/charts/chart.js";
import {usePrefStore, useStore} from "@/store/store.js"; import {usePrefStore, useStore} from "@/store/store.js";
import {lookupSymbol} from "@/charts/datafeed.js"; import {lookupSymbol} from "@/charts/datafeed.js";
import {track} from "@/track.js";
import {socket} from "@/socket.js";
export async function getShareUrl() { export async function getShareUrl() {
const co = useChartOrderStore(); const co = useChartOrderStore();
@@ -25,18 +27,32 @@ export async function getShareUrl() {
} }
const json = JSON.stringify(data) const json = JSON.stringify(data)
console.log('sharing data', json, data) console.log('sharing data', json, data)
const compressed = compressToEncodedURIComponent(json); const snapshot = await takeSnapshot()
const baseUrl = `${window.location.protocol}//${window.location.hostname}:${window.location.port}`;
const imageFile = await takeSnapshot() const code = await new Promise((resolve)=>socket.emit('share', data, snapshot, resolve))
return `${baseUrl}/shared?i=${imageFile}&d=${compressed}`; if (code===null) return null
return import.meta.env.VITE_SHARE_URL+ '/share/'+code;
} }
export function loadShareUrl() { export async function loadShareUrl(code) {
const urlParams = new URLSearchParams(window.location.search); // console.log('loading share url', code)
const dataStr = urlParams.get('d'); const data = await new Promise((resolve, reject) => {
if (!dataStr) return // Set a timeout (e.g., 8 seconds)
const json = decompressFromEncodedURIComponent(dataStr); const timeout = setTimeout(() => {
const data = JSON.parse(json); reject(new Error('Timed out waiting for response from server'));
}, 8000);
socket.emit('shared', code, (response) => {
clearTimeout(timeout);
resolve(response);
});
}).catch(err => {
// Optional: show error to user or log it
console.error('Failed to load shared URL:', err.message);
return null;
});
if (data===null) return false
console.log('loaded shared orders data', data) console.log('loaded shared orders data', data)
const co = useChartOrderStore(); const co = useChartOrderStore();
const s = useStore() const s = useStore()
@@ -44,13 +60,15 @@ export function loadShareUrl() {
const symbol = lookupSymbol(ticker) const symbol = lookupSymbol(ticker)
if (symbol===null) { if (symbol===null) {
console.error('could not find symbol for ticker', ticker) console.error('could not find symbol for ticker', ticker)
return return false
} }
s.chainId = data.chainId s.chainId = data.chainId
const prefs = usePrefStore() const prefs = usePrefStore()
prefs.selectedSymbol = ticker prefs.selectedSymbol = ticker
for (const order of data.orders) { for (const order of data.orders) {
// force amount to be zero so that the user MUST enter a size before confirming the placement
order.amount = 0 order.amount = 0
order.amountIsTokenA = true
order.valid = false order.valid = false
} }
co.orders = data.orders co.orders = data.orders
@@ -59,16 +77,12 @@ export function loadShareUrl() {
setSymbol(symbol) setSymbol(symbol)
.catch((e)=>console.error('could not set symbol', e)) .catch((e)=>console.error('could not set symbol', e))
}) })
track('shared')
console.log('loaded orders', s.chainId, co.orders) console.log('loaded orders', s.chainId, co.orders)
return true;
} }
export async function takeSnapshot() { export async function takeSnapshot() {
const screenshotCanvas = await widget.takeClientScreenshot(); const screenshotCanvas = await widget.takeClientScreenshot();
const image = await new Promise((resolve) => screenshotCanvas.toBlob(resolve)); return await new Promise((resolve) => screenshotCanvas.toBlob(resolve));
const response = await fetch(import.meta.env.VITE_SNAPSHOT_URL, {
method: 'PUT',
body: image,
credentials: 'same-origin',
});
return response.text()
} }

View File

@@ -1,160 +1,8 @@
import {io} from "socket.io-client"; import {io} from "socket.io-client";
import {useStore} from "@/store/store.js";
import {ensureVault, flushOrders} from "@/blockchain/wallet.js";
import {parseElaboratedOrderStatus} from "@/blockchain/orderlib.js";
import { DataFeed } from "./charts/datafeed";
import {notifyFillEvent} from "@/notify.js";
import {refreshOHLCSubs} from "@/blockchain/ohlcs.js";
const socketOptions = { const socketOptions = {
transports: ["websocket"], transport: ['polling', 'websocket'],
pingInterval: 25000, // PING every 25 seconds pingInterval: 25000, // PING every 25 seconds
pingTimeout: 60000 // Timeout if no PONG in 60 seconds pingTimeout: 60000 // Timeout if no PONG in 60 seconds
} }
export const socket = io(import.meta.env.VITE_WS_URL || undefined, socketOptions) export const socket = io(import.meta.env.VITE_WS_URL || undefined, socketOptions)
socket.on('connect', () => {
console.log(new Date(), 'ws connected')
const s = useStore();
s.connected = true
if (s.chainId && s.account)
socket.emit('address', s.chainId, s.account)
refreshOHLCSubs()
})
socket.on('disconnect', () => {
console.log(new Date(), 'ws disconnected')
useStore().connected = false
})
socket.on('approvedRegion', (approved) => {
console.log('approved region', approved)
useStore().regionApproved = approved
})
socket.on('approvedWallet', (approved) => {
console.log('approved wallet', approved)
useStore().walletApproved = approved
})
socket.on('p', async (chainId, pool, price) => {
console.log('pool price from message', chainId, pool, price)
const s = useStore()
if( s.chainId !== chainId )
return
s.poolPrices[[chainId,pool]] = price
})
socket.on('ohlc', async (chainId, poolPeriod, ohlcs) => {
// console.log('pool bars', poolPeriod, ohlcs)
if (ohlcs && ohlcs.length) {
const split = poolPeriod.indexOf('|')
const pool = poolPeriod.slice(0,split)
useStore().poolPrices[[chainId, pool]] = parseFloat(ohlcs[ohlcs.length - 1][4]) // closing price
}
DataFeed.poolCallback(chainId, poolPeriod, ohlcs)
})
socket.on('vb', async (chainId, vault, balances) => {
const s = useStore()
if( s.chainId !== chainId )
return
console.log('vb', vault, balances)
s.vaultBalances[vault] = JSON.parse(balances)
console.log('vault balances', vault, s.vaultBalances[vault])
})
socket.on('vaults', (chainId, owner, vaults)=>{
const s = useStore()
console.log('vaults', chainId, owner, vaults)
if( s.chainId !== chainId || s.account !== owner )
return
if( vaults.length > s.vaults.length ) {
s.vaults = vaults
if( vaults.length ) {
const vault = vaults[0]
flushOrders(chainId, owner, 0, vault)
}
}
})
socket.on('mark.usd', (chainId, token, value)=>{
const s = useStore()
s.markPrices[`${chainId}|${token}`] = Number(value)
// console.log('mark.usd', token, value)
})
function handleOrderStatus(chainId, vault, orderIndex, status) {
const s = useStore()
if( s.chainId !== chainId )
return
// message 'o' is a single order status
const parsed = parseElaboratedOrderStatus(chainId, status);
console.log('o', chainId, vault, orderIndex, status, parsed)
if( !(vault in s.orders) )
s.orders[vault] = {}
s.orders[vault][orderIndex] = parsed
}
socket.on('os', (chainId, vault, orders) => {
// message 'os' has multiple order statuses
console.log('os', orders)
for( const [orderIndex, status] of orders )
handleOrderStatus(chainId, vault, orderIndex, status)
})
socket.on( 'o', handleOrderStatus)
socket.on( 'of', (chainId, vault, orderIndex, filled)=>{
const s = useStore()
if( s.chainId !== chainId )
return
console.log('of', chainId, vault, orderIndex, filled)
if( !(vault in s.orders) ) {
console.log('warning: got fill on an order in an unknown vault')
return
}
if( !(orderIndex in s.orders[vault]) ) {
console.log(`warning: orderIndex ${orderIndex} missing from vault ${vault}`)
return
}
const status = s.orders[vault][orderIndex]
console.log('apply fills', status, filled)
let orderIn = 0n
let orderOut = 0n
for (const i in filled) {
const ts = status.trancheStatus[i]
let filledIn = 0n
let filledOut = 0n
const [activationTime, fills] = filled[i];
const numOld = ts.fills.length;
for (let i=0; i<fills.length; i++) {
const fill = fills[i]
let [tx, time, fi, fo, fee] = fill
fi = BigInt(fi)
fo = BigInt(fo)
fee = BigInt(fee)
filledIn += fi
filledOut += fo
if (i>=numOld) {
// new fill detected
const f = {tx, time, filledIn: fi, filledOut: fo, fee, filled: status.order.amountIsInput ? fi : fo};
console.log('new fill', f)
notifyFillEvent(chainId, status, i, f).catch((e)=>console.log('fill notification error', e))
ts.fills.push(f)
}
}
ts.filledIn = filledIn
ts.filledOut = filledOut
ts.activationTime = activationTime
orderIn += filledIn
orderOut += filledOut
}
status.filledIn = orderIn
status.filledOut = orderOut
status.filled = status.order.amountIsInput ? orderIn : orderOut
console.log('apply fills completed', status)
})

165
src/socketInit.js Normal file
View File

@@ -0,0 +1,165 @@
import {socket} from "@/socket.js";
import {useStore} from "@/store/store.js";
import {flushWalletTransactions} from "@/blockchain/wallet.js";
import {parseElaboratedOrderStatus} from "@/blockchain/orderlib.js";
import {DataFeed} from "./charts/datafeed";
import {notifyFillEvent} from "@/notify.js";
import {refreshOHLCSubs} from "@/blockchain/ohlcs.js";
socket.on('connect', () => {
console.log('ws connected')
const s = useStore();
s.connected = true
if (s.chainId && s.account)
socket.emit('address', s.chainId, s.account)
refreshOHLCSubs()
})
socket.on('disconnect', () => {
console.log('ws disconnected')
useStore().connected = false
})
socket.on('connect_error', (err) => {
console.log('ws connect error', err)
useStore().connected = false
})
socket.on('error', (err) => {
console.log('ws error', err)
useStore().connected = false
})
socket.on('approvedRegion', (approved) => {
console.log('approved region', approved)
useStore().regionApproved = approved
})
socket.on('approvedWallet', (approved) => {
console.log('approved wallet', approved)
useStore().walletApproved = approved
})
socket.on('p', async (chainId, pool, price) => {
console.log('pool price from message', chainId, pool, price)
const s = useStore()
if( s.chainId !== chainId )
return
s.poolPrices[[chainId,pool]] = price
})
socket.on('ohlc', async (chainId, poolPeriod, ohlcs) => {
// console.log('pool bars', poolPeriod, ohlcs)
if (ohlcs && ohlcs.length) {
const split = poolPeriod.indexOf('|')
const pool = poolPeriod.slice(0,split)
useStore().poolPrices[[chainId, pool]] = parseFloat(ohlcs[ohlcs.length - 1][4]) // closing price
}
DataFeed.poolCallback(chainId, poolPeriod, ohlcs)
})
socket.on('vb', async (chainId, vault, balances) => {
const s = useStore()
if( s.chainId !== chainId )
return
console.log('vb', vault, balances)
s.vaultBalances[vault] = JSON.parse(balances)
console.log('vault balances', vault, s.vaultBalances[vault])
})
socket.on('vaults', (chainId, owner, vaults)=>{
const s = useStore()
console.log('vaults', chainId, owner, vaults)
if( s.chainId !== chainId || s.account !== owner )
return
if( vaults.length > s.vaults.length ) {
s.vaults = vaults
if( vaults.length ) {
const vault = vaults[0]
flushWalletTransactions(chainId, owner, 0, vault)
}
}
})
socket.on('mark.usd', (chainId, token, value)=>{
const s = useStore()
s.markPrices[`${chainId}|${token}`] = Number(value)
// console.log('mark.usd', token, value)
})
function handleOrderStatus(chainId, vault, orderIndex, status) {
const s = useStore()
if( s.chainId !== chainId )
return
// message 'o' is a single order status
const parsed = parseElaboratedOrderStatus(chainId, status);
// console.log('o', chainId, vault, orderIndex, status, parsed)
if( !(vault in s.orders) )
s.orders[vault] = {}
s.orders[vault][orderIndex] = parsed
}
socket.on('os', (chainId, vault, orders) => {
// message 'os' has multiple order statuses
console.log('os', orders)
for( const [orderIndex, status] of orders )
handleOrderStatus(chainId, vault, orderIndex, status)
})
socket.on( 'o', handleOrderStatus)
socket.on( 'of', (chainId, vault, orderIndex, filled)=>{
const s = useStore()
if( s.chainId !== chainId )
return
console.log('of', chainId, vault, orderIndex, filled)
if( !(vault in s.orders) ) {
console.log('warning: got fill on an order in an unknown vault')
return
}
if( !(orderIndex in s.orders[vault]) ) {
console.log(`warning: orderIndex ${orderIndex} missing from vault ${vault}`)
return
}
const status = s.orders[vault][orderIndex]
console.log('apply fills', status, filled)
let orderIn = 0n
let orderOut = 0n
for (const i in filled) {
const ts = status.trancheStatus[i]
let filledIn = 0n
let filledOut = 0n
const [activationTime, fills] = filled[i];
const numOld = ts.fills.length;
for (let i=0; i<fills.length; i++) {
const fill = fills[i]
let [tx, time, fi, fo, fee] = fill
fi = BigInt(fi)
fo = BigInt(fo)
fee = BigInt(fee)
filledIn += fi
filledOut += fo
if (i>=numOld) {
// new fill detected
const f = {tx, time, filledIn: fi, filledOut: fo, fee, filled: status.order.amountIsInput ? fi : fo};
console.log('new fill', f)
notifyFillEvent(chainId, status, i, f).catch((e)=>console.log('fill notification error', e))
ts.fills.push(f)
}
}
ts.filledIn = filledIn
ts.filledOut = filledOut
ts.activationTime = activationTime
orderIn += filledIn
orderOut += filledOut
}
status.filledIn = orderIn
status.filledOut = orderOut
status.filled = status.order.amountIsInput ? orderIn : orderOut
console.log('apply fills completed', status)
})
console.log('initialized socketio')

View File

@@ -224,4 +224,3 @@ export const usePrefStore = defineStore({
return {inverted, acceptedTos, selectedTicker, selectedTimeframe, timezone, newbie, hints, } return {inverted, acceptedTos, selectedTicker, selectedTimeframe, timezone, newbie, hints, }
}, },
}) })

View File

@@ -1,9 +1,11 @@
export let tracking_enabled = window.gtag !== undefined export let tracking_enabled = window.gtag !== undefined
if(tracking_enabled) if(tracking_enabled) {
console.log('gtag', tracking_enabled) // console.log('gtag', tracking_enabled)
else }
else {
console.log('tracking disabled') console.log('tracking disabled')
}
export function track(...args) { export function track(...args) {
if (tracking_enabled) { if (tracking_enabled) {

View File

@@ -16,9 +16,9 @@ const versionPromise = fetch('/contract/version.json').then(_json('version.json'
const metadataPromise = fetch('/metadata.json').then(_json('metadata.json')) const metadataPromise = fetch('/metadata.json').then(_json('metadata.json'))
export const version = await versionPromise export const version = await versionPromise
console.log('version', version) // console.log('version', version)
export const metadata = await metadataPromise export const metadata = await metadataPromise
console.log('metadata', metadata) // console.log('metadata', metadata)
export function dexorderAddress(chainId) { return version['chainInfo'][chainId]['dexorder'] } export function dexorderAddress(chainId) { return version['chainInfo'][chainId]['dexorder'] }
export function factoryAddress(chainId) { return version['chainInfo'][chainId]['factory'] } export function factoryAddress(chainId) { return version['chainInfo'][chainId]['factory'] }

View File

@@ -24,6 +24,17 @@ export default defineConfig({
configFile: 'src/styles/settings.scss', configFile: 'src/styles/settings.scss',
}, },
}), }),
/*
{
name: 'log-http-requests',
configureServer(server) {
server.middlewares.use((req, res, next) => {
console.log(`${req.method} ${req.url}`)
next()
})
}
},
*/
], ],
define: { 'process.env': {} }, define: { 'process.env': {} },
resolve: { resolve: {

275
yarn.lock
View File

@@ -650,11 +650,35 @@
find-cache-dir "^3.3.2" find-cache-dir "^3.3.2"
upath "^2.0.1" upath "^2.0.1"
acorn-jsx-walk@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/acorn-jsx-walk/-/acorn-jsx-walk-2.0.0.tgz#a5ed648264e68282d7c2aead80216bfdf232573a"
integrity sha512-uuo6iJj4D4ygkdzd6jPtcxs8vZgDX9YFIkqczGImoypX2fQ4dVImmu3UzA4ynixCIMTrEOWW+95M2HuBaCEOVA==
acorn-jsx@^5.3.2: acorn-jsx@^5.3.2:
version "5.3.2" version "5.3.2"
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
acorn-loose@^8.4.0:
version "8.5.0"
resolved "https://registry.yarnpkg.com/acorn-loose/-/acorn-loose-8.5.0.tgz#11fbd555827d551dd16ca812c078e382400b1bc7"
integrity sha512-ppga7pybjwX2HSJv5ayHe6QG4wmNS1RQ2wjBMFTVnOj0h8Rxsmtc6fnVzINqHSSRz23sTe9IL3UAt/PU9gc4FA==
dependencies:
acorn "^8.14.0"
acorn-walk@^8.3.4:
version "8.3.4"
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7"
integrity sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==
dependencies:
acorn "^8.11.0"
acorn@^8.11.0, acorn@^8.14.1:
version "8.14.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.1.tgz#721d5dc10f7d5b5609a891773d47731796935dfb"
integrity sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==
acorn@^8.14.0, acorn@^8.9.0: acorn@^8.14.0, acorn@^8.9.0:
version "8.14.0" version "8.14.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0"
@@ -675,6 +699,16 @@ ajv@^6.12.4:
json-schema-traverse "^0.4.1" json-schema-traverse "^0.4.1"
uri-js "^4.2.2" uri-js "^4.2.2"
ajv@^8.17.1:
version "8.17.1"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6"
integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==
dependencies:
fast-deep-equal "^3.1.3"
fast-uri "^3.0.1"
json-schema-traverse "^1.0.0"
require-from-string "^2.0.2"
ansi-regex@^5.0.1: ansi-regex@^5.0.1:
version "5.0.1" version "5.0.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
@@ -755,7 +789,7 @@ caniuse-lite@^1.0.30001688:
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001695.tgz#39dfedd8f94851132795fdf9b79d29659ad9c4d4" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001695.tgz#39dfedd8f94851132795fdf9b79d29659ad9c4d4"
integrity sha512-vHyLade6wTgI2u1ec3WQBxv+2BrTERV28UXQu9LO6lZ9pYeMk34vjXFLOxo1A4UBA8XTL4njRQZdno/yYaSmWw== integrity sha512-vHyLade6wTgI2u1ec3WQBxv+2BrTERV28UXQu9LO6lZ9pYeMk34vjXFLOxo1A4UBA8XTL4njRQZdno/yYaSmWw==
chalk@^4.0.0: chalk@^4.0.0, chalk@^4.1.0:
version "4.1.2" version "4.1.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
@@ -810,6 +844,11 @@ color@^4.2.3:
color-convert "^2.0.1" color-convert "^2.0.1"
color-string "^1.9.0" color-string "^1.9.0"
commander@^13.1.0:
version "13.1.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-13.1.0.tgz#776167db68c78f38dcce1f9b8d7b8b9a488abf46"
integrity sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==
commondir@^1.0.1: commondir@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
@@ -888,6 +927,34 @@ defu@^6.1.4:
resolved "https://registry.yarnpkg.com/defu/-/defu-6.1.4.tgz#4e0c9cf9ff68fe5f3d7f2765cc1a012dfdcb0479" resolved "https://registry.yarnpkg.com/defu/-/defu-6.1.4.tgz#4e0c9cf9ff68fe5f3d7f2765cc1a012dfdcb0479"
integrity sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg== integrity sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==
dependency-cruiser@^16.10.1:
version "16.10.1"
resolved "https://registry.yarnpkg.com/dependency-cruiser/-/dependency-cruiser-16.10.1.tgz#6d9dac4e6a3825a48b7755da8d19f684ec986954"
integrity sha512-Uw8/emAfzD+Zcua6Hfg1wCnskJd2QSRGKw5tUWrCtA8PBSz+n/zfUYCTjFi9MwdiTA5oecN93FBvh9jJhTLs4A==
dependencies:
acorn "^8.14.1"
acorn-jsx "^5.3.2"
acorn-jsx-walk "^2.0.0"
acorn-loose "^8.4.0"
acorn-walk "^8.3.4"
ajv "^8.17.1"
commander "^13.1.0"
enhanced-resolve "^5.18.1"
ignore "^7.0.3"
interpret "^3.1.1"
is-installed-globally "^1.0.0"
json5 "^2.2.3"
memoize "^10.1.0"
picocolors "^1.1.1"
picomatch "^4.0.2"
prompts "^2.4.2"
rechoir "^0.8.0"
safe-regex "^2.1.1"
semver "^7.7.1"
teamcity-service-messages "^0.1.14"
tsconfig-paths-webpack-plugin "^4.2.0"
watskeburt "^4.2.3"
destr@^2.0.3: destr@^2.0.3:
version "2.0.3" version "2.0.3"
resolved "https://registry.yarnpkg.com/destr/-/destr-2.0.3.tgz#7f9e97cb3d16dbdca7be52aca1644ce402cfe449" resolved "https://registry.yarnpkg.com/destr/-/destr-2.0.3.tgz#7f9e97cb3d16dbdca7be52aca1644ce402cfe449"
@@ -931,6 +998,14 @@ engine.io-parser@~5.2.1:
resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.2.3.tgz#00dc5b97b1f233a23c9398d0209504cf5f94d92f" resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.2.3.tgz#00dc5b97b1f233a23c9398d0209504cf5f94d92f"
integrity sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q== integrity sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==
enhanced-resolve@^5.18.1, enhanced-resolve@^5.7.0:
version "5.18.1"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz#728ab082f8b7b6836de51f1637aab5d3b9568faf"
integrity sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==
dependencies:
graceful-fs "^4.2.4"
tapable "^2.2.0"
entities@^4.5.0: entities@^4.5.0:
version "4.5.0" version "4.5.0"
resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
@@ -1134,6 +1209,11 @@ fast-levenshtein@^2.0.6:
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==
fast-uri@^3.0.1:
version "3.0.6"
resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.6.tgz#88f130b77cfaea2378d56bf970dea21257a68748"
integrity sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==
fastq@^1.6.0: fastq@^1.6.0:
version "1.18.0" version "1.18.0"
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.18.0.tgz#d631d7e25faffea81887fe5ea8c9010e1b36fee0" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.18.0.tgz#d631d7e25faffea81887fe5ea8c9010e1b36fee0"
@@ -1216,6 +1296,11 @@ fsevents@~2.3.2:
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
function-bind@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
gensync@^1.0.0-beta.2: gensync@^1.0.0-beta.2:
version "1.0.0-beta.2" version "1.0.0-beta.2"
resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
@@ -1261,6 +1346,13 @@ glob@^7.1.3:
once "^1.3.0" once "^1.3.0"
path-is-absolute "^1.0.0" path-is-absolute "^1.0.0"
global-directory@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/global-directory/-/global-directory-4.0.1.tgz#4d7ac7cfd2cb73f304c53b8810891748df5e361e"
integrity sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==
dependencies:
ini "4.1.1"
globals@^11.1.0: globals@^11.1.0:
version "11.12.0" version "11.12.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
@@ -1285,6 +1377,11 @@ globby@^14.0.2:
slash "^5.1.0" slash "^5.1.0"
unicorn-magic "^0.1.0" unicorn-magic "^0.1.0"
graceful-fs@^4.2.4:
version "4.2.11"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
graphemer@^1.4.0: graphemer@^1.4.0:
version "1.4.0" version "1.4.0"
resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6"
@@ -1295,6 +1392,13 @@ has-flag@^4.0.0:
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
hasown@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003"
integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==
dependencies:
function-bind "^1.1.2"
ignore@^5.2.0, ignore@^5.2.4: ignore@^5.2.0, ignore@^5.2.4:
version "5.3.2" version "5.3.2"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5"
@@ -1336,11 +1440,28 @@ inherits@2:
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
ini@4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/ini/-/ini-4.1.1.tgz#d95b3d843b1e906e56d6747d5447904ff50ce7a1"
integrity sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==
interpret@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/interpret/-/interpret-3.1.1.tgz#5be0ceed67ca79c6c4bc5cf0d7ee843dcea110c4"
integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==
is-arrayish@^0.3.1: is-arrayish@^0.3.1:
version "0.3.2" version "0.3.2"
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03"
integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==
is-core-module@^2.16.0:
version "2.16.1"
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4"
integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==
dependencies:
hasown "^2.0.2"
is-extglob@^2.1.1: is-extglob@^2.1.1:
version "2.1.1" version "2.1.1"
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
@@ -1353,6 +1474,14 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3:
dependencies: dependencies:
is-extglob "^2.1.1" is-extglob "^2.1.1"
is-installed-globally@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-1.0.0.tgz#08952c43758c33d815692392f7f8437b9e436d5a"
integrity sha512-K55T22lfpQ63N4KEN57jZUAaAYqYHEe8veb/TycJRk9DdSCLLcovXz/mL6mOnhQaZsQGwPhuFopdQIlqGSEjiQ==
dependencies:
global-directory "^4.0.1"
is-path-inside "^4.0.0"
is-number@^7.0.0: is-number@^7.0.0:
version "7.0.0" version "7.0.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
@@ -1363,6 +1492,11 @@ is-path-inside@^3.0.3:
resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283"
integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
is-path-inside@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-4.0.0.tgz#805aeb62c47c1b12fc3fd13bfb3ed1e7430071db"
integrity sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==
isexe@^2.0.0: isexe@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
@@ -1405,12 +1539,17 @@ json-schema-traverse@^0.4.1:
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
json-schema-traverse@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2"
integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==
json-stable-stringify-without-jsonify@^1.0.1: json-stable-stringify-without-jsonify@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
json5@^2.2.3: json5@^2.2.2, json5@^2.2.3:
version "2.2.3" version "2.2.3"
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
@@ -1422,6 +1561,11 @@ keyv@^4.5.3:
dependencies: dependencies:
json-buffer "3.0.1" json-buffer "3.0.1"
kleur@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
klona@^2.0.6: klona@^2.0.6:
version "2.0.6" version "2.0.6"
resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.6.tgz#85bffbf819c03b2f53270412420a4555ef882e22" resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.6.tgz#85bffbf819c03b2f53270412420a4555ef882e22"
@@ -1489,11 +1633,6 @@ luxon@^3.4.4:
resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.5.0.tgz#6b6f65c5cd1d61d1fd19dbf07ee87a50bf4b8e20" resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.5.0.tgz#6b6f65c5cd1d61d1fd19dbf07ee87a50bf4b8e20"
integrity sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ== integrity sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==
lz-string@^1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941"
integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==
magic-string@^0.30.11, magic-string@^0.30.17: magic-string@^0.30.11, magic-string@^0.30.17:
version "0.30.17" version "0.30.17"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.17.tgz#450a449673d2460e5bbcfba9a61916a1714c7453" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.17.tgz#450a449673d2460e5bbcfba9a61916a1714c7453"
@@ -1508,6 +1647,13 @@ make-dir@^3.0.2:
dependencies: dependencies:
semver "^6.0.0" semver "^6.0.0"
memoize@^10.1.0:
version "10.1.0"
resolved "https://registry.yarnpkg.com/memoize/-/memoize-10.1.0.tgz#32a9d09da985a1ab518dfe9fd52d14d1d130446f"
integrity sha512-MMbFhJzh4Jlg/poq1si90XRlTZRDHVqdlz2mPyGJ6kqMpyHUyVpDd5gpFAvVehW64+RA1eKE9Yt8aSLY7w2Kgg==
dependencies:
mimic-function "^5.0.1"
merge2@^1.3.0: merge2@^1.3.0:
version "1.4.1" version "1.4.1"
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
@@ -1521,6 +1667,11 @@ micromatch@^4.0.5, micromatch@^4.0.8:
braces "^3.0.3" braces "^3.0.3"
picomatch "^2.3.1" picomatch "^2.3.1"
mimic-function@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/mimic-function/-/mimic-function-5.0.1.tgz#acbe2b3349f99b9deaca7fb70e48b83e94e67076"
integrity sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==
minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
version "3.1.2" version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
@@ -1528,6 +1679,11 @@ minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
dependencies: dependencies:
brace-expansion "^1.1.7" brace-expansion "^1.1.7"
minimist@^1.2.6:
version "1.2.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
minipass@^3.0.0: minipass@^3.0.0:
version "3.3.6" version "3.3.6"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a"
@@ -1691,6 +1847,11 @@ path-key@^3.1.0:
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
path-parse@^1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
path-type@^5.0.0: path-type@^5.0.0:
version "5.0.0" version "5.0.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-5.0.0.tgz#14b01ed7aea7ddf9c7c3f46181d4d04f9c785bb8" resolved "https://registry.yarnpkg.com/path-type/-/path-type-5.0.0.tgz#14b01ed7aea7ddf9c7c3f46181d4d04f9c785bb8"
@@ -1782,6 +1943,14 @@ prelude-ls@^1.2.1:
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
prompts@^2.4.2:
version "2.4.2"
resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069"
integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==
dependencies:
kleur "^3.0.3"
sisteransi "^1.0.5"
punycode@^2.1.0: punycode@^2.1.0:
version "2.3.1" version "2.3.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"
@@ -1805,11 +1974,37 @@ readdirp@^4.0.1:
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.1.1.tgz#bd115327129672dc47f87408f05df9bd9ca3ef55" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.1.1.tgz#bd115327129672dc47f87408f05df9bd9ca3ef55"
integrity sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw== integrity sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==
rechoir@^0.8.0:
version "0.8.0"
resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22"
integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==
dependencies:
resolve "^1.20.0"
regexp-tree@~0.1.1:
version "0.1.27"
resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.27.tgz#2198f0ef54518ffa743fe74d983b56ffd631b6cd"
integrity sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==
require-from-string@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
resolve-from@^4.0.0: resolve-from@^4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
resolve@^1.20.0:
version "1.22.10"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.10.tgz#b663e83ffb09bbf2386944736baae803029b8b39"
integrity sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==
dependencies:
is-core-module "^2.16.0"
path-parse "^1.0.7"
supports-preserve-symlinks-flag "^1.0.0"
reusify@^1.0.4: reusify@^1.0.4:
version "1.0.4" version "1.0.4"
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
@@ -1841,6 +2036,13 @@ run-parallel@^1.1.9:
dependencies: dependencies:
queue-microtask "^1.2.2" queue-microtask "^1.2.2"
safe-regex@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-2.1.1.tgz#f7128f00d056e2fe5c11e81a1324dd974aadced2"
integrity sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==
dependencies:
regexp-tree "~0.1.1"
sass@^1.60.0: sass@^1.60.0:
version "1.83.4" version "1.83.4"
resolved "https://registry.yarnpkg.com/sass/-/sass-1.83.4.tgz#5ccf60f43eb61eeec300b780b8dcb85f16eec6d1" resolved "https://registry.yarnpkg.com/sass/-/sass-1.83.4.tgz#5ccf60f43eb61eeec300b780b8dcb85f16eec6d1"
@@ -1867,6 +2069,11 @@ semver@^7.3.6, semver@^7.6.3:
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143"
integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==
semver@^7.7.1:
version "7.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f"
integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==
shebang-command@^2.0.0: shebang-command@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
@@ -1886,6 +2093,11 @@ simple-swizzle@^0.2.2:
dependencies: dependencies:
is-arrayish "^0.3.1" is-arrayish "^0.3.1"
sisteransi@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed"
integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==
slash@^5.1.0: slash@^5.1.0:
version "5.1.0" version "5.1.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-5.1.0.tgz#be3adddcdf09ac38eebe8dcdc7b1a57a75b095ce" resolved "https://registry.yarnpkg.com/slash/-/slash-5.1.0.tgz#be3adddcdf09ac38eebe8dcdc7b1a57a75b095ce"
@@ -1926,6 +2138,11 @@ strip-ansi@^6.0.1:
dependencies: dependencies:
ansi-regex "^5.0.1" ansi-regex "^5.0.1"
strip-bom@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==
strip-json-comments@^3.1.1: strip-json-comments@^3.1.1:
version "3.1.1" version "3.1.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
@@ -1945,6 +2162,16 @@ supports-color@^7.1.0:
dependencies: dependencies:
has-flag "^4.0.0" has-flag "^4.0.0"
supports-preserve-symlinks-flag@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
tapable@^2.2.0, tapable@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0"
integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==
tar@^6.2.1: tar@^6.2.1:
version "6.2.1" version "6.2.1"
resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a"
@@ -1957,6 +2184,11 @@ tar@^6.2.1:
mkdirp "^1.0.3" mkdirp "^1.0.3"
yallist "^4.0.0" yallist "^4.0.0"
teamcity-service-messages@^0.1.14:
version "0.1.14"
resolved "https://registry.yarnpkg.com/teamcity-service-messages/-/teamcity-service-messages-0.1.14.tgz#193d420a5e4aef8e5e50b8c39e7865e08fbb5d8a"
integrity sha512-29aQwaHqm8RMX74u2o/h1KbMLP89FjNiMxD9wbF2BbWOnbM+q+d1sCEC+MqCc4QW3NJykn77OMpTFw/xTHIc0w==
text-table@^0.2.0: text-table@^0.2.0:
version "0.2.0" version "0.2.0"
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
@@ -1974,6 +2206,25 @@ to-regex-range@^5.0.1:
dependencies: dependencies:
is-number "^7.0.0" is-number "^7.0.0"
tsconfig-paths-webpack-plugin@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.2.0.tgz#f7459a8ed1dd4cf66ad787aefc3d37fff3cf07fc"
integrity sha512-zbem3rfRS8BgeNK50Zz5SIQgXzLafiHjOwUAvk/38/o1jHn/V5QAgVUcz884or7WYcPaH3N2CIfUc2u0ul7UcA==
dependencies:
chalk "^4.1.0"
enhanced-resolve "^5.7.0"
tapable "^2.2.1"
tsconfig-paths "^4.1.2"
tsconfig-paths@^4.1.2:
version "4.2.0"
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz#ef78e19039133446d244beac0fd6a1632e2d107c"
integrity sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==
dependencies:
json5 "^2.2.2"
minimist "^1.2.6"
strip-bom "^3.0.0"
tslib@2.7.0: tslib@2.7.0:
version "2.7.0" version "2.7.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01"
@@ -2083,6 +2334,11 @@ util-deprecate@^1.0.2:
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
uuid@^11.1.0:
version "11.1.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-11.1.0.tgz#9549028be1753bb934fc96e2bca09bb4105ae912"
integrity sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==
vite-plugin-vuetify@^1.0.0: vite-plugin-vuetify@^1.0.0:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/vite-plugin-vuetify/-/vite-plugin-vuetify-1.0.2.tgz#d1777c63aa1b3a308756461b3d0299fd101ee8f4" resolved "https://registry.yarnpkg.com/vite-plugin-vuetify/-/vite-plugin-vuetify-1.0.2.tgz#d1777c63aa1b3a308756461b3d0299fd101ee8f4"
@@ -2149,6 +2405,11 @@ vuetify@^3.4.6:
resolved "https://registry.yarnpkg.com/vuetify/-/vuetify-3.7.7.tgz#f40e06abf81c426a29aee53e166fb7fcb05e98cd" resolved "https://registry.yarnpkg.com/vuetify/-/vuetify-3.7.7.tgz#f40e06abf81c426a29aee53e166fb7fcb05e98cd"
integrity sha512-YqKnRo2+BZWQRzfxHVA/5reQo1eLvbS9Z6N+Lvaot/5lpdi7JWooMr/hWoCr7/QPBGRcXArHppqIB+hMfPlsXw== integrity sha512-YqKnRo2+BZWQRzfxHVA/5reQo1eLvbS9Z6N+Lvaot/5lpdi7JWooMr/hWoCr7/QPBGRcXArHppqIB+hMfPlsXw==
watskeburt@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/watskeburt/-/watskeburt-4.2.3.tgz#fc830280f466e201b55c517024aaf3aad2f5121e"
integrity sha512-uG9qtQYoHqAsnT711nG5iZc/8M5inSmkGCOp7pFaytKG2aTfIca7p//CjiVzAE4P7hzaYuCozMjNNaLgmhbK5g==
webfontloader@^1.0.0: webfontloader@^1.0.0:
version "1.6.28" version "1.6.28"
resolved "https://registry.yarnpkg.com/webfontloader/-/webfontloader-1.6.28.tgz#db786129253cb6e8eae54c2fb05f870af6675bae" resolved "https://registry.yarnpkg.com/webfontloader/-/webfontloader-1.6.28.tgz#db786129253cb6e8eae54c2fb05f870af6675bae"