Skip to content

Commit 14ac83d

Browse files
Adds NeuroWeb to Paseo (#1552)
* adds neuroweb * neuroweb to registry * add neuroweb transfer op * neuroweb api progress * trac progress * trac progress * bump versions * remove snowtrac wrapping * revert logs * Revert "remove snowtrac wrapping" This reverts commit b88d23e. * update wrap trac script * adds unwrap * fixes * revert * revert versions
1 parent 4775f18 commit 14ac83d

File tree

9 files changed

+383
-7
lines changed

9 files changed

+383
-7
lines changed

web/packages/api/src/environment.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@ export const SNOWBRIDGE_ENV: { [id: string]: SnowbridgeEnvironment } = {
260260
"1000": "wss://asset-hub-paseo-rpc.dwellir.com",
261261
"1002": "wss://bridge-hub-paseo.dotters.network",
262262
"3369": "wss://paseo-muse-rpc.polkadot.io",
263+
"2043": `wss://parachain-testnet-rpc.origin-trail.network`,
263264
},
264265
GATEWAY_CONTRACT: "0x1607C1368bc943130258318c91bBd8cFf3D063E6",
265266
BEEFY_CONTRACT: "0x2c780945beb1241fE9c645800110cb9C4bBbb639",

web/packages/api/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export * as toEthereumFromEVMV2 from "./toEthereumFromEVM_v2"
2323
export * as parachains from "./parachains"
2424
export * as xcmBuilder from "./xcmBuilder"
2525
export * as toEthereumSnowbridgeV2 from "./toEthereumSnowbridgeV2"
26+
export * as neuroWeb from "./parachains/neuroweb"
2627

2728
interface Parachains {
2829
[paraId: string]: ApiPromise

web/packages/api/src/parachains/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { AcalaParachain } from "./acala"
1111
import { FrequencyParachain } from "./frequency"
1212
import { PenpalParachain } from "./penpal"
1313
import { JamtonParachain } from "./jamton"
14+
import { NeurowebParachain } from "./neuroweb"
1415

1516
export async function paraImplementation(provider: ApiPromise): Promise<ParachainBase> {
1617
let parachainId = 0
@@ -55,6 +56,8 @@ export async function paraImplementation(provider: ApiPromise): Promise<Parachai
5556
return new GenericChain(provider, parachainId, specName, specVersion)
5657
case "jamton-runtime":
5758
return new JamtonParachain(provider, parachainId, specName, specVersion)
59+
case "origintrail-parachain":
60+
return new NeurowebParachain(provider, parachainId, specName, specVersion)
5861
default:
5962
throw Error(
6063
`No parachain provider for ParaId = ${parachainId}, Spec = ${specName}, Version = ${specVersion}`
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import { ParachainBase } from "./parachainBase"
2+
import { DOT_LOCATION, erc20Location } from "../xcmBuilder"
3+
import { PNAMap } from "../assets_v2"
4+
import { AssetMap } from "@snowbridge/base-types"
5+
import { ApiPromise } from "@polkadot/api"
6+
import { SubmittableExtrinsic } from "@polkadot/api/types"
7+
import { ISubmittableResult } from "@polkadot/types/types"
8+
9+
export const NEUROWEB_TEST_CHAIN_ID = 11155111 // Sepolia
10+
export const NEUROWEB_TEST_TOKEN_ID = "0xef32abea56beff54f61da319a7311098d6fbcea9"
11+
export const NEUROWEB_CHAIN_ID = 1 // Ethereum Mainnet
12+
export const NEUROWEB_TOKEN_ID = "0xaa7a9ca87d3694b5755f213b5d04094b8d0f0a6f"
13+
14+
export class NeurowebParachain extends ParachainBase {
15+
getXC20DOT() {
16+
return undefined
17+
}
18+
19+
async getLocationBalance(location: any, account: string, _pnaAssetId?: any): Promise<bigint> {
20+
const accountData = (
21+
await this.provider.query.foreignAssets.account(location, account)
22+
).toPrimitive() as any
23+
return BigInt(accountData?.balance ?? 0n)
24+
}
25+
26+
async getDotBalance(account: string): Promise<bigint> {
27+
const accountData = (
28+
await this.provider.query.foreignAssets.account(DOT_LOCATION, account)
29+
).toPrimitive() as any
30+
return BigInt(accountData?.balance ?? 0n)
31+
}
32+
33+
async getAssets(ethChainId: number, _pnas: PNAMap): Promise<AssetMap> {
34+
const assets: AssetMap = {}
35+
if (this.specName !== "origintrail-parachain") {
36+
throw Error(
37+
`Cannot get balance for spec ${this.specName}. Location = ${JSON.stringify(
38+
location
39+
)}`
40+
)
41+
}
42+
43+
if (ethChainId === NEUROWEB_TEST_CHAIN_ID) {
44+
assets[NEUROWEB_TEST_TOKEN_ID.toLowerCase()] = {
45+
token: NEUROWEB_TEST_TOKEN_ID.toLowerCase(),
46+
name: "Trac",
47+
minimumBalance: 1_000_000_000_000_000n,
48+
symbol: "TRAC",
49+
decimals: 18,
50+
isSufficient: true,
51+
}
52+
} else if (ethChainId === NEUROWEB_CHAIN_ID) {
53+
assets[NEUROWEB_TOKEN_ID.toLowerCase()] = {
54+
token: NEUROWEB_TOKEN_ID.toLowerCase(),
55+
name: "Trac",
56+
minimumBalance: 1_000_000_000_000_000n,
57+
symbol: "TRAC",
58+
decimals: 18,
59+
isSufficient: true,
60+
}
61+
} else {
62+
throw Error(`Cannot get assets for chain ID ${ethChainId}.`)
63+
}
64+
return assets
65+
}
66+
67+
async calculateXcmFee(destinationXcm: any, asset: any): Promise<bigint> {
68+
if (JSON.stringify(asset) == JSON.stringify(DOT_LOCATION)) {
69+
console.warn(
70+
`${this.specName} does not support calculating fee for asset '${JSON.stringify(
71+
asset
72+
)}'. Using default.`
73+
)
74+
return 10_000_000_000n // TODO
75+
}
76+
return await this.calculateXcmFee(destinationXcm, asset)
77+
}
78+
79+
async wrapExecutionFeeInNative(parachain: ApiPromise) {
80+
// Mock transaction to get extrinsic fee
81+
let tx = parachain.tx.wrapper.tracWrap(100000000)
82+
const paymentInfo = await tx.paymentInfo(
83+
"0x0000000000000000000000000000000000000000000000000000000000000000"
84+
)
85+
const executionFee = paymentInfo["partialFee"].toBigInt()
86+
console.log("wrap execution fee:", executionFee)
87+
return executionFee
88+
}
89+
90+
async unwrapExecutionFeeInNative(parachain: ApiPromise) {
91+
// Mock transaction to get extrinsic fee
92+
let tx = parachain.tx.wrapper.tracUnwrap(100000000)
93+
const paymentInfo = await tx.paymentInfo(
94+
"0x0000000000000000000000000000000000000000000000000000000000000000"
95+
)
96+
const executionFee = paymentInfo["partialFee"].toBigInt()
97+
console.log("unwrap execution fee:", executionFee)
98+
return executionFee
99+
}
100+
101+
snowTRACBalance(account: string, ethChainId: number) {
102+
if (ethChainId === NEUROWEB_TEST_CHAIN_ID) {
103+
return this.getLocationBalance(
104+
erc20Location(ethChainId, NEUROWEB_TEST_TOKEN_ID),
105+
account
106+
)
107+
} else if (ethChainId === NEUROWEB_CHAIN_ID) {
108+
return this.getLocationBalance(erc20Location(ethChainId, NEUROWEB_TOKEN_ID), account)
109+
} else {
110+
throw Error(`Cannot get snowTRAC balance for chain ID ${ethChainId}.`)
111+
}
112+
}
113+
114+
async tracBalance(account: string) {
115+
const accountData = (
116+
await this.provider.query.assets.account(1, account)
117+
).toPrimitive() as any
118+
return BigInt(accountData?.balance ?? 0n)
119+
}
120+
121+
createWrapTx(
122+
parachain: ApiPromise,
123+
amount: bigint
124+
): SubmittableExtrinsic<"promise", ISubmittableResult> {
125+
return parachain.tx.wrapper.tracWrap(amount)
126+
}
127+
128+
createUnwrapTx(
129+
parachain: ApiPromise,
130+
amount: bigint
131+
): SubmittableExtrinsic<"promise", ISubmittableResult> {
132+
return parachain.tx.wrapper.tracUnwrap(amount)
133+
}
134+
}

web/packages/operations/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,11 @@
4848
"transferWndToEthereumV2WithFee": "npx ts-node src/transfer_from_p2e_v2_with_fee.ts 1000 WND 0 2000000000",
4949
"transferWEthFromAHV2WithFee": "npx ts-node src/transfer_from_p2e_v2_with_fee.ts 1000 WETH 0 100000000000000",
5050
"transferPenpalTokenToEthereumV2WithFee": "npx ts-node src/transfer_from_p2e_v2_with_fee.ts 2000 pal 0 2000000000",
51-
"transferPenpalTokenToEthereumV2WithNativeFee": "npx ts-node src/transfer_from_p2e_v2_with_fee.ts 2000 pal 1 2000000000"
51+
"transferPenpalTokenToEthereumV2WithNativeFee": "npx ts-node src/transfer_from_p2e_v2_with_fee.ts 2000 pal 1 2000000000",
52+
"transferTRACToNeuroWeb": "npx ts-node src/transfer_from_e2p.ts 2043 TRACE 3000000000000000",
53+
"transferTRACFromNeuroWebtoEthereum": "npx ts-node src/transfer_from_p2e.ts 2043 TRACE 3000000000000000",
54+
"wrapSnowTRAC": "npx ts-node src/wrap_snow_trac.ts",
55+
"unwrapSnowTRAC": "npx ts-node src/unwrap_snow_trac.ts"
5256
},
5357
"devDependencies": {
5458
"@typescript-eslint/eslint-plugin": "^5.62.0",

web/packages/operations/src/transfer_to_polkadot.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Context, contextConfigFor, environment, toPolkadotV2 } from "@snowbridg
33
import { formatEther, Wallet } from "ethers"
44
import { cryptoWaitReady } from "@polkadot/util-crypto"
55
import { assetRegistryFor } from "@snowbridge/registry"
6-
import { WETH9__factory } from "@snowbridge/contract-types"
6+
import {IERC20__factory, WETH9__factory} from "@snowbridge/contract-types"
77

88
export const transferToPolkadot = async (
99
destinationChainId: number,
@@ -56,6 +56,37 @@ export const transferToPolkadot = async (
5656

5757
console.log("deposit tx", depositReceipt?.hash, "approve tx", approveReceipt?.hash)
5858
}
59+
} else if (symbol.toLowerCase().startsWith("trac")) {
60+
console.log("# Approve TRAC (two-step approval)")
61+
const erc20 = IERC20__factory.connect(TOKEN_CONTRACT, ETHEREUM_ACCOUNT);
62+
const [balance, allowance] = await Promise.all([
63+
erc20.balanceOf(ETHEREUM_ACCOUNT_PUBLIC),
64+
erc20.allowance(ETHEREUM_ACCOUNT_PUBLIC, registry.gatewayAddress),
65+
]);
66+
67+
if (allowance < amount) {
68+
// Step 1: Reset allowance to 0 (required by this ERC20 implementation)
69+
console.log("Resetting allowance to 0...")
70+
const resetTx = await erc20.approve(context.config.appContracts.gateway, 0n);
71+
await resetTx.wait();
72+
73+
// Step 2: Set new allowance (higher than transfer amount for gateway fees)
74+
const approveAmount = amount * 5n; // 5x buffer for gateway operations
75+
console.log("Setting new allowance to", approveAmount.toString());
76+
const approveTx = await erc20.approve(context.config.appContracts.gateway, approveAmount);
77+
await approveTx.wait();
78+
79+
const newAllowance = await erc20.allowance(ETHEREUM_ACCOUNT_PUBLIC, context.config.appContracts.gateway);
80+
console.log("newAllowance", newAllowance.toString());
81+
}
82+
83+
console.log("token", TOKEN_CONTRACT);
84+
console.log("gateway", registry.gatewayAddress);
85+
console.log("context gateway", context.config.appContracts.gateway);
86+
console.log("owner", ETHEREUM_ACCOUNT_PUBLIC);
87+
console.log("balance", balance.toString());
88+
console.log("allowance", allowance.toString());
89+
console.log("amount", amount.toString());
5990
}
6091

6192
console.log("# Ethereum to Asset Hub")
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { Keyring } from "@polkadot/keyring"
2+
import { Context, contextConfigFor } from "@snowbridge/api"
3+
import { cryptoWaitReady } from "@polkadot/util-crypto"
4+
import { assetRegistryFor } from "@snowbridge/registry"
5+
import { NeurowebParachain } from "@snowbridge/api/dist/parachains/neuroweb"
6+
7+
const unwrapSnowTRAC = async () => {
8+
await cryptoWaitReady()
9+
10+
let env = "local_e2e"
11+
if (process.env.NODE_ENV !== undefined) {
12+
env = process.env.NODE_ENV
13+
}
14+
console.log(`Using environment '${env}'`)
15+
16+
const context = new Context(contextConfigFor(env))
17+
18+
const polkadot_keyring = new Keyring({ type: "sr25519" })
19+
20+
const POLKADOT_ACCOUNT = polkadot_keyring.addFromUri(process.env.SUBSTRATE_KEY ?? "//Ferdie")
21+
const POLKADOT_ACCOUNT_PUBLIC = POLKADOT_ACCOUNT.address
22+
23+
console.log("sub", POLKADOT_ACCOUNT_PUBLIC)
24+
25+
const registry = assetRegistryFor(env)
26+
const neuroWebParaId = 2043
27+
28+
if (!registry.parachains[neuroWebParaId]) {
29+
throw Error("Neuroweb parachain config not set in registry")
30+
}
31+
const parachainInfo = registry.parachains[neuroWebParaId].info
32+
33+
console.log("Wrap SnowTRAC to TRAC")
34+
{
35+
const parachain = await context.parachain(neuroWebParaId)
36+
const neuroWeb = new NeurowebParachain(
37+
parachain,
38+
neuroWebParaId,
39+
parachainInfo.specName,
40+
parachainInfo.specVersion
41+
)
42+
43+
const fee = await neuroWeb.unwrapExecutionFeeInNative(parachain)
44+
console.log("Execution fee:", fee)
45+
46+
const balance = await neuroWeb.tracBalance(POLKADOT_ACCOUNT_PUBLIC)
47+
console.log("SnowTRAC balance:", balance)
48+
49+
if (balance == 0n) {
50+
console.error("SnowTRAC balance is 0, nothing to wrap")
51+
process.exit(1)
52+
}
53+
54+
const wrapTx = neuroWeb.createUnwrapTx(parachain, balance)
55+
56+
/* await wrapTx.signAndSend(POLKADOT_ACCOUNT, { nonce: -1 }, (result) => {
57+
console.log(`Transaction status: ${result.status}`)
58+
if (result.status.isInBlock) {
59+
console.log(`Transaction included in block: ${result.status.asInBlock}`)
60+
} else if (result.status.isFinalized) {
61+
console.log(`Transaction finalized: ${result.status.asFinalized}`)
62+
process.exit(0)
63+
}
64+
})*/
65+
}
66+
}
67+
68+
if (process.argv.length != 2) {
69+
console.error("Invalid arguments")
70+
process.exit(1)
71+
}
72+
73+
unwrapSnowTRAC()
74+
.then(() => process.exit(0))
75+
.catch((error) => {
76+
console.error("Error:", error)
77+
process.exit(1)
78+
})
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { Keyring } from "@polkadot/keyring"
2+
import { Context, contextConfigFor } from "@snowbridge/api"
3+
import { cryptoWaitReady } from "@polkadot/util-crypto"
4+
import { assetRegistryFor } from "@snowbridge/registry"
5+
import { NeurowebParachain } from "@snowbridge/api/dist/parachains/neuroweb"
6+
7+
const wrapSnowTRAC = async () => {
8+
await cryptoWaitReady()
9+
10+
let env = "local_e2e"
11+
if (process.env.NODE_ENV !== undefined) {
12+
env = process.env.NODE_ENV
13+
}
14+
console.log(`Using environment '${env}'`)
15+
16+
const context = new Context(contextConfigFor(env))
17+
18+
const polkadot_keyring = new Keyring({ type: "sr25519" })
19+
20+
const POLKADOT_ACCOUNT = polkadot_keyring.addFromUri(process.env.SUBSTRATE_KEY ?? "//Ferdie")
21+
const POLKADOT_ACCOUNT_PUBLIC = POLKADOT_ACCOUNT.address
22+
23+
console.log("sub", POLKADOT_ACCOUNT_PUBLIC)
24+
25+
const registry = assetRegistryFor(env)
26+
const neuroWebParaId = 2043
27+
28+
if (!registry.parachains[neuroWebParaId]) {
29+
throw Error("Neuroweb parachain config not set in registry")
30+
}
31+
const parachainInfo = registry.parachains[neuroWebParaId].info
32+
33+
console.log("Wrap SnowTRAC to TRAC")
34+
{
35+
const parachain = await context.parachain(neuroWebParaId)
36+
const neuroWeb = new NeurowebParachain(
37+
parachain,
38+
neuroWebParaId,
39+
parachainInfo.specName,
40+
parachainInfo.specVersion
41+
)
42+
43+
const fee = await neuroWeb.wrapExecutionFeeInNative(parachain)
44+
console.log("Execution fee:", fee)
45+
46+
const balance = await neuroWeb.snowTRACBalance(POLKADOT_ACCOUNT_PUBLIC, registry.ethChainId)
47+
console.log("SnowTRAC balance:", balance)
48+
49+
if (balance == 0n) {
50+
console.error("SnowTRAC balance is 0, nothing to wrap")
51+
process.exit(1)
52+
}
53+
54+
const wrapTx = neuroWeb.createWrapTx(parachain, balance)
55+
56+
await wrapTx.signAndSend(POLKADOT_ACCOUNT, { nonce: -1 }, (result) => {
57+
console.log(`Transaction status: ${result.status}`)
58+
if (result.status.isInBlock) {
59+
console.log(`Transaction included in block: ${result.status.asInBlock}`)
60+
} else if (result.status.isFinalized) {
61+
console.log(`Transaction finalized: ${result.status.asFinalized}`)
62+
process.exit(0)
63+
}
64+
})
65+
}
66+
}
67+
68+
if (process.argv.length != 2) {
69+
console.error("Invalid arguments")
70+
process.exit(1)
71+
}
72+
73+
wrapSnowTRAC()
74+
.then(() => process.exit(0))
75+
.catch((error) => {
76+
console.error("Error:", error)
77+
process.exit(1)
78+
})

0 commit comments

Comments
 (0)