Skip to content

Commit

Permalink
Merge branch 'bridge-next-gen' into ron/polkadot-assets-on-ethereum
Browse files Browse the repository at this point in the history
  • Loading branch information
yrong committed Apr 9, 2024
2 parents b2a666a + 61f9f6d commit 17f328b
Show file tree
Hide file tree
Showing 46 changed files with 16,242 additions and 3,404 deletions.
61 changes: 61 additions & 0 deletions .github/workflows/npm-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
name: npm-publish

on:
release:
types: [published]

env:
NPM_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}

jobs:
publish:
runs-on: snowbridge-runner
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 2

- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1

- name: Build Contracts
working-directory: contracts
run: |
forge build
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
registry-url: 'https://registry.npmjs.org'

- name: Install pnpm
uses: pnpm/action-setup@v3
with:
version: 8

- name: Build Contract Types
working-directory: web/packages/contract-types
run: |
pnpm install
pnpm typechain
pnpm build
- name: Build API
working-directory: web/packages/api
run: |
pnpm install
pnpm build
- name: Publish Contract Types
working-directory: web/packages/contract-types
run: |
pnpm publish
- name: Publish API
working-directory: web/packages/api
run: |
pnpm publish
3 changes: 3 additions & 0 deletions web/.npmrc
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
store-dir=.pnpm-store/
//registry.npmjs.org/:_authToken=${NPM_AUTH_TOKEN}
always-auth=false
access=public
14 changes: 7 additions & 7 deletions web/packages/api/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@snowbridge/api",
"version": "1.0.0",
"version": "0.1.2",
"description": "Snowbridge API client",
"license": "Apache-2.0",
"repository": {
Expand All @@ -26,14 +26,14 @@
"typescript": "^5.1.6"
},
"dependencies": {
"@ethersproject/abi": "^5.7.0",
"@ethersproject/bytes": "^5.7.0",
"@ethersproject/providers": "^5.7.0",
"@ethersproject/units": "^5.7.0",
"ethers": "^6.9.0",
"@polkadot/api": "^10.12.1",
"@polkadot/types": "^10.12.1",
"@polkadot/util": "^12.6.2",
"@polkadot/util-crypto": "^12.6.2",
"@polkadot/keyring": "^12.6.2",
"@snowbridge/contract-types": "workspace:*",
"@typechain/ethers-v5": "^11.1.1",
"ethers": "^5.7.0"
"@typechain/ethers-v6": "^0.5.1",
"rxjs": "^7.8.1"
}
}
110 changes: 89 additions & 21 deletions web/packages/api/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,63 +1,131 @@
// import "@polkadot/api-augment/polkadot"
import { ApiPromise, WsProvider } from "@polkadot/api"
import { ethers } from "ethers"
import { IGateway, IGateway__factory } from "@snowbridge/contract-types"
// import '@polkadot/api-augment/polkadot'
import { ApiPromise, WsProvider } from '@polkadot/api'
import { ethers } from 'ethers'
import { BeefyClient, BeefyClient__factory, IGateway, IGateway__factory } from '@snowbridge/contract-types'

interface Config {
ethereum: {
url: string
}
polkadot: {
url: string
url: {
bridgeHub: string
assetHub: string
relaychain: string
parachains?: string[]
}
}
appContracts: {
gateway: string
beefy: string
}
}

interface AppContracts {
gateway: IGateway
beefyClient: BeefyClient
}

