Skip to content

Commit

Permalink
Merge pull request #61 from dappforce/xcm
Browse files Browse the repository at this point in the history
Add XCM
  • Loading branch information
olehmell authored Nov 29, 2023
2 parents 8fe0aeb + 854eeeb commit 7d571a5
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 5 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ build/
# Optional npm cache directory
.npm
.vscode
.yarn

# Optional eslint cache
.eslintcache
Expand Down
7 changes: 4 additions & 3 deletions src/connections/networks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,23 @@ export const subsocial = {
icon: 'subsocial-parachain.svg',
paraId: 2100,
ipfs: 'https://ipfs.subsocial.network',
offchain: 'https://api.subsocial.network'
offchain: 'https://api.subsocial.network',
isTransferable: true
}

export const standalones: Networks = {
kusama: {
name: 'Kusama',
...resolveOnfinalityUrl('kusama'),
wsNode: 'wss://kusama-rpc.polkadot.io',
// wsNode: 'wss://kusama-rpc.polkadot.io',
icon: 'kusama.svg',
isMixedConnection: true,
isTransferable: true
},
polkadot: {
name: 'Polkadot',
...resolveOnfinalityUrl('polkadot'),
wsNode: 'wss://rpc.polkadot.io',
// wsNode: 'wss://rpc.polkadot.io',
icon: 'polkadot.svg',
isMixedConnection: true,
isTransferable: true
Expand Down
4 changes: 2 additions & 2 deletions src/connections/networks/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { ONFINALITY_API_KEY } from '../../constant'

export const resolveOnfinalityUrl = (chainName: string) => {
return {
// node: `wss://${chainName}.api.onfinality.io/ws?apikey=${ONFINALITY_API_KEY}`,
node: `https://${chainName}.api.onfinality.io/rpc?apikey=${ONFINALITY_API_KEY}`,
node: `wss://${chainName}.api.onfinality.io/ws?apikey=${ONFINALITY_API_KEY}`,
// node: `https://${chainName}.api.onfinality.io/rpc?apikey=${ONFINALITY_API_KEY}`,
wsNode: `wss://${chainName}.api.onfinality.io/ws?apikey=${ONFINALITY_API_KEY}`
}
}
190 changes: 190 additions & 0 deletions src/services/fees/custom/SubsocialAdapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
import { Storage } from '@acala-network/sdk/utils/storage'
import { AnyApi, FixedPointNumber as FN } from '@acala-network/sdk-core'
import { combineLatest, map, Observable } from 'rxjs'

import { SubmittableExtrinsic } from '@polkadot/api/types'
import { DeriveBalancesAll } from '@polkadot/api-derive/balances/types'
import { ISubmittableResult } from '@polkadot/types/types'
import {
createPolkadotXCMAccount,
createPolkadotXCMAsset,
createPolkadotXCMDest,
createRouteConfigs,
validateAddress
} from '@polkawallet/bridge/utils'
import { BalanceData, BasicToken, ChainId, chains, TransferParams } from '@polkawallet/bridge'
import { BalanceAdapter, BalanceAdapterConfigs } from '@polkawallet/bridge/balance-adapter'
import { ApiNotFound, InvalidAddress, TokenNotFound } from '@polkawallet/bridge/errors'
import { BaseCrossChainAdapter } from '@polkawallet/bridge/base-chain-adapter'

// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
chains.subsocial = {
id: 'subsocial',
display: 'Subsocial',
type: 'substrate',
icon: 'https://sub.id/images/subsocial.svg',
paraChainId: 2100,
ss58Prefix: 28
}
export const subsocialRouteConfigs = createRouteConfigs('subsocial' as any, [
{
to: 'hydradx',
token: 'SUB',
xcm: {
fee: { token: 'SUB', amount: '63199000' },
},
},
])

export const subsocialTokensConfig: Record<string, BasicToken> = {
SUB: { name: 'SUB', symbol: 'SUB', decimals: 10, ed: '100000000' },
}

// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
const createBalanceStorages = (api: AnyApi) => {
return {
balances: (address: string) =>
Storage.create<DeriveBalancesAll>({
api,
path: 'derive.balances.all',
params: [ address ],
}),
}
}

class SubsocialBalanceAdapter extends BalanceAdapter {
private storages: ReturnType<typeof createBalanceStorages>

constructor ({ api, chain, tokens }: BalanceAdapterConfigs) {
super({ api, chain, tokens })
this.storages = createBalanceStorages(api)
}

public subscribeBalance (
token: string,
address: string
): Observable<BalanceData> {
const storage = this.storages.balances(address)

if (token !== this.nativeToken) {
throw new TokenNotFound(token)
}

return storage.observable.pipe(
map((data) => ({
free: FN.fromInner(data.freeBalance.toString(), this.decimals),
locked: FN.fromInner(data.lockedBalance.toString(), this.decimals),
reserved: FN.fromInner(data.reservedBalance.toString(), this.decimals),
available: FN.fromInner(
data.availableBalance.toString(),
this.decimals
),
}))
)
}
}

class SubsocialBaseAdapter extends BaseCrossChainAdapter {
private balanceAdapter?: SubsocialBalanceAdapter

public async init (api: AnyApi) {
this.api = api

await api.isReady

const chain = this.chain.id as ChainId

this.balanceAdapter = new SubsocialBalanceAdapter({
chain,
api,
tokens: subsocialTokensConfig,
})
}

public subscribeTokenBalance (
token: string,
address: string
): Observable<BalanceData> {
if (!this.balanceAdapter) {
throw new ApiNotFound(this.chain.id)
}

return this.balanceAdapter.subscribeBalance(token, address)
}

public subscribeMaxInput (
token: string,
address: string,
to: ChainId
): Observable<FN> {
if (!this.balanceAdapter) {
throw new ApiNotFound(this.chain.id)
}

return combineLatest({
txFee: this.estimateTxFee({
amount: FN.ZERO,
to,
token,
address,
signer: address,
}),
balance: this.balanceAdapter
.subscribeBalance(token, address)
.pipe(map((i) => i.available)),
}).pipe(
map(({ balance, txFee }) => {
const tokenMeta = this.balanceAdapter?.getToken(token)
const feeFactor = 1.2
const fee = FN.fromInner(txFee, tokenMeta?.decimals).mul(
new FN(feeFactor)
)

// always minus ed
return balance
.minus(fee)
.minus(FN.fromInner(tokenMeta?.ed || '0', tokenMeta?.decimals))
})
)
}

public createTx (
params: TransferParams
):
| SubmittableExtrinsic<'promise', ISubmittableResult>
| SubmittableExtrinsic<'rxjs', ISubmittableResult> {
if (!this.api) throw new ApiNotFound(this.chain.id)

const { address, amount, to, token } = params

if (!validateAddress(address)) throw new InvalidAddress(address)

const toChain = chains[to]

if (token !== this.balanceAdapter?.nativeToken) {
throw new TokenNotFound(token)
}

const accountId = this.api?.createType('AccountId32', address).toHex()
const paraChainId = toChain.paraChainId
const rawAmount = amount.toChainData()

return this.api?.tx.polkadotXcm.limitedReserveTransferAssets(
createPolkadotXCMDest(this.api, paraChainId),
createPolkadotXCMAccount(this.api, accountId),
createPolkadotXCMAsset(this.api, rawAmount, 'NATIVE'),
0,
this.getDestWeight(token, to)?.toString() as any
)
}
}

export class SubsocialAdapter extends SubsocialBaseAdapter {
constructor () {
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
super(chains.subsocial, subsocialRouteConfigs, subsocialTokensConfig)
}
}

1 change: 1 addition & 0 deletions src/services/fees/transfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ async function getCrossChainTransferFee ({ from, token, to }: GetTransferFeePara
try {
const adapter = await getCrossChainAdapter(from, node)
if (!adapter) return amount

amount = await firstValueFrom(adapter.estimateTxFee({
to: to as ChainId,
address: dummyAccount,
Expand Down
4 changes: 4 additions & 0 deletions src/services/fees/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { StatemineAdapter } from '@polkawallet/bridge/adapters/statemint'
import { ZeitgeistAdapter } from '@polkawallet/bridge/adapters/zeitgeist'
import { BaseCrossChainAdapter } from '@polkawallet/bridge/base-chain-adapter'
import { firstValueFrom } from 'rxjs'
import { SubsocialAdapter } from './custom/SubsocialAdapter'

const transferAdapters: Record<string, { adapter: BaseCrossChainAdapter; chainName?: ChainId }> = {
polkadot: {
Expand Down Expand Up @@ -105,6 +106,9 @@ const transferAdapters: Record<string, { adapter: BaseCrossChainAdapter; chainNa
},
zeitgeist: {
adapter: new ZeitgeistAdapter()
},
subsocial: {
adapter: new SubsocialAdapter(),
}
}
function getPolkawalletChainName(chain: string) {
Expand Down

0 comments on commit 7d571a5

Please sign in to comment.