diff --git a/.prettierignore b/.prettierignore index 712a9294..ff87240a 100644 --- a/.prettierignore +++ b/.prettierignore @@ -19,4 +19,5 @@ favicon.png CHANGELOG.md **/*.svg .prettierignore -.gitignore \ No newline at end of file +.gitignore +_redirects diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..0eb0765f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Pendulum Chain / SatoshiPay + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index a2d7e6e0..80c30326 100644 --- a/README.md +++ b/README.md @@ -37,10 +37,15 @@ matter what URL the browser requests. ## Env Variables -- `VITE_SIGNING_SERVICE_URL`: Optional variable to point to a specific signing backend service URL. If undefined, it +- `VITE_SIGNING_SERVICE_PATH`: Optional variable to point to a specific signing backend service URL. If undefined, it will default to either: - - http://localhost:3000 (if in development mode) - - https://prototype-signer-service-polygon.pendulumchain.tech (if in production mode) + - `http://localhost:3000` (if in development mode) + - `/api/production` (if in production mode) + - this will use the `_redirects` file to direct Netlify to proxy all requests to `/api/production` to + `https://prototype-signer-service-polygon.pendulumchain.tech` + - `/api/staging` (if in staging mode) + - this will use the `_redirects` file to direct Netlify to proxy all requests to `/api/staging` to + `https://prototype-signer-service-polygon-staging.pendulumchain.tech` - `VITE_ALCHEMY_API_KEY`: Optional variable to set the Alchemy API key for the custom RPC provider. If undefined, it will use dhe default endpoint. diff --git a/_redirects b/_redirects new file mode 100644 index 00000000..b8c597fe --- /dev/null +++ b/_redirects @@ -0,0 +1,3 @@ +/api/production/* https://prototype-signer-service-polygon.pendulumchain.tech/:splat 200 +/api/staging/* https://prototype-signer-service-polygon-staging.pendulumchain.tech/:splat 200 +/* /index.html 200 diff --git a/mooncontracts/baseline.sol b/mooncontracts/baseline.sol index 6ee15a6e..8cf0cfb1 100644 --- a/mooncontracts/baseline.sol +++ b/mooncontracts/baseline.sol @@ -8,6 +8,9 @@ contract ReceiveCrossChainXToken { address constant public axlUSDCAddress = 0xCa01a1D0993565291051daFF390892518ACfAD3A; IERC20 constant axlUSDC = IERC20(axlUSDCAddress); + + address constant public squidRouterMultiCallContract = 0xaD6Cea45f98444a922a2b4fE96b8C90F0862D2F4; + Xtokens constant xt = Xtokens(0x0000000000000000000000000000000000000804); event ReceiveBalance(uint256 balance); @@ -38,7 +41,7 @@ contract ReceiveCrossChainXToken { function transferApprovedTokensToSelf(uint256 amount) internal { IERC20 token = IERC20(axlUSDCAddress); - bool success = token.transferFrom(0xEa749Fd6bA492dbc14c24FE8A3d08769229b896c, address(this), amount); + bool success = token.transferFrom(squidRouterMultiCallContract, address(this), amount); require(success, "Transfer failed"); } } diff --git a/mooncontracts/splitReceiver.sol b/mooncontracts/splitReceiver.sol index 66bc7de9..22eb7363 100644 --- a/mooncontracts/splitReceiver.sol +++ b/mooncontracts/splitReceiver.sol @@ -8,6 +8,9 @@ contract ReceiveCrossChainXToken { address constant public axlUSDCAddress = 0xCa01a1D0993565291051daFF390892518ACfAD3A; IERC20 constant axlUSDC = IERC20(axlUSDCAddress); + + address constant public squidRouterMultiCallContract = 0xaD6Cea45f98444a922a2b4fE96b8C90F0862D2F4; + Xtokens constant xt = Xtokens(0x0000000000000000000000000000000000000804); event ReceiveBalance(uint256 balance); @@ -22,7 +25,7 @@ contract ReceiveCrossChainXToken { ) public { require(amount > 0, "Amount cannot be zero"); require(xcmDataMapping[hash] == 0, "Hash already used"); - + xcmDataMapping[hash] = amount; transferApprovedTokensToSelf(amount); @@ -52,7 +55,7 @@ contract ReceiveCrossChainXToken { function transferApprovedTokensToSelf(uint256 amount) internal { IERC20 token = IERC20(axlUSDCAddress); - bool success = token.transferFrom(0xEa749Fd6bA492dbc14c24FE8A3d08769229b896c, address(this), amount); + bool success = token.transferFrom(squidRouterMultiCallContract, address(this), amount); require(success, "Transfer failed"); } } diff --git a/package.json b/package.json index e459ece0..7de31ae1 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "packageManager": "yarn@4.5.0+sha512.837566d24eec14ec0f5f1411adb544e892b3454255e61fdef8fd05f3429480102806bac7446bc9daff3896b01ae4b62d00096c7e989f1596f2af10b927532f39", "scripts": { "dev": "vite --host", - "build": "tsc && vite build && cp -R src/assets/coins dist/assets/coins && echo '/* /index.html 200' | cat > dist/_redirects", + "build": "tsc && vite build && cp -R src/assets/coins dist/assets/coins && cp _redirects dist/_redirects", "preview": "vite preview", "lint": "eslint . --ext .ts,.tsx", "lint:fix": "eslint . --ext .ts,.tsx --fix", @@ -48,6 +48,7 @@ "bn.js": "^5.2.1", "buffer": "^6.0.3", "daisyui": "^4.11.1", + "ethers": "^6.13.4", "framer-motion": "^11.2.14", "postcss": "^8.4.38", "preact": "^10.12.1", @@ -57,6 +58,7 @@ "react-hook-form": "^7.51.5", "react-router-dom": "^6.8.1", "react-toastify": "^10.0.5", + "siwe": "^2.3.2", "stellar-base": "^11.0.1", "stellar-sdk": "^11.3.0", "tailwind": "^4.0.0", diff --git a/signer-service/package.json b/signer-service/package.json index 6dc0e309..73694f6c 100644 --- a/signer-service/package.json +++ b/signer-service/package.json @@ -26,9 +26,11 @@ "big.js": "^6.2.1", "body-parser": "^1.17.0", "compression": "^1.6.2", + "cookie-parser": "^1.4.7", "cors": "^2.8.3", "cross-env": "^7.0.3", "dotenv": "^16.4.5", + "ethers": "^6.13.4", "express": "^5.0.1", "express-rate-limit": "^6.7.0", "express-validation": "^1.0.2", @@ -40,6 +42,7 @@ "method-override": "^3.0.0", "mongoose": "^5.2.17", "morgan": "^1.8.1", + "siwe": "^2.3.2", "stellar-sdk": "^11.3.0", "viem": "^2.21.3", "winston": "^3.1.0" diff --git a/signer-service/src/api/controllers/siwe.controller.js b/signer-service/src/api/controllers/siwe.controller.js new file mode 100644 index 00000000..b07ddd62 --- /dev/null +++ b/signer-service/src/api/controllers/siwe.controller.js @@ -0,0 +1,44 @@ +const { createAndSendNonce, verifyAndStoreSiweMessage } = require('../services/siwe.service'); +const { DEFAULT_LOGIN_EXPIRATION_TIME_HOURS } = require('../../constants/constants'); + +exports.sendSiweMessage = async (req, res) => { + const { walletAddress } = req.body; + try { + const { nonce } = await createAndSendNonce(walletAddress); + return res.json({ + nonce, + }); + } catch (e) { + console.error(e); + return res.status(500).json({ error: 'Error while generating nonce' }); + } +}; + +exports.validateSiweSignature = async (req, res) => { + const { nonce, signature, siweMessage } = req.body; + try { + await verifyAndStoreSiweMessage(nonce, signature, siweMessage); + + const token = { + nonce, + signature, + }; + + res.cookie('authToken', token, { + httpOnly: true, + secure: true, + sameSite: 'Strict', + maxAge: DEFAULT_LOGIN_EXPIRATION_TIME_HOURS * 60 * 60 * 1000, + }); + + return res.status(200).json({ message: 'Signature is valid' }); + } catch (e) { + console.error(e); + + if (e.name === 'SiweValidationError') { + return res.status(401).json({ error: `Siwe validation error: ${e.message}` }); + } + + return res.status(500).json({ error: `Could not validate signature: ${e.message}` }); + } +}; diff --git a/signer-service/src/api/controllers/stellar.controller.js b/signer-service/src/api/controllers/stellar.controller.js index 99b2df9e..fe6b3f07 100644 --- a/signer-service/src/api/controllers/stellar.controller.js +++ b/signer-service/src/api/controllers/stellar.controller.js @@ -1,7 +1,7 @@ require('dotenv').config(); const { Keypair } = require('stellar-sdk'); -const { FUNDING_SECRET } = require('../../constants/constants'); +const { FUNDING_SECRET, SEP10_MASTER_SECRET } = require('../../constants/constants'); const { buildCreationStellarTx, buildPaymentAndMergeTx, sendStatusWithPk } = require('../services/stellar.service'); const { signSep10Challenge } = require('../services/sep10.service'); @@ -54,12 +54,45 @@ exports.changeOpTransaction = async (req, res, next) => { exports.signSep10Challenge = async (req, res, next) => { try { - let { clientSignature, clientPublic } = await signSep10Challenge( + let maybeChallengeSignature; + let maybeNonce; + if (req.cookies?.authToken) { + maybeChallengeSignature = req.cookies.authToken.signature; + maybeNonce = req.cookies.authToken.nonce; + } + + if (Boolean(req.body.memo) && (!maybeChallengeSignature || !maybeNonce)) { + return res.status(401).json({ + error: 'Missing signature or nonce', + }); + } + + let { masterClientSignature, masterClientPublic, clientSignature, clientPublic } = await signSep10Challenge( req.body.challengeXDR, req.body.outToken, req.body.clientPublicKey, + maybeChallengeSignature, + maybeNonce, ); - return res.json({ clientSignature, clientPublic }); + return res.json({ masterClientSignature, masterClientPublic, clientSignature, clientPublic }); + } catch (error) { + if (error.message.includes('Could not verify signature')) { + // Distinguish between failed signature check and other errors. + return res.status(401).json({ + error: 'Signature validation failed.', + details: error.message, + }); + } + + console.error('Error in signSep10Challenge:', error); + return res.status(500).json({ error: 'Failed to sign challenge', details: error.message }); + } +}; + +exports.getSep10MasterPK = async (req, res, next) => { + try { + const masterSep10Public = Keypair.fromSecret(SEP10_MASTER_SECRET).publicKey(); + return res.json({ masterSep10Public }); } catch (error) { console.error('Error in signSep10Challenge:', error); return res.status(500).json({ error: 'Failed to sign challenge', details: error.message }); diff --git a/signer-service/src/api/middlewares/validators.js b/signer-service/src/api/middlewares/validators.js index 70a2c43b..674b508f 100644 --- a/signer-service/src/api/middlewares/validators.js +++ b/signer-service/src/api/middlewares/validators.js @@ -155,6 +155,31 @@ const validateSep10Input = (req, res, next) => { next(); }; +const validateSiweCreate = (req, res, next) => { + const { walletAddress } = req.body; + if (!walletAddress) { + return res.status(400).json({ error: 'Missing param: walletAddress' }); + } + next(); +}; + +const validateSiweValidate = (req, res, next) => { + const { nonce, signature, siweMessage } = req.body; + if (!signature) { + return res.status(400).json({ error: 'Missing param: signature' }); + } + + if (!nonce) { + return res.status(400).json({ error: 'Missing param: nonce' }); + } + + if (!siweMessage) { + return res.status(400).json({ error: 'Missing param: siweMessage' }); + } + + next(); +}; + module.exports = { validateChangeOpInput, validateQuoteInput, @@ -166,4 +191,6 @@ module.exports = { validateRatingInput, validateExecuteXCM, validateSep10Input, + validateSiweCreate, + validateSiweValidate, }; diff --git a/signer-service/src/api/routes/v1/index.js b/signer-service/src/api/routes/v1/index.js index 6acb5b49..00556acc 100644 --- a/signer-service/src/api/routes/v1/index.js +++ b/signer-service/src/api/routes/v1/index.js @@ -7,6 +7,7 @@ const storageRoutes = require('./storage.route'); const emailRoutes = require('./email.route'); const ratingRoutes = require('./rating.route'); const subsidizeRoutes = require('./subsidize.route'); +const siweRoutes = require('./siwe.route'); const quoteRoutes = require('./quote.route'); const router = express.Router({ mergeParams: true }); @@ -76,4 +77,9 @@ router.use('/subsidize', subsidizeRoutes); */ router.use('/rating', ratingRoutes); +/** + * POST v1/siwe + */ +router.use('/siwe', siweRoutes); + module.exports = router; diff --git a/signer-service/src/api/routes/v1/siwe.route.js b/signer-service/src/api/routes/v1/siwe.route.js new file mode 100644 index 00000000..ef02ed87 --- /dev/null +++ b/signer-service/src/api/routes/v1/siwe.route.js @@ -0,0 +1,11 @@ +const express = require('express'); +const controller = require('../../controllers/siwe.controller'); +const { validateSiweCreate, validateSiweValidate } = require('../../middlewares/validators'); + +const router = express.Router({ mergeParams: true }); + +router.route('/create').post(validateSiweCreate, controller.sendSiweMessage); + +router.route('/validate').post(validateSiweValidate, controller.validateSiweSignature); + +module.exports = router; diff --git a/signer-service/src/api/routes/v1/stellar.route.js b/signer-service/src/api/routes/v1/stellar.route.js index 3acd3dd4..bc8f1d74 100644 --- a/signer-service/src/api/routes/v1/stellar.route.js +++ b/signer-service/src/api/routes/v1/stellar.route.js @@ -10,4 +10,6 @@ router.route('/payment').post(validateChangeOpInput, controller.changeOpTransact router.route('/sep10').post(validateSep10Input, controller.signSep10Challenge); +router.route('/sep10').get(controller.getSep10MasterPK); + module.exports = router; diff --git a/signer-service/src/api/services/sep10.service.js b/signer-service/src/api/services/sep10.service.js index e6886ce1..667b9408 100644 --- a/signer-service/src/api/services/sep10.service.js +++ b/signer-service/src/api/services/sep10.service.js @@ -1,17 +1,48 @@ const { Keypair } = require('stellar-sdk'); const { TransactionBuilder, Networks } = require('stellar-sdk'); const { fetchTomlValues } = require('../helpers/anchors'); +const { verifySiweMessage } = require('./siwe.service'); +const { keccak256 } = require('viem/utils'); const { TOKEN_CONFIG } = require('../../constants/tokenConfig'); -const { CLIENT_DOMAIN_SECRET } = require('../../constants/constants'); +const { SEP10_MASTER_SECRET, CLIENT_DOMAIN_SECRET } = require('../../constants/constants'); const NETWORK_PASSPHRASE = Networks.PUBLIC; -exports.signSep10Challenge = async (challengeXDR, outToken, clientPublicKey) => { +async function deriveMemoFromAddress(address) { + const hash = keccak256(address); + return BigInt(hash).toString().slice(0, 15); +} + +// we validate a challenge for a given nonce. From it we obtain the address and derive the memo +// we can then ensure that the memo is the same as the one we expect from the anchor challenge +const validateSignatureAndGetMemo = async (nonce, userChallengeSignature, memoEnabled) => { + if (!userChallengeSignature || !nonce || !memoEnabled) { + return null; // Default memo value when single stellar account is used + } + + let message; + try { + // initialSiweMessage must be undefined after an initial check, + // message must exist on the map. + message = await verifySiweMessage(nonce, userChallengeSignature, undefined); + } catch (e) { + throw new Error(`Could not verify signature: ${e.message}`); + } + + const memo = await deriveMemoFromAddress(message.address); + return memo; +}; + +exports.signSep10Challenge = async (challengeXDR, outToken, clientPublicKey, userChallengeSignature, nonce) => { + const masterStellarKeypair = Keypair.fromSecret(SEP10_MASTER_SECRET); const clientDomainStellarKeypair = Keypair.fromSecret(CLIENT_DOMAIN_SECRET); const { signingKey: anchorSigningKey } = await fetchTomlValues(TOKEN_CONFIG[outToken].tomlFileUrl); - const { homeDomain, clientDomainEnabled } = TOKEN_CONFIG[outToken]; + const { homeDomain, clientDomainEnabled, memoEnabled } = TOKEN_CONFIG[outToken]; + + // Expected memo based on user's signature and nonce. + const memo = await validateSignatureAndGetMemo(nonce, userChallengeSignature, memoEnabled); const transactionSigned = new TransactionBuilder.fromXDR(challengeXDR, NETWORK_PASSPHRASE); if (transactionSigned.source !== anchorSigningKey) { @@ -22,10 +53,10 @@ exports.signSep10Challenge = async (challengeXDR, outToken, clientPublicKey) => } // See https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0010.md#success - // memo field should be empty as we assume (in this implementation) that we use the ephemeral - // to authenticate. But no memo sub account derivation. - if (transactionSigned.memo.value !== null) { - throw new Error('Memo does not match'); + // memo field should be empty for the ephemeral case, or the corresponding one based on evm address + // derivation. + if (transactionSigned.memo.value !== memo) { + throw new Error('Memo does not match with specified user signature or address. Could not validate.'); } const { operations } = transactionSigned; @@ -35,11 +66,19 @@ exports.signSep10Challenge = async (challengeXDR, outToken, clientPublicKey) => throw new Error('The first operation should be manageData'); } - // Only authorize a session that corresponds with the ephemeral client account + // clientPublicKey is either: the ephemeral, or the master account if (firstOp.source !== clientPublicKey) { throw new Error('First manageData operation must have the client account as the source'); } + if (memo !== null && memoEnabled) { + if (firstOp.source !== masterStellarKeypair.publicKey()) { + throw new Error( + 'First manageData operation must have the master signing key as the source when memo is being used.', + ); + } + } + if (firstOp.name !== `${homeDomain} auth`) { throw new Error(`First manageData operation should have key '${homeDomain} auth'`); } @@ -47,11 +86,9 @@ exports.signSep10Challenge = async (challengeXDR, outToken, clientPublicKey) => throw new Error('First manageData operation should have a 64-byte random nonce as value'); } - // Flags to check presence of required operations let hasWebAuthDomain = false; let hasClientDomain = false; - // Verify extra manage_data operations, web_auth and proper client domain. for (let i = 1; i < operations.length; i++) { const op = operations[i]; @@ -88,8 +125,15 @@ exports.signSep10Challenge = async (challengeXDR, outToken, clientPublicKey) => clientDomainSignature = transactionSigned.getKeypairSignature(clientDomainStellarKeypair); } + let masterClientSignature; + if (memo !== null && memoEnabled) { + masterClientSignature = transactionSigned.getKeypairSignature(masterStellarKeypair); + } + return { clientSignature: clientDomainSignature, clientPublic: clientDomainStellarKeypair.publicKey(), + masterClientSignature, + masterClientPublic: masterStellarKeypair.publicKey(), }; }; diff --git a/signer-service/src/api/services/siwe.service.js b/signer-service/src/api/services/siwe.service.js new file mode 100644 index 00000000..a3dda7a6 --- /dev/null +++ b/signer-service/src/api/services/siwe.service.js @@ -0,0 +1,127 @@ +const siwe = require('siwe'); +const { createPublicClient, http } = require('viem'); +const { polygon } = require('viem/chains'); +const { DEFAULT_LOGIN_EXPIRATION_TIME_HOURS, VALID_SIWE_CHAINS } = require('../../constants/constants'); + +class ValidationError extends Error { + constructor(message) { + super(message); + this.name = 'SiweValidationError'; + } +} + +// Map that will hold the siwe messages sent + nonce we defined +const siweMessagesMap = new Map(); + +const createAndSendNonce = async (address) => { + const nonce = siwe.generateNonce(); + const siweMessage = undefined; // Initial message is undefined since it will be created in UI. + siweMessagesMap.set(nonce, { siweMessage, address }); + return { nonce }; +}; + +// Used to verify the integrity and validity of the signature +// For the initial verification, the siweMessage must be provided as parameter +const verifySiweMessage = async (nonce, signature, initialSiweMessage) => { + const existingSiweDataForNonce = siweMessagesMap.get(nonce); + if (!existingSiweDataForNonce) { + throw new ValidationError('Message not found, we have not sent this nonce or nonce is incorrect'); + } + + const siweMessage = existingSiweDataForNonce.siweMessage + ? existingSiweDataForNonce.siweMessage + : new siwe.SiweMessage(initialSiweMessage); + const address = existingSiweDataForNonce.address; + + if (!siweMessage) { + throw new Error('Message must be provided as a parameter if it has not been initially validated.'); + } + + // Verify the integrity of the message + const publicClient = createPublicClient({ + chain: polygon, + transport: http(), + }); + + const valid = await publicClient.verifyMessage({ + address: address, + message: siweMessage.toMessage(), // Validation must be done on the message as string + signature, + }); + + if (!valid) { + throw new ValidationError('Invalid signature'); + } + + // Perform additional checks to ensure message fields + if (siweMessage.nonce !== nonce) { + throw new ValidationError('Nonce mismatch'); + } + + if (siweMessage.expirationTime && new Date(siweMessage.expirationTime) < new Date()) { + throw new ValidationError('Message has expired'); + } + + return siweMessage; +}; + +// Since the message is created in the UI, we need to verify the fields of the message +const verifyInitialMessageFields = (siweMessage) => { + // Fields we validate on initial + const domain = siweMessage.domain; + const uri = siweMessage.uri; + const scheme = siweMessage.scheme; // must be https + const chainId = siweMessage.chainId; + const expirationTime = siweMessage.expirationTime; + + if (!VALID_SIWE_CHAINS.includes(chainId)) { + throw new ValidationError('Incorrect chain ID'); + } + + if (scheme !== 'https') { + throw new ValidationError('Scheme must be https'); + } + + if (!expirationTime || isNaN(new Date(expirationTime).getTime())) { + throw new ValidationError('Must define a valid expiration time'); + } + + // Check if expiration is within a reasonable range from current time + const currentTime = new Date().getTime(); + const expirationTimestamp = new Date(expirationTime).getTime(); + + const expirationGracePeriod = 1000 * 60 * 10; // 10 minutes + const expirationPeriodMs = DEFAULT_LOGIN_EXPIRATION_TIME_HOURS * 60 * 60 * 1000; + const expectedMinExpirationTimestamp = currentTime + expirationPeriodMs - expirationGracePeriod; + const expectedMaxExpirationTimestamp = currentTime + expirationPeriodMs; + + if (expirationTimestamp < expectedMinExpirationTimestamp) { + throw new ValidationError('Expiration time is too low'); + } + + if (expirationTimestamp > expectedMaxExpirationTimestamp) { + throw new ValidationError('Expiration time is too high'); + } +}; + +const verifyAndStoreSiweMessage = async (nonce, signature, siweMessage) => { + const validatedMessage = await verifySiweMessage(nonce, signature, siweMessage); + + // Perform additional checks to ensure message fields are valid + verifyInitialMessageFields(validatedMessage); + + // Verification complete. Update the map and append the message. + const siweData = siweMessagesMap.get(nonce); + siweData.siweMessage = validatedMessage; + siweMessagesMap.set(nonce, siweData); + + // Remove messages with same address from map except for the one we just verified. + // This will keep one valid session per address. + siweMessagesMap.forEach((data, nonce) => { + if (data.address === siweData.address && nonce !== siweData.siweMessage.nonce) { + siweMessagesMap.delete(nonce); + } + }); +}; + +module.exports = { verifySiweMessage, verifyAndStoreSiweMessage, createAndSendNonce }; diff --git a/signer-service/src/config/express.js b/signer-service/src/config/express.js index af0d00e9..43ea97b1 100644 --- a/signer-service/src/config/express.js +++ b/signer-service/src/config/express.js @@ -9,6 +9,7 @@ const rateLimit = require('express-rate-limit'); const routes = require('../api/routes/v1'); const { logs, rateLimitMaxRequests, rateLimitNumberOfProxies, rateLimitWindowMinutes } = require('./vars'); const error = require('../api/middlewares/error'); +const cookieParser = require('cookie-parser'); /** * Express instance @@ -16,6 +17,20 @@ const error = require('../api/middlewares/error'); */ const app = express(); +// enable CORS - Cross Origin Resource Sharing +app.use( + cors({ + origin: [ + 'http://localhost:5173', + 'https://polygon-prototype-staging--pendulum-pay.netlify.app', + 'https://app.vortexfinance.co', + ], + methods: 'GET,HEAD,PUT,PATCH,POST,DELETE', + credentials: true, + allowedHeaders: 'Content-Type,Authorization', + }), +); + // enable rate limiting // Set number of expected proxies app.set('trust proxy', rateLimitNumberOfProxies); @@ -28,6 +43,9 @@ const limiter = rateLimit({ }); app.use(limiter); +// parse cookies +app.use(cookieParser()); + // request logging. dev: console | production: file app.use(morgan(logs)); @@ -45,17 +63,6 @@ app.use(methodOverride()); // secure apps by setting various HTTP headers app.use(helmet()); -// enable CORS - Cross Origin Resource Sharing -app.use(cors()); - -app.use( - cors({ - origin: '*', - methods: 'GET,HEAD,PUT,PATCH,POST,DELETE', - allowedHeaders: 'Content-Type,Authorization', - }), -); - // mount api token routes app.use('/v1', routes); diff --git a/signer-service/src/constants/constants.js b/signer-service/src/constants/constants.js index 72a7b399..f50243d8 100644 --- a/signer-service/src/constants/constants.js +++ b/signer-service/src/constants/constants.js @@ -6,15 +6,18 @@ const PENDULUM_FUNDING_AMOUNT_UNITS = '10'; // 10 PEN. Minimum balance of fundin const STELLAR_FUNDING_AMOUNT_UNITS = '10'; // 10 XLM. Minimum balance of funding account const MOONBEAM_FUNDING_AMOUNT_UNITS = '10'; // 10 GLMR. Minimum balance of funding account const SUBSIDY_MINIMUM_RATIO_FUND_UNITS = '10'; // 10 Subsidies considering maximum subsidy amount use on each (worst case scenario) -const MOONBEAM_RECEIVER_CONTRACT_ADDRESS = '0x0004446021fe650c15fb0b2e046b39130e3bfe36'; +const MOONBEAM_RECEIVER_CONTRACT_ADDRESS = '0x2AB52086e8edaB28193172209407FF9df1103CDc'; const STELLAR_EPHEMERAL_STARTING_BALANCE_UNITS = '2.5'; // Amount to send to the new stellar ephemeral account created const PENDULUM_EPHEMERAL_STARTING_BALANCE_UNITS = '0.1'; // Amount to send to the new pendulum ephemeral account created +const DEFAULT_LOGIN_EXPIRATION_TIME_HOURS = 7 * 24; +const VALID_SIWE_CHAINS = [137]; // 137: Polygon require('dotenv').config(); const PENDULUM_FUNDING_SEED = process.env.PENDULUM_FUNDING_SEED; const FUNDING_SECRET = process.env.FUNDING_SECRET; const MOONBEAM_EXECUTOR_PRIVATE_KEY = process.env.MOONBEAM_EXECUTOR_PRIVATE_KEY; +const SEP10_MASTER_SECRET = FUNDING_SECRET; const CLIENT_DOMAIN_SECRET = process.env.CLIENT_DOMAIN_SECRET; module.exports = { @@ -32,5 +35,8 @@ module.exports = { SUBSIDY_MINIMUM_RATIO_FUND_UNITS, STELLAR_EPHEMERAL_STARTING_BALANCE_UNITS, PENDULUM_EPHEMERAL_STARTING_BALANCE_UNITS, + SEP10_MASTER_SECRET, CLIENT_DOMAIN_SECRET, + DEFAULT_LOGIN_EXPIRATION_TIME_HOURS, + VALID_SIWE_CHAINS, }; diff --git a/signer-service/src/constants/tokenConfig.js b/signer-service/src/constants/tokenConfig.js index 75948a35..3f6edbdc 100644 --- a/signer-service/src/constants/tokenConfig.js +++ b/signer-service/src/constants/tokenConfig.js @@ -8,6 +8,7 @@ const TOKEN_CONFIG = { maximumSubsidyAmountRaw: '1000000000000', // 1 unit homeDomain: 'circle.anchor.mykobo.co', clientDomainEnabled: true, + memoEnabled: false, pendulumCurrencyId: { Stellar: { AlphaNum4: { @@ -30,7 +31,8 @@ const TOKEN_CONFIG = { minWithdrawalAmount: '11000000000000', // 11 ARS. Anchor minimum limit. maximumSubsidyAmountRaw: '100000000000000', // Defined by us: 100 unit ~ 0.1 USD @ Oct/2024 homeDomain: 'api.anclap.com', - clientDomainEnabled: false, + clientDomainEnabled: true, + memoEnabled: true, pendulumCurrencyId: { Stellar: { AlphaNum4: { diff --git a/signer-service/yarn.lock b/signer-service/yarn.lock index 68f64572..cb59178a 100644 --- a/signer-service/yarn.lock +++ b/signer-service/yarn.lock @@ -5,10 +5,17 @@ __metadata: version: 8 cacheKey: 10 -"@adraffy/ens-normalize@npm:1.10.0": - version: 1.10.0 - resolution: "@adraffy/ens-normalize@npm:1.10.0" - checksum: 10/5cdb5d2a9c9f8c0a71a7bb830967da0069cae1f1235cd41ae11147e4000f368f6958386e622cd4d52bf45c1ed3f8275056b387cba28902b83354e40ff323ecde +"@adraffy/ens-normalize@npm:1.10.1": + version: 1.10.1 + resolution: "@adraffy/ens-normalize@npm:1.10.1" + checksum: 10/4cb938c4abb88a346d50cb0ea44243ab3574330c81d4f5aaaf9dfee584b96189d0faa404de0fcbef5a1b73909ea4ebc3e63d84bd23f9949e5c8d4085207a5091 + languageName: node + linkType: hard + +"@adraffy/ens-normalize@npm:^1.10.1": + version: 1.11.0 + resolution: "@adraffy/ens-normalize@npm:1.11.0" + checksum: 10/abef75f21470ea43dd6071168e092d2d13e38067e349e76186c78838ae174a46c3e18ca50921d05bea6ec3203074147c9e271f8cb6531d1c2c0e146f3199ddcb languageName: node linkType: hard @@ -22,31 +29,32 @@ __metadata: linkType: hard "@babel/code-frame@npm:^7.0.0": - version: 7.24.7 - resolution: "@babel/code-frame@npm:7.24.7" + version: 7.26.2 + resolution: "@babel/code-frame@npm:7.26.2" dependencies: - "@babel/highlight": "npm:^7.24.7" + "@babel/helper-validator-identifier": "npm:^7.25.9" + js-tokens: "npm:^4.0.0" picocolors: "npm:^1.0.0" - checksum: 10/4812e94885ba7e3213d49583a155fdffb05292330f0a9b2c41b49288da70cf3c746a3fda0bf1074041a6d741c33f8d7be24be5e96f41ef77395eeddc5c9ff624 + checksum: 10/db2c2122af79d31ca916755331bb4bac96feb2b334cdaca5097a6b467fdd41963b89b14b6836a14f083de7ff887fc78fa1b3c10b14e743d33e12dbfe5ee3d223 languageName: node linkType: hard -"@babel/helper-validator-identifier@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/helper-validator-identifier@npm:7.24.7" - checksum: 10/86875063f57361471b531dbc2ea10bbf5406e12b06d249b03827d361db4cad2388c6f00936bcd9dc86479f7e2c69ea21412c2228d4b3672588b754b70a449d4b +"@babel/helper-validator-identifier@npm:^7.25.9": + version: 7.25.9 + resolution: "@babel/helper-validator-identifier@npm:7.25.9" + checksum: 10/3f9b649be0c2fd457fa1957b694b4e69532a668866b8a0d81eabfa34ba16dbf3107b39e0e7144c55c3c652bf773ec816af8df4a61273a2bb4eb3145ca9cf478e languageName: node linkType: hard -"@babel/highlight@npm:^7.10.4, @babel/highlight@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/highlight@npm:7.24.7" +"@babel/highlight@npm:^7.10.4": + version: 7.25.9 + resolution: "@babel/highlight@npm:7.25.9" dependencies: - "@babel/helper-validator-identifier": "npm:^7.24.7" + "@babel/helper-validator-identifier": "npm:^7.25.9" chalk: "npm:^2.4.2" js-tokens: "npm:^4.0.0" picocolors: "npm:^1.0.0" - checksum: 10/69b73f38cdd4f881b09b939a711e76646da34f4834f4ce141d7a49a6bb1926eab1c594148970a8aa9360398dff800f63aade4e81fafdd7c8d8a8489ea93bfec1 + checksum: 10/0d165283dd4eb312292cea8fec3ae0d376874b1885f476014f0136784ed5b564b2c2ba2d270587ed546ee92505056dab56493f7960c01c4e6394d71d1b2e7db6 languageName: node linkType: hard @@ -133,16 +141,16 @@ __metadata: languageName: node linkType: hard -"@noble/curves@npm:1.4.0": - version: 1.4.0 - resolution: "@noble/curves@npm:1.4.0" +"@noble/curves@npm:1.2.0": + version: 1.2.0 + resolution: "@noble/curves@npm:1.2.0" dependencies: - "@noble/hashes": "npm:1.4.0" - checksum: 10/b21b30a36ff02bfcc0f5e6163d245cdbaf7f640511fff97ccf83fc207ee79cfd91584b4d97977374de04cb118a55eb63a7964c82596a64162bbc42bc685ae6d9 + "@noble/hashes": "npm:1.3.2" + checksum: 10/94e02e9571a9fd42a3263362451849d2f54405cb3ce9fa7c45bc6b9b36dcd7d1d20e2e1e14cfded24937a13d82f1e60eefc4d7a14982ce0bc219a9fc0f51d1f9 languageName: node linkType: hard -"@noble/curves@npm:^1.3.0, @noble/curves@npm:^1.4.0": +"@noble/curves@npm:1.6.0, @noble/curves@npm:^1.3.0, @noble/curves@npm:^1.4.0, @noble/curves@npm:^1.6.0, @noble/curves@npm:~1.6.0": version: 1.6.0 resolution: "@noble/curves@npm:1.6.0" dependencies: @@ -151,23 +159,14 @@ __metadata: languageName: node linkType: hard -"@noble/curves@npm:~1.4.0": - version: 1.4.2 - resolution: "@noble/curves@npm:1.4.2" - dependencies: - "@noble/hashes": "npm:1.4.0" - checksum: 10/f433a2e8811ae345109388eadfa18ef2b0004c1f79417553241db4f0ad0d59550be6298a4f43d989c627e9f7551ffae6e402a4edf0173981e6da95fc7cab5123 - languageName: node - linkType: hard - -"@noble/hashes@npm:1.4.0, @noble/hashes@npm:~1.4.0": - version: 1.4.0 - resolution: "@noble/hashes@npm:1.4.0" - checksum: 10/e156e65794c473794c52fa9d06baf1eb20903d0d96719530f523cc4450f6c721a957c544796e6efd0197b2296e7cd70efeb312f861465e17940a3e3c7e0febc6 +"@noble/hashes@npm:1.3.2": + version: 1.3.2 + resolution: "@noble/hashes@npm:1.3.2" + checksum: 10/685f59d2d44d88e738114b71011d343a9f7dce9dfb0a121f1489132f9247baa60bc985e5ec6f3213d114fbd1e1168e7294644e46cbd0ce2eba37994f28eeb51b languageName: node linkType: hard -"@noble/hashes@npm:1.5.0, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:^1.3.3, @noble/hashes@npm:^1.4.0, @noble/hashes@npm:~1.5.0": +"@noble/hashes@npm:1.5.0, @noble/hashes@npm:^1.1.2, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:^1.3.3, @noble/hashes@npm:^1.4.0, @noble/hashes@npm:^1.5.0, @noble/hashes@npm:~1.5.0": version: 1.5.0 resolution: "@noble/hashes@npm:1.5.0" checksum: 10/da7fc7af52af7afcf59810a7eea6155075464ff462ffda2572dc6d57d53e2669b1ea2ec774e814f6273f1697e567f28d36823776c9bf7068cba2a2855140f26e @@ -204,6 +203,7 @@ __metadata: big.js: "npm:^6.2.1" body-parser: "npm:^1.17.0" compression: "npm:^1.6.2" + cookie-parser: "npm:^1.4.7" cors: "npm:^2.8.3" cross-env: "npm:^7.0.3" dotenv: "npm:^16.4.5" @@ -211,6 +211,7 @@ __metadata: eslint-config-airbnb-base: "npm:^14.2.0" eslint-config-prettier: "npm:^8.8.0" eslint-plugin-import: "npm:^2.2.0" + ethers: "npm:^6.13.4" express: "npm:^5.0.1" express-rate-limit: "npm:^6.7.0" express-validation: "npm:^1.0.2" @@ -226,6 +227,7 @@ __metadata: morgan: "npm:^1.8.1" nodemon: "npm:^2.0.1" prettier: "npm:^2.8.7" + siwe: "npm:^2.3.2" stellar-sdk: "npm:^11.3.0" viem: "npm:^2.21.3" winston: "npm:^3.1.0" @@ -378,27 +380,27 @@ __metadata: linkType: hard "@polkadot/keyring@npm:^13.1.1": - version: 13.2.2 - resolution: "@polkadot/keyring@npm:13.2.2" + version: 13.2.3 + resolution: "@polkadot/keyring@npm:13.2.3" dependencies: - "@polkadot/util": "npm:13.2.2" - "@polkadot/util-crypto": "npm:13.2.2" + "@polkadot/util": "npm:13.2.3" + "@polkadot/util-crypto": "npm:13.2.3" tslib: "npm:^2.8.0" peerDependencies: - "@polkadot/util": 13.2.2 - "@polkadot/util-crypto": 13.2.2 - checksum: 10/552972c5e4c26f8a95dbb18552cbcfb04c87e085022153a705025987bb77655dc6c32709ea4b8300ebe74945a32b7f8a88ab50460d1962b847335daeae19bda1 + "@polkadot/util": 13.2.3 + "@polkadot/util-crypto": 13.2.3 + checksum: 10/c89cbdd3830f54cabcfde01527b7597a215b39dd7f26a374b1f0f43051fb0443385607548528c0b11eb42ca05d90569f38b13aeeed25858ccfa7ecf1d7345a21 languageName: node linkType: hard -"@polkadot/networks@npm:13.2.2, @polkadot/networks@npm:^13.1.1": - version: 13.2.2 - resolution: "@polkadot/networks@npm:13.2.2" +"@polkadot/networks@npm:13.2.3, @polkadot/networks@npm:^13.1.1": + version: 13.2.3 + resolution: "@polkadot/networks@npm:13.2.3" dependencies: - "@polkadot/util": "npm:13.2.2" + "@polkadot/util": "npm:13.2.3" "@substrate/ss58-registry": "npm:^1.51.0" tslib: "npm:^2.8.0" - checksum: 10/a1282e7104ed0c3ca0c8b42db115fa95ae99e03ea878e1837db92b04fada55c0ed7a55a63c2d36d248ec467e471e72c49026f75233eddd7dbd7037e3a1c81bb2 + checksum: 10/83c4d6321b67c8a5eaf55189dba2180e49600d12ebd55fe861780241fbe8969c972a8b184c91b64a03880c74502889f35ec2eef124f7288e27f2e77ecc4f5e39 languageName: node linkType: hard @@ -527,38 +529,38 @@ __metadata: languageName: node linkType: hard -"@polkadot/util-crypto@npm:13.2.2, @polkadot/util-crypto@npm:^13.1.1": - version: 13.2.2 - resolution: "@polkadot/util-crypto@npm:13.2.2" +"@polkadot/util-crypto@npm:13.2.3, @polkadot/util-crypto@npm:^13.1.1": + version: 13.2.3 + resolution: "@polkadot/util-crypto@npm:13.2.3" dependencies: "@noble/curves": "npm:^1.3.0" "@noble/hashes": "npm:^1.3.3" - "@polkadot/networks": "npm:13.2.2" - "@polkadot/util": "npm:13.2.2" + "@polkadot/networks": "npm:13.2.3" + "@polkadot/util": "npm:13.2.3" "@polkadot/wasm-crypto": "npm:^7.4.1" "@polkadot/wasm-util": "npm:^7.4.1" - "@polkadot/x-bigint": "npm:13.2.2" - "@polkadot/x-randomvalues": "npm:13.2.2" + "@polkadot/x-bigint": "npm:13.2.3" + "@polkadot/x-randomvalues": "npm:13.2.3" "@scure/base": "npm:^1.1.7" tslib: "npm:^2.8.0" peerDependencies: - "@polkadot/util": 13.2.2 - checksum: 10/7f00b4a89be841cfa67c2a25717c21ead158ed52b3f166b5140dae6b2b20e011823b2c06b7df7df95216d964265db151d8785e3db8823ab62ffcc8986d769cd7 + "@polkadot/util": 13.2.3 + checksum: 10/47baf5cab1bd2ca20633ef324a35d7b8a4ecb6bff41eb9e0dac8229495d3a7e74f7bc685d652dd465c9339598fa4e885abce8f539329e77b32a1aa0920169825 languageName: node linkType: hard -"@polkadot/util@npm:13.2.2, @polkadot/util@npm:^13.1.1": - version: 13.2.2 - resolution: "@polkadot/util@npm:13.2.2" +"@polkadot/util@npm:13.2.3, @polkadot/util@npm:^13.1.1": + version: 13.2.3 + resolution: "@polkadot/util@npm:13.2.3" dependencies: - "@polkadot/x-bigint": "npm:13.2.2" - "@polkadot/x-global": "npm:13.2.2" - "@polkadot/x-textdecoder": "npm:13.2.2" - "@polkadot/x-textencoder": "npm:13.2.2" + "@polkadot/x-bigint": "npm:13.2.3" + "@polkadot/x-global": "npm:13.2.3" + "@polkadot/x-textdecoder": "npm:13.2.3" + "@polkadot/x-textencoder": "npm:13.2.3" "@types/bn.js": "npm:^5.1.6" bn.js: "npm:^5.2.1" tslib: "npm:^2.8.0" - checksum: 10/acf145fdf49ad7e39a8df2c24eba510956281196902684fa42ce3b4f2152863478a5410b9f64b9a73ab689f37b64f8e01af15027a29812417b1333f143d14c21 + checksum: 10/45c493224599a003cb52c98d0be502088e3e05dc74e1c505d579aae77341bf1769fbe1ca6c13df8b581235690f78c3b36f8525d378af9df5663a1c21becc4766 languageName: node linkType: hard @@ -642,77 +644,77 @@ __metadata: languageName: node linkType: hard -"@polkadot/x-bigint@npm:13.2.2, @polkadot/x-bigint@npm:^13.1.1": - version: 13.2.2 - resolution: "@polkadot/x-bigint@npm:13.2.2" +"@polkadot/x-bigint@npm:13.2.3, @polkadot/x-bigint@npm:^13.1.1": + version: 13.2.3 + resolution: "@polkadot/x-bigint@npm:13.2.3" dependencies: - "@polkadot/x-global": "npm:13.2.2" + "@polkadot/x-global": "npm:13.2.3" tslib: "npm:^2.8.0" - checksum: 10/00cdd6298a82971c700b1cb9d9dda26423a46fe86044fe74c5c14cb15d70ca912f044a1c935416fb363b58f84544e7f7a5a79eedefccdfbc56e5185637781c90 + checksum: 10/1ede67d15b2e66eb8546e6f7ea9bd3599969eda2a75339a5e7077d1db67e1cebd61b5b4522c344ee3210e3117e9c8c36fe623fc691e0d4a8c0f9b0dc39b768a5 languageName: node linkType: hard "@polkadot/x-fetch@npm:^13.1.1": - version: 13.2.2 - resolution: "@polkadot/x-fetch@npm:13.2.2" + version: 13.2.3 + resolution: "@polkadot/x-fetch@npm:13.2.3" dependencies: - "@polkadot/x-global": "npm:13.2.2" + "@polkadot/x-global": "npm:13.2.3" node-fetch: "npm:^3.3.2" tslib: "npm:^2.8.0" - checksum: 10/19110cbe80ec105eb4f86f162f6f86a930e18bd189d422dbb724a5a0cf7ad83167ed86671835fd43948f5f92295b570ec7bb997f308359bf9ddb6cea335b4438 + checksum: 10/55104a2f6ca60acc25e15becf67988f19e702afa985283451436ca42a417e04d87f91332a1afba78537691c370f7a053ec6e07ba111464d25ccbecc99864eac7 languageName: node linkType: hard -"@polkadot/x-global@npm:13.2.2, @polkadot/x-global@npm:^13.1.1": - version: 13.2.2 - resolution: "@polkadot/x-global@npm:13.2.2" +"@polkadot/x-global@npm:13.2.3, @polkadot/x-global@npm:^13.1.1": + version: 13.2.3 + resolution: "@polkadot/x-global@npm:13.2.3" dependencies: tslib: "npm:^2.8.0" - checksum: 10/9b2747c6b581943c82d6228e886fd491ea41068e07d1da3654c9acc27ace5e1b8682c6e9f8ceaffe68aa88fb386758ebe70f595fde8a5a2544361999e39e74f1 + checksum: 10/163b11c938c4496a94fd476b909b8358007c17e9a251d627c4114d14419c8cd462d8246a580a28e859c91d71c384fc3acfeb27d2e0559c4a443226cbb25df560 languageName: node linkType: hard -"@polkadot/x-randomvalues@npm:13.2.2": - version: 13.2.2 - resolution: "@polkadot/x-randomvalues@npm:13.2.2" +"@polkadot/x-randomvalues@npm:13.2.3": + version: 13.2.3 + resolution: "@polkadot/x-randomvalues@npm:13.2.3" dependencies: - "@polkadot/x-global": "npm:13.2.2" + "@polkadot/x-global": "npm:13.2.3" tslib: "npm:^2.8.0" peerDependencies: - "@polkadot/util": 13.2.2 + "@polkadot/util": 13.2.3 "@polkadot/wasm-util": "*" - checksum: 10/d30e37bc659f4fe045b421e80fc37849e92351ff64244c90651f35ef1fb8722ad1d8d421fc459d039a5f53b9072635f4f531a5a0fd029269025a86178b44b327 + checksum: 10/864b94a2f031582095afc4e0b2ecbb315cb76122b35f359f236704d8e753b0225c7a6f78a68c990bfbb7b2ea8287d05f0fcb9c27a3c2bd785408de3074fa9636 languageName: node linkType: hard -"@polkadot/x-textdecoder@npm:13.2.2": - version: 13.2.2 - resolution: "@polkadot/x-textdecoder@npm:13.2.2" +"@polkadot/x-textdecoder@npm:13.2.3": + version: 13.2.3 + resolution: "@polkadot/x-textdecoder@npm:13.2.3" dependencies: - "@polkadot/x-global": "npm:13.2.2" + "@polkadot/x-global": "npm:13.2.3" tslib: "npm:^2.8.0" - checksum: 10/8fcf79d362141f011123230f7f82f9145cd7c0c7f2ac8caa89f0b8a59e457aad6ffb6e05dee98bd7e254dbd87cce6660dea50e7a41b09830f81e1e350f49a276 + checksum: 10/e67cfb4677cd8a43ed7678421562280947bf22b13d8d4b0ac49e20a34939bbd7de02b8e51a1f5f138c0884847d7c782fb6bbb13d2d7556d1bb7a4e39508c949d languageName: node linkType: hard -"@polkadot/x-textencoder@npm:13.2.2": - version: 13.2.2 - resolution: "@polkadot/x-textencoder@npm:13.2.2" +"@polkadot/x-textencoder@npm:13.2.3": + version: 13.2.3 + resolution: "@polkadot/x-textencoder@npm:13.2.3" dependencies: - "@polkadot/x-global": "npm:13.2.2" + "@polkadot/x-global": "npm:13.2.3" tslib: "npm:^2.8.0" - checksum: 10/e382f7b1601b11b91fa17242af0357ecd7db4bd7f6db2cea88eef9a5bb3f64271089569b34e28685d62f8e89e870876f2872ec2f34a8aee44515f47e79cd4f0f + checksum: 10/54630e2ca156cda7d5f8eb3d5283f43d2729c579d032d9d2b088c2a599d3be905c237d9ebcca3fd432e1fca9d1d0349585e4bf8e27000946a2d6f3c8fef1009d languageName: node linkType: hard "@polkadot/x-ws@npm:^13.1.1": - version: 13.2.2 - resolution: "@polkadot/x-ws@npm:13.2.2" + version: 13.2.3 + resolution: "@polkadot/x-ws@npm:13.2.3" dependencies: - "@polkadot/x-global": "npm:13.2.2" + "@polkadot/x-global": "npm:13.2.3" tslib: "npm:^2.8.0" ws: "npm:^8.18.0" - checksum: 10/f98fcf8350c691c78e2a06487dd57071e13d64014c8192fae2b4fd75435edd946bc4847142933f58046c93709160c912d7d8fa990b8f02b4db929f610b34c77b + checksum: 10/35c66899e1bdfeaf2956d663124fc4c33fb1f0b47f3e14f9f9aa8f1e291a7a14cae264aa15baadf7ba85f5c314fbfce61b1fc4bfad3b1d3fc51c1f1dfa60659d languageName: node linkType: hard @@ -723,32 +725,25 @@ __metadata: languageName: node linkType: hard -"@scure/base@npm:^1.1.1, @scure/base@npm:~1.1.6, @scure/base@npm:~1.1.8": - version: 1.1.8 - resolution: "@scure/base@npm:1.1.8" - checksum: 10/5b764c0e98610bc4993479965db718457d91b68d3c6f1339e3cc74e53fc6b0ae0428d1d64d29a0de0cee9d966034674d4464fdbd2d1dbef27013927b2fe05c45 - languageName: node - linkType: hard - -"@scure/base@npm:^1.1.7": +"@scure/base@npm:^1.1.1, @scure/base@npm:^1.1.7, @scure/base@npm:~1.1.7, @scure/base@npm:~1.1.8": version: 1.1.9 resolution: "@scure/base@npm:1.1.9" checksum: 10/f0ab7f687bbcdee2a01377fe3cd808bf63977999672751295b6a92625d5322f4754a96d40f6bd579bc367aad48ecf8a4e6d0390e70296e6ded1076f52adb16bb languageName: node linkType: hard -"@scure/bip32@npm:1.4.0": - version: 1.4.0 - resolution: "@scure/bip32@npm:1.4.0" +"@scure/bip32@npm:1.5.0, @scure/bip32@npm:^1.5.0": + version: 1.5.0 + resolution: "@scure/bip32@npm:1.5.0" dependencies: - "@noble/curves": "npm:~1.4.0" - "@noble/hashes": "npm:~1.4.0" - "@scure/base": "npm:~1.1.6" - checksum: 10/6cd5062d902564d9e970597ec8b1adacb415b2eadfbb95aee1a1a0480a52eb0de4d294d3753aa8b48548064c9795ed108d348a31a8ce3fc88785377bb12c63b9 + "@noble/curves": "npm:~1.6.0" + "@noble/hashes": "npm:~1.5.0" + "@scure/base": "npm:~1.1.7" + checksum: 10/17e296a782e09aec18ed27e2e8bb6a76072604c40997ec49a6840f223296421612dbe6b44275f04db9acd6da6cefb0322141110f5ac9dc686eb0c44d5bd868fa languageName: node linkType: hard -"@scure/bip39@npm:1.4.0": +"@scure/bip39@npm:1.4.0, @scure/bip39@npm:^1.4.0": version: 1.4.0 resolution: "@scure/bip39@npm:1.4.0" dependencies: @@ -781,6 +776,51 @@ __metadata: languageName: node linkType: hard +"@spruceid/siwe-parser@npm:^2.1.2": + version: 2.1.2 + resolution: "@spruceid/siwe-parser@npm:2.1.2" + dependencies: + "@noble/hashes": "npm:^1.1.2" + apg-js: "npm:^4.3.0" + uri-js: "npm:^4.4.1" + valid-url: "npm:^1.0.9" + checksum: 10/48459fe3b4d4b3091375ee87af700864c9023d4a1271d34850c6d27475e5d93a45d1efe8a71da367ad838b6921ced60c387d54737edd0a7a0d8e4e0a3cc2b8b7 + languageName: node + linkType: hard + +"@stablelib/binary@npm:^1.0.1": + version: 1.0.1 + resolution: "@stablelib/binary@npm:1.0.1" + dependencies: + "@stablelib/int": "npm:^1.0.1" + checksum: 10/c5ed769e2b5d607a5cdb72d325fcf98db437627862fade839daad934bd9ccf02a6f6e34f9de8cb3b18d72fce2ba6cc019a5d22398187d7d69d2607165f27f8bf + languageName: node + linkType: hard + +"@stablelib/int@npm:^1.0.1": + version: 1.0.1 + resolution: "@stablelib/int@npm:1.0.1" + checksum: 10/65bfbf50a382eea70c68e05366bf379cfceff8fbc076f1c267ef2f2411d7aed64fd140c415cb6c29f19a3910d3b8b7805d4b32ad5721a5007a8e744a808c7ae3 + languageName: node + linkType: hard + +"@stablelib/random@npm:^1.0.1": + version: 1.0.2 + resolution: "@stablelib/random@npm:1.0.2" + dependencies: + "@stablelib/binary": "npm:^1.0.1" + "@stablelib/wipe": "npm:^1.0.1" + checksum: 10/f5ace0a588dc4c21f01cb85837892d4c872e994ae77a58a8eb7dd61aa0b26fb1e9b46b0445e71af57d963ef7d9f5965c64258fc0d04df7b2947bc48f2d3560c5 + languageName: node + linkType: hard + +"@stablelib/wipe@npm:^1.0.1": + version: 1.0.1 + resolution: "@stablelib/wipe@npm:1.0.1" + checksum: 10/287802eb146810a46ba72af70b82022caf83a8aeebde23605f5ee0decf64fe2b97a60c856e43b6617b5801287c30cfa863cfb0469e7fcde6f02d143cf0c6cbf4 + languageName: node + linkType: hard + "@stellar/js-xdr@npm:^3.1.1": version: 3.1.2 resolution: "@stellar/js-xdr@npm:3.1.2" @@ -807,16 +847,16 @@ __metadata: linkType: hard "@substrate/connect-extension-protocol@npm:^2.0.0": - version: 2.1.0 - resolution: "@substrate/connect-extension-protocol@npm:2.1.0" - checksum: 10/670d006c3120b25a048b235246ca798a84a2b67a13109f9a1f38605cdf3985464045f468ac4257d9c03531cd0052f829f3bfb9a002cacd4d5016e4dfc9cb728c + version: 2.2.1 + resolution: "@substrate/connect-extension-protocol@npm:2.2.1" + checksum: 10/6c57248640c1e3a02d972bc7f63984a7b1241b83e0692dc9cc1b77ac61723eb319e4c3974d919ee2397b1e113c6160560f19ce186dd7df2e81349faf6fa451d7 languageName: node linkType: hard "@substrate/connect-known-chains@npm:^1.1.5": - version: 1.6.0 - resolution: "@substrate/connect-known-chains@npm:1.6.0" - checksum: 10/2be6443c9920d1e1fe1dfaa8650d46b1ecc40b85d6652c9e1109f2a2e85d6549eb38117eaaeeeaf1372c3163c6474ca655b72979490bc83451baa32b27ccec98 + version: 1.7.0 + resolution: "@substrate/connect-known-chains@npm:1.7.0" + checksum: 10/db793dd7cd9eaba70a8945f5ff09a372bc8a51f60e8bcf90076c2e85fdd659017a8d28538207541adddee3429672f07b794fd9c7b7da4736eec58d07f65f7ded languageName: node linkType: hard @@ -899,11 +939,20 @@ __metadata: linkType: hard "@types/node@npm:*": - version: 22.5.4 - resolution: "@types/node@npm:22.5.4" + version: 22.9.0 + resolution: "@types/node@npm:22.9.0" + dependencies: + undici-types: "npm:~6.19.8" + checksum: 10/a7df3426891868b0f5fb03e46aeddd8446178233521c624a44531c92a040cf08a82d8235f7e1e02af731fd16984665d4d71f3418caf9c2788313b10f040d615d + languageName: node + linkType: hard + +"@types/node@npm:22.7.5": + version: 22.7.5 + resolution: "@types/node@npm:22.7.5" dependencies: undici-types: "npm:~6.19.2" - checksum: 10/d46e0abf437b36bdf89011287aa43873d68ea6f2521a11b5c9a033056fd0d07af36daf51439010e8d41c62c55d0b00e9b5e09ed00bb2617723f73f28a873903a + checksum: 10/e8ba102f8c1aa7623787d625389be68d64e54fcbb76d41f6c2c64e8cf4c9f4a2370e7ef5e5f1732f3c57529d3d26afdcb2edc0101c5e413a79081449825c57ac languageName: node linkType: hard @@ -928,9 +977,9 @@ __metadata: languageName: node linkType: hard -"abitype@npm:1.0.5": - version: 1.0.5 - resolution: "abitype@npm:1.0.5" +"abitype@npm:1.0.6, abitype@npm:^1.0.6": + version: 1.0.6 + resolution: "abitype@npm:1.0.6" peerDependencies: typescript: ">=5.0.4" zod: ^3 >=3.22.0 @@ -939,7 +988,7 @@ __metadata: optional: true zod: optional: true - checksum: 10/1acd0d9687945dd78442b71bd84ff3b9dceae27d15f0d8b14b16554a0c8c9518eeb971ff8e94d507f4d9f05a8a8b91eb8fafd735eaecebac37d5c5a4aac06d8e + checksum: 10/d04d58f90405c29a3c68353508502d7e870feb27418a6281ba9a13e6aaee42c26b2c5f08f648f058b8eaffac32927194b33f396d2451d18afeccfb654c7285c2 languageName: node linkType: hard @@ -953,16 +1002,6 @@ __metadata: languageName: node linkType: hard -"accepts@npm:~1.3.5": - version: 1.3.8 - resolution: "accepts@npm:1.3.8" - dependencies: - mime-types: "npm:~2.1.34" - negotiator: "npm:0.6.3" - checksum: 10/67eaaa90e2917c58418e7a9b89392002d2b1ccd69bcca4799135d0c632f3b082f23f4ae4ddeedbced5aa59bcc7bdf4699c69ebed4593696c922462b7bc5744d6 - languageName: node - linkType: hard - "acorn-jsx@npm:^5.3.1": version: 5.3.2 resolution: "acorn-jsx@npm:5.3.2" @@ -981,6 +1020,13 @@ __metadata: languageName: node linkType: hard +"aes-js@npm:4.0.0-beta.5": + version: 4.0.0-beta.5 + resolution: "aes-js@npm:4.0.0-beta.5" + checksum: 10/8f745da2e8fb38e91297a8ec13c2febe3219f8383303cd4ed4660ca67190242ccfd5fdc2f0d1642fd1ea934818fb871cd4cc28d3f28e812e3dc6c3d0f1f97c24 + languageName: node + linkType: hard + "agent-base@npm:^7.0.2, agent-base@npm:^7.1.0, agent-base@npm:^7.1.1": version: 7.1.1 resolution: "agent-base@npm:7.1.1" @@ -1101,6 +1147,13 @@ __metadata: languageName: node linkType: hard +"apg-js@npm:^4.3.0": + version: 4.4.0 + resolution: "apg-js@npm:4.4.0" + checksum: 10/425f19096026742f5f156f26542b68f55602aa60f0c4ae2d72a0a888cf15fe9622223191202262dd8979d76a6125de9d8fd164d56c95fb113f49099f405eb08c + languageName: node + linkType: hard + "argparse@npm:^1.0.7": version: 1.0.10 resolution: "argparse@npm:1.0.10" @@ -1423,13 +1476,6 @@ __metadata: languageName: node linkType: hard -"bytes@npm:3.0.0": - version: 3.0.0 - resolution: "bytes@npm:3.0.0" - checksum: 10/a2b386dd8188849a5325f58eef69c3b73c51801c08ffc6963eddc9be244089ba32d19347caf6d145c86f315ae1b1fc7061a32b0c1aa6379e6a719090287ed101 - languageName: node - linkType: hard - "bytes@npm:3.1.2": version: 3.1.2 resolution: "bytes@npm:3.1.2" @@ -1652,7 +1698,7 @@ __metadata: languageName: node linkType: hard -"compressible@npm:~2.0.16": +"compressible@npm:~2.0.18": version: 2.0.18 resolution: "compressible@npm:2.0.18" dependencies: @@ -1662,17 +1708,17 @@ __metadata: linkType: hard "compression@npm:^1.6.2": - version: 1.7.4 - resolution: "compression@npm:1.7.4" + version: 1.7.5 + resolution: "compression@npm:1.7.5" dependencies: - accepts: "npm:~1.3.5" - bytes: "npm:3.0.0" - compressible: "npm:~2.0.16" + bytes: "npm:3.1.2" + compressible: "npm:~2.0.18" debug: "npm:2.6.9" + negotiator: "npm:~0.6.4" on-headers: "npm:~1.0.2" - safe-buffer: "npm:5.1.2" + safe-buffer: "npm:5.2.1" vary: "npm:~1.1.2" - checksum: 10/469cd097908fe1d3ff146596d4c24216ad25eabb565c5456660bdcb3a14c82ebc45c23ce56e19fc642746cf407093b55ab9aa1ac30b06883b27c6c736e6383c2 + checksum: 10/c69cf6da151db6f9db2e242b6a0039ad41975ee886c385cff2920c5f8f7050678e0ee9a021437af033536c451791de529de376851b8d31fee42ca2d6adca03f0 languageName: node linkType: hard @@ -1706,6 +1752,23 @@ __metadata: languageName: node linkType: hard +"cookie-parser@npm:^1.4.7": + version: 1.4.7 + resolution: "cookie-parser@npm:1.4.7" + dependencies: + cookie: "npm:0.7.2" + cookie-signature: "npm:1.0.6" + checksum: 10/243fa13f217e793d20a57675e6552beea08c5989fcc68495d543997a31646875335e0e82d687b42dcfd466df57891d22bae7f5ba6ab33b7705ed2dd6eb989105 + languageName: node + linkType: hard + +"cookie-signature@npm:1.0.6": + version: 1.0.6 + resolution: "cookie-signature@npm:1.0.6" + checksum: 10/f4e1b0a98a27a0e6e66fd7ea4e4e9d8e038f624058371bf4499cfcd8f3980be9a121486995202ba3fca74fbed93a407d6d54d43a43f96fd28d0bd7a06761591a + languageName: node + linkType: hard + "cookie-signature@npm:^1.2.1": version: 1.2.2 resolution: "cookie-signature@npm:1.2.2" @@ -1720,6 +1783,13 @@ __metadata: languageName: node linkType: hard +"cookie@npm:0.7.2": + version: 0.7.2 + resolution: "cookie@npm:0.7.2" + checksum: 10/24b286c556420d4ba4e9bc09120c9d3db7d28ace2bd0f8ccee82422ce42322f73c8312441271e5eefafbead725980e5996cc02766dbb89a90ac7f5636ede608f + languageName: node + linkType: hard + "core-util-is@npm:~1.0.0": version: 1.0.3 resolution: "core-util-is@npm:1.0.3" @@ -1775,13 +1845,13 @@ __metadata: linkType: hard "cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.1, cross-spawn@npm:^7.0.2": - version: 7.0.3 - resolution: "cross-spawn@npm:7.0.3" + version: 7.0.5 + resolution: "cross-spawn@npm:7.0.5" dependencies: path-key: "npm:^3.1.0" shebang-command: "npm:^2.0.0" which: "npm:^2.0.1" - checksum: 10/e1a13869d2f57d974de0d9ef7acbf69dc6937db20b918525a01dacb5032129bd552d290d886d981e99f1b624cb03657084cc87bd40f115c07ecf376821c729ce + checksum: 10/c95062469d4bdbc1f099454d01c0e77177a3733012d41bf907a71eb8d22d2add43b5adf6a0a14ef4e7feaf804082714d6c262ef4557a1c480b86786c120d18e2 languageName: node linkType: hard @@ -2098,8 +2168,8 @@ __metadata: linkType: hard "es-abstract@npm:^1.22.1, es-abstract@npm:^1.22.3, es-abstract@npm:^1.23.0, es-abstract@npm:^1.23.2": - version: 1.23.3 - resolution: "es-abstract@npm:1.23.3" + version: 1.23.5 + resolution: "es-abstract@npm:1.23.5" dependencies: array-buffer-byte-length: "npm:^1.0.1" arraybuffer.prototype.slice: "npm:^1.0.3" @@ -2116,7 +2186,7 @@ __metadata: function.prototype.name: "npm:^1.1.6" get-intrinsic: "npm:^1.2.4" get-symbol-description: "npm:^1.0.2" - globalthis: "npm:^1.0.3" + globalthis: "npm:^1.0.4" gopd: "npm:^1.0.1" has-property-descriptors: "npm:^1.0.2" has-proto: "npm:^1.0.3" @@ -2132,10 +2202,10 @@ __metadata: is-string: "npm:^1.0.7" is-typed-array: "npm:^1.1.13" is-weakref: "npm:^1.0.2" - object-inspect: "npm:^1.13.1" + object-inspect: "npm:^1.13.3" object-keys: "npm:^1.1.1" object.assign: "npm:^4.1.5" - regexp.prototype.flags: "npm:^1.5.2" + regexp.prototype.flags: "npm:^1.5.3" safe-array-concat: "npm:^1.1.2" safe-regex-test: "npm:^1.0.3" string.prototype.trim: "npm:^1.2.9" @@ -2147,7 +2217,7 @@ __metadata: typed-array-length: "npm:^1.0.6" unbox-primitive: "npm:^1.0.2" which-typed-array: "npm:^1.1.15" - checksum: 10/2da795a6a1ac5fc2c452799a409acc2e3692e06dc6440440b076908617188899caa562154d77263e3053bcd9389a07baa978ab10ac3b46acc399bd0c77be04cb + checksum: 10/2170afde7e1d2497586ad74176c2e12196db947fb1b3287fc097781a871b75ebe3aef5247e951e3bb3972a830b8d0eaa82a509518836a6d9f9fb4934b9294467 languageName: node linkType: hard @@ -2271,21 +2341,21 @@ __metadata: languageName: node linkType: hard -"eslint-module-utils@npm:^2.9.0": - version: 2.11.0 - resolution: "eslint-module-utils@npm:2.11.0" +"eslint-module-utils@npm:^2.12.0": + version: 2.12.0 + resolution: "eslint-module-utils@npm:2.12.0" dependencies: debug: "npm:^3.2.7" peerDependenciesMeta: eslint: optional: true - checksum: 10/1ba42cf48c5f9ec3b76dfa42c16f1c24c10508313689425c05ccb1d0eaa34bdc5c5b9c0c033cd402e9c429666bd3eb8c6d0c66565b0c00949fae743ad3643c95 + checksum: 10/dd27791147eca17366afcb83f47d6825b6ce164abb256681e5de4ec1d7e87d8605641eb869298a0dbc70665e2446dbcc2f40d3e1631a9475dd64dd23d4ca5dee languageName: node linkType: hard "eslint-plugin-import@npm:^2.2.0": - version: 2.30.0 - resolution: "eslint-plugin-import@npm:2.30.0" + version: 2.31.0 + resolution: "eslint-plugin-import@npm:2.31.0" dependencies: "@rtsao/scc": "npm:^1.1.0" array-includes: "npm:^3.1.8" @@ -2295,7 +2365,7 @@ __metadata: debug: "npm:^3.2.7" doctrine: "npm:^2.1.0" eslint-import-resolver-node: "npm:^0.3.9" - eslint-module-utils: "npm:^2.9.0" + eslint-module-utils: "npm:^2.12.0" hasown: "npm:^2.0.2" is-core-module: "npm:^2.15.1" is-glob: "npm:^4.0.3" @@ -2304,10 +2374,11 @@ __metadata: object.groupby: "npm:^1.0.3" object.values: "npm:^1.2.0" semver: "npm:^6.3.1" + string.prototype.trimend: "npm:^1.0.8" tsconfig-paths: "npm:^3.15.0" peerDependencies: - eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 - checksum: 10/a5f85dfe76e27286c28a01d137769726ce3f758bcc03aa6b6f9e18700a40a08f57239f82e07efcab763c4b03a02d425edcc29fbecf40aad0124286978c6bc63c + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 + checksum: 10/6b76bd009ac2db0615d9019699d18e2a51a86cb8c1d0855a35fb1b418be23b40239e6debdc6e8c92c59f1468ed0ea8d7b85c817117a113d5cc225be8a02ad31c languageName: node linkType: hard @@ -2461,7 +2532,22 @@ __metadata: languageName: node linkType: hard -"eventemitter3@npm:^5.0.1": +"ethers@npm:^6.13.4": + version: 6.13.4 + resolution: "ethers@npm:6.13.4" + dependencies: + "@adraffy/ens-normalize": "npm:1.10.1" + "@noble/curves": "npm:1.2.0" + "@noble/hashes": "npm:1.3.2" + "@types/node": "npm:22.7.5" + aes-js: "npm:4.0.0-beta.5" + tslib: "npm:2.7.0" + ws: "npm:8.17.1" + checksum: 10/221192fed93f6b0553f3e5e72bfd667d676220577d34ff854f677e955d6f608e60636a9c08b5d54039c532a9b9b7056384f0d7019eb6e111d53175806f896ac6 + languageName: node + linkType: hard + +"eventemitter3@npm:5.0.1, eventemitter3@npm:^5.0.1": version: 5.0.1 resolution: "eventemitter3@npm:5.0.1" checksum: 10/ac6423ec31124629c84c7077eed1e6987f6d66c31cf43c6fcbf6c87791d56317ce808d9ead483652436df171b526fc7220eccdc9f3225df334e81582c3cf7dd5 @@ -2587,9 +2673,9 @@ __metadata: linkType: hard "fast-uri@npm:^3.0.1": - version: 3.0.1 - resolution: "fast-uri@npm:3.0.1" - checksum: 10/e8ee4712270de0d29eb0fbf41ffad0ac80952e8797be760e8bb62c4707f08f50a86fe2d7829681ca133c07d6eb4b4a75389a5fc36674c5b254a3ac0891a68fc7 + version: 3.0.3 + resolution: "fast-uri@npm:3.0.3" + checksum: 10/92487c75848b03edc45517fca0148287d342c30818ce43d556391db774d8e01644fb6964315a3336eec5a90f301b218b21f71fb9b2528ba25757435a20392c95 languageName: node linkType: hard @@ -2728,13 +2814,13 @@ __metadata: linkType: hard "form-data@npm:^4.0.0": - version: 4.0.0 - resolution: "form-data@npm:4.0.0" + version: 4.0.1 + resolution: "form-data@npm:4.0.1" dependencies: asynckit: "npm:^0.4.0" combined-stream: "npm:^1.0.8" mime-types: "npm:^2.1.12" - checksum: 10/7264aa760a8cf09482816d8300f1b6e2423de1b02bba612a136857413fdc96d7178298ced106817655facc6b89036c6e12ae31c9eb5bdc16aabf502ae8a5d805 + checksum: 10/6adb1cff557328bc6eb8a68da205f9ae44ab0e88d4d9237aaf91eed591ffc64f77411efb9016af7d87f23d0a038c45a788aa1c6634e51175c4efa36c2bc53774 languageName: node linkType: hard @@ -2977,7 +3063,7 @@ __metadata: languageName: node linkType: hard -"globalthis@npm:^1.0.3": +"globalthis@npm:^1.0.4": version: 1.0.4 resolution: "globalthis@npm:1.0.4" dependencies: @@ -2988,8 +3074,8 @@ __metadata: linkType: hard "google-auth-library@npm:^9.11.0": - version: 9.14.1 - resolution: "google-auth-library@npm:9.14.1" + version: 9.15.0 + resolution: "google-auth-library@npm:9.15.0" dependencies: base64-js: "npm:^1.3.0" ecdsa-sig-formatter: "npm:^1.0.11" @@ -2997,7 +3083,7 @@ __metadata: gcp-metadata: "npm:^6.1.0" gtoken: "npm:^7.0.0" jws: "npm:^4.0.0" - checksum: 10/528e6bebadbd9e599c5a3149ed8053ff4cc99f2776b2b56087af8ed28f47a6df153a742d4c07074ef6245e6b4de5e9ab20655a040535400b464683ee09bd1001 + checksum: 10/fba2db9732bbf1b3a3a2e2b45131ba8e8aba297377f1c104d0b2ab3386bbc1e02047f20b8a7afca1c6308492da1540104618f1c7b5cd539703552e10399c560e languageName: node linkType: hard @@ -3165,9 +3251,9 @@ __metadata: linkType: hard "http-status@npm:^1.0.1": - version: 1.7.4 - resolution: "http-status@npm:1.7.4" - checksum: 10/6b510d25326ca3f183f64ecb797fe0a2ede4ca895e8243d92ce4abb35d9bfcd9ab468b2a565ebf4a718c3a986aa8754a23512fb6182a953d20f8f21c9a8f555e + version: 1.8.1 + resolution: "http-status@npm:1.8.1" + checksum: 10/a150d4cad98e0a8c9c2a9d2051fa124a99cefad8b3f5d1393a0efb4f74c268c53efecb259519139b453fef951e825f4be17c5d2b5c15811799ad8c6afb0c0096 languageName: node linkType: hard @@ -3594,12 +3680,12 @@ __metadata: languageName: node linkType: hard -"isows@npm:1.0.4": - version: 1.0.4 - resolution: "isows@npm:1.0.4" +"isows@npm:1.0.6": + version: 1.0.6 + resolution: "isows@npm:1.0.6" peerDependencies: ws: "*" - checksum: 10/a3ee62e3d6216abb3adeeb2a551fe2e7835eac87b05a6ecc3e7739259bf5f8e83290501f49e26137390c8093f207fc3378d4a7653aab76ad7bbab4b2dba9c5b9 + checksum: 10/ab9e85b50bcc3d70aa5ec875aa2746c5daf9321cb376ed4e5434d3c2643c5d62b1f466d93a05cd2ad0ead5297224922748c31707cb4fbd68f5d05d0479dce99c languageName: node linkType: hard @@ -3846,9 +3932,9 @@ __metadata: languageName: node linkType: hard -"logform@npm:^2.6.0, logform@npm:^2.6.1": - version: 2.6.1 - resolution: "logform@npm:2.6.1" +"logform@npm:^2.7.0": + version: 2.7.0 + resolution: "logform@npm:2.7.0" dependencies: "@colors/colors": "npm:1.6.0" "@types/triple-beam": "npm:^1.3.2" @@ -3856,7 +3942,7 @@ __metadata: ms: "npm:^2.1.1" safe-stable-stringify: "npm:^2.3.1" triple-beam: "npm:^1.3.0" - checksum: 10/e67f414787fbfe1e6a997f4c84300c7e06bee3d0bd579778af667e24b36db3ea200ed195d41b61311ff738dab7faabc615a07b174b22fe69e0b2f39e985be64b + checksum: 10/4b861bfd67efe599ab41113ae3ffe92b1873bf86793fb442f58971852430d8f416f9904da69e5043071fb3725690e2499a13acbfe92a57ba7d21690004f9edc0 languageName: node linkType: hard @@ -3948,7 +4034,7 @@ __metadata: languageName: node linkType: hard -"mime-types@npm:^2.1.12, mime-types@npm:^2.1.35, mime-types@npm:~2.1.24, mime-types@npm:~2.1.34": +"mime-types@npm:^2.1.12, mime-types@npm:^2.1.35, mime-types@npm:~2.1.24": version: 2.1.35 resolution: "mime-types@npm:2.1.35" dependencies: @@ -4274,10 +4360,10 @@ __metadata: languageName: node linkType: hard -"negotiator@npm:0.6.3, negotiator@npm:^0.6.3": - version: 0.6.3 - resolution: "negotiator@npm:0.6.3" - checksum: 10/2723fb822a17ad55c93a588a4bc44d53b22855bf4be5499916ca0cab1e7165409d0b288ba2577d7b029f10ce18cf2ed8e703e5af31c984e1e2304277ef979837 +"negotiator@npm:^0.6.3, negotiator@npm:~0.6.4": + version: 0.6.4 + resolution: "negotiator@npm:0.6.4" + checksum: 10/d98c04a136583afd055746168f1067d58ce4bfe6e4c73ca1d339567f81ea1f7e665b5bd1e81f4771c67b6c2ea89b21cb2adaea2b16058c7dc31317778f931dab languageName: node linkType: hard @@ -4296,13 +4382,13 @@ __metadata: linkType: hard "nock@npm:^13.5.4": - version: 13.5.5 - resolution: "nock@npm:13.5.5" + version: 13.5.6 + resolution: "nock@npm:13.5.6" dependencies: debug: "npm:^4.1.0" json-stringify-safe: "npm:^5.0.1" propagate: "npm:^2.0.0" - checksum: 10/c19d7bf9654db056357a22b00127bb5606c1bbdff188a5b6c469825e580e31cd0cb0701bce8dd8b4876dbbd36a145fdb681fd69fd59308d6db4923ce8ab2439e + checksum: 10/a57c265b75e5f7767e2f8baf058773cdbf357c31c5fea2761386ec03a008a657f9df921899fe2a9502773b47145b708863b32345aef529b3c45cba4019120f88 languageName: node linkType: hard @@ -4349,13 +4435,13 @@ __metadata: linkType: hard "node-gyp-build@npm:^4.8.0": - version: 4.8.2 - resolution: "node-gyp-build@npm:4.8.2" + version: 4.8.3 + resolution: "node-gyp-build@npm:4.8.3" bin: node-gyp-build: bin.js node-gyp-build-optional: optional.js node-gyp-build-test: build-test.js - checksum: 10/e3a365eed7a2d950864a1daa34527588c16fe43ae189d0aeb8fd1dfec91ba42a0e1b499322bff86c2832029fec4f5901bf26e32005e1e17a781dcd5177b6a657 + checksum: 10/4cdc07c940bc1ae484d4d62b0627c80bfb5018e597f2c68c0a7a80b17e9b9cef9d566ec52150ff6f867dd42788eff97a3bcf5cb5b4679ef74954b2df2ac57c02 languageName: node linkType: hard @@ -4445,10 +4531,10 @@ __metadata: languageName: node linkType: hard -"object-inspect@npm:^1.13.1": - version: 1.13.2 - resolution: "object-inspect@npm:1.13.2" - checksum: 10/7ef65583b6397570a17c56f0c1841e0920e83900f2c94638927abb7b81ac08a19c7aae135bd9dcca96208cac0c7332b4650fb927f027b0cf92d71df2990d0561 +"object-inspect@npm:^1.13.1, object-inspect@npm:^1.13.3": + version: 1.13.3 + resolution: "object-inspect@npm:1.13.3" + checksum: 10/14cb973d8381c69e14d7f1c8c75044eb4caf04c6dabcf40ca5c2ce42dc2073ae0bb2a9939eeca142b0c05215afaa1cd5534adb7c8879c32cba2576e045ed8368 languageName: node linkType: hard @@ -4625,6 +4711,26 @@ __metadata: languageName: node linkType: hard +"ox@npm:0.1.2": + version: 0.1.2 + resolution: "ox@npm:0.1.2" + dependencies: + "@adraffy/ens-normalize": "npm:^1.10.1" + "@noble/curves": "npm:^1.6.0" + "@noble/hashes": "npm:^1.5.0" + "@scure/bip32": "npm:^1.5.0" + "@scure/bip39": "npm:^1.4.0" + abitype: "npm:^1.0.6" + eventemitter3: "npm:5.0.1" + peerDependencies: + typescript: ">=5.4.0" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10/cba00f13289599ff03cee3dbc19167c1d0f01829379d119f962b4e951ee2bf0d14491c7a45974e6a2a745117b13b22e9e4131d285e1f5247ea4e1cbc43c5c3d8 + languageName: node + linkType: hard + "p-finally@npm:^1.0.0": version: 1.0.0 resolution: "p-finally@npm:1.0.0" @@ -4676,9 +4782,9 @@ __metadata: linkType: hard "package-json-from-dist@npm:^1.0.0": - version: 1.0.0 - resolution: "package-json-from-dist@npm:1.0.0" - checksum: 10/ac706ec856a5a03f5261e4e48fa974f24feb044d51f84f8332e2af0af04fbdbdd5bbbfb9cbbe354190409bc8307c83a9e38c6672c3c8855f709afb0006a009ea + version: 1.0.1 + resolution: "package-json-from-dist@npm:1.0.1" + checksum: 10/58ee9538f2f762988433da00e26acc788036914d57c71c246bf0be1b60cdbd77dd60b6a3e1a30465f0b248aeb80079e0b34cb6050b1dfa18c06953bb1cbc7602 languageName: node linkType: hard @@ -4780,9 +4886,9 @@ __metadata: linkType: hard "picocolors@npm:^1.0.0": - version: 1.1.0 - resolution: "picocolors@npm:1.1.0" - checksum: 10/a2ad60d94d185c30f2a140b19c512547713fb89b920d32cc6cf658fa786d63a37ba7b8451872c3d9fc34883971fb6e5878e07a20b60506e0bb2554dce9169ccb + version: 1.1.1 + resolution: "picocolors@npm:1.1.1" + checksum: 10/e1cf46bf84886c79055fdfa9dcb3e4711ad259949e3565154b004b260cd356c5d54b31a1437ce9782624bf766272fe6b0154f5f0c744fb7af5d454d2b60db045 languageName: node linkType: hard @@ -5016,15 +5122,15 @@ __metadata: languageName: node linkType: hard -"regexp.prototype.flags@npm:^1.5.2": - version: 1.5.2 - resolution: "regexp.prototype.flags@npm:1.5.2" +"regexp.prototype.flags@npm:^1.5.3": + version: 1.5.3 + resolution: "regexp.prototype.flags@npm:1.5.3" dependencies: - call-bind: "npm:^1.0.6" + call-bind: "npm:^1.0.7" define-properties: "npm:^1.2.1" es-errors: "npm:^1.3.0" - set-function-name: "npm:^2.0.1" - checksum: 10/9fffc01da9c4e12670ff95bc5204364615fcc12d86fc30642765af908675678ebb0780883c874b2dbd184505fb52fa603d80073ecf69f461ce7f56b15d10be9c + set-function-name: "npm:^2.0.2" + checksum: 10/fe17bc4eebbc72945aaf9dd059eb7784a5ca453a67cc4b5b3e399ab08452c9a05befd92063e2c52e7b24d9238c60031656af32dd57c555d1ba6330dbf8c23b43 languageName: node linkType: hard @@ -5215,9 +5321,9 @@ __metadata: linkType: hard "scale-ts@npm:^1.6.0": - version: 1.6.0 - resolution: "scale-ts@npm:1.6.0" - checksum: 10/63d966d48196ede40148f50c182f9d8397600e18ca005b994d3bd85f3e1931ae3fe7ec5d7b0cc072df557450e05676fd5acaa8b196963100a74251ca2e9d089f + version: 1.6.1 + resolution: "scale-ts@npm:1.6.1" + checksum: 10/f1f9bf1d9abfcfcaf8ae2ae326270beca5c2456cc72f6b6b8230aa175a30bdcd6387678746a4d873c834efbba9c8e015698d42ee67bd71b70f7adfe2e0ba1d39 languageName: node linkType: hard @@ -5317,7 +5423,7 @@ __metadata: languageName: node linkType: hard -"set-function-name@npm:^2.0.1": +"set-function-name@npm:^2.0.2": version: 2.0.2 resolution: "set-function-name@npm:2.0.2" dependencies: @@ -5431,6 +5537,20 @@ __metadata: languageName: node linkType: hard +"siwe@npm:^2.3.2": + version: 2.3.2 + resolution: "siwe@npm:2.3.2" + dependencies: + "@spruceid/siwe-parser": "npm:^2.1.2" + "@stablelib/random": "npm:^1.0.1" + uri-js: "npm:^4.4.1" + valid-url: "npm:^1.0.9" + peerDependencies: + ethers: ^5.6.8 || ^6.0.8 + checksum: 10/6ea5ad9a9046fa916f85bf9d3092bc898f7e339d9c552714ea53ecc17daa4f78300c3cf7cc9c70fe57baf77dcee5cb38c6e1d692400b874cd84d297b1261918c + languageName: node + linkType: hard + "slash@npm:^3.0.0": version: 3.0.0 resolution: "slash@npm:3.0.0" @@ -5494,12 +5614,11 @@ __metadata: linkType: hard "sodium-native@npm:^4.0.10": - version: 4.1.1 - resolution: "sodium-native@npm:4.1.1" + version: 4.3.1 + resolution: "sodium-native@npm:4.3.1" dependencies: - node-gyp: "npm:latest" node-gyp-build: "npm:^4.8.0" - checksum: 10/cb30564f07c55b2a7f8016b4df16fd2b0ede10981c6d0b4dfe20ef884f702d55884d2128f22da1ffe9a378aec43a4f0c5539aadf23c4fd3fabc5d05ead1301d2 + checksum: 10/d08c7a68b458fa0dfd63a14dca1a114e855f4b857c3999e3e19e975434b3843335929d0539b03ca7f9796007f0560b629b33186e9b1fd6e7781199b32c48d23c languageName: node linkType: hard @@ -5890,17 +6009,17 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.1.0": +"tslib@npm:2.7.0": version: 2.7.0 resolution: "tslib@npm:2.7.0" checksum: 10/9a5b47ddac65874fa011c20ff76db69f97cf90c78cff5934799ab8894a5342db2d17b4e7613a087046bc1d133d21547ddff87ac558abeec31ffa929c88b7fce6 languageName: node linkType: hard -"tslib@npm:^2.7.0, tslib@npm:^2.8.0": - version: 2.8.0 - resolution: "tslib@npm:2.8.0" - checksum: 10/1bc7c43937477059b4d26f2dbde7e49ef0fb4f38f3014e0603eaea76d6a885742c8b1762af45949145e5e7408a736d20ded949da99dabc8ccba1fc5531d2d927 +"tslib@npm:^2.1.0, tslib@npm:^2.7.0, tslib@npm:^2.8.0": + version: 2.8.1 + resolution: "tslib@npm:2.8.1" + checksum: 10/3e2e043d5c2316461cb54e5c7fe02c30ef6dccb3384717ca22ae5c6b5bc95232a6241df19c622d9c73b809bea33b187f6dbc73030963e29950c2141bc32a79f7 languageName: node linkType: hard @@ -6026,7 +6145,7 @@ __metadata: languageName: node linkType: hard -"undici-types@npm:~6.19.2": +"undici-types@npm:~6.19.2, undici-types@npm:~6.19.8": version: 6.19.8 resolution: "undici-types@npm:6.19.8" checksum: 10/cf0b48ed4fc99baf56584afa91aaffa5010c268b8842f62e02f752df209e3dea138b372a60a963b3b2576ed932f32329ce7ddb9cb5f27a6c83040d8cd74b7a70 @@ -6058,7 +6177,7 @@ __metadata: languageName: node linkType: hard -"uri-js@npm:^4.2.2": +"uri-js@npm:^4.2.2, uri-js@npm:^4.4.1": version: 4.4.1 resolution: "uri-js@npm:4.4.1" dependencies: @@ -6104,6 +6223,13 @@ __metadata: languageName: node linkType: hard +"valid-url@npm:^1.0.9": + version: 1.0.9 + resolution: "valid-url@npm:1.0.9" + checksum: 10/343dfaf85eb3691dc8eb93f7bc007be1ee6091e6c6d1a68bf633cb85e4bf2930e34ca9214fb2c3330de5b652510b257a8ee1ff0a0a37df0925e9dabf93ee512d + languageName: node + linkType: hard + "validate-npm-package-license@npm:^3.0.1": version: 3.0.4 resolution: "validate-npm-package-license@npm:3.0.4" @@ -6122,24 +6248,24 @@ __metadata: linkType: hard "viem@npm:^2.21.3": - version: 2.21.6 - resolution: "viem@npm:2.21.6" + version: 2.21.45 + resolution: "viem@npm:2.21.45" dependencies: - "@adraffy/ens-normalize": "npm:1.10.0" - "@noble/curves": "npm:1.4.0" - "@noble/hashes": "npm:1.4.0" - "@scure/bip32": "npm:1.4.0" + "@noble/curves": "npm:1.6.0" + "@noble/hashes": "npm:1.5.0" + "@scure/bip32": "npm:1.5.0" "@scure/bip39": "npm:1.4.0" - abitype: "npm:1.0.5" - isows: "npm:1.0.4" - webauthn-p256: "npm:0.0.5" - ws: "npm:8.17.1" + abitype: "npm:1.0.6" + isows: "npm:1.0.6" + ox: "npm:0.1.2" + webauthn-p256: "npm:0.0.10" + ws: "npm:8.18.0" peerDependencies: typescript: ">=5.0.4" peerDependenciesMeta: typescript: optional: true - checksum: 10/f515b27beacaf9642052c85c5f8ef17819359618ed1d43bf9b8d9da6d8a0d32f8621e4be704981fcaefcc94580ac8f39c52b0ed8ee929441998521a06dc77486 + checksum: 10/93428588882620de5ed442a5a568367762d39660bdb4ff9c430d5409da1dca085a3648bf0afedb6631bb219b410e58ccabf600ed26228ab9ae73a77a1031c576 languageName: node linkType: hard @@ -6150,13 +6276,13 @@ __metadata: languageName: node linkType: hard -"webauthn-p256@npm:0.0.5": - version: 0.0.5 - resolution: "webauthn-p256@npm:0.0.5" +"webauthn-p256@npm:0.0.10": + version: 0.0.10 + resolution: "webauthn-p256@npm:0.0.10" dependencies: "@noble/curves": "npm:^1.4.0" "@noble/hashes": "npm:^1.4.0" - checksum: 10/6bf5d1857dfb99ecb3b318af06eddea874c10135e6ebb9f046270f5cbb162933bc6caf77aedb033e14c09971dda544a5fb367ac545e4ec8001b309ba517555cf + checksum: 10/dde2b6313b6a0f20996f7ee90181258fc7685bfff401df7d904578da75b374f25d5b9c1189cd2fcec30625b1f276b393188d156d49783f0611623cd713bb5b09 languageName: node linkType: hard @@ -6252,33 +6378,33 @@ __metadata: languageName: node linkType: hard -"winston-transport@npm:^4.7.0": - version: 4.7.1 - resolution: "winston-transport@npm:4.7.1" +"winston-transport@npm:^4.9.0": + version: 4.9.0 + resolution: "winston-transport@npm:4.9.0" dependencies: - logform: "npm:^2.6.1" + logform: "npm:^2.7.0" readable-stream: "npm:^3.6.2" triple-beam: "npm:^1.3.0" - checksum: 10/bc48c921ec9b4a71c1445bf274aa6b00c01089a6c26fc0b19534f8a32fa2710c6766c9e6db53a23492c20772934025d312dd9fb08df157ccb6579ad6b9dae9a7 + checksum: 10/5946918720baadd7447823929e94cf0935f92c4cff6d9451c6fcb009bd9d20a3b3df9ad606109e79d1e9f4d2ff678477bf09f81cfefce2025baaf27a617129bb languageName: node linkType: hard "winston@npm:^3.1.0": - version: 3.14.2 - resolution: "winston@npm:3.14.2" + version: 3.17.0 + resolution: "winston@npm:3.17.0" dependencies: "@colors/colors": "npm:^1.6.0" "@dabh/diagnostics": "npm:^2.0.2" async: "npm:^3.2.3" is-stream: "npm:^2.0.0" - logform: "npm:^2.6.0" + logform: "npm:^2.7.0" one-time: "npm:^1.0.0" readable-stream: "npm:^3.4.0" safe-stable-stringify: "npm:^2.3.1" stack-trace: "npm:0.0.x" triple-beam: "npm:^1.3.0" - winston-transport: "npm:^4.7.0" - checksum: 10/ba818714606175f27c38c42b22913e65f17987a0c8c41bcc73d55f3be8d70d629313f45e312ec02eea7bf074f9abee3f228746140245eb5258487c4161f3a798 + winston-transport: "npm:^4.9.0" + checksum: 10/220309a0ead36c1171158ab28cb9133f8597fba19c8c1c190df9329555530565b58f3af0037c1b80e0c49f7f9b6b3b01791d0c56536eb0be38678d36e316c2a3 languageName: node linkType: hard @@ -6344,7 +6470,7 @@ __metadata: languageName: node linkType: hard -"ws@npm:^8.18.0, ws@npm:^8.8.1": +"ws@npm:8.18.0, ws@npm:^8.18.0, ws@npm:^8.8.1": version: 8.18.0 resolution: "ws@npm:8.18.0" peerDependencies: diff --git a/src/components/AssetNumericInput/index.tsx b/src/components/AssetNumericInput/index.tsx index 31493619..7164361e 100644 --- a/src/components/AssetNumericInput/index.tsx +++ b/src/components/AssetNumericInput/index.tsx @@ -9,6 +9,7 @@ interface AssetNumericInputProps { assetIcon: AssetIconType; tokenSymbol: string; onClick: () => void; + onChange?: (e: KeyboardEvent) => void; disabled?: boolean; readOnly?: boolean; registerInput: UseFormRegisterReturn; diff --git a/src/components/FeeComparison/index.tsx b/src/components/FeeComparison/index.tsx index da1acb08..40662fc3 100644 --- a/src/components/FeeComparison/index.tsx +++ b/src/components/FeeComparison/index.tsx @@ -1,11 +1,13 @@ import Big from 'big.js'; import { useMemo } from 'preact/hooks'; +import { useEffect, useRef } from 'preact/compat'; import { useQuery } from '@tanstack/react-query'; import { ChevronDownIcon } from '@heroicons/react/20/solid'; import { Skeleton } from '../Skeleton'; import vortexIcon from '../../assets/logo/blue.svg'; import { QuoteProvider, quoteProviders } from './quoteProviders'; import { NetworkType } from '../../constants/tokenConfig'; +import { OfframpingParameters, useEventsContext } from '../../contexts/events'; type FeeProviderRowProps = FeeComparisonProps & { provider: QuoteProvider }; @@ -35,13 +37,20 @@ function FeeProviderRow({ vortexPrice, network, }: FeeProviderRowProps) { - const { isLoading, error, data } = useQuery({ - queryKey: [sourceAssetSymbol, targetAssetSymbol, vortexPrice, provider.name, network], + const { scheduleQuote } = useEventsContext(); + + const { + isLoading, + error, + data: providerPrice, + } = useQuery({ + queryKey: [amount, sourceAssetSymbol, targetAssetSymbol, vortexPrice, provider.name, network], queryFn: () => provider.query(sourceAssetSymbol, targetAssetSymbol, amount, network), retry: false, // We don't want to retry the request to avoid spamming the server }); - - const providerPrice = data?.lt(0) ? undefined : data; + // The vortex price is sometimes lagging behind the amount (as it first has to be calculated asynchronously) + // We keep a reference to the previous vortex price to avoid spamming the server with the same quote. + const prevVortexPrice = useRef(undefined); const priceDiff = useMemo(() => { if (isLoading || error || !providerPrice) { @@ -51,6 +60,31 @@ function FeeProviderRow({ return providerPrice.minus(vortexPrice); }, [isLoading, error, providerPrice, vortexPrice]); + useEffect(() => { + if (!isLoading && (providerPrice || error)) { + const parameters: OfframpingParameters = { + from_amount: amount.toFixed(2), + from_asset: sourceAssetSymbol, + to_amount: vortexPrice.toFixed(2), + to_asset: targetAssetSymbol, + }; + if (!prevVortexPrice.current || vortexPrice !== prevVortexPrice.current) { + scheduleQuote(provider.name, providerPrice ? providerPrice.toFixed(2, 0) : '-1', parameters); + prevVortexPrice.current = vortexPrice; + } + } + }, [ + amount, + provider.name, + isLoading, + scheduleQuote, + sourceAssetSymbol, + targetAssetSymbol, + providerPrice, + vortexPrice, + error, + ]); + return (
diff --git a/src/components/FeeComparison/quoteProviders.tsx b/src/components/FeeComparison/quoteProviders.tsx index 0a3b2e63..7d239ca8 100644 --- a/src/components/FeeComparison/quoteProviders.tsx +++ b/src/components/FeeComparison/quoteProviders.tsx @@ -1,10 +1,10 @@ -import { getQueryFnForService, QuoteQuery } from '../../services/quotes'; +import { getQueryFnForService, QuoteQuery, QuoteService } from '../../services/quotes'; import alchemyPayIcon from '../../assets/alchemypay.svg'; import moonpayIcon from '../../assets/moonpay.svg'; import transakIcon from '../../assets/transak.svg'; export interface QuoteProvider { - name: string; + name: QuoteService; icon?: JSX.Element; query: QuoteQuery; href: string; @@ -12,19 +12,19 @@ export interface QuoteProvider { export const quoteProviders: QuoteProvider[] = [ { - name: 'AlchemyPay', + name: 'alchemypay', icon: AlchemyPay, query: getQueryFnForService('alchemypay'), href: 'https://alchemypay.org', }, { - name: 'MoonPay', + name: 'moonpay', icon: Moonpay, query: getQueryFnForService('moonpay'), href: 'https://moonpay.com', }, { - name: 'Transak', + name: 'transak', icon: Transak, query: getQueryFnForService('transak'), href: 'https://transak.com', diff --git a/src/components/Nabla/useSwapForm.tsx b/src/components/Nabla/useSwapForm.tsx index 8d19fdc0..5fd3de8f 100644 --- a/src/components/Nabla/useSwapForm.tsx +++ b/src/components/Nabla/useSwapForm.tsx @@ -18,8 +18,8 @@ const storageSet = debounce(storageService.set, 1000); const setStorageForSwapSettings = storageSet.bind(null, storageKeys.SWAP_SETTINGS); export const useSwapForm = () => { - const tokensModal = useState(); - const setTokenModal = tokensModal[1]; + const [isTokenSelectModalVisible, setIsTokenSelectModalVisible] = useState(false); + const [tokenSelectModalType, setTokenModalType] = useState<'from' | 'to'>('from'); const initialState = useMemo(() => { const searchParams = new URLSearchParams(window.location.search); @@ -52,8 +52,8 @@ export const useSwapForm = () => { const from = useWatch({ control, name: 'from' }); const to = useWatch({ control, name: 'to' }); - const fromToken = from ? INPUT_TOKEN_CONFIG[from] : undefined; - const toToken = to ? OUTPUT_TOKEN_CONFIG[to] : undefined; + const fromToken = useMemo(() => (from ? INPUT_TOKEN_CONFIG[from] : undefined), [from]); + const toToken = useMemo(() => (to ? OUTPUT_TOKEN_CONFIG[to] : undefined), [to]); const onFromChange = useCallback( (tokenKey: string) => { @@ -67,9 +67,9 @@ export const useSwapForm = () => { setStorageForSwapSettings(updated); setValue('from', tokenKey as InputTokenType); - setTokenModal(undefined); + setIsTokenSelectModalVisible(false); }, - [getValues, setValue, setTokenModal], + [getValues, setValue], ); const onToChange = useCallback( @@ -85,9 +85,9 @@ export const useSwapForm = () => { setStorageForSwapSettings(updated); setValue('to', tokenKey as OutputTokenType); - setTokenModal(undefined); + setIsTokenSelectModalVisible(false); }, - [getValues, setTokenModal, setValue], + [getValues, setValue], ); const fromAmountString = useWatch({ @@ -96,18 +96,31 @@ export const useSwapForm = () => { defaultValue: '0', }); - let fromAmount: Big | undefined; - try { - fromAmount = new Big(fromAmountString); - } catch { - // no action required - } + const fromAmount: Big | undefined = useMemo(() => { + try { + return new Big(fromAmountString); + } catch { + return undefined; + } + }, [fromAmountString]); + + const openTokenSelectModal = useCallback((type: 'from' | 'to') => { + setTokenModalType(type); + setIsTokenSelectModalVisible(true); + }, []); + + const closeTokenSelectModal = useCallback(() => { + setIsTokenSelectModalVisible(false); + }, []); return { form, from, to, - tokensModal, + isTokenSelectModalVisible, + tokenSelectModalType, + openTokenSelectModal, + closeTokenSelectModal, onFromChange, onToChange, fromAmount, diff --git a/src/components/NumericInput/index.tsx b/src/components/NumericInput/index.tsx index 4f73ea6f..4018c170 100644 --- a/src/components/NumericInput/index.tsx +++ b/src/components/NumericInput/index.tsx @@ -10,6 +10,7 @@ interface NumericInputProps { defaultValue?: string; autoFocus?: boolean; disableStyles?: boolean; + onChange?: (e: KeyboardEvent) => void; } export const NumericInput = ({ @@ -20,9 +21,11 @@ export const NumericInput = ({ defaultValue, autoFocus, disableStyles = false, + onChange, }: NumericInputProps) => { function handleOnChange(e: KeyboardEvent): void { handleOnChangeNumericInput(e, maxDecimals); + if (onChange) onChange(e); register.onChange(e); } diff --git a/src/components/SignIn/index.tsx b/src/components/SignIn/index.tsx new file mode 100644 index 00000000..b14cbd8e --- /dev/null +++ b/src/components/SignIn/index.tsx @@ -0,0 +1,36 @@ +import { FC } from 'react'; +import { Modal } from 'react-daisyui'; + +interface SignInModalProps { + signingPending: boolean; + closeModal: () => void; + handleSignIn: () => void; +} + +export const SignInModal: FC = ({ signingPending, closeModal, handleSignIn }) => { + if (!signingPending) { + return null; + } + + return ( + + + Sign In + + + +

Please sign the message to log-in

+
+ + + + +
+ ); +}; diff --git a/src/components/buttons/AssetButton/index.tsx b/src/components/buttons/AssetButton/index.tsx index 29bd703e..23cc0331 100644 --- a/src/components/buttons/AssetButton/index.tsx +++ b/src/components/buttons/AssetButton/index.tsx @@ -1,10 +1,12 @@ import { AssetIconType, useGetIcon } from '../../../hooks/useGetIcon'; +import { ChevronDownIcon } from '@heroicons/react/20/solid'; interface AssetButtonProps { assetIcon: AssetIconType; tokenSymbol: string; onClick: () => void; } + export function AssetButton({ assetIcon, tokenSymbol, onClick }: AssetButtonProps) { const icon = useGetIcon(assetIcon); @@ -18,6 +20,7 @@ export function AssetButton({ assetIcon, tokenSymbol, onClick }: AssetButtonProp {assetIcon} {tokenSymbol} + ); } diff --git a/src/components/buttons/SwapSubmitButton/index.tsx b/src/components/buttons/SwapSubmitButton/index.tsx index 08ee51b3..56ceeb6f 100644 --- a/src/components/buttons/SwapSubmitButton/index.tsx +++ b/src/components/buttons/SwapSubmitButton/index.tsx @@ -17,7 +17,7 @@ export const SwapSubmitButton: FC = ({ text, disabled, pe if (!isConnected) { return ( -
+
@@ -26,7 +26,7 @@ export const SwapSubmitButton: FC = ({ text, disabled, pe } return ( -
+
diff --git a/src/pages/swap/index.tsx b/src/pages/swap/index.tsx index 6dc61297..a5250cc9 100644 --- a/src/pages/swap/index.tsx +++ b/src/pages/swap/index.tsx @@ -36,6 +36,9 @@ import { getVaultsForCurrency } from '../../services/polkadot/spacewalk'; import { SPACEWALK_REDEEM_SAFETY_MARGIN } from '../../constants/constants'; import { FeeComparison } from '../../components/FeeComparison'; +import { SignInModal } from '../../components/SignIn'; +import { useSiweContext } from '../../contexts/siwe'; + const Arrow = () => (
@@ -47,10 +50,11 @@ export const SwapPage = () => { const [api, setApi] = useState(null); const { isDisconnected, address } = useAccount(); const [initializeFailed, setInitializeFailed] = useState(false); - const [isReady, setIsReady] = useState(false); + const [, setIsReady] = useState(false); const [showCompareFees, setShowCompareFees] = useState(false); const [cachedId, setCachedId] = useState(undefined); const { trackEvent } = useEventsContext(); + const { signingPending, handleSign, handleCancel } = useSiweContext(); // Hook used for services on initialization and pre-offramp check // That is why no dependencies are used @@ -83,6 +87,7 @@ export const SwapPage = () => { isInitiating, signingPhase, setIsInitiating, + maybeCancelSep24First, } = useMainProcess(); // Store the id as it is cleared after the user opens the anchor window @@ -93,7 +98,10 @@ export const SwapPage = () => { }, [firstSep24ResponseState?.id]); const { - tokensModal: [modalType, setModalType], + isTokenSelectModalVisible, + tokenSelectModalType, + openTokenSelectModal, + closeTokenSelectModal, onFromChange, onToChange, form, @@ -106,7 +114,7 @@ export const SwapPage = () => { const fromToken = INPUT_TOKEN_CONFIG[from]; const toToken = OUTPUT_TOKEN_CONFIG[to]; const formToAmount = form.watch('toAmount'); - const vortexPrice = formToAmount ? Big(formToAmount) : Big(0); + const vortexPrice = useMemo(() => (formToAmount ? Big(formToAmount) : Big(0)), [formToAmount]); const userInputTokenBalance = useInputTokenBalance({ fromToken }); @@ -177,6 +185,7 @@ export const SwapPage = () => { outputTokenType: to as OutputTokenType, amountInUnits: fromAmountString, offrampAmount: tokenOutAmountData.roundedDownQuotedAmountOut, + setInitializeFailed, }); }) .catch((_error) => { @@ -231,14 +240,14 @@ export const SwapPage = () => { setModalType('to')} + onClick={() => openTokenSelectModal('to')} registerInput={form.register('toAmount')} disabled={tokenOutAmount.isLoading} readOnly={true} id="toAmount" /> ), - [toToken.fiat.symbol, toToken.fiat.assetIcon, form, tokenOutAmount.isLoading, setModalType], + [toToken.fiat.assetIcon, toToken.fiat.symbol, form, tokenOutAmount.isLoading, openTokenSelectModal], ); const WithdrawNumericInput = useMemo( @@ -248,13 +257,17 @@ export const SwapPage = () => { registerInput={form.register('fromAmount')} tokenSymbol={fromToken.assetSymbol} assetIcon={fromToken.polygonAssetIcon} - onClick={() => setModalType('from')} + onClick={() => openTokenSelectModal('from')} + onChange={(e) => { + // User interacted with the input field + trackEvent({ event: 'amount_type' }); + }} id="fromAmount" /> form.setValue('fromAmount', amount)} /> ), - [form, fromToken, setModalType], + [form, fromToken, openTokenSelectModal, trackEvent], ); function getCurrentErrorMessage() { @@ -293,7 +306,7 @@ export const SwapPage = () => { } const definitions = - modalType === 'from' + tokenSelectModalType === 'from' ? Object.entries(INPUT_TOKEN_CONFIG).map(([key, value]) => ({ type: key as InputTokenType, assetSymbol: value.assetSymbol, @@ -309,11 +322,14 @@ export const SwapPage = () => { <> { + tokenSelectModalType === 'from' ? onFromChange(token) : onToChange(token); + maybeCancelSep24First(); + }} definitions={definitions} - selected={modalType === 'from' ? from : to} - onClose={() => setModalType(undefined)} + selected={tokenSelectModalType === 'from' ? from : to} + onClose={() => closeTokenSelectModal()} isLoading={false} /> @@ -344,6 +360,7 @@ export const SwapPage = () => { const main = (
+
{