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 all 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
2 changes: 1 addition & 1 deletion components/common/ConnectWallet/WalletAssetMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ const menus = ref([
},
{
label: 'Teleport Bridge',
to: '/teleport-bridge',
to: `/${urlPrefix.value}/teleport`,
},
{
label: 'Onchain Identity',
Expand Down
227 changes: 133 additions & 94 deletions components/teleport/Teleport.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<TeleportTabs
:tabs="fromTabs"
:value="fromChain"
@select="onFromChainChange" />
@select="onChainChange" />
</div>

<div class="mb-5 is-flex is-flex-direction-column">
Expand All @@ -32,7 +32,7 @@
</div>

<div
v-if="myKsmBalance"
v-if="myKsmBalance !== undefined"
class="is-size-7 is-flex is-justify-content-end is-align-items-center">
<span class="is-flex is-align-items-center">
<span class="mr-2">{{ $i18n.t('balance') }}:</span
Expand All @@ -47,9 +47,9 @@
<div class="mb-5">
<h1 class="has-text-weight-bold">{{ $i18n.t('teleport.to') }}</h1>
<TeleportTabs
:tabs="fromTabs"
:tabs="toTabs"
:value="toChain"
@select="onToChainChange" />
@select="(chain) => onChainChange(chain, false)" />
</div>

<div class="mb-5">
Expand Down Expand Up @@ -80,12 +80,16 @@
</template>

<script setup lang="ts">
import { ApiPromise, WsProvider } from '@polkadot/api'
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 +102,26 @@ 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'
import { ApiFactory } from '@kodadot1/sub-api'

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) => {
const endpoint = getChainEndpointByPrefix(chainToPrefixMap[from]) as string
return ApiFactory.useApiInstance(endpoint)
}

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 @@ -131,6 +130,23 @@ const resetStatus = () => {
isLoading.value = false
}

const allowedTransitiosn = {
[Chain.KUSAMA]: [Chain.BASILISK, Chain.STATEMINE],
[Chain.BASILISK]: [Chain.KUSAMA],
[Chain.STATEMINE]: [Chain.KUSAMA],
}
const chainBalances = {
[Chain.KUSAMA]: () => identityStore.multiBalances.chains.kusama?.ksm?.balance,
[Chain.BASILISK]: () =>
identityStore.multiBalances.chains.basilisk?.ksm?.balance,
[Chain.STATEMINE]: () =>
identityStore.multiBalances.chains.statemine?.ksm?.balance,
}

const isDisabled = (chain: Chain) => {
return !allowedTransitiosn[fromChain.value].includes(chain)
}

const fromTabs = [
{
label: Chain.KUSAMA,
Expand All @@ -140,30 +156,65 @@ const fromTabs = [
label: Chain.BASILISK,
value: Chain.BASILISK,
},
{
label: Chain.STATEMINE,
value: Chain.STATEMINE,
},
]
const toTabs = [
{
label: Chain.KUSAMA,
value: Chain.KUSAMA,
disabled: computed(() => isDisabled(Chain.KUSAMA)),
},
{
label: Chain.BASILISK,
value: Chain.BASILISK,
disabled: computed(() => isDisabled(Chain.BASILISK)),
},
{
label: Chain.STATEMINE,
value: Chain.STATEMINE,
disabled: computed(() => isDisabled(Chain.STATEMINE)),
},
]

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

const myKsmBalance = computed(() => {
return fromChain.value === Chain.KUSAMA
? ksmBalanceOnKusama.value
: ksmBalanceOnBasilisk.value
const getBalance = chainBalances[fromChain.value]
if (!getBalance) {
throw new Error(`Unsupported chain: ${fromChain.value}`)
}
const balance = Number(getBalance()) || 0
return balance * Math.pow(10, ksmTokenDecimals.value)
})

const explorerUrl = computed(() => {
return `${blockExplorerOf(chainToPrefixMap[toChain.value])}account/${
toAddress.value
}`
})
const getAnotherOption = (val) => {
return chains.value.find((chain) => chain !== val) || Chain.KUSAMA
}
const onFromChainChange = (val) => {
fromChain.value = val
toChain.value = getAnotherOption(val)
const getFirstAllowedDestination = (chain: Chain) => {
return allowedTransitiosn[chain][0]
}

const onToChainChange = (val) => {
toChain.value = val
fromChain.value = getAnotherOption(val)
const onChainChange = (selectedChain, setFrom = true) => {
if (setFrom) {
fromChain.value = selectedChain
toChain.value = getFirstAllowedDestination(selectedChain)
} else {
toChain.value = selectedChain
fromChain.value = getFirstAllowedDestination(selectedChain)
}
}
const getFromChain = (): Chain => {
return Chain[fromChain.value.toUpperCase()] as Chain
}
const getToChain = (): Chain => {
return Chain[toChain.value.toUpperCase()] as Chain
}

const getAddressByChain = (chain) => {
return getss58AddressByPrefix(accountId.value, chainToPrefixMap[chain])
}
Expand All @@ -177,26 +228,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 +240,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 +295,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 +322,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: 6 additions & 1 deletion components/teleport/TeleportTabs.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
:key="tab.value"
class="teleport-tabs-button"
tag="nuxt-link"
:disabled="tab.disabled?.value"
:variant="tab.disabled?.value ? 'disabled-secondary' : undefined"
:active="value === tab.value"
to=""
@click.native="emit('select', tab.value)">
Expand All @@ -18,10 +20,13 @@

<script setup lang="ts">
import { NeoButton } from '@kodadot1/brick'
import { ComputedRef } from '@nuxt/bridge/dist/runtime/composables'
import { Chain } from '@/utils/teleport'

type Tab = {
label: string
value: string
value: Chain
disabled?: ComputedRef<boolean>
}
defineProps<{
tabs: Tab[]
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
Loading