Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

âš¡ add statemine to teleport bridge #6152

Merged
merged 20 commits into from
Jun 16, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
199 changes: 118 additions & 81 deletions components/teleport/Teleport.vue
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
<div class="mb-5">
<h1 class="has-text-weight-bold">{{ $i18n.t('teleport.to') }}</h1>
<TeleportTabs
:tabs="fromTabs"
:tabs="ToTabs"
:value="toChain"
@select="onToChainChange" />
</div>
Expand Down Expand Up @@ -85,7 +85,12 @@ import { web3Enable } from '@polkadot/extension-dapp'
import '@polkadot/api-augment'
import { toDefaultAddress } from '@/utils/account'
import { getAddress } from '@/utils/extension'
import { Chain, chainToPrefixMap } from '@/utils/teleport'
import {
Chain,
TeleprtType,
chainToPrefixMap,
whichTeleportType,
} from '@/utils/teleport'
import { notificationTypes, showNotification } from '@/utils/notification'
import useAuth from '@/composables/useAuth'
import Loader from '@/components/shared/Loader.vue'
Expand All @@ -98,31 +103,27 @@ import { txCb } from '@/utils/transactionExecutor'
import TeleportTabs from './TeleportTabs.vue'
import { NeoButton } from '@kodadot1/brick'
import { getss58AddressByPrefix } from '@/utils/account'
import { getAsssetBalance } from '@/utils/api/bsx/query'
import { blockExplorerOf } from '@/utils/config/chain.config'
import { simpleDivision, subscribeBalance } from '@/utils/balance'
import { simpleDivision } from '@/utils/balance'
import { useFiatStore } from '@/stores/fiat'
import { useIdentityStore } from '@/stores/identity'