class Context {
export class Context {
config: Config
ethereum: EthereumContext
polkadot: PolkadotContext

constructor(ethereum: EthereumContext, polkadot: PolkadotContext) {
constructor(config: Config, ethereum: EthereumContext, polkadot: PolkadotContext) {
this.config = config
this.ethereum = ethereum
this.polkadot = polkadot
}
}

class EthereumContext {
api: ethers.providers.WebSocketProvider
api: ethers.WebSocketProvider
contracts: AppContracts

constructor(api: ethers.providers.WebSocketProvider, contracts: AppContracts) {
constructor(api: ethers.WebSocketProvider, contracts: AppContracts) {
this.api = api
this.contracts = contracts
}
}

class PolkadotContext {
api: ApiPromise
constructor(api: ApiPromise) {
this.api = api
api: {
relaychain: ApiPromise
assetHub: ApiPromise
bridgeHub: ApiPromise
parachains: { [paraId: number]: ApiPromise }
}
constructor(relaychain: ApiPromise, assetHub: ApiPromise, bridgeHub: ApiPromise) {
this.api = {
relaychain: relaychain,
assetHub: assetHub,
bridgeHub: bridgeHub,
parachains: {}
}
}
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const contextFactory = async (config: Config): Promise<Context> => {
let ethApi = new ethers.providers.WebSocketProvider(config.ethereum.url)
let polApi = await ApiPromise.create({
provider: new WsProvider(config.polkadot.url),
export const contextFactory = async (config: Config): Promise<Context> => {
const ethApi = new ethers.WebSocketProvider(config.ethereum.url)
const relaychainApi = await ApiPromise.create({
provider: new WsProvider(config.polkadot.url.relaychain),
})
const assetHubApi = await ApiPromise.create({
provider: new WsProvider(config.polkadot.url.assetHub),
})
const bridgeHubApi = await ApiPromise.create({
provider: new WsProvider(config.polkadot.url.bridgeHub),
})

let gatewayAddr = process.env["GatewayProxyAddress"]!
const gatewayAddr = config.appContracts.gateway
const beefyAddr = config.appContracts.beefy

let appContracts: AppContracts = {
const appContracts: AppContracts = {
//TODO: Get gateway address from bridgehub
gateway: IGateway__factory.connect(gatewayAddr, ethApi),
//TODO: Get beefy client from gateway
beefyClient: BeefyClient__factory.connect(beefyAddr, ethApi),
}

let ethCtx = new EthereumContext(ethApi, appContracts)
let polCtx = new PolkadotContext(polApi)
const ethCtx = new EthereumContext(ethApi, appContracts)
const polCtx = new PolkadotContext(relaychainApi, assetHubApi, bridgeHubApi)

return new Context(ethCtx, polCtx)
const context = new Context(config, ethCtx, polCtx)
for (const parachain of config.polkadot.url.parachains ?? []) {
await addParachainConnection(context, parachain)
}
return context
}

export const addParachainConnection = async (context: Context, url: string): Promise<void> => {
const api = await ApiPromise.create({
provider: new WsProvider(url)
})
const paraId = (await api.query.parachainInfo.parachainId()).toPrimitive() as number
if (paraId in context.polkadot.api.parachains) {
throw new Error(`${paraId} already added.`)
}
context.polkadot.api.parachains[paraId] = api
console.log(`${url} added with parachain id: ${paraId}`)
}

export const destroyContext = async (context: Context): Promise<void> => {
// clean up etheruem
await context.ethereum.contracts.beefyClient.removeAllListeners()
await context.ethereum.contracts.gateway.removeAllListeners()
await context.ethereum.api.destroy()
// clean up polkadot
await context.polkadot.api.relaychain.disconnect()
await context.polkadot.api.bridgeHub.disconnect()
await context.polkadot.api.assetHub.disconnect()

for (const paraId of Object.keys(context.polkadot.api.parachains)) {
await context.polkadot.api.parachains[Number(paraId)].disconnect()
}
}

export * as toPolkadot from './toPolkadot'
export * as toEthereum from './toEthereum'
export * as utils from './utils'
export * as status from './status'
52 changes: 52 additions & 0 deletions web/packages/api/src/query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { ApiPromise } from '@polkadot/api'
import { Codec } from '@polkadot/types/types'
import { filter, firstValueFrom, take } from 'rxjs'

export const waitForMessageQueuePallet = async (
parachain: ApiPromise,
messageId: string | undefined,
siblingParachain: number,
eventFilter: (event: Codec) => boolean,
options = {
scanBlocks: 10,
}
): Promise<{ foundEvent?: Codec, allEvents: Codec, extrinsicSuccess: boolean }> => {
let extrinsicSuccess = false
let returnEvent = undefined
let receivedEvents = await firstValueFrom(
parachain.rx.query.system.events().pipe(
take(options.scanBlocks),
filter(events => {
let foundMessageQueue = false
let foundEvent = false
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[1]?.sibling === siblingParachain) {

foundMessageQueue = true
extrinsicSuccess = eventData[3]
}

if (eventFilter(event)) {
foundEvent = true
returnEvent = event
}
}
return foundMessageQueue && ((extrinsicSuccess && foundEvent) || !extrinsicSuccess)
}),
),
{ defaultValue: undefined }
)

if (receivedEvents === undefined) {
throw Error('Timeout while waiting for event.')
}
return {
foundEvent: returnEvent,
allEvents: receivedEvents,
extrinsicSuccess: extrinsicSuccess
}
}
Loading

0 comments on commit 17f328b

Please sign in to comment.