Compare commits

23 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
tim
38fb66c694 order sharing 2025-04-22 16:15:14 -04:00
tim
14b8b50812 more tracking; hide arb from welcome splash 2025-04-15 19:21:39 -04:00
tim
f35b30e337 removed debug span 2025-04-14 14:40:53 -04:00
73 changed files with 1714 additions and 437 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,2 +1,3 @@
VITE_WS_URL=ws://localhost:3001 VITE_WS_URL=ws://localhost:3001
REQUIRE_AUTH=NOAUTH VITE_SHARE_URL=http://localhost:3001
VITE_REQUIRE_APPROVAL=NO

2
bin/depcruise Executable file
View File

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

View File

@@ -25,6 +25,7 @@
"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",
@@ -33,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

@@ -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,15 +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 {socket} from "@/socket.js";
export let provider = null
export const useWalletStore = defineStore('wallet', ()=>{ export const useWalletStore = defineStore('wallet', ()=>{
@@ -34,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)
@@ -66,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 {
@@ -90,6 +91,7 @@ function changeAccounts(chainId, accounts) {
const addr = accounts[0] const addr = accounts[0]
if (addr !== store.account) { if (addr !== store.account) {
console.log('account logged in', addr) console.log('account logged in', addr)
track('login', {chainId, address: addr})
store.account = addr store.account = addr
store.vaults = [] store.vaults = []
// one of these two methods will call flushTransactions() // one of these two methods will call flushTransactions()
@@ -119,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);
}
catch (e) {
console.log('Could not connect change hooks to wallet', e)
return
}
new ethers.BrowserProvider(window.ethereum).getNetwork().then((network)=>{ new ethers.BrowserProvider(window.ethereum).getNetwork().then((network)=>{
const chainId = network.chainId const chainId = network.chainId
onChainChanged(chainId) onChainChanged(chainId)
}) })
}
catch (e) {
console.log('Could not connect change hooks to wallet', e)
}
} }
detectChain()
const errorHandlingProxy = { const errorHandlingProxy = {
get(target, prop, proxy) { get(target, prop, proxy) {
@@ -242,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)
} }
@@ -284,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)
@@ -306,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
@@ -338,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)
})
}
} }
} }
@@ -351,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
@@ -425,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()
@@ -443,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')
@@ -460,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 {
@@ -577,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",

View File

@@ -1,7 +1,7 @@
import {useChartOrderStore} from "@/orderbuild.js"; import {useChartOrderStore} from "@/orderbuild.js";
import {invokeCallbacks, prototype} from "@/common.js"; import {invokeCallbacks, prototype} from "@/common.js";
import {DataFeed, defaultSymbol, feelessTickerKey, getAllSymbols, lookupSymbol} from "@/charts/datafeed.js"; import {DataFeed, defaultSymbol, feelessTickerKey, getAllSymbols, lookupSymbol} from "@/charts/datafeed.js";
import {intervalToSeconds, SingletonCoroutine, toHuman, toPrecision} from "@/misc.js"; import {intervalToSeconds, secondsToInterval, SingletonCoroutine, toHuman, toPrecision} from "@/misc.js";
import {usePrefStore, useStore} from "@/store/store.js"; import {usePrefStore, useStore} from "@/store/store.js";
import {tvCustomThemes} from "../../theme.js"; import {tvCustomThemes} from "../../theme.js";
@@ -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)
} }
@@ -58,15 +58,23 @@ export async function setSymbolTicker(ticker) {
function changeInterval(interval) { function changeInterval(interval) {
co.intervalSecs = intervalToSeconds(interval) const secs = intervalToSeconds(interval)
co.intervalSecs = secs
prefs.selectedTimeframe = interval prefs.selectedTimeframe = interval
DataFeed.intervalChanged(co.intervalSecs) DataFeed.intervalChanged(secs)
} }
export function changeIntervalSecs(secs) {
const interval = secondsToInterval(secs);
co.intervalSecs = secs
prefs.selectedTimeframe = interval
DataFeed.intervalChanged(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
} }
@@ -150,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({
@@ -164,8 +173,8 @@ export function initWidget(el) {
container: el, container: el,
datafeed: DataFeed, // use this for ohlc datafeed: DataFeed, // use this for ohlc
locale: "en", locale: "en",
disabled_features: ['main_series_scale_menu',], disabled_features: ['main_series_scale_menu','display_market_status',],
enabled_features: ['saveload_separate_drawings_storage',], enabled_features: ['saveload_separate_drawings_storage','snapshot_trading_drawings','show_exchange_logos','show_symbol_logos',],
// drawings_access: {type: 'white', tools: [],}, // show no tools // drawings_access: {type: 'white', tools: [],}, // show no tools
custom_themes: tvCustomThemes, custom_themes: tvCustomThemes,
theme: useStore().theme, theme: useStore().theme,
@@ -193,8 +202,18 @@ export function initWidget(el) {
} }
export function onChartReady(f) {
if (co.chartReady)
f(widget, chart)
else
chartInitCbs.push(f)
}
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))
@@ -217,7 +236,12 @@ function initChart() {
} }
changeInterval(widget.symbolInterval().interval) changeInterval(widget.symbolInterval().interval)
co.chartReady = true co.chartReady = true
console.log('chart ready') setTimeout(()=>{
for (const cb of chartInitCbs)
cb(widget, chart)
chartInitCbs = []
}, 1)
// console.log('chart ready')
} }
@@ -262,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
@@ -328,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,10 +6,10 @@ 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";
const DEBUG_LOGGING = false const DEBUG_LOGGING = false
const log = DEBUG_LOGGING ? console.log : ()=>{} const log = DEBUG_LOGGING ? console.log : ()=>{}
@@ -63,7 +64,7 @@ const configurationData = {
value: 'UNIv3', value: 'UNIv3',
name: 'Uniswap v3', name: 'Uniswap v3',
desc: 'Uniswap v3', desc: 'Uniswap v3',
logo: 'https://upload.wikimedia.org/wikipedia/commons/e/e7/Uniswap_Logo.svg', logo: '/uniswap-logo.svg',
}, },
], ],
// The `symbols_types` arguments are used for the `searchSymbols` method if a user selects this symbol type // The `symbols_types` arguments are used for the `searchSymbols` method if a user selects this symbol type
@@ -110,8 +111,10 @@ export function feelessTickerKey(ticker) {
function addSymbol(chainId, p, base, quote, inverted) { function addSymbol(chainId, p, base, quote, inverted) {
const symbol = base.s + '/' + quote.s const symbol = base.s + '/' + quote.s
const fee = `${(p.f/10000).toFixed(2)}%` // const fee = `${(p.f/10000).toFixed(2)}%`
const exchange = ['Uniswap v2', 'Uniswap v3'][p.e] + ' ' + fee // const exchange = ['Uniswap v2', 'Uniswap v3'][p.e] + ' ' + fee
const exchange = ['Uniswap v2', 'Uniswap v3'][p.e]
const exchange_logo = '/uniswap-logo.svg'
const full_name = exchange + ':' + symbol // + '%' + formatFee(fee) const full_name = exchange + ':' + symbol // + '%' + formatFee(fee)
const ticker = tickerKey(chainId, p.e, base.a, quote.a, p.f) const ticker = tickerKey(chainId, p.e, base.a, quote.a, p.f)
// add the search index only if this is the natural, noninverted base/quote pair // add the search index only if this is the natural, noninverted base/quote pair
@@ -122,7 +125,7 @@ function addSymbol(chainId, p, base, quote, inverted) {
const symbolInfo = { const symbolInfo = {
key: ticker, ticker, key: ticker, ticker,
chainId, address: p.a, exchangeId: p.e, chainId, address: p.a, exchangeId: p.e,
full_name, symbol, description, exchange, type, inverted, base, quote, decimals, x:p.x, fee:p.f, full_name, symbol, description, exchange, exchange_logo, type, inverted, base, quote, decimals, x:p.x, fee:p.f,
}; };
_symbols[ticker] = symbolInfo _symbols[ticker] = symbolInfo
const feelessKey = feelessTickerKey(ticker) const feelessKey = feelessTickerKey(ticker)
@@ -332,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');
@@ -356,6 +367,8 @@ export const DataFeed = {
result.push(_symbols[ticker]) result.push(_symbols[ticker])
seen[ticker] = true seen[ticker] = true
} }
if (userInput.length>=3)
track('search', {search_term: userInput})
onResultReadyCallback(result); onResultReadyCallback(result);
}, },
@@ -376,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;
} }
@@ -394,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)
@@ -446,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

