diff --git a/src/core/formatters.ts b/src/core/formatters.ts index 5d6fc6d3..22efaffe 100644 --- a/src/core/formatters.ts +++ b/src/core/formatters.ts @@ -1,8 +1,9 @@ -import type { AuthMethod } from "../types/authHandle" +import { + authIdentity, + prepareBroadcastRequest, +} from "@defuse-protocol/internal-utils" +import type { AuthMethod, walletMessage } from "@defuse-protocol/internal-utils" import type { IntentsUserId } from "../types/intentsUserId" -import type { WalletSignatureResult } from "../types/walletMessage" -import { authHandleToIntentsUserId } from "../utils/authIdentity" -import { prepareSwapSignedData } from "../utils/prepareBroadcastRequest" export type { IntentsUserId } @@ -23,10 +24,10 @@ export interface SignerCredentials { * @returns Intent data serialized in protocol wire format */ export function formatSignedIntent( - signature: WalletSignatureResult, + signature: walletMessage.WalletSignatureResult, credentials: SignerCredentials ) { - return prepareSwapSignedData(signature, { + return prepareBroadcastRequest.prepareSwapSignedData(signature, { userAddress: credentials.credential, userChainType: credentials.credentialType, }) @@ -40,7 +41,7 @@ export function formatSignedIntent( export function formatUserIdentity( credentials: SignerCredentials ): IntentsUserId { - return authHandleToIntentsUserId( + return authIdentity.authHandleToIntentsUserId( credentials.credential, credentials.credentialType ) diff --git a/src/core/messages.test.ts b/src/core/messages.test.ts index d93dc707..d288f5b8 100644 --- a/src/core/messages.test.ts +++ b/src/core/messages.test.ts @@ -1,5 +1,5 @@ +import { authIdentity } from "@defuse-protocol/internal-utils" import { describe, expect, it } from "vitest" -import { authHandleToIntentsUserId } from "../utils/authIdentity" import { createEmptyIntentMessage, createSwapIntentMessage, @@ -8,7 +8,7 @@ import { } from "./messages" const TEST_TIMESTAMP = 1704110400000 // 2024-01-01T12:00:00.000Z -const TEST_USER = authHandleToIntentsUserId("user.near", "near") +const TEST_USER = authIdentity.authHandleToIntentsUserId("user.near", "near") describe("createSwapIntentMessage()", () => { it("creates a valid swap intent message", () => { diff --git a/src/core/messages.ts b/src/core/messages.ts index 9e68b5ce..21260954 100644 --- a/src/core/messages.ts +++ b/src/core/messages.ts @@ -1,13 +1,8 @@ -import type { IntentsUserId } from "../types/intentsUserId" -import type { WalletMessage } from "../types/walletMessage" import { - type WithdrawParams, - makeEmptyMessage, - makeInnerSwapAndWithdrawMessage, - makeInnerSwapMessage, - makeInnerTransferMessage, - makeSwapMessage, -} from "../utils/messageFactory" + messageFactory, + type walletMessage, +} from "@defuse-protocol/internal-utils" +import type { IntentsUserId } from "../types/intentsUserId" import type { SignerCredentials } from "./formatters" import { formatUserIdentity } from "./formatters" @@ -37,7 +32,7 @@ export interface IntentMessageConfig { memo?: string } -export type WithdrawIntentMessageConfig = WithdrawParams +export type WithdrawIntentMessageConfig = messageFactory.WithdrawParams function resolveSignerId( signerId: IntentsUserId | SignerCredentials @@ -54,8 +49,8 @@ function resolveSignerId( export function createSwapIntentMessage( swapConfig: [string, bigint][], options: IntentMessageConfig -): WalletMessage { - const innerMessage = makeInnerSwapMessage({ +): walletMessage.WalletMessage { + const innerMessage = messageFactory.makeInnerSwapMessage({ tokenDeltas: swapConfig, signerId: resolveSignerId(options.signerId), deadlineTimestamp: options.deadlineTimestamp ?? minutesFromNow(5), @@ -63,7 +58,7 @@ export function createSwapIntentMessage( memo: options.memo, }) - return makeSwapMessage({ + return messageFactory.makeSwapMessage({ innerMessage, nonce: options.nonce, }) @@ -78,8 +73,8 @@ export function createSwapIntentMessage( export function createWithdrawIntentMessage( withdrawConfig: WithdrawIntentMessageConfig, options: IntentMessageConfig -): WalletMessage { - const innerMessage = makeInnerSwapAndWithdrawMessage({ +): walletMessage.WalletMessage { + const innerMessage = messageFactory.makeInnerSwapAndWithdrawMessage({ tokenDeltas: [], storageTokenDeltas: [], withdrawParams: withdrawConfig, @@ -87,7 +82,7 @@ export function createWithdrawIntentMessage( deadlineTimestamp: options.deadlineTimestamp ?? minutesFromNow(5), }) - return makeSwapMessage({ + return messageFactory.makeSwapMessage({ innerMessage, nonce: options.nonce, }) @@ -100,8 +95,8 @@ export function createWithdrawIntentMessage( */ export function createEmptyIntentMessage( options: IntentMessageConfig -): WalletMessage { - return makeEmptyMessage({ +): walletMessage.WalletMessage { + return messageFactory.makeEmptyMessage({ signerId: resolveSignerId(options.signerId), deadlineTimestamp: options.deadlineTimestamp ?? minutesFromNow(5), nonce: options.nonce, @@ -121,8 +116,8 @@ function minutesFromNow(minutes: number): number { export function createTransferMessage( tokenDeltas: [string, bigint][], options: IntentMessageConfig & { receiverId: string } -): WalletMessage { - const innerMessage = makeInnerTransferMessage({ +): walletMessage.WalletMessage { + const innerMessage = messageFactory.makeInnerTransferMessage({ tokenDeltas, signerId: resolveSignerId(options.signerId), deadlineTimestamp: options.deadlineTimestamp ?? minutesFromNow(5), @@ -130,7 +125,7 @@ export function createTransferMessage( memo: options.memo, }) - return makeSwapMessage({ + return messageFactory.makeSwapMessage({ innerMessage, nonce: options.nonce, }) diff --git a/src/features/1Click/components/SwapSubmitter.tsx b/src/features/1Click/components/SwapSubmitter.tsx index e6c27cb6..db42051d 100644 --- a/src/features/1Click/components/SwapSubmitter.tsx +++ b/src/features/1Click/components/SwapSubmitter.tsx @@ -1,7 +1,7 @@ +import type { AuthMethod } from "@defuse-protocol/internal-utils" import { type ReactNode, createContext } from "react" import { nearClient } from "../../../constants/nearClient" import { logger } from "../../../logger" -import type { AuthMethod } from "../../../types/authHandle" import { SwapUIMachineContext } from "./SwapUIMachineProvider" export const SwapSubmitterContext = createContext<{ diff --git a/src/features/1Click/components/SwapUIMachineFormSyncProvider.tsx b/src/features/1Click/components/SwapUIMachineFormSyncProvider.tsx index b4d83a16..a291080c 100644 --- a/src/features/1Click/components/SwapUIMachineFormSyncProvider.tsx +++ b/src/features/1Click/components/SwapUIMachineFormSyncProvider.tsx @@ -1,7 +1,7 @@ +import type { AuthMethod } from "@defuse-protocol/internal-utils" import { useSelector } from "@xstate/react" import { type PropsWithChildren, useEffect, useRef } from "react" import { useFormContext } from "react-hook-form" -import type { AuthMethod } from "../../../types/authHandle" import type { SwapWidgetProps } from "../../../types/swap" import { usePublicKeyModalOpener } from "../../swap/hooks/usePublicKeyModalOpener" import type { SwapFormValues } from "./SwapForm" diff --git a/src/features/1Click/components/SwapUIMachineProvider.tsx b/src/features/1Click/components/SwapUIMachineProvider.tsx index e47a3136..b518fa1d 100644 --- a/src/features/1Click/components/SwapUIMachineProvider.tsx +++ b/src/features/1Click/components/SwapUIMachineProvider.tsx @@ -1,3 +1,4 @@ +import type { walletMessage } from "@defuse-protocol/internal-utils" import { createActorContext } from "@xstate/react" import type { PropsWithChildren, ReactElement, ReactNode } from "react" import { useFormContext } from "react-hook-form" @@ -10,10 +11,6 @@ import { fromPromise, } from "xstate" import type { SwappableToken } from "../../../types/swap" -import type { - WalletMessage, - WalletSignatureResult, -} from "../../../types/walletMessage" import { computeTotalDeltaDifferentDecimals } from "../../../utils/tokenUtils" import { swapIntentMachine } from "../../machines/swapIntentMachine" import { swapUIMachine } from "../../machines/swapUIMachine1Click" @@ -53,7 +50,9 @@ interface SwapUIMachineProviderProps extends PropsWithChildren { initialTokenIn?: SwappableToken initialTokenOut?: SwappableToken tokenList: SwappableToken[] - signMessage: (params: WalletMessage) => Promise + signMessage: ( + params: walletMessage.WalletMessage + ) => Promise referral?: string } diff --git a/src/features/account/components/AccountWidget.tsx b/src/features/account/components/AccountWidget.tsx index 0e259e71..4ac444f1 100644 --- a/src/features/account/components/AccountWidget.tsx +++ b/src/features/account/components/AccountWidget.tsx @@ -1,8 +1,8 @@ +import { authIdentity } from "@defuse-protocol/internal-utils" +import type { AuthMethod } from "@defuse-protocol/internal-utils" import { WidgetRoot } from "../../../components/WidgetRoot" -import type { AuthMethod } from "../../../types/authHandle" import type { BaseTokenInfo, UnifiedTokenInfo } from "../../../types/base" import type { RenderHostAppLink } from "../../../types/hostAppLink" -import { authHandleToIntentsUserId } from "../../../utils/authIdentity" import { useWatchHoldings } from "../hooks/useWatchHoldings" import { computeTotalUsdValue } from "../utils/holdingsUtils" import { HoldingsIsland } from "./HoldingsIsland" @@ -25,7 +25,7 @@ export function AccountWidget({ }: AccountWidgetProps) { const userId = userAddress != null && userChainType != null - ? authHandleToIntentsUserId(userAddress, userChainType) + ? authIdentity.authHandleToIntentsUserId(userAddress, userChainType) : null const holdings = useWatchHoldings({ userId, tokenList }) @@ -33,7 +33,7 @@ export function AccountWidget({ const internalUserAddress = userAddress != null && userChainType != null - ? authHandleToIntentsUserId(userAddress, userChainType) + ? authIdentity.authHandleToIntentsUserId(userAddress, userChainType) : null return ( diff --git a/src/features/deposit/components/DepositForm/index.tsx b/src/features/deposit/components/DepositForm/index.tsx index e28d1069..fff9336a 100644 --- a/src/features/deposit/components/DepositForm/index.tsx +++ b/src/features/deposit/components/DepositForm/index.tsx @@ -1,4 +1,5 @@ import type { BlockchainEnum } from "@defuse-protocol/internal-utils" +import type { AuthMethod } from "@defuse-protocol/internal-utils" import { ExclamationTriangleIcon } from "@radix-ui/react-icons" import { Callout } from "@radix-ui/themes" import { useSelector } from "@xstate/react" @@ -26,7 +27,6 @@ import { getPOABridgeInfo } from "../../../../features/machines/poaBridgeInfoAct import { useModalStore } from "../../../../providers/ModalStoreProvider" import { getAvailableDepositRoutes } from "../../../../services/depositService" import { ModalType } from "../../../../stores/modalStore" -import type { AuthMethod } from "../../../../types/authHandle" import type { BaseTokenInfo, SupportedChainName, diff --git a/src/features/deposit/components/DepositUIMachineFormSyncProvider.tsx b/src/features/deposit/components/DepositUIMachineFormSyncProvider.tsx index cb6c1f6d..3b909e33 100644 --- a/src/features/deposit/components/DepositUIMachineFormSyncProvider.tsx +++ b/src/features/deposit/components/DepositUIMachineFormSyncProvider.tsx @@ -1,6 +1,6 @@ +import type { AuthMethod } from "@defuse-protocol/internal-utils" import { type PropsWithChildren, useEffect } from "react" import { useFormContext } from "react-hook-form" -import type { AuthMethod } from "../../../types/authHandle" import { reverseAssetNetworkAdapter } from "../../../utils/adapters" import type { DepositFormValues } from "./DepositForm" import { DepositUIMachineContext } from "./DepositUIMachineProvider" diff --git a/src/features/deposit/components/DepositUIMachineProvider.tsx b/src/features/deposit/components/DepositUIMachineProvider.tsx index e9b1700d..b538a716 100644 --- a/src/features/deposit/components/DepositUIMachineProvider.tsx +++ b/src/features/deposit/components/DepositUIMachineProvider.tsx @@ -1,3 +1,4 @@ +import { authIdentity } from "@defuse-protocol/internal-utils" import { createActorContext } from "@xstate/react" import type { PropsWithChildren, ReactElement, ReactNode } from "react" import { useFormContext } from "react-hook-form" @@ -31,7 +32,6 @@ import type { Transaction } from "../../../types/deposit" import type { SwappableToken } from "../../../types/swap" import { assetNetworkAdapter } from "../../../utils/adapters" import { assert } from "../../../utils/assert" -import { authHandleToIntentsUserId } from "../../../utils/authIdentity" import { getEVMChainId } from "../../../utils/evmChainId" import { isFungibleToken, isNativeToken } from "../../../utils/token" import { depositGenerateAddressMachine } from "../../machines/depositGenerateAddressMachine" @@ -94,7 +94,10 @@ export function DepositUIMachineProvider({ const { userAddress, blockchain, userChainType } = input const generatedResult = await generateDepositAddress( - authHandleToIntentsUserId(userAddress, userChainType), + authIdentity.authHandleToIntentsUserId( + userAddress, + userChainType + ), assetNetworkAdapter[blockchain] ) diff --git a/src/features/gift/actors/giftMakerRootMachine.ts b/src/features/gift/actors/giftMakerRootMachine.ts index f87cf6b7..19347ea5 100644 --- a/src/features/gift/actors/giftMakerRootMachine.ts +++ b/src/features/gift/actors/giftMakerRootMachine.ts @@ -1,4 +1,5 @@ import { errors, solverRelay } from "@defuse-protocol/internal-utils" +import type { walletMessage } from "@defuse-protocol/internal-utils" import { type ActorRefFrom, type DoneActorEvent, @@ -14,10 +15,6 @@ import type { SignerCredentials } from "../../../core/formatters" import { logger } from "../../../logger" import { emitEvent } from "../../../services/emitter" import type { BaseTokenInfo, UnifiedTokenInfo } from "../../../types/base" -import type { - WalletMessage, - WalletSignatureResult, -} from "../../../types/walletMessage" import { assert } from "../../../utils/assert" import { type Events as DepositedBalanceEvents, @@ -93,8 +90,8 @@ export const giftMakerRootMachine = setup({ type: "REQUEST_SIGN" signerCredentials: SignerCredentials signMessage: ( - params: WalletMessage - ) => Promise + params: walletMessage.WalletMessage + ) => Promise } | { type: "COMPLETE_SIGN" diff --git a/src/features/gift/actors/giftMakerSignActor.ts b/src/features/gift/actors/giftMakerSignActor.ts index 9ba07976..61166b61 100644 --- a/src/features/gift/actors/giftMakerSignActor.ts +++ b/src/features/gift/actors/giftMakerSignActor.ts @@ -1,6 +1,9 @@ +import { + messageFactory, + type walletMessage, +} from "@defuse-protocol/internal-utils" import { base64 } from "@scure/base" import type { MultiPayload } from "src/types/defuse-contracts-types" - import { type PromiseActorLogic, assertEvent, setup } from "xstate" import { type SignerCredentials, @@ -15,12 +18,7 @@ import type { TokenValue, UnifiedTokenInfo, } from "../../../types/base" -import type { - WalletMessage, - WalletSignatureResult, -} from "../../../types/walletMessage" import { findError } from "../../../utils/errors" -import { randomDefuseNonce } from "../../../utils/messageFactory" import { adjustDecimals, getAnyBaseTokenInfo, @@ -60,7 +58,7 @@ export type GiftMakerSignActorOutput = value: { multiPayload: MultiPayload signerCredentials: SignerCredentials - signatureResult: WalletSignatureResult + signatureResult: walletMessage.WalletSignatureResult escrowCredentials: EscrowCredentials giftId: string } @@ -69,7 +67,7 @@ export type GiftMakerSignActorOutput = export type GiftMakerSignActorContext = { parsed: GiftMakerSignActorInput["parsed"] signerCredentials: GiftMakerSignActorInput["signerCredentials"] - walletMessage: WalletMessage + walletMessage: walletMessage.WalletMessage escrowCredentials: EscrowCredentials } @@ -185,7 +183,7 @@ export const giftMakerSignActor = setup({ value: { ...event.output.value, escrowCredentials: context.escrowCredentials, - giftId: base64.encode(randomDefuseNonce()), + giftId: base64.encode(messageFactory.randomDefuseNonce()), }, } } diff --git a/src/features/gift/components/GiftHistoryWidget.tsx b/src/features/gift/components/GiftHistoryWidget.tsx index f8e845db..fb41d091 100644 --- a/src/features/gift/components/GiftHistoryWidget.tsx +++ b/src/features/gift/components/GiftHistoryWidget.tsx @@ -1,9 +1,9 @@ +import { authIdentity } from "@defuse-protocol/internal-utils" +import type { AuthMethod } from "@defuse-protocol/internal-utils" import { useMemo } from "react" -import { authHandleToIntentsUserId } from "src/utils/authIdentity" import { WidgetRoot } from "../../../components/WidgetRoot" import type { SignerCredentials } from "../../../core/formatters" import { SwapWidgetProvider } from "../../../providers/SwapWidgetProvider" -import type { AuthMethod } from "../../../types/authHandle" import type { BaseTokenInfo, UnifiedTokenInfo } from "../../../types/base" import { useGiftMakerHistory } from "../stores/giftMakerHistory" import type { GenerateLink } from "../types/sharedTypes" @@ -35,7 +35,7 @@ export function GiftHistoryWidget({ if (!signerCredentials) { return undefined } - const userId = authHandleToIntentsUserId( + const userId = authIdentity.authHandleToIntentsUserId( signerCredentials.credential, signerCredentials.credentialType ) diff --git a/src/features/gift/components/GiftMakerForm.tsx b/src/features/gift/components/GiftMakerForm.tsx index 2b6bc65b..814d4e9a 100644 --- a/src/features/gift/components/GiftMakerForm.tsx +++ b/src/features/gift/components/GiftMakerForm.tsx @@ -1,3 +1,4 @@ +import type { AuthMethod } from "@defuse-protocol/internal-utils" import { useActorRef, useSelector } from "@xstate/react" import clsx from "clsx" import { useEffect, useMemo } from "react" @@ -12,7 +13,6 @@ import { usePublicKeyModalOpener } from "../../../features/swap/hooks/usePublicK import { useTokensUsdPrices } from "../../../hooks/useTokensUsdPrices" import { useModalStore } from "../../../providers/ModalStoreProvider" import { ModalType } from "../../../stores/modalStore" -import type { AuthMethod } from "../../../types/authHandle" import type { BaseTokenInfo, UnifiedTokenInfo } from "../../../types/base" import type { RenderHostAppLink } from "../../../types/hostAppLink" import type { SwappableToken } from "../../../types/swap" diff --git a/src/features/gift/components/GiftTakerWidget.tsx b/src/features/gift/components/GiftTakerWidget.tsx index 386feb81..85f75463 100644 --- a/src/features/gift/components/GiftTakerWidget.tsx +++ b/src/features/gift/components/GiftTakerWidget.tsx @@ -1,10 +1,10 @@ +import type { AuthMethod } from "@defuse-protocol/internal-utils" import { useActorRef, useSelector } from "@xstate/react" import { useCallback, useEffect } from "react" import type { ActorRefFrom } from "xstate" import { WidgetRoot } from "../../../components/WidgetRoot" import type { SignerCredentials } from "../../../core/formatters" import { SwapWidgetProvider } from "../../../providers/SwapWidgetProvider" -import type { AuthMethod } from "../../../types/authHandle" import type { BaseTokenInfo, UnifiedTokenInfo } from "../../../types/base" import type { RenderHostAppLink } from "../../../types/hostAppLink" import { giftTakerRootMachine } from "../actors/giftTakerRootMachine" diff --git a/src/features/gift/hooks/useBalanceUpdaterSyncWithHistory.ts b/src/features/gift/hooks/useBalanceUpdaterSyncWithHistory.ts index 1fa00c48..2bc323c3 100644 --- a/src/features/gift/hooks/useBalanceUpdaterSyncWithHistory.ts +++ b/src/features/gift/hooks/useBalanceUpdaterSyncWithHistory.ts @@ -1,7 +1,7 @@ +import { authIdentity } from "@defuse-protocol/internal-utils" import { useEffect, useRef } from "react" import type { ActorRefFrom } from "xstate" import type { SignerCredentials } from "../../../core/formatters" -import { authHandleToIntentsUserId } from "../../../utils/authIdentity" import type { giftMakerRootMachine } from "../actors/giftMakerRootMachine" import { type GiftMakerHistory, @@ -18,7 +18,7 @@ export function useBalanceUpdaterSyncWithHistory( if (signerCredentials == null) { return EMPTY_GIFTS } - const userId = authHandleToIntentsUserId( + const userId = authIdentity.authHandleToIntentsUserId( signerCredentials.credential, signerCredentials.credentialType ) diff --git a/src/features/gift/stores/storageOperations.ts b/src/features/gift/stores/storageOperations.ts index 91d2f58d..181b886d 100644 --- a/src/features/gift/stores/storageOperations.ts +++ b/src/features/gift/stores/storageOperations.ts @@ -1,6 +1,6 @@ +import { authIdentity } from "@defuse-protocol/internal-utils" import type { IntentsUserId, SignerCredentials } from "../../../core/formatters" import { logger } from "../../../logger" -import { authHandleToIntentsUserId } from "../../../utils/authIdentity" import { deserialize } from "../../../utils/deserialize" import { serialize } from "../../../utils/serialize" import type { GiftStorageState, State } from "./giftMakerHistory" @@ -88,5 +88,8 @@ export function getUserId( ): IntentsUserId { return typeof user === "string" ? user - : authHandleToIntentsUserId(user.credential, user.credentialType) + : authIdentity.authHandleToIntentsUserId( + user.credential, + user.credentialType + ) } diff --git a/src/features/gift/types/sharedTypes.ts b/src/features/gift/types/sharedTypes.ts index fa223b4c..b4cff4be 100644 --- a/src/features/gift/types/sharedTypes.ts +++ b/src/features/gift/types/sharedTypes.ts @@ -1,14 +1,11 @@ +import type { walletMessage } from "@defuse-protocol/internal-utils" import type { SignerCredentials } from "../../../core/formatters" import type { MultiPayload } from "../../../types/defuse-contracts-types" -import type { - WalletMessage, - WalletSignatureResult, -} from "../../../types/walletMessage" import type { StorageOperationErr } from "../stores/storageOperations" export type SignMessage = ( - params: WalletMessage -) => Promise + params: walletMessage.WalletMessage +) => Promise export type GiftLinkData = { secretKey: string @@ -18,7 +15,7 @@ export type GiftLinkData = { export type GiftSignedResult = { multiPayload: MultiPayload signerCredentials: SignerCredentials - signatureResult: WalletSignatureResult + signatureResult: walletMessage.WalletSignatureResult } export type CreateGiftIntent = ( diff --git a/src/features/gift/utils/determineGiftToken.ts b/src/features/gift/utils/determineGiftToken.ts index 619902f8..bf325307 100644 --- a/src/features/gift/utils/determineGiftToken.ts +++ b/src/features/gift/utils/determineGiftToken.ts @@ -1,8 +1,8 @@ +import { authIdentity } from "@defuse-protocol/internal-utils" import { Err, Ok, type Result } from "@thames/monads" import { nearClient } from "../../../constants/nearClient" import { getDepositedBalances } from "../../../services/defuseBalanceService" import type { BaseTokenInfo, UnifiedTokenInfo } from "../../../types/base" -import { authHandleToIntentsUserId } from "../../../utils/authIdentity" import { isBaseToken, isUnifiedToken } from "../../../utils/token" import { getUnderlyingBaseTokenInfos } from "../../../utils/tokenUtils" import type { EscrowCredentials } from "./generateEscrowCredentials" @@ -27,7 +27,7 @@ export async function determineGiftToken( .map((t) => t.defuseAssetId) const balances = await getDepositedBalances( - authHandleToIntentsUserId( + authIdentity.authHandleToIntentsUserId( escrowCredentials.credential, escrowCredentials.credentialType ), diff --git a/src/features/gift/utils/signGiftTakerMessage.ts b/src/features/gift/utils/signGiftTakerMessage.ts index 89162bfa..1f2aa2c9 100644 --- a/src/features/gift/utils/signGiftTakerMessage.ts +++ b/src/features/gift/utils/signGiftTakerMessage.ts @@ -1,14 +1,9 @@ +import { authIdentity, messageFactory } from "@defuse-protocol/internal-utils" +import type { walletMessage } from "@defuse-protocol/internal-utils" import { base64 } from "@scure/base" import { KeyPair } from "near-api-js" -import { authHandleToIntentsUserId } from "src/utils/authIdentity" import type { IntentsUserId, SignerCredentials } from "../../../core/formatters" import { formatUserIdentity } from "../../../core/formatters" -import type { NEP413SignatureData } from "../../../types/walletMessage" -import { - makeInnerTransferMessage, - makeSwapMessage, -} from "../../../utils/messageFactory" -import { randomDefuseNonce } from "../../../utils/messageFactory" import type { GiftInfo } from "../actors/shared/getGiftInfo" import { hashing } from "./hashing" @@ -20,7 +15,7 @@ type GiftTakerMessage = { export async function signGiftTakerMessage({ giftInfo, signerCredentials, -}: GiftTakerMessage): Promise { +}: GiftTakerMessage): Promise { const walletMessage = assembleWalletMessage({ giftInfo, signerCredentials }) const keyPair = KeyPair.fromString(giftInfo.secretKey) @@ -46,23 +41,23 @@ function assembleWalletMessage({ giftInfo, signerCredentials, }: GiftTakerMessage) { - const nonce = randomDefuseNonce() + const nonce = messageFactory.randomDefuseNonce() // Signer should be with `near` credential type as we use ED25519 signing const signerId = resolveSignerId( - authHandleToIntentsUserId(giftInfo.accountId, "near") + authIdentity.authHandleToIntentsUserId(giftInfo.accountId, "near") ) - const innerMessage = makeInnerTransferMessage({ + const innerMessage = messageFactory.makeInnerTransferMessage({ tokenDeltas: [...Object.entries(giftInfo.tokenDiff)], signerId, deadlineTimestamp: minutesFromNow(5), - receiverId: authHandleToIntentsUserId( + receiverId: authIdentity.authHandleToIntentsUserId( signerCredentials.credential, signerCredentials.credentialType ), }) - return makeSwapMessage({ + return messageFactory.makeSwapMessage({ innerMessage, nonce: nonce, }) diff --git a/src/features/machines/depositGenerateAddressMachine.ts b/src/features/machines/depositGenerateAddressMachine.ts index 41b1b687..145a5afa 100644 --- a/src/features/machines/depositGenerateAddressMachine.ts +++ b/src/features/machines/depositGenerateAddressMachine.ts @@ -1,7 +1,7 @@ +import type { AuthMethod } from "@defuse-protocol/internal-utils" import type { SupportedChainName } from "src/types/base" import { assert } from "src/utils/assert" import { assertEvent, assign, fromPromise, setup } from "xstate" -import type { AuthMethod } from "../../types/authHandle" export type Context = { userAddress: string | null diff --git a/src/features/machines/depositUIMachine.ts b/src/features/machines/depositUIMachine.ts index fc99ea94..7f2e58d8 100644 --- a/src/features/machines/depositUIMachine.ts +++ b/src/features/machines/depositUIMachine.ts @@ -1,3 +1,4 @@ +import type { AuthMethod } from "@defuse-protocol/internal-utils" import { assert } from "src/utils/assert" import { type ActorRefFrom, @@ -8,7 +9,6 @@ import { setup, } from "xstate" import { config } from "../../config" -import type { AuthMethod } from "../../types/authHandle" import type { BaseTokenInfo, SupportedChainName } from "../../types/base" import type { SwappableToken } from "../../types/swap" import { depositEstimationMachine } from "./depositEstimationActor" diff --git a/src/features/machines/depositedBalanceMachine.ts b/src/features/machines/depositedBalanceMachine.ts index 27c29147..8c66dda2 100644 --- a/src/features/machines/depositedBalanceMachine.ts +++ b/src/features/machines/depositedBalanceMachine.ts @@ -1,3 +1,5 @@ +import { authIdentity } from "@defuse-protocol/internal-utils" +import type { AuthMethod } from "@defuse-protocol/internal-utils" import { type QueryClient, QueryObserver } from "@tanstack/query-core" import { nearClient } from "src/constants/nearClient" import { @@ -12,14 +14,12 @@ import { import { queryClient } from "../../providers/QueryClientProvider" import { getDepositedBalances } from "../../services/defuseBalanceService" import { getTransitBalance } from "../../services/getTransitBalance" -import type { AuthMethod } from "../../types/authHandle" import type { BaseTokenInfo, TokenValue, UnifiedTokenInfo, } from "../../types/base" import type { IntentsUserId } from "../../types/intentsUserId" -import { authHandleToIntentsUserId } from "../../utils/authIdentity" import { computeTotalBalanceDifferentDecimals, getUnderlyingBaseTokenInfos, @@ -264,7 +264,7 @@ export const depositedBalanceMachine = setup({ { type: "updateUser", params: ({ event }) => - authHandleToIntentsUserId( + authIdentity.authHandleToIntentsUserId( event.params.userAddress, event.params.userChainType ), diff --git a/src/features/machines/signIntentMachine.ts b/src/features/machines/signIntentMachine.ts index 23a5974a..9507227e 100644 --- a/src/features/machines/signIntentMachine.ts +++ b/src/features/machines/signIntentMachine.ts @@ -1,4 +1,5 @@ import { errors } from "@defuse-protocol/internal-utils" +import type { walletMessage } from "@defuse-protocol/internal-utils" import { assertEvent, assign, fromPromise, setup } from "xstate" import { nearClient } from "../../constants/nearClient" import { @@ -7,10 +8,6 @@ import { } from "../../core/formatters" import { logger } from "../../logger" import type { MultiPayload } from "../../types/defuse-contracts-types" -import type { - WalletMessage, - WalletSignatureResult, -} from "../../types/walletMessage" import { assert } from "../../utils/assert" import { verifyWalletSignature } from "../../utils/verifyWalletSignature" import { @@ -36,20 +33,20 @@ export type Errors = { export type Success = { multiPayload: MultiPayload - signatureResult: WalletSignatureResult + signatureResult: walletMessage.WalletSignatureResult signerCredentials: SignerCredentials } type Context = { signerCredentials: SignerCredentials - signature: WalletSignatureResult | null + signature: walletMessage.WalletSignatureResult | null error: null | Errors } export type Input = { signerCredentials: SignerCredentials signMessage: SignMessage - walletMessage: WalletMessage + walletMessage: walletMessage.WalletMessage } export type Output = @@ -70,7 +67,8 @@ export const signIntentMachine = setup({ error: (_, error: Errors) => error, }), setSignature: assign({ - signature: (_, signature: WalletSignatureResult | null) => signature, + signature: (_, signature: walletMessage.WalletSignatureResult | null) => + signature, }), }, actors: { @@ -79,7 +77,7 @@ export const signIntentMachine = setup({ input, }: { input: { - signature: WalletSignatureResult + signature: walletMessage.WalletSignatureResult signerCredentials: SignerCredentials } }) => diff --git a/src/features/machines/swapIntentMachine.ts b/src/features/machines/swapIntentMachine.ts index 33fa97dd..62287962 100644 --- a/src/features/machines/swapIntentMachine.ts +++ b/src/features/machines/swapIntentMachine.ts @@ -1,5 +1,8 @@ import type { FeeEstimation } from "@defuse-protocol/bridge-sdk" import { errors, solverRelay } from "@defuse-protocol/internal-utils" +import { messageFactory } from "@defuse-protocol/internal-utils" +import type { walletMessage } from "@defuse-protocol/internal-utils" +import type { AuthMethod } from "@defuse-protocol/internal-utils" import { secp256k1 } from "@noble/curves/secp256k1" import type { providers } from "near-api-js" import { assign, fromPromise, setup } from "xstate" @@ -8,7 +11,6 @@ import { logger } from "../../logger" import { convertPublishIntentToLegacyFormat } from "../../sdk/solverRelay/utils/parseFailedPublishError" import { emitEvent } from "../../services/emitter" import type { AggregatedQuote } from "../../services/quoteService" -import type { AuthMethod } from "../../types/authHandle" import type { BaseTokenInfo, SupportedChainName, @@ -19,15 +21,7 @@ import type { Nep413DefuseMessageFor_DefuseIntents, } from "../../types/defuse-contracts-types" import type { IntentsUserId } from "../../types/intentsUserId" -import type { - WalletMessage, - WalletSignatureResult, -} from "../../types/walletMessage" import { assert } from "../../utils/assert" -import { - makeInnerSwapMessage, - makeSwapMessage, -} from "../../utils/messageFactory" import { PriorityQueue } from "../../utils/priorityQueue" import { accountSlippageExactIn, @@ -99,10 +93,10 @@ type Context = { // Queue stores all quotes coming from the background quoter quotes: PriorityQueue messageToSign: null | { - walletMessage: WalletMessage + walletMessage: walletMessage.WalletMessage innerMessage: Nep413DefuseMessageFor_DefuseIntents } - signature: WalletSignatureResult | null + signature: walletMessage.WalletSignatureResult | null intentHash: string | null error: null | { tag: "err" @@ -210,7 +204,7 @@ export const swapIntentMachine = setup({ "Operation must be swap" ) - const innerMessage = makeInnerSwapMessage({ + const innerMessage = messageFactory.makeInnerSwapMessage({ tokenDeltas: accountSlippageExactIn( context.intentOperationParams.quote.tokenDeltas, context.slippageBasisPoints @@ -222,12 +216,13 @@ export const swapIntentMachine = setup({ return { innerMessage, - walletMessage: makeSwapMessage({ innerMessage }), + walletMessage: messageFactory.makeSwapMessage({ innerMessage }), } }, }), setSignature: assign({ - signature: (_, signature: WalletSignatureResult | null) => signature, + signature: (_, signature: walletMessage.WalletSignatureResult | null) => + signature, }), setIntentHash: assign({ intentHash: (_, intentHash: string) => intentHash, @@ -241,7 +236,10 @@ export const swapIntentMachine = setup({ ({ input, }: { - input: { signature: WalletSignatureResult; userAddress: string } + input: { + signature: walletMessage.WalletSignatureResult + userAddress: string + } }) => { return verifyWalletSignature(input.signature, input.userAddress) } @@ -249,8 +247,8 @@ export const swapIntentMachine = setup({ publicKeyVerifierActor: publicKeyVerifierMachine, signMessage: fromPromise( async (_: { - input: WalletMessage - }): Promise => { + input: walletMessage.WalletMessage + }): Promise => { throw new Error("not implemented") } ), @@ -259,7 +257,7 @@ export const swapIntentMachine = setup({ input, }: { input: { - signatureData: WalletSignatureResult + signatureData: walletMessage.WalletSignatureResult userInfo: { userAddress: string; userChainType: AuthMethod } quoteHashes: string[] } @@ -281,7 +279,8 @@ export const swapIntentMachine = setup({ const hasQuote = context.quoteToPublish != null return hadQuote === hasQuote }, - isSigned: (_, params: WalletSignatureResult | null) => params != null, + isSigned: (_, params: walletMessage.WalletSignatureResult | null) => + params != null, isTrue: (_, params: boolean) => params, isOk: (_, params: { tag: "ok" } | { tag: "err" }) => params.tag === "ok", isQuoteOk: ({ event }) => { diff --git a/src/features/machines/swapUIMachine.ts b/src/features/machines/swapUIMachine.ts index 2ecdff9c..ede055e3 100644 --- a/src/features/machines/swapUIMachine.ts +++ b/src/features/machines/swapUIMachine.ts @@ -1,3 +1,5 @@ +import { authIdentity } from "@defuse-protocol/internal-utils" +import type { AuthMethod } from "@defuse-protocol/internal-utils" import type { providers } from "near-api-js" import { type ActorRefFrom, @@ -10,7 +12,6 @@ import { } from "xstate" import { logger } from "../../logger" import type { QuoteResult } from "../../services/quoteService" -import type { AuthMethod } from "../../types/authHandle" import type { BaseTokenInfo, TokenValue, @@ -18,7 +19,6 @@ import type { } from "../../types/base" import type { SwappableToken } from "../../types/swap" import { assert } from "../../utils/assert" -import { authHandleToIntentsUserId } from "../../utils/authIdentity" import { parseUnits } from "../../utils/parse" import { getAnyBaseTokenInfo, @@ -417,7 +417,7 @@ export const swapUIMachine = setup({ return { userAddress: event.params.userAddress, userChainType: event.params.userChainType, - defuseUserId: authHandleToIntentsUserId( + defuseUserId: authIdentity.authHandleToIntentsUserId( event.params.userAddress, event.params.userChainType ), diff --git a/src/features/machines/swapUIMachine1Click.ts b/src/features/machines/swapUIMachine1Click.ts index 2ecdff9c..ede055e3 100644 --- a/src/features/machines/swapUIMachine1Click.ts +++ b/src/features/machines/swapUIMachine1Click.ts @@ -1,3 +1,5 @@ +import { authIdentity } from "@defuse-protocol/internal-utils" +import type { AuthMethod } from "@defuse-protocol/internal-utils" import type { providers } from "near-api-js" import { type ActorRefFrom, @@ -10,7 +12,6 @@ import { } from "xstate" import { logger } from "../../logger" import type { QuoteResult } from "../../services/quoteService" -import type { AuthMethod } from "../../types/authHandle" import type { BaseTokenInfo, TokenValue, @@ -18,7 +19,6 @@ import type { } from "../../types/base" import type { SwappableToken } from "../../types/swap" import { assert } from "../../utils/assert" -import { authHandleToIntentsUserId } from "../../utils/authIdentity" import { parseUnits } from "../../utils/parse" import { getAnyBaseTokenInfo, @@ -417,7 +417,7 @@ export const swapUIMachine = setup({ return { userAddress: event.params.userAddress, userChainType: event.params.userChainType, - defuseUserId: authHandleToIntentsUserId( + defuseUserId: authIdentity.authHandleToIntentsUserId( event.params.userAddress, event.params.userChainType ), diff --git a/src/features/machines/withdrawUIMachine.ts b/src/features/machines/withdrawUIMachine.ts index 32c851c8..2a9e8bde 100644 --- a/src/features/machines/withdrawUIMachine.ts +++ b/src/features/machines/withdrawUIMachine.ts @@ -1,3 +1,5 @@ +import { authIdentity } from "@defuse-protocol/internal-utils" +import type { AuthMethod } from "@defuse-protocol/internal-utils" import type { providers } from "near-api-js" import { type ActorRefFrom, @@ -11,10 +13,8 @@ import { import { logger } from "../../logger" import { emitEvent } from "../../services/emitter" import type { QuoteResult } from "../../services/quoteService" -import type { AuthMethod } from "../../types/authHandle" import type { BaseTokenInfo, UnifiedTokenInfo } from "../../types/base" import { assert } from "../../utils/assert" -import { authHandleToIntentsUserId } from "../../utils/authIdentity" import { isNearIntentsNetwork } from "../withdraw/components/WithdrawForm/utils" import { type Events as BackgroundQuoterEvents, @@ -598,7 +598,7 @@ export const withdrawUIMachine = setup({ return { userAddress: context.submitDeps.userAddress, userChainType: context.submitDeps.userChainType, - defuseUserId: authHandleToIntentsUserId( + defuseUserId: authIdentity.authHandleToIntentsUserId( context.submitDeps.userAddress, context.submitDeps.userChainType ), diff --git a/src/features/otcDesk/actors/otcMakerOrderCancellationActor.ts b/src/features/otcDesk/actors/otcMakerOrderCancellationActor.ts index b7d81dda..3d4ad0c7 100644 --- a/src/features/otcDesk/actors/otcMakerOrderCancellationActor.ts +++ b/src/features/otcDesk/actors/otcMakerOrderCancellationActor.ts @@ -1,4 +1,5 @@ import { solverRelay } from "@defuse-protocol/internal-utils" +import type { walletMessage } from "@defuse-protocol/internal-utils" import { base64 } from "@scure/base" import { createEmptyIntentMessage } from "src/core/messages" import { assertEvent, assign, fromPromise, setup } from "xstate" @@ -9,7 +10,6 @@ import { convertPublishIntentsToLegacyFormat, } from "../../../sdk/solverRelay/publishIntents" import type { MultiPayload } from "../../../types/defuse-contracts-types" -import type { WalletSignatureResult } from "../../../types/walletMessage" import { assert } from "../../../utils/assert" import { type Errors as SignIntentErrors, @@ -58,7 +58,7 @@ export const otcMakerOrderCancellationActor = setup({ | { type: "_INTERNAL_SIGNED" multiPayload: MultiPayload - signatureResult: WalletSignatureResult + signatureResult: walletMessage.WalletSignatureResult signerCredentials: SignerCredentials }, }, diff --git a/src/features/otcDesk/actors/otcMakerRootMachine.ts b/src/features/otcDesk/actors/otcMakerRootMachine.ts index adbbfe99..83670832 100644 --- a/src/features/otcDesk/actors/otcMakerRootMachine.ts +++ b/src/features/otcDesk/actors/otcMakerRootMachine.ts @@ -1,4 +1,5 @@ import { errors } from "@defuse-protocol/internal-utils" +import type { walletMessage } from "@defuse-protocol/internal-utils" import { type ActorRefFrom, type PromiseActorLogic, @@ -12,10 +13,6 @@ import { logger } from "../../../logger" import { emitEvent } from "../../../services/emitter" import type { BaseTokenInfo, UnifiedTokenInfo } from "../../../types/base" import type { MultiPayload } from "../../../types/defuse-contracts-types" -import type { - WalletMessage, - WalletSignatureResult, -} from "../../../types/walletMessage" import { assert } from "../../../utils/assert" import { type Events as DepositedBalanceEvents, @@ -76,8 +73,8 @@ type EventType = type: "REQUEST_SIGN" signerCredentials: SignerCredentials signMessage: ( - params: WalletMessage - ) => Promise + params: walletMessage.WalletMessage + ) => Promise } | CompleteSignEvent | CompleteStoringEvent diff --git a/src/features/otcDesk/actors/otcMakerSignActor.ts b/src/features/otcDesk/actors/otcMakerSignActor.ts index 98bc91c8..b18085ba 100644 --- a/src/features/otcDesk/actors/otcMakerSignActor.ts +++ b/src/features/otcDesk/actors/otcMakerSignActor.ts @@ -1,3 +1,5 @@ +import { messageFactory } from "@defuse-protocol/internal-utils" +import type { walletMessage } from "@defuse-protocol/internal-utils" import { base64 } from "@scure/base" import { assertEvent, setup } from "xstate" import { @@ -14,12 +16,7 @@ import type { UnifiedTokenInfo, } from "../../../types/base" import type { MultiPayload } from "../../../types/defuse-contracts-types" -import type { - WalletMessage, - WalletSignatureResult, -} from "../../../types/walletMessage" import { findError } from "../../../utils/errors" -import { randomDefuseNonce } from "../../../utils/messageFactory" import { adjustDecimals, getAnyBaseTokenInfo, @@ -53,7 +50,7 @@ export type OTCMakerSignActorOutput = export type OTCMakerSignActorSuccess = { multiPayload: MultiPayload - signatureResult: WalletSignatureResult + signatureResult: walletMessage.WalletSignatureResult signerCredentials: SignerCredentials usedNonceBase64: string } @@ -62,7 +59,7 @@ export type OTCMakerSignActorContext = { nonce: Uint8Array parsed: OTCMakerSignActorInput["parsed"] signerCredentials: OTCMakerSignActorInput["signerCredentials"] - walletMessage: WalletMessage + walletMessage: walletMessage.WalletMessage } export type OTCMakerSignActorErrors = SignIntentErrors | { reason: "EXCEPTION" } @@ -91,7 +88,7 @@ export const otcMakerSignMachine = setup({ initial: "signing", context: ({ input }) => { - const nonce = randomDefuseNonce() + const nonce = messageFactory.randomDefuseNonce() let tokenInDiff: Record diff --git a/src/features/otcDesk/components/OtcMakerForm.tsx b/src/features/otcDesk/components/OtcMakerForm.tsx index ce77c93c..f8bb3271 100644 --- a/src/features/otcDesk/components/OtcMakerForm.tsx +++ b/src/features/otcDesk/components/OtcMakerForm.tsx @@ -1,3 +1,4 @@ +import type { AuthMethod } from "@defuse-protocol/internal-utils" import { ArrowsDownUp } from "@phosphor-icons/react" import { useActorRef, useSelector } from "@xstate/react" import clsx from "clsx" @@ -13,7 +14,6 @@ import type { SignerCredentials } from "../../../core/formatters" import { useTokensUsdPrices } from "../../../hooks/useTokensUsdPrices" import { useModalStore } from "../../../providers/ModalStoreProvider" import { ModalType } from "../../../stores/modalStore" -import type { AuthMethod } from "../../../types/authHandle" import type { BaseTokenInfo, UnifiedTokenInfo } from "../../../types/base" import type { RenderHostAppLink } from "../../../types/hostAppLink" import type { SwappableToken } from "../../../types/swap" diff --git a/src/features/otcDesk/components/OtcMakerTrades.tsx b/src/features/otcDesk/components/OtcMakerTrades.tsx index e9fa342d..899737ed 100644 --- a/src/features/otcDesk/components/OtcMakerTrades.tsx +++ b/src/features/otcDesk/components/OtcMakerTrades.tsx @@ -1,3 +1,4 @@ +import { authIdentity } from "@defuse-protocol/internal-utils" import { Check as CheckIcon, Copy as CopyIcon, @@ -26,7 +27,6 @@ import { isNonceUsed } from "../../../services/intentsContractService" import type { BaseTokenInfo, UnifiedTokenInfo } from "../../../types/base" import type { MultiPayload } from "../../../types/defuse-contracts-types" import { assert } from "../../../utils/assert" -import { authHandleToIntentsUserId } from "../../../utils/authIdentity" import { formatTokenValue } from "../../../utils/format" import { computeTotalBalanceDifferentDecimals } from "../../../utils/tokenUtils" import type { SendNearTransaction } from "../../machines/publicKeyVerifierMachine" @@ -69,7 +69,7 @@ export function OtcMakerTrades({ sendNearTransaction, }: OtcMakerTradesProps) { const trades = useOtcMakerTrades((s) => { - const userId = authHandleToIntentsUserId( + const userId = authIdentity.authHandleToIntentsUserId( signerCredentials.credential, signerCredentials.credentialType ) diff --git a/src/features/otcDesk/components/OtcTakerForm.tsx b/src/features/otcDesk/components/OtcTakerForm.tsx index 025361ce..02f13ed5 100644 --- a/src/features/otcDesk/components/OtcTakerForm.tsx +++ b/src/features/otcDesk/components/OtcTakerForm.tsx @@ -1,3 +1,4 @@ +import { authIdentity } from "@defuse-protocol/internal-utils" import { ArrowDown } from "@phosphor-icons/react" import { useQuery } from "@tanstack/react-query" import { None } from "@thames/monads" @@ -13,7 +14,6 @@ import type { BaseTokenInfo, UnifiedTokenInfo } from "../../../types/base" import type { MultiPayload } from "../../../types/defuse-contracts-types" import type { RenderHostAppLink } from "../../../types/hostAppLink" import { assert } from "../../../utils/assert" -import { authHandleToIntentsUserId } from "../../../utils/authIdentity" import { formatTokenValue, formatUsdAmount } from "../../../utils/format" import getTokenUsdPrice from "../../../utils/getTokenUsdPrice" import { @@ -57,7 +57,7 @@ export function OtcTakerForm({ }: OtcTakerFormProps) { const signerId = signerCredentials != null - ? authHandleToIntentsUserId( + ? authIdentity.authHandleToIntentsUserId( signerCredentials.credential, signerCredentials.credentialType ) diff --git a/src/features/otcDesk/components/OtcTakerWidget.tsx b/src/features/otcDesk/components/OtcTakerWidget.tsx index 910547df..2daaf4fe 100644 --- a/src/features/otcDesk/components/OtcTakerWidget.tsx +++ b/src/features/otcDesk/components/OtcTakerWidget.tsx @@ -1,3 +1,4 @@ +import type { AuthMethod } from "@defuse-protocol/internal-utils" import { useQuery } from "@tanstack/react-query" import { Err, Ok, type Result } from "@thames/monads" import { type ReactNode, useMemo, useState } from "react" @@ -11,7 +12,6 @@ import { getProtocolFee, isNonceUsed, } from "../../../services/intentsContractService" -import type { AuthMethod } from "../../../types/authHandle" import type { BaseTokenInfo, UnifiedTokenInfo } from "../../../types/base" import type { RenderHostAppLink } from "../../../types/hostAppLink" import type { SendNearTransaction } from "../../machines/publicKeyVerifierMachine" diff --git a/src/features/otcDesk/hooks/useOtcTakerConfirmTrade.ts b/src/features/otcDesk/hooks/useOtcTakerConfirmTrade.ts index 877e5fa1..54fc510b 100644 --- a/src/features/otcDesk/hooks/useOtcTakerConfirmTrade.ts +++ b/src/features/otcDesk/hooks/useOtcTakerConfirmTrade.ts @@ -1,4 +1,5 @@ import { solverRelay } from "@defuse-protocol/internal-utils" +import { authIdentity } from "@defuse-protocol/internal-utils" import { useMutation } from "@tanstack/react-query" import { Err, type Result } from "@thames/monads" import { useContext } from "react" @@ -14,7 +15,6 @@ import { import { emitEvent } from "../../../services/emitter" import type { MultiPayload } from "../../../types/defuse-contracts-types" import { assert } from "../../../utils/assert" -import { authHandleToIntentsUserId } from "../../../utils/authIdentity" import { SignIntentContext, type SignIntentErr, @@ -60,7 +60,7 @@ export function useOtcTakerConfirmTrade({ PublishIntentsErr | SignIntentErr | AggregatedQuoteErr > > => { - const signerId = authHandleToIntentsUserId( + const signerId = authIdentity.authHandleToIntentsUserId( signerCredentials.credential, signerCredentials.credentialType ) diff --git a/src/features/otcDesk/providers/SignIntentActorProvider.tsx b/src/features/otcDesk/providers/SignIntentActorProvider.tsx index 3e9b57bb..12c1902f 100644 --- a/src/features/otcDesk/providers/SignIntentActorProvider.tsx +++ b/src/features/otcDesk/providers/SignIntentActorProvider.tsx @@ -1,9 +1,9 @@ +import type { walletMessage } from "@defuse-protocol/internal-utils" import { Err, type Result } from "@thames/monads" import { useSelector } from "@xstate/react" import { type ReactNode, createContext, useEffect, useState } from "react" import { type ActorRefFrom, createActor, toPromise } from "xstate" import type { SignerCredentials } from "../../../core/formatters" -import type { WalletMessage } from "../../../types/walletMessage" import type { SendNearTransaction } from "../../machines/publicKeyVerifierMachine" import { type Errors, @@ -21,7 +21,7 @@ export const SignIntentContext = createContext<{ signIntent: (arg: { signerCredentials: SignerCredentials signMessage: SignMessage - walletMessage: WalletMessage + walletMessage: walletMessage.WalletMessage }) => Promise> }>({ signIntent: async () => { @@ -56,7 +56,7 @@ export function SignIntentActorProvider({ }: { signerCredentials: SignerCredentials signMessage: SignMessage - walletMessage: WalletMessage + walletMessage: walletMessage.WalletMessage }) => { if (actorRef) { return Err({ reason: "SIGNING_IN_PROGRESS" }) diff --git a/src/features/otcDesk/stores/otcMakerTrades.ts b/src/features/otcDesk/stores/otcMakerTrades.ts index fc4a5211..4420e25b 100644 --- a/src/features/otcDesk/stores/otcMakerTrades.ts +++ b/src/features/otcDesk/stores/otcMakerTrades.ts @@ -1,9 +1,9 @@ +import { authIdentity } from "@defuse-protocol/internal-utils" import { create } from "zustand" import { createJSONStorage, persist } from "zustand/middleware" import type { SignerCredentials } from "../../../core/formatters" import type { MultiPayload } from "../../../types/defuse-contracts-types" import type { IntentsUserId } from "../../../types/intentsUserId" -import { authHandleToIntentsUserId } from "../../../utils/authIdentity" type OtcMakerTrade = { tradeId: string @@ -39,7 +39,10 @@ export const otcMakerTradesStore = create()( const userId = typeof user === "string" ? user - : authHandleToIntentsUserId(user.credential, user.credentialType) + : authIdentity.authHandleToIntentsUserId( + user.credential, + user.credentialType + ) set((state) => ({ trades: { @@ -56,7 +59,10 @@ export const otcMakerTradesStore = create()( const userId = typeof user === "string" ? user - : authHandleToIntentsUserId(user.credential, user.credentialType) + : authIdentity.authHandleToIntentsUserId( + user.credential, + user.credentialType + ) set((state) => ({ trades: { diff --git a/src/features/otcDesk/types/sharedTypes.ts b/src/features/otcDesk/types/sharedTypes.ts index 53ab3067..f7f9d2bb 100644 --- a/src/features/otcDesk/types/sharedTypes.ts +++ b/src/features/otcDesk/types/sharedTypes.ts @@ -1,14 +1,11 @@ +import type { walletMessage } from "@defuse-protocol/internal-utils" import type { Result } from "@thames/monads" import type { TokenValue } from "../../../types/base" import type { MultiPayload } from "../../../types/defuse-contracts-types" -import type { - WalletMessage, - WalletSignatureResult, -} from "../../../types/walletMessage" export type SignMessage = ( - params: WalletMessage -) => Promise + params: walletMessage.WalletMessage +) => Promise export type TradeBreakdown = { makerSends: TokenValue diff --git a/src/features/otcDesk/utils/parseTradeTerms.test.ts b/src/features/otcDesk/utils/parseTradeTerms.test.ts index 2346f68c..0e8c215b 100644 --- a/src/features/otcDesk/utils/parseTradeTerms.test.ts +++ b/src/features/otcDesk/utils/parseTradeTerms.test.ts @@ -1,3 +1,4 @@ +import { authIdentity } from "@defuse-protocol/internal-utils" import { generatePrivateKey, privateKeyToAccount } from "viem/accounts" import { beforeEach, describe, expect, it, vi } from "vitest" import { @@ -9,7 +10,6 @@ import { createSwapIntentMessage, } from "../../../core/messages" import { logger } from "../../../logger" -import { authHandleToIntentsUserId } from "../../../utils/authIdentity" import { parseTradeTerms } from "./parseTradeTerms" vi.mock("../../../logger", () => ({ @@ -21,7 +21,7 @@ describe("parseTradeTerms", () => { credential: "joe.near", credentialType: "near", } - const trade1Id = authHandleToIntentsUserId( + const trade1Id = authIdentity.authHandleToIntentsUserId( trader1.credential, trader1.credentialType ) diff --git a/src/features/otcDesk/utils/schemaMultipayload.test.ts b/src/features/otcDesk/utils/schemaMultipayload.test.ts index 3448f888..e9773ada 100644 --- a/src/features/otcDesk/utils/schemaMultipayload.test.ts +++ b/src/features/otcDesk/utils/schemaMultipayload.test.ts @@ -1,3 +1,5 @@ +import { prepareBroadcastRequest } from "@defuse-protocol/internal-utils" +import type { walletMessage } from "@defuse-protocol/internal-utils" import { base64 } from "@scure/base" import { Keypair } from "@solana/web3.js" import nacl from "tweetnacl" @@ -14,8 +16,6 @@ import { createWithdrawIntentMessage, } from "../../../core/messages" import type { MultiPayload } from "../../../types/defuse-contracts-types" -import type { WalletMessage } from "../../../types/walletMessage" -import { normalizeERC191Signature } from "../../../utils/prepareBroadcastRequest" import { GeneralPayloadObjectSchema, MultiPayloadDeepSchema, @@ -132,7 +132,9 @@ function genWithdrawIntent(signerId: SignerCredentials) { } type FakeSign = ( - walletMessageFactory: (signerCreds: SignerCredentials) => WalletMessage + walletMessageFactory: ( + signerCreds: SignerCredentials + ) => walletMessage.WalletMessage ) => Promise const signERC191: FakeSign = async (walletMessageFactory) => { @@ -147,7 +149,7 @@ const signERC191: FakeSign = async (walletMessageFactory) => { return formatSignedIntent( { type: "ERC191", - signatureData: normalizeERC191Signature( + signatureData: prepareBroadcastRequest.normalizeERC191Signature( await signer.signMessage(walletMessage.ERC191) ), signedData: walletMessage.ERC191, diff --git a/src/features/otcDesk/utils/schemaPrimitives.test.ts b/src/features/otcDesk/utils/schemaPrimitives.test.ts index 5499fb59..6a6e75ee 100644 --- a/src/features/otcDesk/utils/schemaPrimitives.test.ts +++ b/src/features/otcDesk/utils/schemaPrimitives.test.ts @@ -1,10 +1,10 @@ +import { prepareBroadcastRequest } from "@defuse-protocol/internal-utils" import { base58, hex } from "@scure/base" import { Keypair } from "@solana/web3.js" import nacl from "tweetnacl" import * as v from "valibot" import { generatePrivateKey, privateKeyToAccount } from "viem/accounts" import { describe, expect, it } from "vitest" -import { transformERC191Signature } from "../../../utils/prepareBroadcastRequest" import { normalizeSignatureS } from "../../../utils/webAuthn" import { PublicKeyED25519Schema, @@ -57,7 +57,7 @@ describe("SignatureED25519Schema", () => { describe("SignatureSecp256k1Schema", () => { it("valid signature", async () => { const signer = privateKeyToAccount(generatePrivateKey()) - const formatted = transformERC191Signature( + const formatted = prepareBroadcastRequest.transformERC191Signature( await signer.signMessage({ message: "0x" }) ) expect(() => v.parse(SignatureSecp256k1Schema, formatted)).not.toThrow() diff --git a/src/features/otcDesk/utils/schemaPrimitives.ts b/src/features/otcDesk/utils/schemaPrimitives.ts index 28942cff..1dc7e28d 100644 --- a/src/features/otcDesk/utils/schemaPrimitives.ts +++ b/src/features/otcDesk/utils/schemaPrimitives.ts @@ -1,9 +1,9 @@ +import { prepareBroadcastRequest } from "@defuse-protocol/internal-utils" import { base58, base64, base64urlnopad, hex } from "@scure/base" import * as v from "valibot" import { AssertionError } from "../../../errors/assert" import { findError } from "../../../utils/errors" import { isLegitAccountId } from "../../../utils/near" -import { normalizeERC191Signature } from "../../../utils/prepareBroadcastRequest" import { parseDefuseAssetId } from "../../../utils/tokenUtils" import { normalizeSignatureS } from "../../../utils/webAuthn" @@ -52,7 +52,8 @@ export const SignatureSecp256k1Schema = v.pipe( if (dataset.typed) { const signatureHex = hex.encode(dataset.value) try { - const normalizedSignature = normalizeERC191Signature(signatureHex) + const normalizedSignature = + prepareBroadcastRequest.normalizeERC191Signature(signatureHex) if (signatureHex !== normalizedSignature) { addIssue({ message: diff --git a/src/features/swap/components/SwapSubmitter.tsx b/src/features/swap/components/SwapSubmitter.tsx index e6c27cb6..db42051d 100644 --- a/src/features/swap/components/SwapSubmitter.tsx +++ b/src/features/swap/components/SwapSubmitter.tsx @@ -1,7 +1,7 @@ +import type { AuthMethod } from "@defuse-protocol/internal-utils" import { type ReactNode, createContext } from "react" import { nearClient } from "../../../constants/nearClient" import { logger } from "../../../logger" -import type { AuthMethod } from "../../../types/authHandle" import { SwapUIMachineContext } from "./SwapUIMachineProvider" export const SwapSubmitterContext = createContext<{ diff --git a/src/features/swap/components/SwapUIMachineFormSyncProvider.tsx b/src/features/swap/components/SwapUIMachineFormSyncProvider.tsx index be27763c..5215a45a 100644 --- a/src/features/swap/components/SwapUIMachineFormSyncProvider.tsx +++ b/src/features/swap/components/SwapUIMachineFormSyncProvider.tsx @@ -1,7 +1,7 @@ +import type { AuthMethod } from "@defuse-protocol/internal-utils" import { useSelector } from "@xstate/react" import { type PropsWithChildren, useEffect, useRef } from "react" import { useFormContext } from "react-hook-form" -import type { AuthMethod } from "../../../types/authHandle" import type { SwapWidgetProps } from "../../../types/swap" import { usePublicKeyModalOpener } from "../hooks/usePublicKeyModalOpener" import type { SwapFormValues } from "./SwapForm" diff --git a/src/features/swap/components/SwapUIMachineProvider.tsx b/src/features/swap/components/SwapUIMachineProvider.tsx index 95d326e3..77690a85 100644 --- a/src/features/swap/components/SwapUIMachineProvider.tsx +++ b/src/features/swap/components/SwapUIMachineProvider.tsx @@ -1,3 +1,4 @@ +import type { walletMessage } from "@defuse-protocol/internal-utils" import { createActorContext } from "@xstate/react" import type { PropsWithChildren, ReactElement, ReactNode } from "react" import { useRef } from "react" @@ -11,10 +12,6 @@ import { fromPromise, } from "xstate" import type { SwappableToken } from "../../../types/swap" -import type { - WalletMessage, - WalletSignatureResult, -} from "../../../types/walletMessage" import { computeTotalDeltaDifferentDecimals } from "../../../utils/tokenUtils" import { swapIntentMachine } from "../../machines/swapIntentMachine" import { swapUIMachine } from "../../machines/swapUIMachine" @@ -55,7 +52,9 @@ interface SwapUIMachineProviderProps extends PropsWithChildren { initialTokenIn?: SwappableToken initialTokenOut?: SwappableToken tokenList: SwappableToken[] - signMessage: (params: WalletMessage) => Promise + signMessage: ( + params: walletMessage.WalletMessage + ) => Promise referral?: string onTokenChange?: (params: { tokenIn: SwappableToken | null diff --git a/src/features/tokenMigration/components/TokenMigration.tsx b/src/features/tokenMigration/components/TokenMigration.tsx index 99e6debc..f82b5f05 100644 --- a/src/features/tokenMigration/components/TokenMigration.tsx +++ b/src/features/tokenMigration/components/TokenMigration.tsx @@ -1,5 +1,5 @@ +import type { AuthMethod } from "@defuse-protocol/internal-utils" import { useEffect, useState } from "react" -import type { AuthMethod } from "../../../types/authHandle" import type { SignMessage } from "../../otcDesk/types/sharedTypes" import { TokenMigrationDialog } from "./TokenMigrationDialog" diff --git a/src/features/tokenMigration/components/TokenMigrationDialog.tsx b/src/features/tokenMigration/components/TokenMigrationDialog.tsx index a7ddea3e..26fed50d 100644 --- a/src/features/tokenMigration/components/TokenMigrationDialog.tsx +++ b/src/features/tokenMigration/components/TokenMigrationDialog.tsx @@ -1,10 +1,9 @@ +import { type AuthMethod, authIdentity } from "@defuse-protocol/internal-utils" import { ExclamationTriangleIcon } from "@radix-ui/react-icons" import { Button, Callout, Dialog, Spinner } from "@radix-ui/themes" import { useActor } from "@xstate/react" import { CopyButton } from "../../../components/IntentCard/CopyButton" import { BaseModalDialog } from "../../../components/Modal/ModalDialog" -import type { AuthMethod } from "../../../types/authHandle" -import { authHandleToIntentsUserId } from "../../../utils/authIdentity" import { blockExplorerTxLinkFactory } from "../../../utils/chainTxExplorer" import type { SignMessage } from "../../otcDesk/types/sharedTypes" import { tokenMigrationMachine } from "../machines/tokenMigrationMachine" @@ -22,7 +21,10 @@ export function TokenMigrationDialog({ signMessage: SignMessage onExit: () => void }) { - const userId = authHandleToIntentsUserId(userAddress, userChainType) + const userId = authIdentity.authHandleToIntentsUserId( + userAddress, + userChainType + ) const [state, send] = useActor(tokenMigrationMachine, { input: { diff --git a/src/features/tokenMigration/machines/tokenMigrationMachine.ts b/src/features/tokenMigration/machines/tokenMigrationMachine.ts index 7c0cd9d7..6bbf205f 100644 --- a/src/features/tokenMigration/machines/tokenMigrationMachine.ts +++ b/src/features/tokenMigration/machines/tokenMigrationMachine.ts @@ -1,4 +1,5 @@ -import { solverRelay } from "@defuse-protocol/internal-utils" +import { messageFactory, solverRelay } from "@defuse-protocol/internal-utils" +import type { walletMessage } from "@defuse-protocol/internal-utils" import { assign, fromPromise, setup } from "xstate" import { config } from "../../../config" import { nearClient } from "../../../constants/nearClient" @@ -7,9 +8,7 @@ import { logger } from "../../../logger" import { convertPublishIntentToLegacyFormat } from "../../../sdk/solverRelay/utils/parseFailedPublishError" import { getDepositedBalances } from "../../../services/defuseBalanceService" import type { IntentsUserId } from "../../../types/intentsUserId" -import type { WalletSignatureResult } from "../../../types/walletMessage" import { assert } from "../../../utils/assert" -import { makeSwapMessage } from "../../../utils/messageFactory" import { signIntentMachine } from "../../machines/signIntentMachine" import type { SignMessage } from "../../otcDesk/types/sharedTypes" import type { TokenBalances } from "../../otcDesk/utils/fillWithMinimalExchanges" @@ -28,7 +27,7 @@ export const tokenMigrationMachine = setup({ signerCredentials: SignerCredentials signMessage: SignMessage tokensToMigrate: TokenBalances - signature: null | WalletSignatureResult + signature: null | walletMessage.WalletSignatureResult intentHash: null | string error: null | string intentStatus: null | solverRelay.WaitForIntentSettlementReturnType @@ -141,7 +140,7 @@ export const tokenMigrationMachine = setup({ src: "signIntent", input: ({ context }) => { - const walletMessage = makeSwapMessage({ + const walletMessage = messageFactory.makeSwapMessage({ innerMessage: { signer_id: context.userId, deadline: new Date(Date.now() + 5 * 60 * 1000).toISOString(), diff --git a/src/features/withdraw/components/WithdrawForm/components/RecipientSubForm/RecipientSubForm.tsx b/src/features/withdraw/components/WithdrawForm/components/RecipientSubForm/RecipientSubForm.tsx index 1278342b..672458e4 100644 --- a/src/features/withdraw/components/WithdrawForm/components/RecipientSubForm/RecipientSubForm.tsx +++ b/src/features/withdraw/components/WithdrawForm/components/RecipientSubForm/RecipientSubForm.tsx @@ -1,4 +1,5 @@ import type { BlockchainEnum } from "@defuse-protocol/internal-utils" +import type { AuthMethod } from "@defuse-protocol/internal-utils" import { MagicWandIcon, PersonIcon } from "@radix-ui/react-icons" import { Box, Flex, IconButton, Text, TextField } from "@radix-ui/themes" import { useSelector } from "@xstate/react" @@ -19,7 +20,6 @@ import { import { parseDestinationMemo } from "../../../../../../features/machines/withdrawFormReducer" import { WithdrawUIMachineContext } from "../../../../../../features/withdraw/WithdrawUIMachineContext" import { useSolverLiquidityQuery } from "../../../../../../queries/solverLiquidityQuerires" -import type { AuthMethod } from "../../../../../../types" import type { SupportedChainName, TokenValue, diff --git a/src/features/withdraw/components/WithdrawForm/components/RecipientSubForm/validation.test.ts b/src/features/withdraw/components/WithdrawForm/components/RecipientSubForm/validation.test.ts index 465a890f..f44e11e7 100644 --- a/src/features/withdraw/components/WithdrawForm/components/RecipientSubForm/validation.test.ts +++ b/src/features/withdraw/components/WithdrawForm/components/RecipientSubForm/validation.test.ts @@ -1,6 +1,5 @@ -import { authHandleToIntentsUserId } from "src/utils/authIdentity" +import { AuthMethod, authIdentity } from "@defuse-protocol/internal-utils" import { describe, expect, it } from "vitest" -import { AuthMethod } from "../../../../../../types" import { validateAddressSoft } from "./validation" describe("validateAddressSoft", () => { @@ -43,7 +42,10 @@ describe("validateAddressSoft", () => { const result = validateAddressSoft( "valid-recipient.near", "near_intents", - authHandleToIntentsUserId(webAuthnPublicKey, AuthMethod.WebAuthn), + authIdentity.authHandleToIntentsUserId( + webAuthnPublicKey, + AuthMethod.WebAuthn + ), AuthMethod.Near ) expect(result).toBeNull() @@ -51,7 +53,7 @@ describe("validateAddressSoft", () => { it("should reject self WebAuthn address", () => { const webAuthnPublicKey = "p256:3NSY8SFTWoPFMrTGdLVqPogirCyt3kMnUajXoDQuVeCsA6wzkMMp5whBqymAPM7xFiBthDKueiUv1zVAj7GDT8rQ" - const userAddress = authHandleToIntentsUserId( + const userAddress = authIdentity.authHandleToIntentsUserId( webAuthnPublicKey, AuthMethod.WebAuthn ) @@ -93,13 +95,13 @@ describe("validateAddressSoft", () => { const result = validateAddressSoft( "valid-recipient.near", "near_intents", - authHandleToIntentsUserId(userAddress, AuthMethod.Solana), + authIdentity.authHandleToIntentsUserId(userAddress, AuthMethod.Solana), AuthMethod.Near ) expect(result).toBeNull() }) it("should reject self Solana address", () => { - const userAddress = authHandleToIntentsUserId( + const userAddress = authIdentity.authHandleToIntentsUserId( "DRpbCBMxVnDK7maPGv7vhuYme3jNdBAt4YHw2wKgqWPU", AuthMethod.Solana ) @@ -120,13 +122,13 @@ describe("validateAddressSoft", () => { const result = validateAddressSoft( "valid-recipient.near", "near_intents", - authHandleToIntentsUserId(userAddress, AuthMethod.Ton), + authIdentity.authHandleToIntentsUserId(userAddress, AuthMethod.Ton), AuthMethod.Near ) expect(result).toBeNull() }) it("should reject self Ton address", () => { - const userAddress = authHandleToIntentsUserId( + const userAddress = authIdentity.authHandleToIntentsUserId( "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", AuthMethod.Ton ) @@ -150,7 +152,7 @@ describe("validateAddressSoft", () => { it("should accept EVM address for NEAR network", () => { const result = validateAddressSoft( - authHandleToIntentsUserId( + authIdentity.authHandleToIntentsUserId( "0x32Be343B94f860124dC4fEe278FDCBD38C102D88", AuthMethod.Near ), @@ -161,7 +163,7 @@ describe("validateAddressSoft", () => { it("should accept any EVM address for NEAR network", () => { const result = validateAddressSoft( - authHandleToIntentsUserId("0xd", AuthMethod.Near), // TODO: this is invalid EVM address + authIdentity.authHandleToIntentsUserId("0xd", AuthMethod.Near), // TODO: this is invalid EVM address "near" ) expect(result).toBeNull() diff --git a/src/features/withdraw/components/WithdrawForm/components/RecipientSubForm/validation.ts b/src/features/withdraw/components/WithdrawForm/components/RecipientSubForm/validation.ts index 5c6ed626..d90b2826 100644 --- a/src/features/withdraw/components/WithdrawForm/components/RecipientSubForm/validation.ts +++ b/src/features/withdraw/components/WithdrawForm/components/RecipientSubForm/validation.ts @@ -1,6 +1,5 @@ -import { authHandleToIntentsUserId } from "src/utils/authIdentity" +import { type AuthMethod, authIdentity } from "@defuse-protocol/internal-utils" import { isAddress } from "viem" -import type { AuthMethod } from "../../../../../../types" import type { SupportedChainName } from "../../../../../../types/base" import { validateAddress } from "../../../../../../utils/validateAddress" import { isNearIntentsNetwork } from "../../utils" @@ -53,7 +52,10 @@ function isSelfWithdrawal( return true } // Internal user ID match (for Near Intents) - const internalUserAddress = authHandleToIntentsUserId(userAddress, chainType) + const internalUserAddress = authIdentity.authHandleToIntentsUserId( + userAddress, + chainType + ) if (internalUserAddress === recipientAddress.toLowerCase()) { return true } diff --git a/src/features/withdraw/components/WithdrawForm/utils.ts b/src/features/withdraw/components/WithdrawForm/utils.ts index 1f1030fb..360fefd1 100644 --- a/src/features/withdraw/components/WithdrawForm/utils.ts +++ b/src/features/withdraw/components/WithdrawForm/utils.ts @@ -1,8 +1,8 @@ +import { AuthMethod } from "@defuse-protocol/internal-utils" import { reverseAssetNetworkAdapter } from "src/utils/adapters" import { formatUnits } from "viem" import { getBlockchainsOptions } from "../../../../constants/blockchains" import type { TokenBalances as TokenBalancesRecord } from "../../../../services/defuseBalanceService" -import { AuthMethod } from "../../../../types/authHandle" import type { BaseTokenInfo, SupportedChainName, diff --git a/src/features/withdraw/components/WithdrawWidget.tsx b/src/features/withdraw/components/WithdrawWidget.tsx index e6111165..ae61ec27 100644 --- a/src/features/withdraw/components/WithdrawWidget.tsx +++ b/src/features/withdraw/components/WithdrawWidget.tsx @@ -1,13 +1,10 @@ +import { messageFactory } from "@defuse-protocol/internal-utils" import { assign, fromPromise } from "xstate" import { WidgetRoot } from "../../../components/WidgetRoot" import { settings } from "../../../constants/settings" import { WithdrawWidgetProvider } from "../../../providers/WithdrawWidgetProvider" import type { WithdrawWidgetProps } from "../../../types/withdraw" import { assert } from "../../../utils/assert" -import { - makeInnerSwapMessage, - makeSwapMessage, -} from "../../../utils/messageFactory" import { isBaseToken } from "../../../utils/token" import { swapIntentMachine } from "../../machines/swapIntentMachine" import { withdrawUIMachine } from "../../machines/withdrawUIMachine" @@ -65,7 +62,7 @@ export const WithdrawWidget = (props: WithdrawWidgetProps) => { const { quote } = context.intentOperationParams - const innerMessage = makeInnerSwapMessage({ + const innerMessage = messageFactory.makeInnerSwapMessage({ deadlineTimestamp: Date.now() + settings.swapExpirySec * 1000, referral: context.referral, @@ -81,7 +78,9 @@ export const WithdrawWidget = (props: WithdrawWidgetProps) => { return { innerMessage, - walletMessage: makeSwapMessage({ innerMessage }), + walletMessage: messageFactory.makeSwapMessage({ + innerMessage, + }), } }, }), diff --git a/src/services/depositService.ts b/src/services/depositService.ts index b5eb44f0..a9ad2ead 100644 --- a/src/services/depositService.ts +++ b/src/services/depositService.ts @@ -1,4 +1,5 @@ import { BlockchainEnum, poaBridge } from "@defuse-protocol/internal-utils" +import { AuthMethod, authIdentity } from "@defuse-protocol/internal-utils" import { createAssociatedTokenAccountInstruction, createTransferInstruction, @@ -31,13 +32,11 @@ import type { depositTokenBalanceMachine } from "../features/machines/depositTok import { getNearTxSuccessValue } from "../features/machines/getTxMachine" import type { storageDepositAmountMachine } from "../features/machines/storageDepositAmountMachine" import { logger } from "../logger" -import { AuthMethod } from "../types/authHandle" import type { BaseTokenInfo, SupportedChainName } from "../types/base" import type { SendTransactionEVMParams, Transaction } from "../types/deposit" import type { SendTransactionTonParams } from "../types/deposit" import type { IntentsUserId } from "../types/intentsUserId" import { assert } from "../utils/assert" -import { authHandleToIntentsUserId } from "../utils/authIdentity" import { getEVMChainId } from "../utils/evmChainId" import { isNativeToken } from "../utils/token" import { createTonClient } from "./tonJettonService" @@ -518,7 +517,7 @@ export function createDepositFromSiloTransaction( getAddress(tokenAddress), amount, depositAddress, - authHandleToIntentsUserId(userAddress, AuthMethod.EVM), + authIdentity.authHandleToIntentsUserId(userAddress, AuthMethod.EVM), ], }) const tx: SendTransactionEVMParams = { @@ -892,6 +891,7 @@ export function getAvailableDepositRoutes( network satisfies never throw new Error("exhaustive check failed") } + case AuthMethod.Stellar: case AuthMethod.Solana: switch (network) { /* allowed all */ diff --git a/src/types.ts b/src/types.ts index 0bff82da..2a977862 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,2 +1 @@ export type { BaseTokenInfo, UnifiedTokenInfo } from "./types/base" -export { AuthMethod } from "./types/authHandle" diff --git a/src/types/authHandle.ts b/src/types/authHandle.ts deleted file mode 100644 index 7c45c5be..00000000 --- a/src/types/authHandle.ts +++ /dev/null @@ -1,21 +0,0 @@ -export type AuthMethod = "near" | "evm" | "solana" | "webauthn" | "ton" - -export const AuthMethod = { - Near: "near", - EVM: "evm", - Solana: "solana", - WebAuthn: "webauthn", - Ton: "ton", -} as const - -/** - * Represents a public identifier used for authentication. - * This could be a blockchain address, account name, or public key. - * This is always a public value - never contains private keys or passwords. - */ -export type AuthIdentifier = string - -export type AuthHandle = { - identifier: AuthIdentifier - method: AuthMethod -} diff --git a/src/types/deposit.ts b/src/types/deposit.ts index d8179bf8..7f32172e 100644 --- a/src/types/deposit.ts +++ b/src/types/deposit.ts @@ -1,12 +1,12 @@ +import type { authHandle } from "@defuse-protocol/internal-utils" import type { Transaction as TransactionSolana } from "@solana/web3.js" import type { Address, Hash } from "viem" -import type { AuthHandle } from "./authHandle" import type { RenderHostAppLink } from "./hostAppLink" import type { SwappableToken } from "./swap" export type DepositWidgetProps = { - userAddress: AuthHandle["identifier"] | undefined - chainType: AuthHandle["method"] | undefined + userAddress: authHandle.AuthHandle["identifier"] | undefined + chainType: authHandle.AuthHandle["method"] | undefined userWalletAddress: string | null renderHostAppLink: RenderHostAppLink tokenList: SwappableToken[] diff --git a/src/types/swap.ts b/src/types/swap.ts index 19f2d179..f7add262 100644 --- a/src/types/swap.ts +++ b/src/types/swap.ts @@ -1,8 +1,7 @@ +import type { AuthMethod, walletMessage } from "@defuse-protocol/internal-utils" import type { SendNearTransaction } from "../features/machines/publicKeyVerifierMachine" -import type { AuthMethod } from "./authHandle" import type { BaseTokenInfo, UnifiedTokenInfo } from "./base" import type { RenderHostAppLink } from "./hostAppLink" -import type { WalletMessage, WalletSignatureResult } from "./walletMessage" export type SwapEvent = { type: string @@ -26,7 +25,9 @@ export type SwapWidgetProps = { sendNearTransaction: SendNearTransaction - signMessage: (params: WalletMessage) => Promise + signMessage: ( + params: walletMessage.WalletMessage + ) => Promise onSuccessSwap: (params: { amountIn: bigint amountOut: bigint diff --git a/src/types/walletMessage.ts b/src/types/walletMessage.ts deleted file mode 100644 index a03bf85d..00000000 --- a/src/types/walletMessage.ts +++ /dev/null @@ -1,114 +0,0 @@ -import type { DefusePayloadFor_DefuseIntents } from "./defuse-contracts-types" - -// Message for EVM wallets -export type ERC191Message = { - message: string -} - -export type ERC191SignatureData = { - type: "ERC191" - signatureData: string - signedData: ERC191Message -} - -// Message for NEAR wallets -export type NEP413Message = { - message: string - recipient: string - nonce: Uint8Array - callbackUrl?: string -} - -export type NEP413SignatureData = { - type: "NEP413" - signatureData: { - accountId: string - /** - * Base58-encoded signature with curve prefix. Example: - * ed25519:Gxa24TGbJu4mqdhW3GbvLXmf4bSEyxVicrtpChDWbgga - */ - publicKey: string - /** Base64-encoded signature */ - signature: string - } - /** - * The exact data that was signed. Wallet connectors may modify this during the signing process, - * so this property contains the actual data that was signed by the wallet. - */ - signedData: NEP413Message -} - -// Message for Solana wallets -export type SolanaMessage = { - message: Uint8Array -} - -export type SolanaSignatureData = { - type: "SOLANA" - signatureData: Uint8Array - signedData: SolanaMessage -} - -// WebAuthn -export type WebAuthnMessage = { - /** Hash that needs to be signed */ - challenge: Uint8Array - /** Underlying payload that will be executed onchain */ - payload: string - /** Parsed payload in case UI needs to display it */ - parsedPayload: DefusePayloadFor_DefuseIntents -} - -/** Full response of WebAuthn Login */ -export type WebAuthnSignature = AuthenticatorAssertionResponse - -export type WebAuthnSignatureData = { - type: "WEBAUTHN" - signatureData: WebAuthnSignature - signedData: WebAuthnMessage -} - -// Message for TON wallets -export type TonConnectMessage = { - message: { type: "text"; text: string } -} - -export type TonConnectSignatureData = { - type: "TON_CONNECT" - signatureData: { - signature: string - address: string - timestamp: number - domain: string - payload: - | { - type: "text" - text: string - } - | { - type: "cell" - schema: string - cell: string - } - | { - type: "binary" - bytes: string - } - } - signedData: TonConnectMessage -} - -export type WalletMessage = { - ERC191: ERC191Message - NEP413: NEP413Message - SOLANA: SolanaMessage - WEBAUTHN: WebAuthnMessage - TON_CONNECT: TonConnectMessage -} - -export type WalletSignatureResult = - | ERC191SignatureData - | NEP413SignatureData - | SolanaSignatureData - | WebAuthnSignatureData - | TonConnectSignatureData diff --git a/src/types/withdraw.ts b/src/types/withdraw.ts index 62302ed8..b472fba5 100644 --- a/src/types/withdraw.ts +++ b/src/types/withdraw.ts @@ -1,19 +1,20 @@ +import type { authHandle, walletMessage } from "@defuse-protocol/internal-utils" import type { SendNearTransaction } from "../features/machines/publicKeyVerifierMachine" -import type { AuthHandle } from "./authHandle" import type { BaseTokenInfo, UnifiedTokenInfo } from "./base" import type { RenderHostAppLink } from "./hostAppLink" -import type { WalletMessage, WalletSignatureResult } from "./walletMessage" export type WithdrawWidgetProps = { - userAddress: AuthHandle["identifier"] | undefined - chainType: AuthHandle["method"] | undefined + userAddress: authHandle.AuthHandle["identifier"] | undefined + chainType: authHandle.AuthHandle["method"] | undefined presetTokenSymbol: string | undefined presetAmount: string | undefined presetRecipient: string | undefined presetNetwork: string | undefined renderHostAppLink: RenderHostAppLink tokenList: (BaseTokenInfo | UnifiedTokenInfo)[] - signMessage: (params: WalletMessage) => Promise + signMessage: ( + params: walletMessage.WalletMessage + ) => Promise sendNearTransaction: SendNearTransaction /** * Optional referral code, used for tracking purposes. diff --git a/src/utils/__snapshots__/prepareBroadcastRequest.test.ts.snap b/src/utils/__snapshots__/prepareBroadcastRequest.test.ts.snap index f4dbe96b..af9928c0 100644 --- a/src/utils/__snapshots__/prepareBroadcastRequest.test.ts.snap +++ b/src/utils/__snapshots__/prepareBroadcastRequest.test.ts.snap @@ -23,6 +23,15 @@ exports[`prepareSwapSignedData() > should return the correct signed data for a S } `; +exports[`prepareSwapSignedData() > should return the correct signed data for a Stellar signature 1`] = ` +{ + "payload": "{"foo":"bar"}", + "public_key": "ed25519:DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy", + "signature": "ed25519:S88bG8B", + "standard": "raw_ed25519", +} +`; + exports[`prepareSwapSignedData() > should return the correct signed data for a TonConnect signature 1`] = ` { "address": "0:fa63f5195b0f8682d3f3413e2b40decfae7778b3691748a2d55dae5b243a3054", diff --git a/src/utils/authIdentity.test.ts b/src/utils/authIdentity.test.ts deleted file mode 100644 index 05616b25..00000000 --- a/src/utils/authIdentity.test.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { describe, expect, it } from "vitest" -import { authHandleToIntentsUserId } from "./authIdentity" - -describe("authHandleToIntentsUserId", () => { - it("returns lowercased Near account ID for 'near' chain type", () => { - const result = authHandleToIntentsUserId("Bob.Near", "near") - expect(result).toBe("bob.near") - }) - - it("returns lowercased Ethereum address for 'evm' chain type", () => { - const result = authHandleToIntentsUserId( - "0xc0ffee254729296a45a3885639AC7E10F9d54979", - "evm" - ) - expect(result).toBe("0xc0ffee254729296a45a3885639ac7e10f9d54979") - }) - - it("returns hex encoded Solana address for 'solana' chain type", () => { - const result = authHandleToIntentsUserId( - "3yAnWiDUbv2Ckjptk1D1HAwYHgqZKoqbR755ckY3n9oV", - "solana" - ) - expect(result).toBe( - "2c1af676c3580b2ecde77673a38886cc16429b4b17744da5985a25152843a570" - ) - }) - - it("returns derived address for 'webauthn' chain type with P-256 curve", () => { - const result = authHandleToIntentsUserId( - "p256:3NSY8SFTWoPFMrTGdLVqPogirCyt3kMnUajXoDQuVeCsA6wzkMMp5whBqymAPM7xFiBthDKueiUv1zVAj7GDT8rQ", - "webauthn" - ) - expect(result).toBe("0xf54df4d2598c83e2293c616384442a70335e7859") - }) - - it("returns hex encoded public key for 'webauthn' chain type with Ed25519 curve", () => { - const result = authHandleToIntentsUserId( - "ed25519:Gz9STDrgGWdt2fh1g91v2n6SUsy5QKHbx86Nrjy2kFz5", - "webauthn" - ) - expect(result).toBe( - "ed82f3aaf32b8825b67d23d1581edee4f90240ee57a2963c46f529c93d6ce5ae" - ) - }) - - it("throws if incorrect curve is provided", () => { - expect(() => - authHandleToIntentsUserId( - "foo:Gz9STDrgGWdt2fh1g91v2n6SUsy5QKHbx86Nrjy2kFz5", - "webauthn" - ) - ).toThrow() - }) -}) diff --git a/src/utils/authIdentity.ts b/src/utils/authIdentity.ts deleted file mode 100644 index c8fa4ad8..00000000 --- a/src/utils/authIdentity.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { keccak_256 } from "@noble/hashes/sha3" -import { base58, hex } from "@scure/base" -import type { - AuthHandle, - AuthIdentifier, - AuthMethod, -} from "../types/authHandle" -import type { IntentsUserId } from "../types/intentsUserId" -import { assert } from "./assert" -import { parsePublicKey } from "./webAuthn" - -/** - * Converts a blockchain address to a standardized Defuse user ID. - * - * The conversion follows these rules: - * 1. NEAR addresses: Used as-is (lowercased) - * - Explicit: "bob.near" - * - Implicit: "17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1" - * - * 2. EVM addresses: Used as-is (lowercased) - * - Format: "0xc0ffee254729296a45a3885639ac7e10f9d54979" - * - * 3. Solana addresses: Converted from base58 to hex - * - Input: base58 public key - * - Output: hex-encoded string - * - * 4. WebAuthn credentials: Converted based on curve type - * - P-256: Keccak256(prefix + pubkey) -> last 20 bytes -> hex with 0x prefix - * - Ed25519: Raw public key -> hex encoding - * - * @param authIdentifier - The user's identifier (blockchain address or WebAuthn public key) - * @param authMethod - The type of credential ("evm", "near", "solana", "webauthn") - * @returns A standardized Defuse user ID - */ -export function authHandleToIntentsUserId( - authIdentifier: AuthIdentifier, - authMethod: AuthMethod -): IntentsUserId -export function authHandleToIntentsUserId(authHandle: AuthHandle): IntentsUserId -export function authHandleToIntentsUserId( - authIdentifier: AuthIdentifier | AuthHandle, - authMethod?: AuthMethod -): IntentsUserId { - let authHandle: AuthHandle - if (typeof authIdentifier === "object") { - authHandle = authIdentifier - } else if (authMethod != null) { - authHandle = { - identifier: authIdentifier, - method: authMethod, - } - } else { - // This should never happen, because of argument types - throw new Error("Invalid arguments") - } - - const method = authHandle.method - switch (method) { - case "evm": - case "near": - return authHandle.identifier.toLowerCase() as IntentsUserId - - case "solana": - return hex.encode(base58.decode(authHandle.identifier)) as IntentsUserId - - case "webauthn": { - return webAuthnIdentifierToIntentsUserId( - authHandle.identifier - ) as IntentsUserId - } - - case "ton": { - assert(authHandle.identifier.length === 64) - hex.decode(authHandle.identifier) - return authHandle.identifier.toLowerCase() as IntentsUserId - } - - default: - method satisfies never - throw new Error("Unsupported auth method") - } -} - -function webAuthnIdentifierToIntentsUserId(credential: string): string { - const { curveType, publicKey } = parsePublicKey(credential) - - switch (curveType) { - case "p256": { - const p256 = new TextEncoder().encode("p256") - const addressBytes = keccak_256( - new Uint8Array([...p256, ...publicKey]) - ).slice(-20) - - // biome-ignore lint/style/useTemplate: it's fine - return "0x" + hex.encode(addressBytes) - } - - case "ed25519": { - return hex.encode(publicKey) - } - - default: - curveType satisfies never - throw new Error("Unsupported curve type") - } -} diff --git a/src/utils/messageFactory.test.ts b/src/utils/messageFactory.test.ts deleted file mode 100644 index 868dc0d2..00000000 --- a/src/utils/messageFactory.test.ts +++ /dev/null @@ -1,513 +0,0 @@ -import { describe, expect, it } from "vitest" -import { authHandleToIntentsUserId } from "./authIdentity" -import { - makeEmptyMessage, // Add this import - makeInnerSwapAndWithdrawMessage, - makeInnerSwapMessage, - makeSwapMessage, -} from "./messageFactory" - -describe("makeSwapMessage()", () => { - const innerMessage = makeInnerSwapMessage({ - tokenDeltas: [["foo.near", 100n]], - signerId: authHandleToIntentsUserId("user.near", "near"), - deadlineTimestamp: 1704110400000, // 2024-01-01T12:00:00.000Z - }) - - it("should return a WalletMessage object", () => { - const message = makeSwapMessage({ - innerMessage, - nonce: new Uint8Array(32), - }) - - expect(message).toEqual({ - NEP413: { - message: `{"deadline":"2024-01-01T12:00:00.000Z","intents":[{"intent":"token_diff","diff":{"foo.near":"100"}}],"signer_id":"user.near"}`, - recipient: "intents.near", - nonce: new Uint8Array(32), - }, - ERC191: { - message: `{ - "signer_id": "user.near", - "verifying_contract": "intents.near", - "deadline": "2024-01-01T12:00:00.000Z", - "nonce": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", - "intents": [ - { - "intent": "token_diff", - "diff": { - "foo.near": "100" - } - } - ] -}`, - }, - SOLANA: { - message: Uint8Array.from( - Buffer.from( - JSON.stringify({ - signer_id: "user.near", - verifying_contract: "intents.near", - deadline: "2024-01-01T12:00:00.000Z", - nonce: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", - intents: [{ intent: "token_diff", diff: { "foo.near": "100" } }], - }), - "utf-8" - ) - ), - }, - WEBAUTHN: expect.any(Object), - TON_CONNECT: { - message: { - type: "text", - text: `{ - "signer_id": "user.near", - "verifying_contract": "intents.near", - "deadline": "2024-01-01T12:00:00.000Z", - "nonce": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", - "intents": [ - { - "intent": "token_diff", - "diff": { - "foo.near": "100" - } - } - ] -}`, - }, - }, - }) - }) - - describe("WEBAUTHN format", () => { - const config = { - innerMessage: makeInnerSwapMessage({ - tokenDeltas: [["foo.near", 100n]], - signerId: authHandleToIntentsUserId("user.near", "near"), - deadlineTimestamp: 1704110400000, // 2024-01-01T12:00:00.000Z, - }), - recipient: "recipient.near", - nonce: new Uint8Array(32), - } - - it("should return WEBAUTHN object", () => { - const message = makeSwapMessage(config) - - expect(message.WEBAUTHN.payload).toMatchInlineSnapshot( - `"{"signer_id":"user.near","verifying_contract":"intents.near","deadline":"2024-01-01T12:00:00.000Z","nonce":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=","intents":[{"intent":"token_diff","diff":{"foo.near":"100"}}]}"` - ) - expect(message.WEBAUTHN.challenge).toHaveLength(32) // SHA-256 is 32 bytes - }) - - it("should compute challenge using SHA-256", async () => { - const message = makeSwapMessage(config) - - const webauthnPayload = message.WEBAUTHN.payload - const webauthnChallenge = message.WEBAUTHN.challenge - - expect(webauthnChallenge).toEqual( - new Uint8Array( - await crypto.subtle.digest( - "SHA-256", - Buffer.from(webauthnPayload, "utf-8") - ) - ) - ) - }) - - it("should generate deterministic challenge for same inputs", () => { - const message1 = makeSwapMessage(structuredClone(config)) - const message2 = makeSwapMessage(structuredClone(config)) - - expect(message1.WEBAUTHN.challenge).toEqual(message2.WEBAUTHN.challenge) - }) - - it("should change challenge when any input field changes", () => { - const baseMessage = makeSwapMessage(config) - - const differentDeltasMsg = makeSwapMessage({ - ...config, - nonce: crypto.getRandomValues(new Uint8Array(32)), - }) - - expect(differentDeltasMsg.WEBAUTHN.challenge).not.toEqual( - baseMessage.WEBAUTHN.challenge - ) - }) - }) - - it("should return a WalletMessage with random nonce", () => { - const msg1 = makeSwapMessage({ innerMessage }) - const msg2 = makeSwapMessage({ innerMessage }) - expect(msg1.NEP413.nonce).not.toEqual(msg2.NEP413.nonce) - }) - - it("should include referral in token_diff intent", () => { - const innerMessage = makeInnerSwapMessage({ - tokenDeltas: [ - ["foo.near", -100n], - ["bar.near", 200n], - ], - signerId: authHandleToIntentsUserId("user.near", "near"), - deadlineTimestamp: 1704110400000, - referral: "referrer.near", - }) - - expect(innerMessage).toMatchInlineSnapshot(` - { - "deadline": "2024-01-01T12:00:00.000Z", - "intents": [ - { - "diff": { - "bar.near": "200", - "foo.near": "-100", - }, - "intent": "token_diff", - "memo": undefined, - "referral": "referrer.near", - }, - ], - "signer_id": "user.near", - } - `) - }) - - it("should merge amounts in/out with same token", () => { - const innerMessage = makeInnerSwapMessage({ - tokenDeltas: [ - ["foo.near", -150n], - ["bar.near", -200n], - ["bar.near", 270n], - ["foo.near", 100n], - ], - signerId: authHandleToIntentsUserId("user.near", "near"), - deadlineTimestamp: 1704110400000, - }) - - expect(innerMessage).toMatchInlineSnapshot(` - { - "deadline": "2024-01-01T12:00:00.000Z", - "intents": [ - { - "diff": { - "bar.near": "70", - "foo.near": "-50", - }, - "intent": "token_diff", - "memo": undefined, - "referral": undefined, - }, - ], - "signer_id": "user.near", - } - `) - }) -}) - -describe("makeInnerSwapAndWithdrawMessage()", () => { - const DEADLINE = 1704110400000 // 2024-01-01T12:00:00.000Z - - it("generates message with swaps", () => { - const innerMessage = makeInnerSwapAndWithdrawMessage({ - tokenDeltas: [ - ["foo.near", -100n], - ["bar.near", 200n], - ], - storageTokenDeltas: [], - withdrawParams: { - type: "to_near", - amount: 200n, - tokenAccountId: "bar.near", - receiverId: "receiver.near", - storageDeposit: 0n, - }, - signerId: authHandleToIntentsUserId("user.near", "near"), - deadlineTimestamp: DEADLINE, - }) - - expect(innerMessage).toMatchInlineSnapshot(` - { - "deadline": "2024-01-01T12:00:00.000Z", - "intents": [ - { - "diff": { - "bar.near": "200", - "foo.near": "-100", - }, - "intent": "token_diff", - "memo": undefined, - "referral": undefined, - }, - { - "amount": "200", - "intent": "ft_withdraw", - "receiver_id": "receiver.near", - "storage_deposit": null, - "token": "bar.near", - }, - ], - "signer_id": "user.near", - } - `) - }) - - it("generates message with regular swap and storage swap", () => { - const innerMessage = makeInnerSwapAndWithdrawMessage({ - tokenDeltas: [ - ["foo.near", -100n], - ["bar.near", 200n], - ], - storageTokenDeltas: [ - ["bar.near", -7n], - ["wrap.near", 125n], - ], - withdrawParams: { - type: "to_near", - amount: 193n, - tokenAccountId: "bar.near", - receiverId: "receiver.near", - storageDeposit: 125n, - }, - signerId: authHandleToIntentsUserId("user.near", "near"), - deadlineTimestamp: DEADLINE, - referral: "referrer.near", - }) - - expect(innerMessage).toMatchInlineSnapshot(` - { - "deadline": "2024-01-01T12:00:00.000Z", - "intents": [ - { - "diff": { - "bar.near": "200", - "foo.near": "-100", - }, - "intent": "token_diff", - "memo": undefined, - "referral": "referrer.near", - }, - { - "diff": { - "bar.near": "-7", - "wrap.near": "125", - }, - "intent": "token_diff", - "memo": undefined, - "referral": "referrer.near", - }, - { - "amount": "193", - "intent": "ft_withdraw", - "receiver_id": "receiver.near", - "storage_deposit": "125", - "token": "bar.near", - }, - ], - "signer_id": "user.near", - } - `) - }) - - it("generates message without swaps", () => { - const innerMessage = makeInnerSwapAndWithdrawMessage({ - tokenDeltas: [], - storageTokenDeltas: [], - withdrawParams: { - type: "to_near", - amount: 200n, - tokenAccountId: "bar.near", - receiverId: "receiver.near", - storageDeposit: 100n, - }, - signerId: authHandleToIntentsUserId("user.near", "near"), - deadlineTimestamp: DEADLINE, - }) - - expect(innerMessage).toMatchInlineSnapshot(` - { - "deadline": "2024-01-01T12:00:00.000Z", - "intents": [ - { - "amount": "200", - "intent": "ft_withdraw", - "receiver_id": "receiver.near", - "storage_deposit": "100", - "token": "bar.near", - }, - ], - "signer_id": "user.near", - } - `) - }) - - it("generates message for withdrawing via POA Bridge", () => { - const innerMessage = makeInnerSwapAndWithdrawMessage({ - tokenDeltas: [], - storageTokenDeltas: [], - withdrawParams: { - type: "via_poa_bridge", - amount: 200n, - tokenAccountId: "bar-poa-bridge-token.near", - destinationAddress: "0xdead", - destinationMemo: null, - }, - signerId: authHandleToIntentsUserId("user.near", "near"), - deadlineTimestamp: DEADLINE, - }) - - expect(innerMessage).toMatchInlineSnapshot(` - { - "deadline": "2024-01-01T12:00:00.000Z", - "intents": [ - { - "amount": "200", - "intent": "ft_withdraw", - "memo": "WITHDRAW_TO:0xdead", - "receiver_id": "bar-poa-bridge-token.near", - "token": "bar-poa-bridge-token.near", - }, - ], - "signer_id": "user.near", - } - `) - }) - - it("generates message for withdrawing via POA Bridge to XRP Ledger", () => { - const innerMessage = makeInnerSwapAndWithdrawMessage({ - tokenDeltas: [], - storageTokenDeltas: [], - withdrawParams: { - type: "via_poa_bridge", - amount: 200n, - tokenAccountId: "xrp.omft.near", - destinationAddress: "rJHkpNJVRUKMGV3NV6FNccySKNwEFF9C4c", - destinationMemo: "2682497019", - }, - signerId: authHandleToIntentsUserId("user.near", "near"), - deadlineTimestamp: DEADLINE, - }) - - expect(innerMessage).toMatchInlineSnapshot(` - { - "deadline": "2024-01-01T12:00:00.000Z", - "intents": [ - { - "amount": "200", - "intent": "ft_withdraw", - "memo": "WITHDRAW_TO:rJHkpNJVRUKMGV3NV6FNccySKNwEFF9C4c:2682497019", - "receiver_id": "xrp.omft.near", - "token": "xrp.omft.near", - }, - ], - "signer_id": "user.near", - } - `) - }) - - it("generates message for withdrawing to AuroraEngine powered blockchains", () => { - const innerMessage = makeInnerSwapAndWithdrawMessage({ - tokenDeltas: [], - storageTokenDeltas: [], - withdrawParams: { - type: "to_aurora_engine", - amount: 200n, - tokenAccountId: "usdt.near", - auroraEngineContractId: "foo.cloud.aurora", - destinationAddress: "0xDeAdBeEf00000000000000000000000000000001", - }, - signerId: authHandleToIntentsUserId("user.near", "near"), - deadlineTimestamp: DEADLINE, - }) - - expect(innerMessage).toMatchInlineSnapshot(` - { - "deadline": "2024-01-01T12:00:00.000Z", - "intents": [ - { - "amount": "200", - "intent": "ft_withdraw", - "msg": "deadbeef00000000000000000000000000000001", - "receiver_id": "foo.cloud.aurora", - "token": "usdt.near", - }, - ], - "signer_id": "user.near", - } - `) - }) - - it("should include referral in token_diff intent when swapping and withdrawing", () => { - const innerMessage = makeInnerSwapAndWithdrawMessage({ - tokenDeltas: [ - ["foo.near", -100n], - ["bar.near", 200n], - ], - storageTokenDeltas: [], - withdrawParams: { - type: "to_near", - amount: 200n, - tokenAccountId: "bar.near", - receiverId: "receiver.near", - storageDeposit: 0n, - }, - signerId: authHandleToIntentsUserId("user.near", "near"), - deadlineTimestamp: DEADLINE, - referral: "referrer.near", - }) - - expect(innerMessage).toMatchInlineSnapshot(` - { - "deadline": "2024-01-01T12:00:00.000Z", - "intents": [ - { - "diff": { - "bar.near": "200", - "foo.near": "-100", - }, - "intent": "token_diff", - "memo": undefined, - "referral": "referrer.near", - }, - { - "amount": "200", - "intent": "ft_withdraw", - "receiver_id": "receiver.near", - "storage_deposit": null, - "token": "bar.near", - }, - ], - "signer_id": "user.near", - } - `) - }) -}) - -describe("makeEmptyMessage()", () => { - const TEST_TIMESTAMP = 1704110400000 // 2024-01-01T12:00:00.000Z - const TEST_NONCE = new Uint8Array(32) - - it("should create message with empty intents array", () => { - const message = makeEmptyMessage({ - signerId: authHandleToIntentsUserId("user.near", "near"), - deadlineTimestamp: TEST_TIMESTAMP, - nonce: TEST_NONCE, - }) - - expect(message.NEP413).toEqual({ - message: `{"deadline":"2024-01-01T12:00:00.000Z","intents":[],"signer_id":"user.near"}`, - recipient: "intents.near", - nonce: TEST_NONCE, - }) - }) - - it("should use default nonce when not provided", () => { - const message = makeEmptyMessage({ - signerId: authHandleToIntentsUserId("user.near", "near"), - deadlineTimestamp: TEST_TIMESTAMP, - }) - - expect(message.NEP413.nonce).toHaveLength(32) - const parsed = JSON.parse(message.NEP413.message) - expect(parsed.intents).toEqual([]) - }) -}) diff --git a/src/utils/messageFactory.ts b/src/utils/messageFactory.ts deleted file mode 100644 index 555af25f..00000000 --- a/src/utils/messageFactory.ts +++ /dev/null @@ -1,376 +0,0 @@ -import { poaBridge } from "@defuse-protocol/internal-utils" -import { sha256 } from "@noble/hashes/sha256" -import { base64 } from "@scure/base" -import { getAddress } from "viem" -import { config } from "../config" -import { logger } from "../logger" -import type { SupportedChainName } from "../types/base" -import type { - Intent, - Nep413DefuseMessageFor_DefuseIntents, -} from "../types/defuse-contracts-types" -import type { IntentsUserId } from "../types/intentsUserId" -import type { WalletMessage } from "../types/walletMessage" -import { assert } from "./assert" -import { buildHotOmniWithdrawIntent } from "./hotOmniUtils" - -/** - * @param tokenDeltas - * @param signerId - * @param deadlineTimestamp Unix timestamp in milliseconds - * @param referral - * @param memo - */ -export function makeInnerSwapMessage({ - tokenDeltas, - signerId, - deadlineTimestamp, - referral, - memo, -}: { - tokenDeltas: [string, bigint][] - signerId: IntentsUserId - deadlineTimestamp: number - referral?: string - memo?: string -}): Nep413DefuseMessageFor_DefuseIntents { - const tokenDiff: Record = {} - const tokenDiffNum: Record = {} - - for (const [token, amount] of tokenDeltas) { - tokenDiffNum[token] ??= 0n - tokenDiffNum[token] += amount - // biome-ignore lint/style/noNonNullAssertion: it is checked above - tokenDiff[token] = tokenDiffNum[token]!.toString() - } - - if (Object.keys(tokenDiff).length === 0) { - logger.warn("Empty diff") - return { - deadline: new Date(deadlineTimestamp).toISOString(), - intents: [], - signer_id: signerId, - } - } - - return { - deadline: new Date(deadlineTimestamp).toISOString(), - intents: [ - { - intent: "token_diff", - diff: tokenDiff, - referral, - memo, - }, - ], - signer_id: signerId, - } -} - -/** - * Explanation of `tokenDelta` and `storageTokenDelta`. - * - * Say we withdraw token $Y, but we have token $X. We need to swap $X to $Y first. - * `tokenDelta` represents the swap from $X to $Y. - * - * Say we withdraw token $Y to Near, and we need to pay for storage. - * Storage is paid in token $N. - * `storageTokenDelta` represents the swap from $Y to $N. - * - * Important: We must execute these swaps separately rather than combining them. - * This is because protocol fees are applied to each outgoing token in a swap. - * - * Example of why combining swaps fails when fee is 0.3%: - * - * User withdraws 1.0 USDC:eth to Near blockchain and pays for storage. - * Quote #1: 1.0 USDC:eth -> 0.994009 USDC:near - * Quote #2: 0.003866 USDC:near -> 0.00125 NEAR - * - * User combined diff: [-1000000 USDC:eth, +994009-3866=+990143 USDC:near, +125000 NEAR] - * - Shared pool state: [+997000 USDC:eth, -990143 USDC:near, -125000 NEAR] - * - Note: 1000000*0.003=3000 USDC:eth taken as protocol fee - * - * Solver #1 diff: [+997000 USDC:eth, -997000 USDC:near, 0 NEAR] - * - Shared pool state: [+997000-997000=0 USDC:eth, -990143+994009=+3866 USDC:near, -125000 NEAR] - * - * Solver #2 diff: [0 USDC:eth, +3854 USDC:near, -125377 NEAR] - * - Shared pool state: [0 USDC:eth, +3866-3854=12 USDC:near, -125000+125000=0 NEAR] - * - * Result: 12 USDC:near remains in the pool - violating the invariant that at the end the pool should be empty. - * - * By executing swaps separately, we properly account for protocol fees at each step. - * - * @param tokenDeltas - the swap from X to Y, where Y to be withdrawn - * @param storageTokenDeltas - the swap from Y to N, where Y to be withdrawn, N to be paid for storage - * @param withdrawParams - * @param signerId - * @param deadlineTimestamp - unix timestamp in seconds - * @param referral - */ -export function makeInnerSwapAndWithdrawMessage({ - tokenDeltas, - storageTokenDeltas, - withdrawParams, - signerId, - deadlineTimestamp, - referral, -}: { - tokenDeltas: [string, bigint][] - storageTokenDeltas: [string, bigint][] - withdrawParams: WithdrawParams - signerId: IntentsUserId - deadlineTimestamp: number - referral?: string -}): Nep413DefuseMessageFor_DefuseIntents { - const intents: NonNullable = - [] - - if (tokenDeltas.length) { - const { intents: swapIntents } = makeInnerSwapMessage({ - tokenDeltas, - signerId, - deadlineTimestamp, - referral, - }) - assert(swapIntents, "swapIntents must be defined") - intents.push(...swapIntents) - } - - if (storageTokenDeltas.length) { - const { intents: storageIntents } = makeInnerSwapMessage({ - tokenDeltas: storageTokenDeltas, - signerId, - deadlineTimestamp, - referral, - }) - assert(storageIntents, "storageIntents must be defined") - intents.push(...storageIntents) - } - - intents.push(makeInnerWithdrawMessage(withdrawParams)) - - return { - deadline: new Date(deadlineTimestamp).toISOString(), - intents: intents, - signer_id: signerId, - } -} - -export type WithdrawParams = - | { - type: "to_near" - amount: bigint - tokenAccountId: string - receiverId: string - storageDeposit: bigint - } - | { - type: "via_poa_bridge" - amount: bigint - tokenAccountId: string - destinationAddress: string - destinationMemo: string | null - } - | { - type: "to_aurora_engine" - amount: bigint - tokenAccountId: string - auroraEngineContractId: string - destinationAddress: string - } - | { - type: "hot_omni" - chainName: SupportedChainName - amount: bigint - defuseAssetId: string - destinationAddress: string // todo: consider renaming `receiverId` and `destinationAddress` to `recipient`? - } - -function makeInnerWithdrawMessage(params: WithdrawParams): Intent { - const paramsType = params.type - switch (paramsType) { - case "to_near": - if (params.tokenAccountId === "wrap.near") { - return { - intent: "native_withdraw", - receiver_id: params.receiverId, - amount: params.amount.toString(), - } - } - return { - intent: "ft_withdraw", - token: params.tokenAccountId, - receiver_id: params.receiverId, - amount: params.amount.toString(), - storage_deposit: - params.storageDeposit > 0n ? params.storageDeposit.toString() : null, - } - - case "via_poa_bridge": { - return { - intent: "ft_withdraw", - token: params.tokenAccountId, - receiver_id: params.tokenAccountId, - amount: params.amount.toString(), - memo: poaBridge.createWithdrawMemo({ - receiverAddress: params.destinationAddress, - xrpMemo: params.destinationMemo, - }), - } - } - - case "to_aurora_engine": - return { - intent: "ft_withdraw", - token: params.tokenAccountId, - receiver_id: params.auroraEngineContractId, - amount: params.amount.toString(), - msg: makeAuroraEngineDepositMsg(params.destinationAddress), - } - - case "hot_omni": { - return buildHotOmniWithdrawIntent({ - chainName: params.chainName, - defuseAssetId: params.defuseAssetId, - amount: params.amount, - receiver: params.destinationAddress, - }) - } - - default: - paramsType satisfies never - throw new Error(`Unknown withdraw type: ${paramsType}`) - } -} - -export function makeSwapMessage({ - innerMessage, - nonce = randomDefuseNonce(), -}: { - innerMessage: Nep413DefuseMessageFor_DefuseIntents - nonce?: Uint8Array -}): WalletMessage { - const payload = { - signer_id: innerMessage.signer_id, - verifying_contract: config.env.contractID, - deadline: innerMessage.deadline, - nonce: base64.encode(nonce), - intents: innerMessage.intents, - } - const payloadSerialized = JSON.stringify(payload) - const payloadBytes = new TextEncoder().encode(payloadSerialized) - - return { - NEP413: { - message: JSON.stringify(innerMessage), - // This is who will be verifying the message - recipient: config.env.contractID, - nonce, - }, - ERC191: { - message: JSON.stringify(payload, null, 2), - }, - SOLANA: { - message: payloadBytes, - }, - WEBAUTHN: { - challenge: makeChallenge(payloadBytes), - payload: payloadSerialized, - parsedPayload: payload, - }, - TON_CONNECT: { - message: { - type: "text", - text: JSON.stringify(payload, null, 2), - }, - }, - } -} - -export function makeEmptyMessage({ - signerId, - deadlineTimestamp, - nonce = randomDefuseNonce(), -}: { - signerId: IntentsUserId - deadlineTimestamp: number - nonce?: Uint8Array -}): WalletMessage { - const innerMessage: Nep413DefuseMessageFor_DefuseIntents = { - deadline: new Date(deadlineTimestamp).toISOString(), - intents: [], - signer_id: signerId, - } - - return makeSwapMessage({ - innerMessage, - nonce, - }) -} - -export function randomDefuseNonce(): Uint8Array { - return randomBytes(32) -} - -function randomBytes(length: number): Uint8Array { - return crypto.getRandomValues(new Uint8Array(length)) -} - -/** - * In order to deposit to AuroraEngine powered chain, we need to have a `msg` - * with the destination address in special format (lower case + without 0x). - */ -function makeAuroraEngineDepositMsg(recipientAddress: string): string { - const parsedRecipientAddress = getAddress(recipientAddress) - return parsedRecipientAddress.slice(2).toLowerCase() -} - -/** - * Converts UTF-8 string to bytes for WebAuthn challenge - */ -export function makeChallenge(payload: Uint8Array): Uint8Array { - // It's possible to use native crypto, but it's async, and this would break existing flow: - // await crypto.subtle.digest("SHA-256", messageBytes) - const hash = sha256(payload) - return new Uint8Array(hash) -} - -export function makeInnerTransferMessage({ - tokenDeltas, - signerId, - deadlineTimestamp, - receiverId, - memo, -}: { - tokenDeltas: [string, bigint][] - signerId: IntentsUserId - deadlineTimestamp: number - receiverId: string - memo?: string -}): Nep413DefuseMessageFor_DefuseIntents { - const tokens: Record = {} - const seenTokens = new Set() - - for (const [token, amount] of tokenDeltas) { - assert(!seenTokens.has(token), `Duplicate token found: ${token}`) - seenTokens.add(token) - assert( - amount > 0n, - `Transfer amount must be positive, got: ${amount} for token ${token}` - ) - tokens[token] = amount.toString() - } - - return { - deadline: new Date(deadlineTimestamp).toISOString(), - intents: [ - { - intent: "transfer", - tokens, - receiver_id: receiverId, - ...(memo ? { memo } : {}), - }, - ], - signer_id: signerId, - } -} diff --git a/src/utils/multiPayload/webauthn.ts b/src/utils/multiPayload/webauthn.ts index 09d9cace..48a72775 100644 --- a/src/utils/multiPayload/webauthn.ts +++ b/src/utils/multiPayload/webauthn.ts @@ -1,14 +1,13 @@ +import type { AuthMethod, walletMessage } from "@defuse-protocol/internal-utils" import { base58, base64urlnopad } from "@scure/base" -import type { AuthMethod } from "../../types/authHandle" import type { MultiPayload } from "../../types/defuse-contracts-types" -import type { WebAuthnSignatureData } from "../../types/walletMessage" import type { CurveType, FormattedPublicKey } from "../../types/webAuthn" import { assert } from "../assert" import { extractRawSignature, parsePublicKey } from "../webAuthn" export function makeWebAuthnMultiPayload( userInfo: { userAddress: string; userChainType: AuthMethod }, - signature: WebAuthnSignatureData + signature: walletMessage.WebAuthnSignatureData ): MultiPayload { assert( userInfo.userChainType === "webauthn", diff --git a/src/utils/prepareBroadcastRequest.test.ts b/src/utils/prepareBroadcastRequest.test.ts deleted file mode 100644 index df221d60..00000000 --- a/src/utils/prepareBroadcastRequest.test.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { describe, expect, it } from "vitest" -import type { - ERC191SignatureData, - NEP413SignatureData, - SolanaSignatureData, - TonConnectSignatureData, - WalletMessage, - WebAuthnSignatureData, -} from "../types/walletMessage" -import { authHandleToIntentsUserId } from "./authIdentity" -import { makeInnerSwapMessage, makeSwapMessage } from "./messageFactory" -import { prepareSwapSignedData } from "./prepareBroadcastRequest" - -describe("prepareSwapSignedData()", () => { - const swapMessage = makeSwapMessage({ - innerMessage: makeInnerSwapMessage({ - tokenDeltas: [["foo.near", 100n]], - signerId: authHandleToIntentsUserId("user.near", "near"), - deadlineTimestamp: 1704110400000, - }), - nonce: new Uint8Array(32), - }) - - const walletMessage: WalletMessage = { - NEP413: { - message: `{"foo":"bar"}`, - recipient: "defuse.near", - nonce: Buffer.from( - "esXbbxyJNApGznX1v8kT5ojuat7jqUv84Ib+Q6hWdzI=", - "base64" - ), - }, - ERC191: { - message: JSON.stringify({ foo: "bar" }), - }, - SOLANA: { - message: Uint8Array.from( - Buffer.from(JSON.stringify({ foo: "bar" }), "utf8") - ), - }, - WEBAUTHN: swapMessage.WEBAUTHN, - TON_CONNECT: swapMessage.TON_CONNECT, - } - - it("should return the correct signed data for a NEP141 signature", () => { - const signature: NEP413SignatureData = { - type: "NEP413", - signatureData: { - accountId: "user.near", - publicKey: "ed25519:Gxa24TGbJu4mqdhW3GbvLXmf4bSEyxVicrtpChDWbgga", - signature: "stH6JnShG+GwWCsfN9iu/m4un6qwYLN9Df+5oQYsL7Q=", - }, - signedData: walletMessage.NEP413, - } - - expect( - prepareSwapSignedData(signature, { - userAddress: "user.near", - userChainType: "near", - }) - ).toMatchSnapshot() - }) - - it("should return the correct signed data for an ERC191 signature", () => { - const signature: ERC191SignatureData = { - type: "ERC191", - signatureData: "0xdeadbeef1c", - signedData: walletMessage.ERC191, - } - - expect( - prepareSwapSignedData(signature, { - userAddress: "0xabcd", - userChainType: "evm", - }) - ).toMatchSnapshot() - }) - - it("should return the correct signed data for a Solana signature", () => { - const signature: SolanaSignatureData = { - type: "SOLANA", - signatureData: Buffer.from("deadbeef1c", "hex"), - signedData: walletMessage.SOLANA, - } - - expect( - prepareSwapSignedData(signature, { - userAddress: "DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy", - userChainType: "solana", - }) - ).toMatchSnapshot() - }) - - it("should return the correct signed data for a WebAuthn signature", async () => { - const signature: WebAuthnSignatureData = { - type: "WEBAUTHN", - signatureData: { - authenticatorData: Buffer.from("dead", "hex"), - clientDataJSON: Buffer.from('{"some": "json"}', "utf-8"), - signature: Buffer.from("beef", "hex"), - userHandle: Buffer.from("1ee7", "hex"), - }, - signedData: walletMessage.WEBAUTHN, - } - - expect( - prepareSwapSignedData(signature, { - userAddress: "ed25519:Gxa24TGbJu4mqdhW3GbvLXmf4bSEyxVicrtpChDWbgga", - userChainType: "webauthn", - }) - ).toMatchSnapshot() - }) - - it("should return the correct signed data for a TonConnect signature", () => { - const signature: TonConnectSignatureData = { - type: "TON_CONNECT", - signatureData: { - signature: - "4o7K0k+5pQUHVTX3fD03JIwcixdxmPE7pGgVmQsUHxdZ+G2OJEeXKDv5cnrgPPbZQDgUrMGWfXhYvRFpdJxuAg==", - address: - "0:fa63f5195b0f8682d3f3413e2b40decfae7778b3691748a2d55dae5b243a3054", - timestamp: 1748949269, - domain: "ton-connect.github.io", - payload: walletMessage.TON_CONNECT.message, - }, - signedData: walletMessage.TON_CONNECT, - } - - expect( - prepareSwapSignedData(signature, { - userAddress: - "d1e7c122f8a43c7d7433548c4604edd4dffcfe5bb1d036499684980c115500bf", - userChainType: "ton", - }) - ).toMatchSnapshot() - }) -}) diff --git a/src/utils/prepareBroadcastRequest.ts b/src/utils/prepareBroadcastRequest.ts deleted file mode 100644 index 6f483121..00000000 --- a/src/utils/prepareBroadcastRequest.ts +++ /dev/null @@ -1,110 +0,0 @@ -import type { solverRelay } from "@defuse-protocol/internal-utils" -import { base58, base64, hex } from "@scure/base" -import type { AuthMethod } from "../types/authHandle" -import type { WalletSignatureResult } from "../types/walletMessage" -import { assert } from "./assert" -import { makeWebAuthnMultiPayload } from "./multiPayload/webauthn" - -export function prepareSwapSignedData( - signature: WalletSignatureResult, - userInfo: { userAddress: string; userChainType: AuthMethod } -): solverRelay.Params["signed_data"] { - const signatureType = signature.type - switch (signatureType) { - case "NEP413": { - return { - standard: "nep413", - payload: { - message: signature.signedData.message, - nonce: base64.encode(signature.signedData.nonce), - recipient: signature.signedData.recipient, - callbackUrl: signature.signedData.callbackUrl, - }, - public_key: signature.signatureData.publicKey, // publicKey is already in the correct format - signature: transformNEP141Signature(signature.signatureData.signature), - } - } - - case "ERC191": { - return { - standard: "erc191", - payload: signature.signedData.message, - signature: transformERC191Signature(signature.signatureData), - } - } - - case "SOLANA": - assert( - userInfo.userChainType === "solana", - "User chain and signature chain must match" - ) - return { - standard: "raw_ed25519", - payload: new TextDecoder().decode(signature.signedData.message), - // Solana address is its public key encoded in base58 - public_key: `ed25519:${userInfo.userAddress}`, - signature: transformSolanaSignature(signature.signatureData), - } - - case "WEBAUTHN": { - return makeWebAuthnMultiPayload(userInfo, signature) - } - - case "TON_CONNECT": { - return { - standard: "ton_connect", - address: signature.signatureData.address, - domain: signature.signatureData.domain, - timestamp: signature.signatureData.timestamp, - payload: signature.signatureData.payload, - public_key: `ed25519:${base58.encode(hex.decode(userInfo.userAddress))}`, - signature: `ed25519:${base58.encode(base64.decode(signature.signatureData.signature))}`, - } - } - - default: - signatureType satisfies never - throw new Error("exhaustive check failed") - } -} - -function transformNEP141Signature(signature: string) { - const encoded = base58.encode(base64.decode(signature)) - return `ed25519:${encoded}` -} - -export function transformERC191Signature(signature: string) { - const normalizedSignature = normalizeERC191Signature(signature) - const bytes = hex.decode( - normalizedSignature.startsWith("0x") - ? normalizedSignature.slice(2) - : normalizedSignature - ) - return `secp256k1:${base58.encode(bytes)}` -} - -export function normalizeERC191Signature(signature: string): string { - // Get `v` from the last two characters - let v = Number.parseInt(signature.slice(-2), 16) - - // // Normalize `v` to be either 0 or 1 - v = toRecoveryBit(v) - - // Convert `v` back to hex - const vHex = v.toString(16).padStart(2, "0") - - // Reconstruct the full signature with the adjusted `v` - return signature.slice(0, -2) + vHex -} - -// Copy from viem/utils/signature/recoverPublicKey.ts -function toRecoveryBit(yParityOrV: number) { - if (yParityOrV === 0 || yParityOrV === 1) return yParityOrV - if (yParityOrV === 27) return 0 - if (yParityOrV === 28) return 1 - throw new Error("Invalid yParityOrV value") -} - -function transformSolanaSignature(signature: Uint8Array) { - return `ed25519:${base58.encode(signature)}` -} diff --git a/src/utils/verifyWalletSignature.ts b/src/utils/verifyWalletSignature.ts index 1f296306..ad65665e 100644 --- a/src/utils/verifyWalletSignature.ts +++ b/src/utils/verifyWalletSignature.ts @@ -1,15 +1,15 @@ +import type { walletMessage } from "@defuse-protocol/internal-utils" import { secp256k1 } from "@noble/curves/secp256k1" import { base58 } from "@scure/base" import { sign } from "tweetnacl" import { verifyMessage as verifyMessageViem } from "viem" -import type { WalletSignatureResult } from "../types/walletMessage" import { parsePublicKey, verifyAuthenticatorAssertion } from "./webAuthn" // No-op usage to prevent tree-shaking. sec256k1 is dynamically loaded by viem. const _noop = secp256k1.getPublicKey || null export async function verifyWalletSignature( - signature: WalletSignatureResult, + signature: walletMessage.WalletSignatureResult, userAddress: string ) { const signatureType = signature.type @@ -26,7 +26,8 @@ export async function verifyWalletSignature( signature: signature.signatureData as "0x${string}", }) } - case "SOLANA": { + case "SOLANA": + case "STELLAR": { return sign.detached.verify( signature.signedData.message, signature.signatureData,