diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e3e1973a..ea710326 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,7 +36,7 @@ jobs: run: npm run lint - name: Typecheck - run: yarn typecheck + run: yarn check - name: Build all apps run: npm run build diff --git a/apps/subbridge/components/BridgeBody/Action/EvmAction.tsx b/apps/subbridge/components/BridgeBody/Action/EvmAction.tsx index 4a8e6507..f04b2ff2 100644 --- a/apps/subbridge/components/BridgeBody/Action/EvmAction.tsx +++ b/apps/subbridge/components/BridgeBody/Action/EvmAction.tsx @@ -120,7 +120,7 @@ const EvmAction: FC<{onConfirm: () => void}> = ({onConfirm}) => { mx: 'auto', }} > - {needApproval && ( + {needApproval && bridgeErrorMessage == null && ( void}> = ({onConfirm}) => { disabled={(!approved && needApproval) || bridgeErrorMessage != null} onClick={onConfirm} > - {(approved || !needApproval) && bridgeErrorMessage != null - ? bridgeErrorMessage - : 'Transfer'} + {bridgeErrorMessage != null ? bridgeErrorMessage : 'Transfer'} ) diff --git a/apps/subbridge/components/BridgeBody/index.tsx b/apps/subbridge/components/BridgeBody/index.tsx index 1ff2f085..91360417 100644 --- a/apps/subbridge/components/BridgeBody/index.tsx +++ b/apps/subbridge/components/BridgeBody/index.tsx @@ -51,7 +51,7 @@ const BridgeBody: FC = (props) => { ) const destinationAccount = useAtomValue(destinationAccountAtom) const bridgeLimit = useBridgeLimit() - const bridgeInfo = useAtomValue(bridgeInfoAtom) + const bridge = useAtomValue(bridgeInfoAtom) const handleSelectFromChain = (newFromChainId: ChainId): void => { setFromChain(newFromChainId) @@ -150,8 +150,7 @@ const BridgeBody: FC = (props) => { {toChain.kind !== 'evm' && ( )} - {(bridgeInfo.kind === 'evmSygma' || - bridgeInfo.kind === 'phalaSygma') && + {(bridge.kind === 'evmSygma' || bridge.kind === 'phalaSygma') && Boolean(amount) && bridgeLimit != null && new Decimal(amount).gt(bridgeLimit) && ( @@ -160,8 +159,7 @@ const BridgeBody: FC = (props) => { - {(bridgeInfo.kind === 'evmSygma' || - bridgeInfo.kind === 'phalaSygma') && ( + {(bridge.kind === 'evmSygma' || bridge.kind === 'phalaSygma') && ( )} diff --git a/apps/subbridge/config/asset.ts b/apps/subbridge/config/asset.ts index e788c861..8a1cdc86 100644 --- a/apps/subbridge/config/asset.ts +++ b/apps/subbridge/config/asset.ts @@ -65,6 +65,7 @@ export interface Asset { } destChainTransactionFee: Partial> existentialDeposit: Partial> + sygmaResourceId?: string } export const ASSETS: Readonly> = { @@ -129,6 +130,8 @@ export const ASSETS: Readonly> = { phala: '5EYCAe5jLbHcAAMKvLFSXgCTbPrLgBJusvPwfKcaKzuf5X5e', khala: '5EYCAe5jLbHcAAMKvLFSXgCTbPrLgBJusvPwfKcaKzuf5X5e', }, + sygmaResourceId: + '0x0000000000000000000000000000000000000000000000000000000000000001', }, movr: { id: 'movr', diff --git a/apps/subbridge/config/bridge.ts b/apps/subbridge/config/bridge.ts index 8e5fdb6a..e5f30383 100644 --- a/apps/subbridge/config/bridge.ts +++ b/apps/subbridge/config/bridge.ts @@ -14,7 +14,7 @@ type AssetsConfig = Array<{ assetId: AssetId estimatedTime: string kind: BridgeKind - isThroughKhala?: boolean + proxy?: ChainId disabled?: boolean }> @@ -68,7 +68,7 @@ const moonriverToBifrostAssets: AssetsConfig = [ assetId: 'zlk', estimatedTime: '< 3 mins', kind: 'evmChainBridge', - isThroughKhala: true, + proxy: 'khala', }, ] @@ -77,7 +77,7 @@ const bifrostToMoonriverAssets: AssetsConfig = [ assetId: 'zlk', estimatedTime: '~ 5 mins', kind: 'polkadotXTokens', - isThroughKhala: true, + proxy: 'khala', }, ] @@ -93,6 +93,10 @@ export const BRIDGES: Readonly = [ id: 'khala', assets: [{assetId: 'pha', estimatedTime: '~ 5 mins', kind: 'evmSygma'}], }, + { + id: 'astar', + assets: [{assetId: 'pha', estimatedTime: '~ 5 mins', kind: 'evmSygma'}], + }, ], }, { @@ -340,6 +344,17 @@ export const BRIDGES: Readonly = [ {assetId: 'pha', estimatedTime: '< 1 min', kind: 'polkadotXcm'}, ], }, + { + id: 'ethereum', + assets: [ + { + assetId: 'pha', + estimatedTime: '~ 5 mins', + kind: 'polkadotXcm', + proxy: 'phala', + }, + ], + }, ], }, { @@ -363,5 +378,5 @@ export const BRIDGES: Readonly = [ ] export const ALL_FROM_CHAINS = BRIDGES.filter((bridge) => - bridge.toChains.some((x) => x.assets.length > 0) + bridge.toChains.some((x) => x.assets.length > 0), ).map((bridge) => bridge.fromChain) diff --git a/apps/subbridge/hooks/useBridgeFee.ts b/apps/subbridge/hooks/useBridgeFee.ts index 5b38c27c..32ece94d 100644 --- a/apps/subbridge/hooks/useBridgeFee.ts +++ b/apps/subbridge/hooks/useBridgeFee.ts @@ -10,7 +10,7 @@ import Decimal from 'decimal.js' import {useAtomValue} from 'jotai' import useSWR from 'swr' import {useEthersWeb3Provider} from './useEthersProvider' -import {useCurrentPolkadotApi} from './usePolkadotApi' +import {useCurrentPolkadotApi, usePolkadotApi} from './usePolkadotApi' const ALICE = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY' const zlkChainBridgeFee = new Decimal('0.25') @@ -21,13 +21,17 @@ export const useBridgeFee = (): Decimal | undefined => { const asset = useAtomValue(assetAtom) const bridge = useAtomValue(bridgeInfoAtom) const api = useCurrentPolkadotApi() + const phalaApi = usePolkadotApi('phala') const provider = useEthersWeb3Provider() const evmAccount = useAtomValue(evmAccountAtom) - const isTransferringZlkThroughChainBridge = + const isTransferringZlkProxiedByChainBridge = fromChain.kind === 'polkadot' && toChain.kind === 'evm' && asset.id === 'zlk' + const isProxiedByPhalaSygma = + bridge.proxy === 'phala' && toChain.id === 'ethereum' + const {data: evmSygmaFee} = useSWR( bridge.kind === 'evmSygma' && provider != null && @@ -67,6 +71,22 @@ export const useBridgeFee = (): Decimal | undefined => { }, ) + const {data: phalaProxySygmaFee} = useSWR( + isProxiedByPhalaSygma && + phalaApi != null && [phalaApi, 'phalaProxySygmaFee'], + async ([api]) => { + const fee = await api.query.sygmaBasicFeeHandler.assetFees([ + 1, + {concrete: {parents: 0, interior: 'Here'}}, + ]) + return new Decimal(fee.toString()).div(Decimal.pow(10, 12)) + }, + ) + + if (isProxiedByPhalaSygma) { + return phalaProxySygmaFee + } + if (bridge.kind === 'phalaSygma') { return phalaSygmaFee } @@ -75,7 +95,7 @@ export const useBridgeFee = (): Decimal | undefined => { return evmSygmaFee } - if (isTransferringZlkThroughChainBridge) { + if (isTransferringZlkProxiedByChainBridge) { return zlkChainBridgeFee } diff --git a/apps/subbridge/hooks/useBridgeLimit.ts b/apps/subbridge/hooks/useBridgeLimit.ts index e7dbd208..8c43d3ee 100644 --- a/apps/subbridge/hooks/useBridgeLimit.ts +++ b/apps/subbridge/hooks/useBridgeLimit.ts @@ -46,7 +46,9 @@ export const useBridgeLimit = (): Decimal | undefined => { ((fromChain.id === 'moonriver' || toChain.id === 'moonriver') && asset.id === 'zlk') || bridge.kind === 'evmSygma' || - bridge.kind === 'phalaSygma' + bridge.kind === 'phalaSygma' || + (toChain.id === 'ethereum' && + (bridge.proxy === 'phala' || bridge.proxy === 'khala')) const {data: moonriverReservedZlk} = useSWR( toChain.id === 'moonriver' && @@ -75,9 +77,11 @@ export const useBridgeLimit = (): Decimal | undefined => { const {data: ethereumReservedPha} = useSWR( ethereumPhaContract != null && + (bridge.kind === 'phalaSygma' || + bridge.proxy === 'phala' || + bridge.proxy === 'khala') && toChain.id === 'ethereum' && - asset.id === 'pha' && - bridge.kind === 'phalaSygma' && [ + asset.id === 'pha' && [ ethereumPhaContract, asset.reservedAddress?.ethereum, asset.decimals.ethereum ?? asset.decimals.default, @@ -88,7 +92,7 @@ export const useBridgeLimit = (): Decimal | undefined => { const {data: phalaReservedPha} = useSWR( phalaApi != null && - toChain.id === 'phala' && + (toChain.id === 'phala' || bridge.proxy === 'phala') && asset.id === 'pha' && fromChain.id === 'ethereum' && [phalaApi, asset.reservedAddress?.phala], polkadotAvailableBalanceFetcher, diff --git a/apps/subbridge/hooks/useEstimatedGasFee.ts b/apps/subbridge/hooks/useEstimatedGasFee.ts index 2e03ad7d..a14b9717 100644 --- a/apps/subbridge/hooks/useEstimatedGasFee.ts +++ b/apps/subbridge/hooks/useEstimatedGasFee.ts @@ -40,7 +40,7 @@ export const useEstimatedGasFee = (): Decimal | undefined => { toChain.id === 'phala' || toChain.id === 'thala' ? toChain.id : 'khala', ) const polkadotApi = useCurrentPolkadotApi() - const {kind: bridgeKind, isThroughKhala} = useAtomValue(bridgeInfoAtom) + const bridge = useAtomValue(bridgeInfoAtom) const resourceId = typeof asset.chainBridgeResourceId === 'string' ? asset.chainBridgeResourceId @@ -51,7 +51,7 @@ export const useEstimatedGasFee = (): Decimal | undefined => { ethersGasPriceFetcher, ) const {data: evmChainBridgeEstimatedGas} = useSWR( - bridgeKind === 'evmChainBridge' && + bridge.kind === 'evmChainBridge' && ethersChainBridgeContract != null && khalaApi != null && resourceId != null && [ @@ -64,7 +64,7 @@ export const useEstimatedGasFee = (): Decimal | undefined => { ) const {data: evmXTokensEstimatedGas} = useSWR( - bridgeKind === 'evmXTokens' && + bridge.kind === 'evmXTokens' && ethersXTokensContract != null && toChain.paraId != null && asset.xc20Address?.[fromChain.id] != null && [ @@ -77,7 +77,7 @@ export const useEstimatedGasFee = (): Decimal | undefined => { ) const {data: evmSygmaEstimatedGas} = useSWR( - bridgeKind === 'evmSygma' && + bridge.kind === 'evmSygma' && ethersWeb3Provider != null && evmAccount != null && [ ethersWeb3Provider, @@ -105,32 +105,38 @@ export const useEstimatedGasFee = (): Decimal | undefined => { ) const {data: xTokensPartialFee} = useSWR( - bridgeKind === 'polkadotXTokens' && + bridge.kind === 'polkadotXTokens' && polkadotApi != null && [ polkadotApi, fromChain.id, toChain.id, asset.id, - isThroughKhala, + bridge.proxy, ], xTokensPartialFeeFetcher, ) const {data: phalaPartialFee} = useSWR( - (bridgeKind === 'phalaChainBridge' || bridgeKind === 'phalaSygma') && + (bridge.kind === 'phalaChainBridge' || bridge.kind === 'phalaSygma') && polkadotApi != null && [ polkadotApi, fromChain.id, toChain.id, asset.id, - bridgeKind, + bridge.kind, ], phalaXTransferPartialFeeFetcher, ) const {data: polkadotXcmPartialFee} = useSWR( - bridgeKind === 'polkadotXcm' && - polkadotApi != null && [polkadotApi, fromChain.id, toChain.id, asset.id], + bridge.kind === 'polkadotXcm' && + polkadotApi != null && [ + polkadotApi, + fromChain.id, + toChain.id, + asset.id, + bridge.proxy, + ], polkadotXcmTransferPartialFeeFetcher, ) diff --git a/apps/subbridge/hooks/useTransfer.ts b/apps/subbridge/hooks/useTransfer.ts index 3ce46a7e..dd176cbb 100644 --- a/apps/subbridge/hooks/useTransfer.ts +++ b/apps/subbridge/hooks/useTransfer.ts @@ -46,7 +46,7 @@ export const useTransfer = (): (({ const decimals = useAtomValue(decimalsAtom) const evmAccount = useAtomValue(evmAccountAtom) const polkadotAccount = useAtomValue(polkadotAccountAtom) - const {kind: bridgeKind, isThroughKhala} = useAtomValue(bridgeInfoAtom) + const bridge = useAtomValue(bridgeInfoAtom) const khalaApi = usePolkadotApi( toChain.id === 'phala' || toChain.id === 'thala' ? toChain.id : 'khala', ) @@ -61,7 +61,7 @@ export const useTransfer = (): (({ ) return async ({onReady}: {onReady: () => void}) => { - if (bridgeKind === 'evmChainBridge') { + if (bridge.kind === 'evmChainBridge') { if (ethersChainBridgeContract == null || khalaApi == null) { throw new Error('Transfer missing required parameters') } @@ -79,7 +79,7 @@ export const useTransfer = (): (({ }) } - if (bridgeKind === 'polkadotXTokens') { + if (bridge.kind === 'polkadotXTokens') { if (polkadotApi == null || polkadotAccount?.wallet?.signer == null) { throw new Error('Transfer missing required parameters') } @@ -90,7 +90,7 @@ export const useTransfer = (): (({ fromChainId: fromChain.id, toChainId: toChain.id, destinationAccount, - isThroughKhala, + proxy: bridge.proxy, }) return await waitSignAndSend({ api: polkadotApi, @@ -101,7 +101,7 @@ export const useTransfer = (): (({ }) } - if (bridgeKind === 'phalaChainBridge' || bridgeKind === 'phalaSygma') { + if (bridge.kind === 'phalaChainBridge' || bridge.kind === 'phalaSygma') { if (polkadotApi == null || polkadotAccount?.wallet?.signer == null) { throw new Error('Transfer missing required parameters') } @@ -113,7 +113,7 @@ export const useTransfer = (): (({ amount: rawAmount, destinationAccount, assetId: asset.id, - kind: bridgeKind, + kind: bridge.kind, }) return await waitSignAndSend({ @@ -125,7 +125,7 @@ export const useTransfer = (): (({ }) } - if (bridgeKind === 'evmXTokens') { + if (bridge.kind === 'evmXTokens') { if (ethersXTokensBridgeContract == null) { throw new Error('Transfer missing required parameters') } @@ -144,7 +144,7 @@ export const useTransfer = (): (({ }) } - if (bridgeKind === 'polkadotXcm') { + if (bridge.kind === 'polkadotXcm') { if (polkadotApi == null || polkadotAccount?.wallet?.signer == null) { throw new Error('Transfer missing required parameters') } @@ -155,6 +155,7 @@ export const useTransfer = (): (({ fromChainId: fromChain.id, toChainId: toChain.id, destinationAccount, + proxy: bridge.proxy, }) return await waitSignAndSend({ api: polkadotApi, @@ -165,7 +166,7 @@ export const useTransfer = (): (({ }) } - if (bridgeKind === 'evmSygma') { + if (bridge.kind === 'evmSygma') { if (ethersWeb3Provider == null || evmAccount == null) { throw new Error('Transfer missing required parameters') } diff --git a/apps/subbridge/hooks/useValidation.ts b/apps/subbridge/hooks/useValidation.ts index f46b8169..e4ccc9f8 100644 --- a/apps/subbridge/hooks/useValidation.ts +++ b/apps/subbridge/hooks/useValidation.ts @@ -29,12 +29,12 @@ export const useValidation = (): void => { const destChainTransactionFee = useAtomValue(destChainTransactionFeeAtom) const existentialDeposit = useAtomValue(existentialDepositAtom) const bridgeLimit = useBridgeLimit() - const [bridgeInfo] = useAtom(bridgeInfoAtom) + const [bridge] = useAtom(bridgeInfoAtom) useEffect(() => { let unmounted = false const validate = async (): Promise => { - if (bridgeInfo.disabled) { + if (bridge.disabled) { return 'Disabled' } if (amount.length === 0) { @@ -102,6 +102,6 @@ export const useValidation = (): void => { fromChain.id, toChain.id, asset.id, - bridgeInfo.disabled, + bridge.disabled, ]) } diff --git a/apps/subbridge/lib/createPhalaMultilocation.ts b/apps/subbridge/lib/createPhalaMultilocation.ts new file mode 100644 index 00000000..74d04263 --- /dev/null +++ b/apps/subbridge/lib/createPhalaMultilocation.ts @@ -0,0 +1,24 @@ +import getGeneralKey, {type GeneralKey, type Hex} from './getGeneralKey' + +type Multilocation = [ + {GeneralKey: GeneralKey}, + {GeneralIndex: number}, + {GeneralKey: GeneralKey}, +] + +export const createPhalaMultilocation = ( + kind: 'cb' | 'sygma', + generalIndex: number, + destinationAccount: Hex, +): Multilocation => { + return [ + { + GeneralKey: + kind === 'sygma' + ? getGeneralKey('0x7379676d61') // string "sygma" + : getGeneralKey('0x6362'), // string "cb" + }, + {GeneralIndex: generalIndex}, + {GeneralKey: getGeneralKey(destinationAccount)}, + ] +} diff --git a/apps/subbridge/lib/evmSygma.ts b/apps/subbridge/lib/evmSygma.ts index b5939bcc..6f099583 100644 --- a/apps/subbridge/lib/evmSygma.ts +++ b/apps/subbridge/lib/evmSygma.ts @@ -1,5 +1,5 @@ import {type Asset} from '@/config/asset' -import {type Chain} from '@/config/chain' +import {CHAINS, type Chain} from '@/config/chain' import { type EVMAssetTransfer, type EvmFee, @@ -30,34 +30,32 @@ export const getEvmSygmaTransfer = async ( provider, fromChain.isTest === true ? Environment.TESTNET : Environment.MAINNET, ) - const domains = assetTransfer.config.getDomains() - const resources = assetTransfer.config.getDomainResources() - const erc20Resource = resources.find( - (resource) => resource.symbol === asset.symbol.toUpperCase(), - ) - if (erc20Resource == null) { - throw new Error('Resource not found') - } - const from = domains.find( - (domain) => domain.chainId === fromChain.sygmaChainId, - ) - if (from == null) { - throw new Error(`Network ${fromChain.id} not supported`) + let destinationChainId + let parachainId + + if (toChain.id === 'phala' || toChain.id === 'khala') { + destinationChainId = toChain.sygmaChainId + } else { + parachainId = toChain.paraId + if (toChain.id === 'astar') { + destinationChainId = CHAINS.phala.sygmaChainId + } } - const to = domains.find((domain) => domain.chainId === toChain.sygmaChainId) - if (to == null) { - throw new Error(`Network ${toChain.id} not supported`) + + if (destinationChainId == null || asset.sygmaResourceId == null) { + throw new Error('Chain or asset not supported') } - const transfer: Transfer = { + const transfer = assetTransfer.createFungibleTransfer( sender, - amount: {amount}, - from, - to, - resource: erc20Resource, - recipient: destinationAccount, - } + destinationChainId, + destinationAccount, + asset.sygmaResourceId, + amount, + parachainId, + ) + const fee = await assetTransfer.getFee(transfer) const tx = await assetTransfer.buildTransferTransaction(transfer, fee) diff --git a/apps/subbridge/lib/getGeneralKey.ts b/apps/subbridge/lib/getGeneralKey.ts index 41182b06..6fda80c5 100644 --- a/apps/subbridge/lib/getGeneralKey.ts +++ b/apps/subbridge/lib/getGeneralKey.ts @@ -1,4 +1,8 @@ export type Hex = `0x${string}` +export interface GeneralKey { + length: number + data: Hex +} const getGeneralKey = (hex: Hex): {length: number; data: Hex} => { return { diff --git a/apps/subbridge/lib/polkadotFetcher.ts b/apps/subbridge/lib/polkadotFetcher.ts index 7c3783f2..d43e837f 100644 --- a/apps/subbridge/lib/polkadotFetcher.ts +++ b/apps/subbridge/lib/polkadotFetcher.ts @@ -60,8 +60,8 @@ export const xTokensPartialFeeFetcher = async ([ fromChainId, toChainId, assetId, - isThroughKhala, -]: [ApiPromise, ChainId, ChainId, AssetId, boolean]): Promise => { + proxy, +]: [ApiPromise, ChainId, ChainId, AssetId, ChainId]): Promise => { const {partialFee} = await transferByPolkadotXTokens({ polkadotApi, assetId, @@ -69,7 +69,7 @@ export const xTokensPartialFeeFetcher = async ([ fromChainId, toChainId, destinationAccount: ALICE, - isThroughKhala, + proxy, }).paymentInfo(ALICE) const decimals = polkadotApi.registry.chainDecimals[0] @@ -104,14 +104,17 @@ export const polkadotXcmTransferPartialFeeFetcher = async ([ fromChainId, toChainId, assetId, -]: [ApiPromise, ChainId, ChainId, AssetId]): Promise => { + proxy, +]: [ApiPromise, ChainId, ChainId, AssetId, ChainId]): Promise => { + const toChain = CHAINS[toChainId] const extrinsic = transferByPolkadotXcm({ polkadotApi, amount: '1', - destinationAccount: ALICE, + destinationAccount: toChain.kind === 'polkadot' ? ALICE : BLACK_HOLE, fromChainId, toChainId, assetId, + proxy, }) const decimals = polkadotApi.registry.chainDecimals[0] const {partialFee} = await extrinsic.paymentInfo(ALICE) diff --git a/apps/subbridge/lib/transferByPhalaXTransfer.ts b/apps/subbridge/lib/transferByPhalaXTransfer.ts index 50c20af0..89804fae 100644 --- a/apps/subbridge/lib/transferByPhalaXTransfer.ts +++ b/apps/subbridge/lib/transferByPhalaXTransfer.ts @@ -136,9 +136,9 @@ export const transferByPhalaXTransfer = ({ throw new Error(`Unsupported asset: ${assetId}`) } - const isThroughChainBridge = isTransferringZLKToMoonriver + const isChainBridge = isTransferringZLKToMoonriver - if ((isThroughChainBridge || isSygma) && typeof generalIndex !== 'number') { + if ((isChainBridge || isSygma) && typeof generalIndex !== 'number') { throw new Error('Transfer missing required parameters') } @@ -148,9 +148,9 @@ export const transferByPhalaXTransfer = ({ fun: {Fungible: amount}, }, { - parents: isThroughChainBridge || isSygma ? 0 : 1, + parents: isChainBridge || isSygma ? 0 : 1, interior: - isThroughChainBridge || isSygma + isChainBridge || isSygma ? { X3: [ { @@ -175,7 +175,7 @@ export const transferByPhalaXTransfer = ({ ], }, }, - isThroughChainBridge || isSygma + isChainBridge || isSygma ? null // No need to specify a certain weight if transfer will not through XCM : {refTime: '6000000000', proofSize: '1000000'}, ) diff --git a/apps/subbridge/lib/transferByPolkadotXTokens.ts b/apps/subbridge/lib/transferByPolkadotXTokens.ts index 2897c816..405a0481 100644 --- a/apps/subbridge/lib/transferByPolkadotXTokens.ts +++ b/apps/subbridge/lib/transferByPolkadotXTokens.ts @@ -5,9 +5,8 @@ import type {SubmittableExtrinsic} from '@polkadot/api/types' import type {ISubmittableResult} from '@polkadot/types/types' import {u8aToHex} from '@polkadot/util' import {decodeAddress} from '@polkadot/util-crypto' -import getGeneralKey, {type Hex} from './getGeneralKey' - -const khalaParaId = CHAINS.khala.paraId +import {createPhalaMultilocation} from './createPhalaMultilocation' +import {type Hex} from './getGeneralKey' export const transferByPolkadotXTokens = ({ polkadotApi, @@ -16,7 +15,7 @@ export const transferByPolkadotXTokens = ({ fromChainId, toChainId, destinationAccount, - isThroughKhala = false, + proxy, }: { polkadotApi: ApiPromise assetId: AssetId @@ -24,7 +23,7 @@ export const transferByPolkadotXTokens = ({ toChainId: ChainId amount: string destinationAccount: string - isThroughKhala?: boolean + proxy?: ChainId }): SubmittableExtrinsic<'promise', ISubmittableResult> => { const asset = ASSETS[assetId] const toChain = CHAINS[toChainId] @@ -44,7 +43,7 @@ export const transferByPolkadotXTokens = ({ ? palletAssetId === undefined : asset.ormlToken === undefined) || toChain.paraId == null || - (isThroughKhala && typeof generalIndex !== 'number') + (proxy != null && typeof generalIndex !== 'number') ) { throw new Error('Transfer missing required parameters') } @@ -79,50 +78,56 @@ export const transferByPolkadotXTokens = ({ ? { V3: { parents: 1, - interior: isThroughKhala - ? { - X4: [ - {Parachain: khalaParaId}, - {GeneralKey: getGeneralKey('0x6362')}, - {GeneralIndex: generalIndex}, - {GeneralKey: getGeneralKey(destinationAccount as Hex)}, - ], - } - : { - X2: [ - {Parachain: toChain.paraId}, - { - AccountId32: { - id: u8aToHex(decodeAddress(destinationAccount)), + interior: + proxy != null + ? { + X4: [ + {Parachain: CHAINS[proxy].paraId}, + ...createPhalaMultilocation( + 'cb', + generalIndex as number, + destinationAccount as Hex, + ), + ], + } + : { + X2: [ + {Parachain: toChain.paraId}, + { + AccountId32: { + id: u8aToHex(decodeAddress(destinationAccount)), + }, }, - }, - ], - }, + ], + }, }, } : { V1: { parents: 1, - interior: isThroughKhala - ? { - X4: [ - {Parachain: khalaParaId}, - {GeneralKey: '0x6362'}, // string "cb" - {GeneralIndex: generalIndex}, - {GeneralKey: destinationAccount}, - ], - } - : { - X2: [ - {Parachain: toChain.paraId}, - { - AccountId32: { - network: 'Any', - id: u8aToHex(decodeAddress(destinationAccount)), + interior: + proxy != null + ? { + X4: [ + {Parachain: CHAINS[proxy].paraId}, + ...createPhalaMultilocation( + 'cb', + generalIndex as number, + destinationAccount as Hex, + ), + ], + } + : { + X2: [ + {Parachain: toChain.paraId}, + { + AccountId32: { + network: 'Any', + id: u8aToHex(decodeAddress(destinationAccount)), + }, }, - }, - ], - }, + ], + }, }, }, fromChainId === 'bifrost-kusama' || fromChainId === 'bifrost-test' diff --git a/apps/subbridge/lib/transferByPolkadotXcm.ts b/apps/subbridge/lib/transferByPolkadotXcm.ts index 04858960..797a3fff 100644 --- a/apps/subbridge/lib/transferByPolkadotXcm.ts +++ b/apps/subbridge/lib/transferByPolkadotXcm.ts @@ -5,6 +5,8 @@ import type {SubmittableExtrinsic} from '@polkadot/api/types' import type {ISubmittableResult} from '@polkadot/types/types' import {u8aToHex} from '@polkadot/util' import {decodeAddress} from '@polkadot/util-crypto' +import {createPhalaMultilocation} from './createPhalaMultilocation' +import {type Hex} from './getGeneralKey' export const transferByPolkadotXcm = ({ polkadotApi, @@ -13,6 +15,7 @@ export const transferByPolkadotXcm = ({ fromChainId, toChainId, destinationAccount, + proxy, }: { polkadotApi: ApiPromise assetId: AssetId @@ -20,9 +23,11 @@ export const transferByPolkadotXcm = ({ toChainId: ChainId amount: string destinationAccount: string + proxy?: ChainId }): SubmittableExtrinsic<'promise', ISubmittableResult> => { const fromChain = CHAINS[fromChainId] const toChain = CHAINS[toChainId] + const proxyChain = proxy != null ? CHAINS[proxy] : undefined let Concrete if (fromChainId === 'crab' && assetId === 'crab') { Concrete = {parents: 0, interior: {X1: {PalletInstance: 5}}} @@ -39,30 +44,50 @@ export const transferByPolkadotXcm = ({ Concrete = {parents: 0, interior: 'Here'} } } + const generalIndex = toChain.kind === 'evm' ? toChain.generalIndex : null + + if (proxy != null && typeof generalIndex !== 'number') { + throw new Error('Transfer missing required parameters') + } const isNativeAsset = fromChain.nativeAsset === assetId const functionName = isNativeAsset ? 'reserveTransferAssets' : 'reserveWithdrawAssets' - const xcmVersion = fromChainId === 'shiden' ? 'V3' : 'V1' - return polkadotApi.tx.polkadotXcm[functionName]( - {[xcmVersion]: {parents: 1, interior: {X1: {Parachain: toChain.paraId}}}}, { - [xcmVersion]: { - parents: 0, + V3: { + parents: 1, interior: { X1: { - AccountId32: { - ...(xcmVersion === 'V1' && {network: 'Any'}), - id: u8aToHex(decodeAddress(destinationAccount)), - }, + Parachain: proxyChain == null ? toChain.paraId : proxyChain.paraId, }, }, }, }, - {[xcmVersion]: [{id: {Concrete}, fun: {Fungible: amount}}]}, + { + V3: { + parents: 0, + interior: + proxy === null + ? { + X1: { + AccountId32: { + id: u8aToHex(decodeAddress(destinationAccount)), + }, + }, + } + : { + X3: createPhalaMultilocation( + 'sygma', + generalIndex as number, + destinationAccount as Hex, + ), + }, + }, + }, + {V3: [{id: {Concrete}, fun: {Fungible: amount}}]}, 0, ) } diff --git a/apps/subbridge/package.json b/apps/subbridge/package.json index d43b8a19..ecb3438a 100644 --- a/apps/subbridge/package.json +++ b/apps/subbridge/package.json @@ -11,7 +11,7 @@ }, "dependencies": { "@buildwithsygma/sygma-contracts": "2.3.0", - "@buildwithsygma/sygma-sdk-core": "2.1.0", + "@buildwithsygma/sygma-sdk-core": "2.2.0", "@emotion/react": "^11.11.1", "@emotion/server": "^11.11.0", "@emotion/styled": "^11.11.0", diff --git a/apps/subbridge/store/bridge.ts b/apps/subbridge/store/bridge.ts index fe02cbfd..8fb944fd 100644 --- a/apps/subbridge/store/bridge.ts +++ b/apps/subbridge/store/bridge.ts @@ -93,24 +93,24 @@ export const bridgeInfoAtom = atom((get) => { return { kind: bridge?.kind ?? null, estimatedTime: bridge?.estimatedTime ?? null, - isThroughKhala: bridge?.isThroughKhala ?? false, + proxy: bridge?.proxy, disabled: toChainConfig?.disabled ?? bridge?.disabled ?? false, } }) export const destChainTransactionFeeAtom = atom((get) => { - const {kind, isThroughKhala} = get(bridgeInfoAtom) + const {kind, proxy} = get(bridgeInfoAtom) const toChain = get(toChainAtom) const asset = get(assetAtom) const destChainTransactionFee = asset.destChainTransactionFee[toChain.id] - const khalaFee = asset.destChainTransactionFee.khala ?? new Decimal(0) - if (kind !== 'evmChainBridge' && isThroughKhala) { - return khalaFee.add(destChainTransactionFee ?? 0) + if (kind !== 'evmChainBridge' && proxy != null) { + const proxyFee = asset.destChainTransactionFee[proxy] ?? new Decimal(0) + return proxyFee.add(destChainTransactionFee ?? 0) } if ( - (kind === 'evmChainBridge' && !isThroughKhala) || + (kind === 'evmChainBridge' && proxy == null) || kind === 'evmSygma' || destChainTransactionFee == null ) { diff --git a/package.json b/package.json index fec9549f..896fc9e1 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "build": "turbo run build", "lint": "turbo run lint", "lint:fix": "turbo run lint:fix", - "typecheck": "turbo run typecheck" + "check": "turbo run check" }, "devDependencies": { "@total-typescript/ts-reset": "^0.4.2", diff --git a/packages/lib/package.json b/packages/lib/package.json index a31aada7..721bfb8e 100644 --- a/packages/lib/package.json +++ b/packages/lib/package.json @@ -7,7 +7,7 @@ "scripts": { "lint": "eslint --cache --ext .ts,.tsx src", "lint:fix": "eslint --fix --cache --ext .ts,.tsx src", - "typecheck": "tsc --project ./tsconfig.json --noEmit" + "check": "tsc --project ./tsconfig.json --noEmit" }, "dependencies": { "@phala/util": "workspace:^", diff --git a/packages/store/package.json b/packages/store/package.json index 19941f9f..4cbf7117 100644 --- a/packages/store/package.json +++ b/packages/store/package.json @@ -7,7 +7,7 @@ "scripts": { "lint": "eslint --cache --ext .ts,.tsx src", "lint:fix": "eslint --fix --cache --ext .ts,.tsx src", - "typecheck": "tsc --project ./tsconfig.json --noEmit" + "check": "tsc --project ./tsconfig.json --noEmit" }, "peerDependencies": { "@talismn/connect-wallets": "^1.1.3", diff --git a/packages/utils/package.json b/packages/utils/package.json index 10fc390e..f74a40b7 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -7,7 +7,7 @@ "scripts": { "lint": "eslint --cache --ext .ts,.tsx src", "lint:fix": "eslint --fix --cache --ext .ts,.tsx src", - "typecheck": "tsc --project ./tsconfig.json --noEmit" + "check": "tsc --project ./tsconfig.json --noEmit" }, "dependencies": { "@polkadot/keyring": "^12.3.2", diff --git a/turbo.json b/turbo.json index 17d2d70f..15a3c16d 100644 --- a/turbo.json +++ b/turbo.json @@ -3,17 +3,17 @@ "pipeline": { "build": { "dependsOn": ["^build"], - "outputs": ["out/**", ".next/**"], + "outputs": ["out/**", ".next/**", "build/**"], "env": ["CONTEXT", "NODE_ENV"] }, "lint": { "outputs": [], - "inputs": ["../../.prettierrc", "../../packages/eslint-config/**"] + "inputs": ["./**", "../../.prettierrc", "../../packages/eslint-config/**"] }, "lint:fix": { "outputs": [], - "inputs": ["../../.prettierrc", "../../packages/eslint-config/**"] + "inputs": ["./**", "../../.prettierrc", "../../packages/eslint-config/**"] }, - "typecheck": {"outputs": []} + "check": {"outputs": []} } } diff --git a/yarn.lock b/yarn.lock index 62b32fe0..e06a711c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1454,9 +1454,9 @@ __metadata: languageName: node linkType: hard -"@buildwithsygma/sygma-sdk-core@npm:2.1.0": - version: 2.1.0 - resolution: "@buildwithsygma/sygma-sdk-core@npm:2.1.0" +"@buildwithsygma/sygma-sdk-core@npm:2.2.0": + version: 2.2.0 + resolution: "@buildwithsygma/sygma-sdk-core@npm:2.2.0" dependencies: "@buildwithsygma/sygma-contracts": 2.1.2 "@ethersproject/abi": ^5.7.0 @@ -1474,7 +1474,7 @@ __metadata: axios: ^1.4.0 cross-fetch: ^3.1.5 ethers: 5.6.2 - checksum: 9a70bb5be2a324b51caded6618b8301e65b6d1bd5a9f9dc92125c459631d7715d38937730c4c6ad6ddc0469a88b7a910cb2f1f6d43cef77023331c2aec194381 + checksum: 221fc6675f54ada27848451ab6be6ae2fa61c8c908cb67928d6bdcfe11d40a692a984af00087d470114caf2da80c6ee4341f57282a3ca9170f9e531aa143e751 languageName: node linkType: hard @@ -3609,7 +3609,7 @@ __metadata: resolution: "@phala/subbridge@workspace:apps/subbridge" dependencies: "@buildwithsygma/sygma-contracts": 2.3.0 - "@buildwithsygma/sygma-sdk-core": 2.1.0 + "@buildwithsygma/sygma-sdk-core": 2.2.0 "@emotion/react": ^11.11.1 "@emotion/server": ^11.11.0 "@emotion/styled": ^11.11.0