@@ -68,7 +68,6 @@ function reload() {
} }
async function connect() { async function connect() {
track('connect_wallet')
disabled.value = true disabled.value = true
try { try {
await addNetworkAndConnectWallet(s.chainId); await addNetworkAndConnectWallet(s.chainId);

View File

@@ -12,9 +12,11 @@ const props = defineProps({
name: {type: String, required: true}, name: {type: String, required: true},
when: {type: Boolean, default: true}, // optional conditional for when to show when: {type: Boolean, default: true}, // optional conditional for when to show
after: {type: String, default: null}, // set to the name of another hint that must happen before this hint, to chain hints into a tutorial. after: {type: String, default: null}, // set to the name of another hint that must happen before this hint, to chain hints into a tutorial.
onComplete: {type: Function, default: null},
}) })
const forceClose = ref(false) const forceClose = ref(false)
const shown = ref(false)
const show = computed({ const show = computed({
get() { get() {
@@ -23,11 +25,13 @@ const show = computed({
const afterOk = props.after === null || prefs.hints[props.after]; const afterOk = props.after === null || prefs.hints[props.after];
const result = !forceClose.value && !shownBefore && whenOk && afterOk const result = !forceClose.value && !shownBefore && whenOk && afterOk
// console.log(`show ${props.name}? ${result} <=`, !forceClose.value, whenOk, afterOk, prefs.hints) // console.log(`show ${props.name}? ${result} <=`, !forceClose.value, whenOk, afterOk, prefs.hints)
if (result) if (result) {
shown.value = true
prefs.hints[props.name] = true prefs.hints[props.name] = true
}
return result return result
}, },
set(v) { if(!v) forceClose.value=true; } set(v) { if(!v) { forceClose.value=true; if (shown.value && props.onComplete) props.onComplete(); } }
}) })
</script> </script>

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

@@ -18,6 +18,7 @@
<v-list-item prepend-icon="mdi-chart-line"><b>Breakout Orders</b> <small>buy <i>above</i> a price level</small></v-list-item> <v-list-item prepend-icon="mdi-chart-line"><b>Breakout Orders</b> <small>buy <i>above</i> a price level</small></v-list-item>
<v-list-item prepend-icon="mdi-plus-minus"><b>Stop-loss</b> <small>coming soon</small></v-list-item> <v-list-item prepend-icon="mdi-plus-minus"><b>Stop-loss</b> <small>coming soon</small></v-list-item>
<!-- <v-list-item prepend-icon="mdi-cancel">One-click Cancel All</v-list-item>--> <!-- <v-list-item prepend-icon="mdi-cancel">One-click Cancel All</v-list-item>-->
<!--
<v-list-item> <v-list-item>
<template #prepend> <template #prepend>
<v-avatar image="/arbitrum-logo.svg" size="1.5em" class="mr-4"/> <v-avatar image="/arbitrum-logo.svg" size="1.5em" class="mr-4"/>
@@ -26,6 +27,7 @@
<b>Arbitrum One</b> support <small>fast and cheap</small> <b>Arbitrum One</b> support <small>fast and cheap</small>
</template> </template>
</v-list-item> </v-list-item>
-->
<v-list-item> <v-list-item>
<template #prepend> <template #prepend>
<v-avatar image="/uniswap-logo.svg" size="1.5em" class="mr-4" style="background-color: white"/> <v-avatar image="/uniswap-logo.svg" size="1.5em" class="mr-4" style="background-color: white"/>
@@ -51,13 +53,13 @@ import Logo from "@/components/Logo.vue";
const modelValue = defineModel() const modelValue = defineModel()
function tryIt() { function tryIt() {
track('try-it') track('tutorial_begin')
modelValue.value = false modelValue.value = false
} }
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

@@ -46,7 +46,6 @@
<!-- mdi-ray-start-end mdi-vector-polyline --> <!-- mdi-ray-start-end mdi-vector-polyline -->
<!-- after="newbie"--> <!-- after="newbie"-->
<span>{{builders.length}}</span>
<one-time-hint name="choose-builder" :activator="hintData.activator" <one-time-hint name="choose-builder" :activator="hintData.activator"
:text="hintData.text" location="top" :text="hintData.text" location="top"
:when="!builtAny"/> :when="!builtAny"/>
@@ -78,6 +77,7 @@ const co = useChartOrderStore()
const marketBuilder = newBuilder('MarketBuilder') const marketBuilder = newBuilder('MarketBuilder')
console.log('chart order', props.order)
const builders = computed(()=>props.order.builders.length > 0 ? props.order.builders : [marketBuilder]) const builders = computed(()=>props.order.builders.length > 0 ? props.order.builders : [marketBuilder])
const tokenIn = computed(()=>props.order.buy ? co.quoteToken : co.baseToken) const tokenIn = computed(()=>props.order.buy ? co.quoteToken : co.baseToken)
const tokenOut = computed(()=>props.order.buy ? co.baseToken : co.quoteToken) const tokenOut = computed(()=>props.order.buy ? co.baseToken : co.quoteToken)
@@ -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

@@ -8,6 +8,12 @@
</v-btn> </v-btn>
<v-btn variant="text" prepend-icon="mdi-delete" v-if="co.orders.length>0" <v-btn variant="text" prepend-icon="mdi-delete" v-if="co.orders.length>0"
: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"
:disabled="sharing"
@click="shareOrder">{{sharing?'Preparing...':'Share Order'}}</v-btn>
<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"
/>
</template> </template>
<div class="overflow-y-auto"> <div class="overflow-y-auto">
<needs-chart> <needs-chart>
@@ -66,6 +72,7 @@ import NeedsChart from "@/components/NeedsChart.vue";
import {PlaceOrderTransaction} from "@/blockchain/transaction.js"; import {PlaceOrderTransaction} from "@/blockchain/transaction.js";
import {errorSuggestsMissingVault} from "@/misc.js"; import {errorSuggestsMissingVault} from "@/misc.js";
import {track} from "@/track.js"; import {track} from "@/track.js";
import {getShareUrl} from "@/share.js";
const s = useStore() const s = useStore()
const co = useChartOrderStore() const co = useChartOrderStore()
@@ -73,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()
@@ -149,7 +156,7 @@ watchEffect(()=>{
let built = [] let built = []
async function placeOrder() { async function placeOrder() {
track('place_order') track('place-order')
const chartOrders = co.orders; const chartOrders = co.orders;
const allWarns = [] const allWarns = []
built = [] built = []
@@ -196,6 +203,42 @@ async function doPlaceOrder() {
} }
} }
const sharing = ref(false)
const showSharedTooltip = ref(false)
const shareError = ref(false)
const showShareDialog = ref(false)
const shareUrl = ref(null)
function shareOrder() {
sharing.value = true
track('share')
getShareUrl().then(url => {
shareError.value = url === null
if (url === null) {
sharing.value = false
return
}
console.log('share url', url)
shareUrl.value = url
sharing.value = false
navigator.permissions.query({name: "clipboard-write"}).then((permission) => {
const permitted = permission.state === "granted" || permission.state === "prompt"
if (!permitted) {
showShareDialog.value = true
} else {
navigator.clipboard.writeText(url)
.then(() => {
showSharedTooltip.value = true
setTimeout(() => showSharedTooltip.value = false, 3000)
})
.catch(() => {
showShareDialog.value = true
})
}
}).catch(() => null)
})
}
</script> </script>
<style lang="scss"> // NOT scoped <style lang="scss"> // NOT scoped

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";
@@ -177,10 +177,8 @@ function update(a, b, updateA, updateB) {
a = maxA a = maxA
} }
_timeEndpoints.value = [a, b] _timeEndpoints.value = [a, b]
const newBuilder = {...props.builder} props.builder.timeA = a
newBuilder.timeA = a props.builder.timeB = b
newBuilder.timeB = b
emit('update:builder', newBuilder)
} }
const flipped = computed(()=>{ const flipped = computed(()=>{

View File

@@ -89,13 +89,16 @@
</tr> </tr>
</tbody> </tbody>
</table> </table>
<one-time-hint name="click-chart" activator="#tv-widget" location="center" :when="builder.lineA===null && !co.drew" text="Click the chart!"/> <one-time-hint name="click-chart" activator="#tv-widget" location="center"
:when="builder.lineA===null && !co.drew" text="Click the chart!"
:on-complete="()=>track('click-chart')"
/>
</rung-builder> </rung-builder>
</template> </template>
<script setup> <script setup>
import {applyLinePoints, builderDefaults, useChartOrderStore} from "@/orderbuild.js"; import {applyLinePoints, builderDefaults, useChartOrderStore} from "@/orderbuild.js";
import {sideColor} from "@/misc.js"; import {sideColor, toPrecisionOrNull} from "@/misc.js";
import {MAX_FRACTION, newTranche} from "@/blockchain/orderlib.js"; import {MAX_FRACTION, newTranche} from "@/blockchain/orderlib.js";
import RungBuilder from "@/components/chart/RungBuilder.vue"; import RungBuilder from "@/components/chart/RungBuilder.vue";
import {computed, ref} from "vue"; import {computed, ref} from "vue";
@@ -104,6 +107,7 @@ import {vectorEquals, vectorInterpolate} from "@/vector.js";
import AbsoluteTimeEntry from "@/components/AbsoluteTimeEntry.vue"; import AbsoluteTimeEntry from "@/components/AbsoluteTimeEntry.vue";
import {useStore} from "@/store/store.js"; import {useStore} from "@/store/store.js";
import OneTimeHint from "@/components/OneTimeHint.vue"; import OneTimeHint from "@/components/OneTimeHint.vue";
import {track} from "@/track.js";
const s = useStore() const s = useStore()
const co = useChartOrderStore() const co = useChartOrderStore()
@@ -206,7 +210,7 @@ const time1A = computed({
}) })
const price1A = computed({ const price1A = computed({
get() { return _endpoints.value[0] === null ? null : _endpoints.value[0][1] }, get() { return toPrecisionOrNull(_endpoints.value[0] === null ? null : _endpoints.value[0][1], 6) },
set(v) { set(v) {
const flatline0 = _endpoints.value[0]; const flatline0 = _endpoints.value[0];
update( update(
@@ -228,7 +232,7 @@ const time1B = computed({
}) })
const price1B = computed({ const price1B = computed({
get() { return _endpoints.value[0] === null ? null : _endpoints.value[0][3] }, get() { return toPrecisionOrNull(_endpoints.value[0] === null ? null : _endpoints.value[0][3], 6) },
set(v) { set(v) {
const flatline0 = _endpoints.value[0]; const flatline0 = _endpoints.value[0];
update( update(
@@ -250,7 +254,7 @@ const time2A = computed({
}) })
const price2A = computed({ const price2A = computed({
get() { return _endpoints.value[1] === null ? null : _endpoints.value[1][1] }, get() { return toPrecisionOrNull(_endpoints.value[1] === null ? null : _endpoints.value[1][1], 6) },
set(v) { set(v) {
const flatline = _endpoints.value[1]; const flatline = _endpoints.value[1];
update( update(
@@ -272,7 +276,7 @@ const time2B = computed({
}) })
const price2B = computed({ const price2B = computed({
get() { return _endpoints.value[1] === null ? null : _endpoints.value[1][3] }, get() { return toPrecisionOrNull(_endpoints.value[1] === null ? null : _endpoints.value[1][3], 6) },
set(v) { set(v) {
const flatline = _endpoints.value[1]; const flatline = _endpoints.value[1];
update( update(
@@ -285,10 +289,8 @@ const price2B = computed({
function update(a, b) { // a and b are lines of two points function update(a, b) { // a and b are lines of two points
if (!vectorEquals(props.builder.lineA, a) || !vectorEquals(props.builder.lineB, b)) { if (!vectorEquals(props.builder.lineA, a) || !vectorEquals(props.builder.lineB, b)) {
_endpoints.value = [flattenLine(a), flattenLine(b)] _endpoints.value = [flattenLine(a), flattenLine(b)]
const newBuilder = {...props.builder} props.builder.lineA = a
newBuilder.lineA = a props.builder.lineB = b
newBuilder.lineB = b
emit('update:builder', newBuilder)
} }
} }

View File

@@ -21,7 +21,7 @@
<td class="weight" style="vertical-align: bottom">{{ allocationTexts[higherIndex] }}</td> <td class="weight" style="vertical-align: bottom">{{ allocationTexts[higherIndex] }}</td>
</tr> </tr>
<tr v-for="i in innerIndexes" class="ml-5"> <tr v-for="i in innerIndexes" class="ml-5">
<td class="pl-5">{{ prices[i] }}</td> <td class="pl-5">{{ toPrecision(prices[i],6) }}</td>
<td class="weight">{{ allocationTexts[i] }}</td> <td class="weight">{{ allocationTexts[i] }}</td>
</tr> </tr>
</template> </template>
@@ -37,7 +37,10 @@
</tr> </tr>
</tbody> </tbody>
</table> </table>
<one-time-hint name="click-chart" activator="#tv-widget" location="center" :when="priceA===null" text="Click the chart!"/> <one-time-hint name="click-chart" activator="#tv-widget" location="center"
:when="priceA===null" text="Click the chart!"
:on-complete="()=>track('click-chart')"
/>
</rung-builder> </rung-builder>
</template> </template>
@@ -50,6 +53,8 @@ import RungBuilder from "@/components/chart/RungBuilder.vue";
import {computed, ref} from "vue"; import {computed, ref} from "vue";
import {allocationText, HLine} from "@/charts/shape.js"; import {allocationText, HLine} from "@/charts/shape.js";
import OneTimeHint from "@/components/OneTimeHint.vue"; import OneTimeHint from "@/components/OneTimeHint.vue";
import {track} from "@/track.js";
import {toPrecision, toPrecisionOrNull} from "@/misc.js";
const s = useStore() const s = useStore()
const os = useOrderStore() const os = useOrderStore()
@@ -134,10 +139,8 @@ const priceEndpoints = computed({
function update(a, b) { function update(a, b) {
_priceEndpoints.value = [a, b] _priceEndpoints.value = [a, b]
const newBuilder = {...props.builder} props.builder.priceA = a
newBuilder.priceA = a props.builder.priceB = b
newBuilder.priceB = b
emit('update:builder', newBuilder)
} }
const flipped = computed(()=>{ const flipped = computed(()=>{
@@ -147,7 +150,7 @@ const flipped = computed(()=>{
}) })
const higherPrice = computed({ const higherPrice = computed({
get() { return flipped.value ? priceA.value : priceB.value }, get() { return toPrecisionOrNull(flipped.value ? priceA.value : priceB.value, 6) },
set(v) { set(v) {
if (flipped.value) if (flipped.value)
priceA.value = v priceA.value = v
@@ -168,9 +171,7 @@ const innerIndexes = computed(()=>{
}) })
const lowerPrice = computed({ const lowerPrice = computed({
get() { get() {return toPrecisionOrNull(!flipped.value ? priceA.value : priceB.value, 6)},
return !flipped.value ? priceA.value : priceB.value
},
set(v) { set(v) {
if (!flipped.value) if (!flipped.value)
priceA.value = v priceA.value = v

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,12 +19,16 @@
: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"
:on-complete="()=>track('rungs')"
/>
<v-tooltip v-if="builder.breakout!==undefined" <v-tooltip v-if="builder.breakout!==undefined"
:text="order.buy?'Breakout orders buy above the breakout line':'Breakdown orders sell below the breakdown line'"> :text="order.buy?'Breakout orders buy above the breakout line':'Breakdown orders sell below the breakdown line'">
<template #activator="{ props }"> <template #activator="{ props }">
@@ -48,13 +55,21 @@
</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"
:on-complete="()=>track('balance-slider')"
/>
<v-text-field type="number" v-model="balance100" min="-100" max="100" <v-text-field type="number" v-model="balance100" min="-100" max="100"
density="compact" hide-details variant="outlined" label="Balance" step="5" density="compact" hide-details variant="outlined" label="Balance" step="5"
class="balance"> class="balance">
@@ -88,6 +103,8 @@ import {
} from "@/vector.js"; } from "@/vector.js";
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 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
@@ -114,14 +131,16 @@ 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
watchEffect(()=>{ watchEffect(()=>{
const rungs = props.builder.rungs const rungs = props.builder.rungs
// const prev = props.builder.valid // const prev = props.builder.valid
props.builder.valid = rungs >= 1 && endpoints.value[0] && (rungs < 2 || endpoints.value[1]) props.builder.valid = rungs >= 1 && endpoints.value[0] > 0 && (rungs < 2 || endpoints.value[1])
// console.log('valid?', prev, props.builder.valid, rungs, valueA.value, valueB.value) // console.log('valid?', prev, props.builder.valid, rungs, valueA.value, valueB.value)
}) })
@@ -231,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)
@@ -452,6 +467,8 @@ function deleteShapes() {
if (!endpoints.value[0]) if (!endpoints.value[0])
shapeA.createOrDraw(); // initiate drawing mode shapeA.createOrDraw(); // initiate drawing mode
else
adjustShapes()
</script> </script>

View File

@@ -0,0 +1,28 @@
<template>
<h2>Shared Order</h2>
<p>Loading the shared order into the app...</p>
</template>
<script setup>
import {loadShareUrl} from "@/share.js";
import {router} from "@/router/router.js";
import {useRoute} from "vue-router";
const route = useRoute()
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>
<style scoped lang="scss">
</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()
@@ -230,6 +224,21 @@ export function intervalToSeconds(interval) {
} }
export function secondsToInterval(seconds) {
const units = [
[30 * 24 * 60 * 60, 'M'],
[7 * 24 * 60 * 60, 'W'],
[24 * 60 * 60, 'D'],
[60, ''],
[1, 'S'],
]
for( const [unit, suffix] of units)
if (seconds % unit === 0)
return `${seconds / unit}${suffix}`
throw Error(`invalid secondsToInterval ${seconds}`)
}
export function interpolate(a, b, zeroToOne) { export function interpolate(a, b, zeroToOne) {
const d = (b-a) const d = (b-a)
return a + d * zeroToOne return a + d * zeroToOne
@@ -260,6 +269,12 @@ export function toPrecision(value, significantDigits = 3) {
return value.toFixed(decimalsNeeded); // Use toFixed to completely avoid scientific notation return value.toFixed(decimalsNeeded); // Use toFixed to completely avoid scientific notation
} }
export function toPrecisionOrNull(value, significantDigits = 3) {
if (value===null) return null
if (value===undefined) return undefined
return toPrecision(value, significantDigits)
}
export function toHuman(value, significantDigits = 2) { export function toHuman(value, significantDigits = 2) {
if (!isFinite(value)) return value.toString(); // Handle Infinity and NaN if (!isFinite(value)) return value.toString(); // Handle Infinity and NaN
let suffix = '' let suffix = ''

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) {
Notification.requestPermission()
.then(permission => { .then(permission => {
console.log(`notification permission: ${permission}`); notificationsAllowed = permission === 'granted'
native = permission === 'granted' if (!notificationsAllowed)
console.log(`notification permission denied: ${permission}`);
}) })
.catch(error => { .catch(error => {console.error(`notification permission error: ${error}`);});
console.error(`notification permission error: ${error}`); }
native = false;
});
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,

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.
@@ -199,8 +194,9 @@ export function timesliceTranches() {
export function builderDefaults(builder, defaults) { export function builderDefaults(builder, defaults) {
for (const k in defaults) for (const k in defaults)
if (builder[k] === undefined) if (!Object.prototype.hasOwnProperty.call(builder, k)) {
builder[k] = defaults[k] instanceof Function ? defaults[k]() : defaults[k] builder[k] = defaults[k] instanceof Function ? defaults[k]() : defaults[k]
}
} }
export function linearWeights(num, skew) { export function linearWeights(num, skew) {

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,50 +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: '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'),
},
],
},
]

88
src/share.js Normal file
View File

@@ -0,0 +1,88 @@
import {useChartOrderStore} from "@/orderbuild.js";
import {changeIntervalSecs, onChartReady, setSymbol, widget} from "@/charts/chart.js";
import {usePrefStore, useStore} from "@/store/store.js";
import {lookupSymbol} from "@/charts/datafeed.js";
import {track} from "@/track.js";
import {socket} from "@/socket.js";
export async function getShareUrl() {
const co = useChartOrderStore();
const s = useStore()
const sym = co.selectedSymbol
console.log('symbol', sym)
const data = {
version: 1,
chainId: s.chainId,
orders: co.orders,
symbol: {
base: {a: sym.base.a, s: sym.base.s},
quote: {a: sym.quote.a, s: sym.quote.s},
route: {
fee: sym.fee,
exchange: sym.exchangeId,
}
},
period: co.intervalSecs,
}
const json = JSON.stringify(data)
console.log('sharing data', json, data)
const snapshot = await takeSnapshot()
const code = await new Promise((resolve)=>socket.emit('share', data, snapshot, resolve))
if (code===null) return null
return import.meta.env.VITE_SHARE_URL+ '/share/'+code;
}
export async function loadShareUrl(code) {
// console.log('loading share url', code)
const data = await new Promise((resolve, reject) => {
// Set a timeout (e.g., 8 seconds)
const timeout = setTimeout(() => {
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)
const co = useChartOrderStore();
const s = useStore()
const ticker = `${data.chainId}|${data.symbol.route.exchange}|${data.symbol.base.a}|${data.symbol.quote.a}|${data.symbol.route.fee}`;
const symbol = lookupSymbol(ticker)
if (symbol===null) {
console.error('could not find symbol for ticker', ticker)
return false
}
s.chainId = data.chainId
const prefs = usePrefStore()
prefs.selectedSymbol = ticker
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.amountIsTokenA = true
order.valid = false
}
co.orders = data.orders
changeIntervalSecs(data.period)
onChartReady(()=>{
setSymbol(symbol)
.catch((e)=>console.error('could not set symbol', e))
})
track('shared')
console.log('loaded orders', s.chainId, co.orders)
return true;
}
export async function takeSnapshot() {
const screenshotCanvas = await widget.takeClientScreenshot();
return await new Promise((resolve) => screenshotCanvas.toBlob(resolve));
}

View File

@@ -1,155 +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";
export const socket = io(import.meta.env.VITE_WS_URL || undefined, {transports: ["websocket"]}) const socketOptions = {
transport: ['polling', 'websocket'],
socket.on('connect', () => { pingInterval: 25000, // PING every 25 seconds
console.log(new Date(), 'ws connected') pingTimeout: 60000 // Timeout if no PONG in 60 seconds
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
} }
export const socket = io(import.meta.env.VITE_WS_URL || undefined, socketOptions)
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,12 +1,17 @@
export let tracking_enabled = true export let tracking_enabled = window.gtag !== undefined
export function track(event, info) { if(tracking_enabled) {
// console.log('gtag', tracking_enabled)
}
else {
console.log('tracking disabled')
}
export function track(...args) {
if (tracking_enabled) { if (tracking_enabled) {
if (window.gtag !== undefined) try {
window.gtag('event', event, info) window.gtag('event', ...args)
else { } catch (e) {
console.log('gtag not available')
tracking_enabled = false
} }
} }
} }

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: {

270
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"
@@ -1503,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"
@@ -1516,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"
@@ -1523,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"
@@ -1686,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"
@@ -1777,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"
@@ -1800,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"
@@ -1836,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"
@@ -1862,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"
@@ -1881,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"
@@ -1921,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"
@@ -1940,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"
@@ -1952,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"
@@ -1969,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"
@@ -2078,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"
@@ -2144,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"