Skip to content

Commit

Permalink
Api version 0.1.4 (#1198)
Browse files Browse the repository at this point in the history
* separate sign and send

* asset information

* utils and fixes

* increase asset fee

* fixes

* revert separate sign and send code

* remove log
  • Loading branch information
alistair-singh authored May 14, 2024
1 parent b02684b commit 18fe233
Show file tree
Hide file tree
Showing 11 changed files with 212 additions and 125 deletions.
2 changes: 1 addition & 1 deletion web/packages/api/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@snowbridge/api",
"version": "0.1.3",
"version": "0.1.4",
"description": "Snowbridge API client",
"license": "Apache-2.0",
"repository": {
Expand Down
101 changes: 101 additions & 0 deletions web/packages/api/src/assets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { ApiPromise } from "@polkadot/api"
import { Codec, Registry } from "@polkadot/types/types"
import { IERC20Metadata__factory, IERC20__factory } from "@snowbridge/contract-types"
import { Context } from './index'

export const parachainNativeToken = async (api: ApiPromise): Promise<{
tokenSymbol: string
tokenDecimal: number
ss58Format: number | null
}> => {
const properties = await api.rpc.system.properties()
const tokenSymbols = properties.tokenSymbol.unwrapOrDefault()
const tokenDecimals = properties.tokenDecimals.unwrapOrDefault()

return {
tokenSymbol: tokenSymbols.at(0)?.toString() ?? "DOT",
tokenDecimal: tokenDecimals.at(0)?.toNumber() ?? 10,
ss58Format: properties.ss58Format.unwrapOr(null)?.toNumber() ?? null,
}
}

export const erc20TokenToAssetLocation = (registry: Registry, ethChainId: bigint, token: string) => {
return registry.createType('StagingXcmV3MultiLocation', {
parents: 2,
interior: {
X2: [
{ GlobalConsensus: { Ethereum: { chain_id: ethChainId } } },
{ AccountKey20: { key: token } },
]
}
})
}

export const assetStatusInfo = async (context: Context, tokenAddress: string, ownerAddress?: string) => {
let [ethereumNetwork, isTokenRegistered] = await Promise.all([
context.ethereum.api.getNetwork(),
context.ethereum.contracts.gateway.isTokenRegistered(tokenAddress)
])

const ethereumChainId = ethereumNetwork.chainId
const multiLocation = erc20TokenToAssetLocation(context.polkadot.api.assetHub.registry, ethereumChainId, tokenAddress)
const foreignAsset = (await context.polkadot.api.assetHub.query.foreignAssets.asset(multiLocation)).toPrimitive() as { status: 'Live' }

const tokenContract = IERC20__factory.connect(tokenAddress, context.ethereum.api)
let ownerBalance = BigInt(0)
let tokenGatewayAllowance = BigInt(0)
let isValidERC20 = true
try {
const erc20balance = await assetErc20Balance(context, tokenAddress, ownerAddress ?? '0x0000000000000000000000000000000000000000')
ownerBalance = erc20balance.balance
tokenGatewayAllowance = erc20balance.gatewayAllowance

} catch {
isValidERC20 = false
}

return {
ethereumChainId,
multiLocation,
isValidERC20,
tokenContract,
isTokenRegistered,
tokenGatewayAllowance,
ownerBalance,
foreignAssetExists: foreignAsset !== null,
foreignAsset,
}
}

export const assetErc20Balance = async (context: Context, token: string, owner: string): Promise<{
balance: bigint,
gatewayAllowance: bigint,
}> => {
const tokenContract = IERC20__factory.connect(token, context.ethereum.api)
const gateway = await context.ethereum.contracts.gateway.getAddress()
const [balance, gatewayAllowance] = await Promise.all([
tokenContract.balanceOf(owner),
tokenContract.allowance(owner, gateway),
])
return {
balance, gatewayAllowance
}
}

export const assetErc20Metadata = async (context: Context, tokenAddress: string) => {
const tokenMetadata = IERC20Metadata__factory.connect(tokenAddress, context.ethereum.api)
const [name, symbol, decimals] = await Promise.all([
tokenMetadata.name(),
tokenMetadata.symbol(),
tokenMetadata.decimals(),
])
return { name, symbol, decimals }
}

export const palletAssetsBalance = async (api: ApiPromise, location: Codec, address: string, instance = "assets"): Promise<bigint | null> => {
let account = (await api.query[instance].account(location, address)).toPrimitive() as any
if (account !== null) {
return BigInt(account.balance)
}
return null
}
6 changes: 3 additions & 3 deletions web/packages/api/src/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export type Config = {

export type SourceType = 'substrate' | 'ethereum'
export type Relayer = { name: string, account: string, type: SourceType }
export type ParachainInfo = { paraId: number, destinationFeeDOT: bigint, has20ByteAccounts: boolean, decimals: number }
export type ParachainInfo = { paraId: number, destinationFeeDOT: bigint, has20ByteAccounts: boolean, decimals: number, ss58Format?: number }
export type TransferLocation = {
id: string
name: string
Expand Down Expand Up @@ -77,7 +77,7 @@ export const SNOWBRIDGE_ENV: { [id: string]: SnowbridgeEnvironment } = {
}],
config: {
BEACON_HTTP_API: 'http://127.0.0.1:9596',
ETHEREUM_WS_API: (_) => 'ws://127.0.0.1:8546',
ETHEREUM_WS_API: () => 'ws://127.0.0.1:8546',
RELAY_CHAIN_WS_URL: 'ws://127.0.0.1:9944',
ASSET_HUB_WS_URL: 'ws://127.0.0.1:12144',
BRIDGE_HUB_WS_URL: 'ws://127.0.0.1:11144',
Expand Down Expand Up @@ -139,7 +139,7 @@ export const SNOWBRIDGE_ENV: { [id: string]: SnowbridgeEnvironment } = {
destinationIds: [],
paraInfo: {
paraId: 3369,
destinationFeeDOT: 4_000_000_000n,
destinationFeeDOT: 4_000_000_000_000n,
has20ByteAccounts: true,
decimals: 12,
},
Expand Down
1 change: 1 addition & 0 deletions web/packages/api/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,4 +135,5 @@ export * as toPolkadot from './toPolkadot'
export * as toEthereum from './toEthereum'
export * as utils from './utils'
export * as status from './status'
export * as assets from './assets'
export * as environment from './environment'
5 changes: 2 additions & 3 deletions web/packages/api/src/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const scanSubstrateEvents = async (

export const waitForMessageQueuePallet = async (
parachain: ApiPromise,
messageId: string | undefined,
messageId: string,
siblingParachain: number,
eventFilter: (event: Codec) => boolean,
options = {
Expand All @@ -50,8 +50,7 @@ export const waitForMessageQueuePallet = async (
for (const event of (events as any)) {
let eventData = event.event.toPrimitive().data
if (parachain.events.messageQueue.Processed.is(event.event)
// TODO: Use SetTopic to forward the message id to the destination chain and then remove undefined check.
&& (messageId === undefined || eventData[0].toLowerCase() === messageId.toLowerCase())
&& eventData[0].toLowerCase() === messageId.toLowerCase()
&& eventData[1]?.sibling === siblingParachain) {

foundMessageQueue = true
Expand Down
59 changes: 0 additions & 59 deletions web/packages/api/src/status.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { IERC20Metadata__factory, IERC20__factory } from '@snowbridge/contract-types'
import { Context } from './index'
import { fetchBeaconSlot } from './utils'

Expand Down Expand Up @@ -109,61 +108,3 @@ export const channelStatusInfo = async (context: Context, channelId: string): Pr
},
}
}

export const assetStatusInfo = async (context: Context, tokenAddress: string, ownerAddress?: string) => {
let [ethereumNetwork, gatewayAddress, isTokenRegistered] = await Promise.all([
context.ethereum.api.getNetwork(),
context.ethereum.contracts.gateway.getAddress(),
context.ethereum.contracts.gateway.isTokenRegistered(tokenAddress)
])

const ethereumChainId = ethereumNetwork.chainId
const multiLocation = context.polkadot.api.assetHub.createType('StagingXcmV3MultiLocation', {
parents: 2,
interior: {
X2: [
{ GlobalConsensus: { Ethereum: { chain_id: ethereumChainId } } },
{ AccountKey20: { key: tokenAddress } },
]
}
})
const foreignAsset = (await context.polkadot.api.assetHub.query.foreignAssets.asset(multiLocation)).toPrimitive() as { status: 'Live' }

const tokenContract = IERC20__factory.connect(tokenAddress, context.ethereum.api)
let ownerBalance = BigInt(0)
let tokenGatewayAllowance = BigInt(0)
let isValidERC20 = true
try {
const owner = ownerAddress || "0x0000000000000000000000000000000000000000"
const [tokenBalance_, tokenGatewayAllowance_] = await Promise.all([
tokenContract.balanceOf(owner),
tokenContract.allowance(owner, gatewayAddress),
])
ownerBalance = tokenBalance_
tokenGatewayAllowance = tokenGatewayAllowance_
} catch {
isValidERC20 = false
}

return {
ethereumChainId,
multiLocation,
isValidERC20,
tokenContract,
isTokenRegistered,
tokenGatewayAllowance,
ownerBalance,
foreignAssetExists: foreignAsset !== null,
foreignAsset,
}
}

export const assetMetadata = async (context: Context, tokenAddress: string) => {
const tokenMetadata = IERC20Metadata__factory.connect(tokenAddress, context.ethereum.api)
const [name, symbol, decimal] = await Promise.all([
tokenMetadata.name(),
tokenMetadata.symbol(),
tokenMetadata.decimals(),
])
return { name, symbol, decimal }
}
Loading

0 comments on commit 18fe233

Please sign in to comment.