Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
2a3cd79
caip
kaladinlight Dec 2, 2025
b6e9a96
env vars
kaladinlight Dec 2, 2025
f2ce149
base types
kaladinlight Dec 2, 2025
5bf8fa9
utils
kaladinlight Dec 2, 2025
7cedb30
unchained client
kaladinlight Dec 2, 2025
673c233
chain adapters
kaladinlight Dec 2, 2025
c748405
plugin
kaladinlight Dec 2, 2025
f7b65f1
feature flag
kaladinlight Dec 2, 2025
b0e8272
asset gen
kaladinlight Dec 2, 2025
da72ee2
missing pieces
kaladinlight Dec 3, 2025
8c077a3
Merge branch 'develop' into zcash-support
kaladinlight Dec 3, 2025
b0eb6a7
more asset gen
kaladinlight Dec 4, 2025
2bbdfa2
missing chain id checks
kaladinlight Dec 4, 2025
c52f1ca
zcash coinselect logic
kaladinlight Dec 4, 2025
fe561c7
bump hdwallet alpha
kaladinlight Dec 4, 2025
8d2fc46
lint and test
kaladinlight Dec 4, 2025
0bdcd46
one more missing ref
kaladinlight Dec 4, 2025
6bf2fe0
Merge branch 'develop' into zcash-support
kaladinlight Dec 4, 2025
3990a50
asset migration
kaladinlight Dec 4, 2025
be67b7b
return best guess fee
kaladinlight Dec 4, 2025
9947833
Merge remote-tracking branch 'origin/develop' into zcash-support
gomesalexandre Dec 5, 2025
7e6b85e
chore: bump hdwallet packages to 1.62.23
gomesalexandre Dec 5, 2025
677a96b
chore: regenerate assets after develop merge
gomesalexandre Dec 5, 2025
0c0c37a
remove old unused patch
kaladinlight Dec 5, 2025
17d5826
feat: mayachain zcash support
kaladinlight Dec 5, 2025
1fec5c5
Merge branch 'develop' into mayachain-zcash
gomesalexandre Dec 5, 2025
502261e
fix inbound address halted check
kaladinlight Dec 5, 2025
461f60b
Merge branch 'mayachain-zcash' of github.com:shapeshift/web into maya…
kaladinlight Dec 5, 2025
777735e
update zip-317 fee calc to handle input/output size (including op ret…
kaladinlight Dec 5, 2025
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
27 changes: 21 additions & 6 deletions packages/chain-adapters/src/utxo/utxoSelect/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import split from 'coinselect/split'

const ZCASH_MARGINAL_FEE = 5000
const ZCASH_GRACE_ACTIONS = 2
const ZCASH_P2PKH_STANDARD_INPUT_SIZE = 150
const ZCASH_P2PKH_STANDARD_OUTPUT_SIZE = 34
const TX_OUTPUT_BASE = 9

export type UTXOSelectInput = {
utxos: unchained.bitcoin.Utxo[]
Expand Down Expand Up @@ -90,8 +93,11 @@ export const utxoSelect = (input: UTXOSelectInput) => {
return { ...result, outputs: result.outputs?.filter(o => !o.script) }
}

const calculateZip317Fee = (numInputs: number, numOutputs: number): number => {
const logicalActions = Math.max(numInputs, numOutputs)
// https://zips.z.cash/zip-0317
const calculateZip317Fee = (txInTotalSize: number, txOutTotalSize: number): number => {
const inputActions = Math.ceil(txInTotalSize / ZCASH_P2PKH_STANDARD_INPUT_SIZE)
const outputActions = Math.ceil(txOutTotalSize / ZCASH_P2PKH_STANDARD_OUTPUT_SIZE)
const logicalActions = Math.max(inputActions, outputActions)
return ZCASH_MARGINAL_FEE * Math.max(ZCASH_GRACE_ACTIONS, logicalActions)
}

Expand All @@ -104,9 +110,16 @@ const coinSelectZcash = (
value: number
}
> => {
const opReturnOutputSize = extraOutput[0]?.script
? TX_OUTPUT_BASE + extraOutput[0].script.length
: 0

if (input.sendMax) {
const numOutputs = 1 + (input.opReturnData ? 1 : 0)
const feeWithoutChange = calculateZip317Fee(utxos.length, numOutputs)
const txInTotalSize = utxos.length * ZCASH_P2PKH_STANDARD_INPUT_SIZE
const txOutTotalSize = ZCASH_P2PKH_STANDARD_OUTPUT_SIZE + opReturnOutputSize

const feeWithoutChange = calculateZip317Fee(txInTotalSize, txOutTotalSize)

const totalIn = utxos.reduce((sum, { value }) => sum + value, 0)
const remainder = totalIn - feeWithoutChange

Expand All @@ -126,8 +139,10 @@ const coinSelectZcash = (
inputs.push(utxo)
totalIn += utxo.value

const numOutputs = 2 + (input.opReturnData ? 1 : 0)
feeWithChange = calculateZip317Fee(inputs.length, numOutputs)
const txInTotalSize = inputs.length * ZCASH_P2PKH_STANDARD_INPUT_SIZE
const txOutTotalSize = 2 * ZCASH_P2PKH_STANDARD_OUTPUT_SIZE + opReturnOutputSize

feeWithChange = calculateZip317Fee(txInTotalSize, txOutTotalSize)

if (totalIn >= Number(input.value) + feeWithChange) {
const remainder = totalIn - Number(input.value) - feeWithChange
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@
"ETH.USDT-0XDAC17F958D2EE523A2206206994597C13D831EC7": "eip155:1/erc20:0xdac17f958d2ee523a2206206994597c13d831ec7",
"ETH.WSTETH-0X7F39C581F595B53C5CB19BD0B3F8DA6C935E2CA0": "eip155:1/erc20:0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0",
"THOR.RUNE": "cosmos:thorchain-1/slip44:931",
"ZEC.ZEC": "bip122:00040fe8ec8471911baa1db1266ea15d/slip44:133",
"MAYA.CACAO": "cosmos:mayachain-mainnet-v1/slip44:931"
}
3 changes: 3 additions & 0 deletions scripts/generateTradableAssetMap/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
thorchainChainId,
toAssetId,
tronChainId,
zecChainId,
} from '@shapeshiftoss/caip'
import type { ThornodePoolResponse } from '@shapeshiftoss/swapper'
import { KnownChainIds } from '@shapeshiftoss/types'
Expand All @@ -36,6 +37,7 @@ enum Chain {
LTC = 'LTC',
THOR = 'THOR',
TRON = 'TRON',
ZEC = 'ZEC',
}

const chainToChainId: Record<Chain, ChainId> = {
Expand All @@ -52,6 +54,7 @@ const chainToChainId: Record<Chain, ChainId> = {
[Chain.LTC]: ltcChainId,
[Chain.THOR]: thorchainChainId,
[Chain.TRON]: tronChainId,
[Chain.ZEC]: zecChainId,
}

const getFeeAssetFromChain = (chain: Chain): AssetId => {
Expand Down
4 changes: 2 additions & 2 deletions src/lib/utils/thorchain/hooks/useSendThorTx.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { fromAccountId, fromAssetId, thorchainAssetId } from '@shapeshiftoss/cai
import type { FeeDataEstimate } from '@shapeshiftoss/chain-adapters'
import { CONTRACT_INTERACTION, FeeDataKey } from '@shapeshiftoss/chain-adapters'
import { isTrezor } from '@shapeshiftoss/hdwallet-trezor'
import { assertAndProcessMemo, depositWithExpiry } from '@shapeshiftoss/swapper'
import { assertAndProcessMemo, depositWithExpiry, SwapperName } from '@shapeshiftoss/swapper'
import type { KnownChainIds } from '@shapeshiftoss/types'
import { isToken } from '@shapeshiftoss/utils'
import { useQuery } from '@tanstack/react-query'
Expand Down Expand Up @@ -127,7 +127,7 @@ export const useSendThorTx = ({
const { data: inboundAddressData } = useQuery({
...reactQueries.thornode.inboundAddresses(),
staleTime: 60_000,
select: data => selectInboundAddressData(data, assetId),
select: data => selectInboundAddressData(data, assetId, SwapperName.Thorchain),
enabled: Boolean(assetId && assetId !== thorchainAssetId),
})

Expand Down
4 changes: 2 additions & 2 deletions src/pages/Lending/Pool/components/Repay/RepayConfirm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import type { AccountId, AssetId } from '@shapeshiftoss/caip'
import { fromAssetId } from '@shapeshiftoss/caip'
import { isEvmChainId } from '@shapeshiftoss/chain-adapters'
import { isLedger } from '@shapeshiftoss/hdwallet-ledger'
import { assertAndProcessMemo } from '@shapeshiftoss/swapper'
import { assertAndProcessMemo, SwapperName } from '@shapeshiftoss/swapper'
import type { Asset } from '@shapeshiftoss/types'
import { TxStatus } from '@shapeshiftoss/unchained-client'
import { isToken } from '@shapeshiftoss/utils'
Expand Down Expand Up @@ -244,7 +244,7 @@ export const RepayConfirm = ({
const { data: inboundAddressData, isLoading: isInboundAddressLoading } = useQuery({
...reactQueries.thornode.inboundAddresses(),
staleTime: 60_000,
select: data => selectInboundAddressData(data, repaymentAsset?.assetId),
select: data => selectInboundAddressData(data, repaymentAsset?.assetId, SwapperName.Thorchain),
enabled: !!repaymentAsset?.assetId,
})

Expand Down
4 changes: 2 additions & 2 deletions src/pages/Lending/Pool/components/Repay/RepayInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
} from '@chakra-ui/react'
import type { AccountId, AssetId } from '@shapeshiftoss/caip'
import { fromAccountId } from '@shapeshiftoss/caip'
import { assertAndProcessMemo } from '@shapeshiftoss/swapper'
import { assertAndProcessMemo, SwapperName } from '@shapeshiftoss/swapper'
import type { Asset } from '@shapeshiftoss/types'
import { TxStatus } from '@shapeshiftoss/unchained-client'
import { useQuery } from '@tanstack/react-query'
Expand Down Expand Up @@ -131,7 +131,7 @@ export const RepayInput = ({
const { data: inboundAddressData, isLoading: isInboundAddressLoading } = useQuery({
...reactQueries.thornode.inboundAddresses(),
staleTime: 60_000,
select: data => selectInboundAddressData(data, repaymentAsset?.assetId),
select: data => selectInboundAddressData(data, repaymentAsset?.assetId, SwapperName.Thorchain),
enabled: !!repaymentAsset?.assetId,
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ export const TransactionRow: React.FC<TransactionRowProps> = ({
// halted by the time the user clicked the confirm button
// But we still have some sane 60s stale time rather than 0 for paranoia's sake, as a balance of safety and not overfetching
staleTime: 60_000,
select: data => selectInboundAddressData(data, assetId),
select: data => selectInboundAddressData(data, assetId, SwapperName.Thorchain),
enabled: !!assetId,
})

Expand Down
6 changes: 5 additions & 1 deletion src/pages/ThorChainLP/queries/hooks/usePools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,11 @@ export const usePools = () => {
const assetId = thorPoolAssetIdToAssetId(pool.asset)
if (!assetId) return acc

const inboundAddressesResponse = selectInboundAddressData(inboundAddressesData ?? [], assetId)
const inboundAddressesResponse = selectInboundAddressData(
inboundAddressesData ?? [],
assetId,
SwapperName.Thorchain,
)

const isTradingActive = selectIsTradingActive({
assetId,
Expand Down
2 changes: 1 addition & 1 deletion src/react-queries/hooks/useIsTradingActive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export const useIsTradingActive = ({
refetchOnMount: true,
}
: {}),
select: data => selectInboundAddressData(data, assetId),
select: data => selectInboundAddressData(data, assetId, swapperName),
})

const {
Expand Down
14 changes: 13 additions & 1 deletion src/react-queries/selectors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { AssetId } from '@shapeshiftoss/caip'
import type { evm } from '@shapeshiftoss/chain-adapters'
import type { InboundAddressResponse } from '@shapeshiftoss/swapper'
import {
assetIdToMayaPoolAssetId,
assetIdToThorPoolAssetId,
isNativeAsset,
isRuji,
Expand All @@ -18,10 +19,21 @@ import type { ThorchainMimir } from '@/lib/utils/thorchain/types'
export const selectInboundAddressData = (
data: InboundAddressResponse[],
assetId: AssetId | undefined,
swapperName: SwapperName,
): InboundAddressResponse | undefined => {
if (!assetId) throw new Error(`AssetId is required: ${assetId}`)

const assetPoolId = assetIdToThorPoolAssetId({ assetId })
const assetPoolId = (() => {
switch (swapperName) {
case SwapperName.Thorchain:
return assetIdToThorPoolAssetId({ assetId })
case SwapperName.Mayachain:
return assetIdToMayaPoolAssetId({ assetId })
default:
throw new Error(`Invalid swapper name: ${swapperName}`)
}
})()

const assetChainSymbol = assetPoolId?.slice(0, assetPoolId.indexOf('.'))

return data.find(inbound => inbound.chain === assetChainSymbol)
Expand Down
6 changes: 5 additions & 1 deletion src/state/apis/swapper/helpers/swapperApiHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,11 @@ export const checkTradingActivity = async (
gcTime: 0,
})

const inboundAddressResponse = selectInboundAddressData(inboundAddresses, assetId)
const inboundAddressResponse = selectInboundAddressData(
inboundAddresses,
assetId,
swapperName,
)

const mimir = await queryClient.fetchQuery({
...getMimirQuery(chainId),
Expand Down