-
Notifications
You must be signed in to change notification settings - Fork 356
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add custom handlers for swap-live-app
- Loading branch information
1 parent
edd7bfb
commit f058e5b
Showing
11 changed files
with
471 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
export const SEG_WIT_ABANDON_SEED_ADDRESS = "bc1qed3mqr92zvq2s782aqkyx785u23723w02qfrgs"; | ||
export const DEFAULT_SWAP_APP_ID = "swap-live-app-demo-3"; | ||
|
||
export const SWAP_VERSION = "2.35"; | ||
|
||
export const SWAP_TRACKING_PROPERTIES = { | ||
swapVersion: SWAP_VERSION, | ||
flow: "swap", | ||
}; |
128 changes: 128 additions & 0 deletions
128
apps/ledger-live-mobile/src/screens/Swap/LiveApp/customHandlers/getFee.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
import { Strategy } from "@ledgerhq/coin-evm/lib/types/index"; | ||
import { getMainAccount, getParentAccount } from "@ledgerhq/live-common/account/index"; | ||
import { getAccountBridge } from "@ledgerhq/live-common/bridge/index"; | ||
import { getAbandonSeedAddress } from "@ledgerhq/live-common/currencies/index"; | ||
import { Transaction, TransactionStatus } from "@ledgerhq/live-common/generated/types"; | ||
import { getAccountIdFromWalletAccountId } from "@ledgerhq/live-common/wallet-api/converters"; | ||
import { Account, AccountLike } from "@ledgerhq/types-live"; | ||
import BigNumber from "bignumber.js"; | ||
import { SEG_WIT_ABANDON_SEED_ADDRESS } from "../consts"; | ||
import { | ||
convertToAtomicUnit, | ||
convertToNonAtomicUnit, | ||
getCustomFeesPerFamily, | ||
transformToBigNumbers, | ||
} from "../utils"; | ||
|
||
interface FeeParams { | ||
fromAccountId: string; | ||
fromAmount: string; | ||
feeStrategy: Strategy; | ||
openDrawer: boolean; | ||
customFeeConfig: Record<string, unknown>; | ||
SWAP_VERSION: string; | ||
} | ||
|
||
interface FeeData { | ||
feesStrategy: Strategy; | ||
estimatedFees: BigNumber | undefined; | ||
errors: TransactionStatus["errors"]; | ||
warnings: TransactionStatus["warnings"]; | ||
customFeeConfig: Record<string, unknown>; | ||
} | ||
|
||
interface GenerateFeeDataParams { | ||
account: AccountLike; | ||
feePayingAccount: Account; | ||
feesStrategy: Strategy; | ||
fromAmount: BigNumber | undefined; | ||
customFeeConfig: Record<string, unknown>; | ||
} | ||
|
||
const getRecipientAddress = ( | ||
transactionFamily: Transaction["family"], | ||
currencyId: string, | ||
): string => { | ||
switch (transactionFamily) { | ||
case "evm": | ||
return getAbandonSeedAddress(currencyId); | ||
case "bitcoin": | ||
return SEG_WIT_ABANDON_SEED_ADDRESS; | ||
default: | ||
throw new Error(`Unsupported transaction family: ${transactionFamily}`); | ||
} | ||
}; | ||
|
||
const generateFeeData = async ({ | ||
account, | ||
feePayingAccount, | ||
feesStrategy = "medium", | ||
fromAmount, | ||
customFeeConfig, | ||
}: GenerateFeeDataParams): Promise<FeeData> => { | ||
const bridge = getAccountBridge(account, feePayingAccount); | ||
const baseTransaction = bridge.createTransaction(feePayingAccount); | ||
|
||
const recipient = getRecipientAddress(baseTransaction.family, feePayingAccount.currency.id); | ||
|
||
const transactionConfig: Transaction = { | ||
...baseTransaction, | ||
subAccountId: account.type !== "Account" ? account.id : undefined, | ||
recipient, | ||
amount: fromAmount ?? new BigNumber(0), | ||
feesStrategy, | ||
...transformToBigNumbers(customFeeConfig), | ||
}; | ||
|
||
const preparedTransaction = await bridge.updateTransaction(feePayingAccount, transactionConfig); | ||
const transactionStatus = await bridge.getTransactionStatus( | ||
feePayingAccount, | ||
preparedTransaction, | ||
); | ||
|
||
return { | ||
feesStrategy, | ||
estimatedFees: convertToNonAtomicUnit({ | ||
amount: transactionStatus.estimatedFees, | ||
account: feePayingAccount, | ||
}), | ||
errors: transactionStatus.errors, | ||
warnings: transactionStatus.warnings, | ||
customFeeConfig: getCustomFeesPerFamily(preparedTransaction), | ||
}; | ||
}; | ||
|
||
export const getFee = | ||
(accounts: AccountLike[]) => | ||
async ({ params }: { params: FeeParams }): Promise<FeeData> => { | ||
const accountId = getAccountIdFromWalletAccountId(params.fromAccountId); | ||
if (!accountId) { | ||
throw new Error(`Invalid wallet account ID: ${params.fromAccountId}`); | ||
} | ||
|
||
const account = accounts.find(acc => acc.id === accountId); | ||
if (!account) { | ||
throw new Error(`Account not found: ${accountId}`); | ||
} | ||
|
||
const parentAccount = | ||
account.type === "TokenAccount" ? getParentAccount(account, accounts) : undefined; | ||
|
||
const feePayingAccount = getMainAccount(account, parentAccount); | ||
|
||
const amount = new BigNumber(params.fromAmount); | ||
const atomicAmount = convertToAtomicUnit({ amount, account }); | ||
|
||
if (params.openDrawer) { | ||
// display the drawer to the user and return their selected fee as the values | ||
console.log("hello world xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx") | ||
} | ||
|
||
return await generateFeeData({ | ||
account, | ||
feePayingAccount, | ||
feesStrategy: params.feeStrategy, | ||
fromAmount: atomicAmount, | ||
customFeeConfig: params.customFeeConfig, | ||
}); | ||
}; |
50 changes: 50 additions & 0 deletions
50
apps/ledger-live-mobile/src/screens/Swap/LiveApp/customHandlers/getTransactionByHash.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import { getMainAccount, getParentAccount } from "@ledgerhq/live-common/account/index"; | ||
import { getAccountIdFromWalletAccountId } from "@ledgerhq/live-common/wallet-api/converters"; | ||
import { AccountLike } from "@ledgerhq/types-live"; | ||
import { getNodeApi } from "@ledgerhq/coin-evm/api/node/index"; | ||
|
||
export function getTransactionByHash(accounts: AccountLike[]) { | ||
return async ({ | ||
params, | ||
}: { | ||
params: { | ||
transactionHash: string; | ||
fromAccountId: string; | ||
SWAP_VERSION: string; | ||
}; | ||
}): Promise< | ||
| { | ||
hash: string; | ||
blockHeight: number | undefined; | ||
blockHash: string | undefined; | ||
nonce: number; | ||
gasUsed: string; | ||
gasPrice: string; | ||
value: string; | ||
} | ||
| object | ||
> => { | ||
const realFromAccountId = getAccountIdFromWalletAccountId(params.fromAccountId); | ||
if (!realFromAccountId) { | ||
return Promise.reject(new Error(`accountId ${params.fromAccountId} unknown`)); | ||
} | ||
|
||
const fromAccount = accounts.find(acc => acc.id === realFromAccountId); | ||
if (!fromAccount) { | ||
return Promise.reject(new Error(`accountId ${params.fromAccountId} unknown`)); | ||
} | ||
|
||
const fromParentAccount = getParentAccount(fromAccount, accounts); | ||
const mainAccount = getMainAccount(fromAccount, fromParentAccount); | ||
|
||
const nodeAPI = getNodeApi(mainAccount.currency); | ||
|
||
try { | ||
const tx = await nodeAPI.getTransaction(mainAccount.currency, params.transactionHash); | ||
return Promise.resolve(tx); | ||
} catch (error) { | ||
// not a real error, the node just didn't find the transaction yet | ||
return Promise.resolve({}); | ||
} | ||
}; | ||
} |
20 changes: 20 additions & 0 deletions
20
apps/ledger-live-mobile/src/screens/Swap/LiveApp/customHandlers/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { AccountLike } from "@ledgerhq/types-live"; | ||
import { Dispatch } from "redux"; | ||
|
||
import { getFee } from "./getFee"; | ||
import { getTransactionByHash } from "./getTransactionByHash"; | ||
import { saveSwapToHistory } from "./saveSwapToHistory"; | ||
import { swapRedirectToHistory } from "./swapRedirectToHistory"; | ||
|
||
export const swapCustomHandlers = ({ | ||
accounts, | ||
dispatch, | ||
}: { | ||
accounts: AccountLike[]; | ||
dispatch: Dispatch; | ||
}) => ({ | ||
"custom.getFee": getFee(accounts), | ||
"custom.getTransactionByHash": getTransactionByHash(accounts), | ||
"custom.saveSwapToHistory": saveSwapToHistory(accounts, dispatch), | ||
"custom.swapRedirectToHistory": swapRedirectToHistory, | ||
}); |
91 changes: 91 additions & 0 deletions
91
apps/ledger-live-mobile/src/screens/Swap/LiveApp/customHandlers/saveSwapToHistory.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import { getParentAccount } from "@ledgerhq/live-common/account/index"; | ||
import { getAccountIdFromWalletAccountId } from "@ledgerhq/live-common/wallet-api/converters"; | ||
import { Account, AccountLike, SwapOperation } from "@ledgerhq/types-live"; | ||
import { convertToAtomicUnit } from "../utils"; | ||
import BigNumber from "bignumber.js"; | ||
import { updateAccountWithUpdater } from "~/actions/accounts"; | ||
import { Dispatch } from "redux"; | ||
|
||
export type SwapProps = { | ||
provider: string; | ||
fromAccountId: string; | ||
fromParentAccountId?: string; | ||
toAccountId: string; | ||
fromAmount: string; | ||
toAmount?: string; | ||
quoteId: string; | ||
rate: string; | ||
feeStrategy: string; | ||
customFeeConfig: string; | ||
cacheKey: string; | ||
loading: boolean; | ||
error: boolean; | ||
providerRedirectURL: string; | ||
toNewTokenId: string; | ||
swapApiBase: string; | ||
estimatedFees: string; | ||
estimatedFeesUnit: string; | ||
swapId?: string; | ||
}; | ||
|
||
export function saveSwapToHistory(accounts: AccountLike[], dispatch: Dispatch) { | ||
return async ({ params }: { params: { swap: SwapProps; transaction_id: string } }) => { | ||
const { swap, transaction_id } = params; | ||
if ( | ||
!swap || | ||
!transaction_id || | ||
!swap.provider || | ||
!swap.fromAmount || | ||
!swap.toAmount || | ||
!swap.swapId | ||
) { | ||
return Promise.reject("Cannot save swap missing params"); | ||
} | ||
const fromId = getAccountIdFromWalletAccountId(swap.fromAccountId); | ||
const toId = getAccountIdFromWalletAccountId(swap.toAccountId); | ||
if (!fromId || !toId) return Promise.reject("Accounts not found"); | ||
const operationId = `${fromId}-${transaction_id}-OUT`; | ||
const fromAccount = accounts.find(acc => acc.id === fromId); | ||
const toAccount = accounts.find(acc => acc.id === toId); | ||
if (!fromAccount || !toAccount) { | ||
return Promise.reject(new Error(`accountId ${fromId} unknown`)); | ||
} | ||
const accountId = | ||
fromAccount.type === "TokenAccount" ? getParentAccount(fromAccount, accounts).id : fromId; | ||
const swapOperation: SwapOperation = { | ||
status: "pending", | ||
provider: swap.provider, | ||
operationId, | ||
swapId: swap.swapId, | ||
receiverAccountId: toId, | ||
tokenId: toId, | ||
fromAmount: convertToAtomicUnit({ | ||
amount: new BigNumber(swap.fromAmount), | ||
account: fromAccount, | ||
})!, | ||
toAmount: convertToAtomicUnit({ | ||
amount: new BigNumber(swap.toAmount), | ||
account: toAccount, | ||
})!, | ||
}; | ||
|
||
dispatch( | ||
updateAccountWithUpdater(accountId, (account: Account) => { | ||
if (fromId === account.id) { | ||
return { ...account, swapHistory: [...account.swapHistory, swapOperation] }; | ||
} | ||
return { | ||
...account, | ||
subAccounts: account.subAccounts?.map<SubAccount>((a: SubAccount) => { | ||
const subAccount = { | ||
...a, | ||
swapHistory: [...a.swapHistory, swapOperation], | ||
}; | ||
return a.id === fromId ? subAccount : a; | ||
}), | ||
}; | ||
}), | ||
); | ||
return Promise.resolve(); | ||
}; | ||
} |
4 changes: 4 additions & 0 deletions
4
apps/ledger-live-mobile/src/screens/Swap/LiveApp/customHandlers/swapRedirectToHistory.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export const swapRedirectToHistory = async () => { | ||
console.log("swapRedirectToHistory"); | ||
return Promise.resolve(undefined); | ||
}; |
25 changes: 25 additions & 0 deletions
25
apps/ledger-live-mobile/src/screens/Swap/LiveApp/hooks/useSwapLiveAppCustomHandlers.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { LiveAppManifest } from "@ledgerhq/live-common/platform/types"; | ||
import { WalletAPICustomHandlers } from "@ledgerhq/live-common/wallet-api/types"; | ||
import { useMemo } from "react"; | ||
import { useDispatch, useSelector } from "react-redux"; | ||
import { usePTXCustomHandlers } from "~/components/WebPTXPlayer/CustomHandlers"; | ||
import { accountsSelector } from "~/reducers/accounts"; | ||
import { swapCustomHandlers } from "../customHandlers"; | ||
|
||
export function useSwapLiveAppCustomHandlers(manifest: LiveAppManifest) { | ||
const accounts = useSelector(accountsSelector); | ||
const ptxCustomHandlers = usePTXCustomHandlers(manifest, accounts); | ||
const dispatch = useDispatch(); | ||
|
||
return useMemo<WalletAPICustomHandlers>( | ||
() => | ||
({ | ||
...ptxCustomHandlers, | ||
...swapCustomHandlers({ | ||
accounts, | ||
dispatch, | ||
}), | ||
}) as WalletAPICustomHandlers, | ||
[ptxCustomHandlers, accounts, dispatch], | ||
); | ||
} |
21 changes: 21 additions & 0 deletions
21
apps/ledger-live-mobile/src/screens/Swap/LiveApp/hooks/useSwapLiveAppManifest.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { useFeature } from "@ledgerhq/live-common/featureFlags/index"; | ||
import { useRemoteLiveAppManifest } from "@ledgerhq/live-common/platform/providers/RemoteLiveAppProvider/index"; | ||
import { LiveAppManifest } from "@ledgerhq/live-common/platform/types"; | ||
import { useLocalLiveAppManifest } from "@ledgerhq/live-common/wallet-api/LocalLiveAppProvider/index"; | ||
import { useMemo } from "react"; | ||
import { DEFAULT_SWAP_APP_ID } from "../consts"; | ||
|
||
export function useSwapLiveAppManifest() { | ||
const ptxSwapCoreExperiment = useFeature("ptxSwapCoreExperiment"); | ||
|
||
const manifestIdToUse = useMemo(() => { | ||
return ptxSwapCoreExperiment?.enabled && ptxSwapCoreExperiment.params?.manifest_id | ||
? ptxSwapCoreExperiment.params?.manifest_id | ||
: DEFAULT_SWAP_APP_ID; | ||
}, [ptxSwapCoreExperiment?.enabled, ptxSwapCoreExperiment?.params?.manifest_id]); | ||
|
||
const localManifest: LiveAppManifest | undefined = useLocalLiveAppManifest(manifestIdToUse); | ||
const remoteManifest: LiveAppManifest | undefined = useRemoteLiveAppManifest(manifestIdToUse); | ||
|
||
return !localManifest ? remoteManifest : localManifest; | ||
} |
Oops, something went wrong.