Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,16 @@ import generatedTradableAssetMap from '../../generated/generatedTradableAssetMap

const thorPoolIdAssetIdSymbolMap = generatedTradableAssetMap as Record<string, AssetId>

export const assetIdToThorPoolAssetIdMap = invert(thorPoolIdAssetIdSymbolMap)
export const assetIdToThorPoolAssetIdMap: Record<string, string> = invert(
thorPoolIdAssetIdSymbolMap,
)

const assetIdToThorPoolAssetIdMapLower: Record<string, string> = Object.fromEntries(
Object.entries(assetIdToThorPoolAssetIdMap).map(([k, v]) => [k.toLowerCase(), v]),
)

export const thorPoolAssetIdToAssetId = (id: string): AssetId | undefined =>
thorPoolIdAssetIdSymbolMap[id.toUpperCase()]

export const assetIdToThorPoolAssetId = ({ assetId }: { assetId: AssetId }): string | undefined =>
assetIdToThorPoolAssetIdMap[assetId]
assetIdToThorPoolAssetIdMapLower[assetId.toLowerCase()]
3 changes: 3 additions & 0 deletions packages/swapper/src/thorchain-utils/solana/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Solana Memo Program v2
// https://spl.solana.com/memo
export const MEMO_PROGRAM_ID = 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'
47 changes: 34 additions & 13 deletions src/components/Modals/Send/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@ import type {
GetFeeDataInput,
} from '@shapeshiftoss/chain-adapters'
import { utxoChainIds } from '@shapeshiftoss/chain-adapters'
import type { HDWallet } from '@shapeshiftoss/hdwallet-core'
import type { HDWallet, SolanaTxInstruction } from '@shapeshiftoss/hdwallet-core'
import { isGridPlus, supportsETH, supportsSolana } from '@shapeshiftoss/hdwallet-core/wallet'
import { isLedger } from '@shapeshiftoss/hdwallet-ledger'
import { isTrezor } from '@shapeshiftoss/hdwallet-trezor'
import type { CosmosSdkChainId, EvmChainId, KnownChainIds, UtxoChainId } from '@shapeshiftoss/types'
import { contractAddressOrUndefined } from '@shapeshiftoss/utils'
import { PublicKey, TransactionInstruction } from '@solana/web3.js'

import type { SendInput } from './Form'

Expand Down Expand Up @@ -59,6 +60,8 @@ export type EstimateFeesInput = {
contractAddress: string | undefined
}

const SOLANA_MEMO_PROGRAM_ID = 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'