const getKusamaApi = async () =>
await ApiPromise.create({
provider: new WsProvider(getChainEndpointByPrefix('kusama') as string),
})
const getBasiliskApi = async () =>
await ApiPromise.create({
provider: new WsProvider(getChainEndpointByPrefix('basilisk') as string),
const getApi = (from: Chain) => {
return ApiPromise.create({
provider: new WsProvider(getChainEndpointByPrefix(chainToPrefixMap[from])),
daiagi marked this conversation as resolved.
Show resolved Hide resolved
})
}

const { accountId } = useAuth()
const { assets } = usePrefix()
const { $i18n } = useNuxtApp()
const fiatStore = useFiatStore()
const identityStore = useIdentityStore()

const chains = ref([Chain.KUSAMA, Chain.BASILISK])
const fromChain = ref(Chain.KUSAMA) //Selected origin parachain
const toChain = ref(Chain.BASILISK) //Selected destination parachain
const amount = ref() //Required amount to be transfered is stored here
const ksmBalanceOnBasilisk = ref()
const ksmBalanceOnKusama = ref()
const currency = ref('KSM') //Selected currency is stored here
const isLoading = ref(false)
const unsubscribeKusamaBalance = ref()
Expand All @@ -140,13 +141,51 @@ const fromTabs = [
label: Chain.BASILISK,
value: Chain.BASILISK,
},
{
label: Chain.STATEMINE,
value: Chain.STATEMINE,
},
]
const ToTabs = [
daiagi marked this conversation as resolved.
Show resolved Hide resolved
{
label: Chain.KUSAMA,
value: Chain.KUSAMA,
},
{
label: Chain.BASILISK,
value: Chain.BASILISK,
},
{
label: Chain.STATEMINE,
value: Chain.STATEMINE,
},
]

const ksmTokenDecimals = computed(() => assets(5).decimals)

const myKsmBalance = computed(() => {
return fromChain.value === Chain.KUSAMA
? ksmBalanceOnKusama.value
: ksmBalanceOnBasilisk.value
let balance = ''

switch (fromChain.value) {
case Chain.KUSAMA:
balance = identityStore.multiBalances.chains.kusama?.ksm
?.balance as string
break
case Chain.BASILISK:
balance = identityStore.multiBalances.chains.basilisk?.ksm
?.balance as string
break
case Chain.STATEMINE:
balance = identityStore.multiBalances.chains.statemine?.ksm
?.balance as string
break
default:
throw new Error(`Unsupported chain: ${fromChain.value}`)
}

return Number(balance) * Math.pow(10, ksmTokenDecimals.value)
})

const explorerUrl = computed(() => {
return `${blockExplorerOf(chainToPrefixMap[toChain.value])}account/${
toAddress.value
Expand All @@ -157,12 +196,22 @@ const getAnotherOption = (val) => {
}
const onFromChainChange = (val) => {
fromChain.value = val
toChain.value = getAnotherOption(val)
if (toChain.value === fromChain.value) {
toChain.value = getAnotherOption(val)
}
}
const getFromChain = (): Chain => {
return Chain[fromChain.value.toUpperCase()] as Chain
}
const getToChain = (): Chain => {
return Chain[toChain.value.toUpperCase()] as Chain
}

const onToChainChange = (val) => {
toChain.value = val
fromChain.value = getAnotherOption(val)
if (toChain.value === fromChain.value) {
fromChain.value = getAnotherOption(val)
}
}
const getAddressByChain = (chain) => {
return getss58AddressByPrefix(accountId.value, chainToPrefixMap[chain])
Expand All @@ -177,26 +226,6 @@ const ksmValue = computed(() =>
)
)

const fetchBasiliskBalance = async () => {
const api = await getBasiliskApi()
getAsssetBalance(api, getAddressByChain(fromChain.value), '1').then(
(data) => {
ksmBalanceOnBasilisk.value = data
}
)
}

const fetchKusamaBalance = async () => {
const api = await getKusamaApi()
unsubscribeKusamaBalance.value = await subscribeBalance(
api,
getAddressByChain(Chain.KUSAMA),
(...data) => {
ksmBalanceOnKusama.value = data[0]
}
)
}

const insufficientBalance = computed(
() => Number(amount.value) > myKsmBalanceWithoutDivision.value
)
Expand All @@ -209,21 +238,53 @@ const isDisabledButton = computed(() => {
return !amount.value || amount.value <= 0 || insufficientBalance.value
})

const ksmTokenDecimals = computed(() => assets(5).decimals)

const handleMaxClick = () => {
amount.value =
Math.floor((myKsmBalanceWithoutDivision.value || 0) * 10 ** 4) / 10 ** 4
}
onMounted(() => {
fetchBasiliskBalance()
fetchKusamaBalance()
})

onBeforeUnmount(() => {
unsubscribeKusamaBalance.value && unsubscribeKusamaBalance.value()
})

const getTransaction = async () => {
const amountValue = amount.value * Math.pow(10, ksmTokenDecimals.value)

const api = await getApi(getFromChain())
const telportType = whichTeleportType({
from: getFromChain(),
to: getToChain(),
})
if (telportType === TeleprtType.RelayToPara) {
return paraspell
.Builder(api)
.to(Chain[toChain.value.toUpperCase()])
.amount(amountValue)
.address(toAddress.value)
.build()
}
if (telportType === TeleprtType.ParaToRelay) {
return paraspell
.Builder(api)
.from(Chain[fromChain.value.toUpperCase()])
.currency('KSM')
.amount(amountValue)
.address(toAddress.value)
.build()
}

if (telportType === TeleprtType.ParaToPara) {
return paraspell
.Builder(api)
.from(Chain[fromChain.value.toUpperCase()])
.to(Chain[toChain.value.toUpperCase()])
.currency('KSM')
.amount(amountValue)
.address(toAddress.value)
.build()
}
}

//Used to create XCM transfer
const sendXCM = async () => {
if (!amount.value || amount.value < 0) {
Expand All @@ -232,7 +293,6 @@ const sendXCM = async () => {
await web3Enable('Kodadot')
let isFirstStatus = true
isLoading.value = true
const amountValue = 10 ** ksmTokenDecimals.value * amount.value
const transactionHandler = txCb(
(blockHash) => {
showNotification(
Expand Down Expand Up @@ -260,50 +320,27 @@ const sendXCM = async () => {
showNotification('Cancelled', notificationTypes.warn)
isLoading.value = false
}
const injector = await getAddress(toDefaultAddress(fromAddress.value))
if (fromChain.value === Chain.KUSAMA) {
const apiKusama = await getKusamaApi()
const promise = paraspell.xcmPallet.transferRelayToPara(
apiKusama,
Chain.BASILISK,
amountValue,
toAddress.value
)

promise
.signAndSend(
fromAddress.value,
{ signer: injector.signer },
transactionHandler
)
.catch(errorHandler)
} else if (fromChain.value === Chain.BASILISK) {
const apiBasilisk = await getBasiliskApi()

const promise = paraspell.xcmPallet.send(
apiBasilisk,
Chain.BASILISK,
currency.value,
1,
amountValue,
toAddress.value
)

promise
.signAndSend(
fromAddress.value,
{ signer: injector.signer },
transactionHandler
)
.catch(errorHandler)
const promise = await getTransaction()
if (promise === undefined) {
return
}

const injector = await getAddress(toDefaultAddress(fromAddress.value))
promise
.signAndSend(
fromAddress.value,
{ signer: injector.signer },
transactionHandler
)
.catch(errorHandler)
}
</script>
<style lang="scss" scoped>
@import '@/styles/abstracts/variables.scss';

.teleport-container {
max-width: 30rem;
max-width: 50rem;

.submit-button {
width: 100%;
Expand Down
7 changes: 7 additions & 0 deletions libs/static/src/chains.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ export const chainPrefixes: Prefix[] = [
'dot',
'stt',
]
export const chainPrefixesMap = chainPrefixes.reduce(
(acc: Partial<Record<Prefix, Prefix>>, prefix: Prefix) => ({
...acc,
[prefix]: prefix,
}),
{}
) as Record<Prefix, Prefix>

export const chainInfo: Record<Prefix, string> = {
bsx: 'basilisk',
Expand Down
33 changes: 32 additions & 1 deletion utils/teleport.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright 2017-2021 @polkadot/app-config authors & contributors
// SPDX-License-Identifier: Apache-2.0

import { Prefix } from '@kodadot1/static'
import type { ApiPromise } from '@polkadot/api'
import { SubmittableExtrinsicFunction } from '@polkadot/api/types'
import { XcmVersionedMultiLocation } from '@polkadot/types/lookup'
Expand All @@ -24,11 +25,41 @@ const KNOWN_WEIGHTS: Record<string, number> = {
export enum Chain {
KUSAMA = 'Kusama',
BASILISK = 'Basilisk',
STATEMINE = 'Statemine',
}

export const chainToPrefixMap = {
export const chainToPrefixMap: Record<Chain, Prefix> = {
[Chain.KUSAMA]: 'rmrk',
[Chain.BASILISK]: 'bsx',
[Chain.STATEMINE]: 'stmn',
}

export enum TeleprtType {
RelayToPara = 'RelayToPara',
ParaToRelay = 'ParaToRelay',
ParaToPara = 'ParaToPara',
}

export const whichTeleportType = ({
from,
to,
}: {
from: Chain
to: Chain
}): TeleprtType => {
switch (from) {
case Chain.KUSAMA:
return TeleprtType.RelayToPara

case Chain.BASILISK:
case Chain.STATEMINE:
return to === Chain.KUSAMA
? TeleprtType.ParaToRelay
: TeleprtType.ParaToPara

default:
throw new Error(`Unknown chain: ${from}`)
}
}

export function getTeleportWeight(api: ApiPromise): number {
Expand Down