export const estimateFees = async ({
amountCryptoPrecision,
assetId,
Expand Down Expand Up @@ -112,15 +115,26 @@ export const estimateFees = async ({
case CHAIN_NAMESPACE.Solana: {
const adapter = assertGetSolanaChainAdapter(asset.chainId)

const memoInstruction: TransactionInstruction | undefined = memo
? new TransactionInstruction({
keys: [],
programId: new PublicKey(SOLANA_MEMO_PROGRAM_ID),
data: Buffer.from(memo, 'utf8'),
})
: undefined

// For SPL transfers, build complete instruction set including compute budget
// For SOL transfers (pure sends i.e not e.g a Jup swap), pass no instructions to get 0 count (avoids blind signing)
// For SOL transfers with memo (e.g. THORChain), pass memo instruction for accurate fee estimation
// For pure SOL transfers, pass no instructions to get 0 count (avoids blind signing)
const instructions = contractAddress
? await adapter.buildEstimationInstructions({
from: account,
to,
tokenId: contractAddress,
value,
})
: memoInstruction
? [memoInstruction]
: undefined

const getFeeDataInput: GetFeeDataInput<KnownChainIds.SolanaMainnet> = {
Expand Down Expand Up @@ -376,29 +390,36 @@ export const handleSendWithMetadata = async ({

const solanaAdapter = assertGetSolanaChainAdapter(chainId)
const { account } = fromAccountId(sendInput.accountId)
const instructions = await solanaAdapter.buildEstimationInstructions({

const memoInstruction: SolanaTxInstruction | undefined = memo
? { keys: [], programId: SOLANA_MEMO_PROGRAM_ID, data: Buffer.from(memo, 'utf8') }
: undefined

const estimationInstructions = await solanaAdapter.buildEstimationInstructions({
from: account,
to,
tokenId: contractAddress,
value,
})

const shouldAddComputeBudget = estimationInstructions.length > 1 || Boolean(memoInstruction)

const input: BuildSendTxInput<KnownChainIds.SolanaMainnet> = {
to,
value,
wallet,
accountNumber: bip44Params.accountNumber,
pubKey: skipDeviceDerivation ? fromAccountId(sendInput.accountId).account : undefined,
chainSpecific:
instructions.length <= 1
? {
tokenId: contractAddress,
}
: {
tokenId: contractAddress,
computeUnitLimit: fees.chainSpecific.computeUnits,
computeUnitPrice: fees.chainSpecific.priorityFee,
},
chainSpecific: shouldAddComputeBudget
? {
tokenId: contractAddress,
computeUnitLimit: fees.chainSpecific.computeUnits,
computeUnitPrice: fees.chainSpecific.priorityFee,
instructions: memoInstruction ? [memoInstruction] : undefined,
}
: {
tokenId: contractAddress,
},
}

return solanaAdapter.buildSendTransaction(input)
Expand Down
8 changes: 7 additions & 1 deletion src/lib/utils/thorchain/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
fromAccountId,
fromAssetId,
rujiAssetId,
solanaChainId,
tcyAssetId,
thorchainChainId,
tronChainId,
Expand Down Expand Up @@ -308,7 +309,12 @@ export const getThorchainTransactionType = (chainId: ChainId) => {
if (supportedEvmChainIds.includes(chainId as KnownChainIds)) {
return 'EvmCustomTx'
}
if (isUtxoChainId(chainId) || chainId === cosmosChainId || chainId === tronChainId) {
if (
isUtxoChainId(chainId) ||
chainId === cosmosChainId ||
chainId === tronChainId ||
chainId === solanaChainId
) {
return 'Send'
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1673,6 +1673,7 @@ export const AddLiquidityInput: React.FC<AddLiquidityInputProps> = ({
{maybeOpportunityNotSupportedExplainer}
{maybeAlert}
<ButtonWalletPredicate
data-testid='lp-deposit-button'
isValidWallet={Boolean(walletSupportsOpportunity)}
mx={-2}
size='lg'
Expand Down
16 changes: 11 additions & 5 deletions src/pages/ThorChainLP/components/LpType.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,13 @@ const TypeLabel: React.FC<{ assetIds: AssetId[] }> = ({ assetIds }) => {
)
}

const TypeRadio: React.FC<RadioProps> = props => {
const { getInputProps, getRadioProps } = useRadio(props)
const TypeRadio: React.FC<RadioProps & { 'data-testid'?: string }> = props => {
const { 'data-testid': dataTestId, ...radioProps } = props
const { getInputProps, getRadioProps } = useRadio(radioProps)
const input = getInputProps()
const checkbox = getRadioProps()
return (
<Box width='33.33%' flex={1} cursor='pointer' as='label'>
<Box width='33.33%' flex={1} cursor='pointer' as='label' data-testid={dataTestId}>
<input {...input} />
<Box
bg='background.surface.raised.base'
Expand Down Expand Up @@ -280,7 +281,12 @@ export const LpType = ({
})()

return (
<TypeRadio key={`type-${index}`} {...radio} isDisabled={isDisabled}>
<TypeRadio
key={`type-${index}`}
{...radio}
isDisabled={isDisabled}
data-testid={`lp-type-${option.value}`}
>
<Tooltip
isDisabled={
(!isDisabled && !isDeposit) || (isDeposit && !currentSideBalances.tooltipText)
Expand Down Expand Up @@ -335,7 +341,7 @@ export const LpType = ({

const group = getRootProps()
return (
<Flex px={4} gap={2} {...group}>
<Flex px={4} gap={2} {...group} data-testid='lp-type-selector'>
{radioOptions}
</Flex>
)
Expand Down
Loading