Initial commit
This commit is contained in:
58
packages/bridge/README.md
Normal file
58
packages/bridge/README.md
Normal file
@@ -0,0 +1,58 @@
|
||||
# @dbis-thirdweb/bridge
|
||||
|
||||
Bridge routes and execution for Chain 138.
|
||||
|
||||
## Usage
|
||||
|
||||
### Get Supported Routes
|
||||
|
||||
```typescript
|
||||
import { getSupportedRoutes, isRouteSupported } from '@dbis-thirdweb/bridge';
|
||||
|
||||
const routes = getSupportedRoutes();
|
||||
const isSupported = isRouteSupported(1, 138); // Ethereum to Chain 138
|
||||
```
|
||||
|
||||
### Generate Bridge Quote
|
||||
|
||||
```typescript
|
||||
import { generateBridgeQuote, getAllBridgeableTokens } from '@dbis-thirdweb/bridge';
|
||||
import { ethers } from 'ethers';
|
||||
|
||||
const tokens = getAllBridgeableTokens();
|
||||
const nativeToken = tokens.find(t => t.isNative);
|
||||
|
||||
const quote = await generateBridgeQuote({
|
||||
fromChainId: 1,
|
||||
toChainId: 138,
|
||||
token: nativeToken!,
|
||||
amount: ethers.utils.parseEther('0.1'),
|
||||
slippageBps: 50, // 0.5%
|
||||
});
|
||||
```
|
||||
|
||||
### Execute Bridge
|
||||
|
||||
```typescript
|
||||
import { executeBridge, getBridgeStatus } from '@dbis-thirdweb/bridge';
|
||||
|
||||
const status = await executeBridge(quote, signer, provider);
|
||||
|
||||
// Poll for status
|
||||
const finalStatus = await getBridgeStatus(status.sourceTxHash, provider);
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Canonical chain mapping for Chain 138
|
||||
- Supported routes configuration
|
||||
- Token lists (native, wrapped, stablecoins)
|
||||
- Quote generation with slippage protection
|
||||
- Bridge execution helpers
|
||||
- Status tracking and finality checks
|
||||
|
||||
## Notes
|
||||
|
||||
- This implementation uses simplified bridge logic
|
||||
- In production, integrate with thirdweb Bridge SDK or bridge provider APIs
|
||||
- Token addresses need to be configured for Chain 138's actual deployed tokens
|
||||
38
packages/bridge/package.json
Normal file
38
packages/bridge/package.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "@dbis-thirdweb/bridge",
|
||||
"version": "0.1.0",
|
||||
"description": "Bridge routes and execution for Chain 138",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.js"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsup src/index.ts --format cjs,esm --dts",
|
||||
"lint": "eslint src",
|
||||
"test": "echo \"No tests yet\""
|
||||
},
|
||||
"keywords": [
|
||||
"thirdweb",
|
||||
"bridge",
|
||||
"chain-138",
|
||||
"cross-chain"
|
||||
],
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@dbis-thirdweb/chain": "workspace:*",
|
||||
"@thirdweb-dev/sdk": "^4.0.0",
|
||||
"ethers": "^5.7.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.0.0",
|
||||
"tsup": "^8.0.0",
|
||||
"typescript": "^5.0.0"
|
||||
}
|
||||
}
|
||||
90
packages/bridge/src/execution.ts
Normal file
90
packages/bridge/src/execution.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import type { Signer, providers } from 'ethers';
|
||||
import type { BridgeQuote, BridgeStatus } from './types';
|
||||
import { validateQuote } from './quote';
|
||||
|
||||
/**
|
||||
* Execute bridge transaction
|
||||
* Note: This is a simplified implementation. In production, you'd use thirdweb Bridge SDK
|
||||
* or a bridge provider's SDK/API
|
||||
*/
|
||||
export async function executeBridge(
|
||||
quote: BridgeQuote,
|
||||
signer: Signer,
|
||||
provider: providers.Provider
|
||||
): Promise<BridgeStatus> {
|
||||
// Validate quote
|
||||
if (!validateQuote(quote)) {
|
||||
throw new Error('Invalid bridge quote');
|
||||
}
|
||||
|
||||
// In production, this would:
|
||||
// 1. Use thirdweb Bridge SDK: await bridge.bridge(quote)
|
||||
// 2. Or call bridge provider API
|
||||
// 3. Or interact with bridge contract directly
|
||||
|
||||
// Simplified implementation - send native transfer as placeholder
|
||||
// This should be replaced with actual bridge contract interaction
|
||||
const tx = await signer.sendTransaction({
|
||||
to: quote.token.address,
|
||||
value: quote.amount,
|
||||
// Bridge contract would have specific data/contract interaction here
|
||||
});
|
||||
|
||||
// Wait for transaction
|
||||
const receipt = await provider.waitForTransaction(tx.hash, 1);
|
||||
|
||||
if (!receipt || receipt.status !== 1) {
|
||||
throw new Error('Bridge transaction failed');
|
||||
}
|
||||
|
||||
return {
|
||||
sourceTxHash: receipt.transactionHash,
|
||||
status: 'processing', // Would track through bridge provider to get final status
|
||||
step: 'Transaction confirmed on source chain',
|
||||
sourceBlockNumber: receipt.blockNumber,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bridge status (polling for destination chain confirmation)
|
||||
*/
|
||||
export async function getBridgeStatus(
|
||||
sourceTxHash: string,
|
||||
provider: providers.Provider
|
||||
): Promise<BridgeStatus> {
|
||||
// In production, this would query the bridge provider API or monitor on-chain events
|
||||
const receipt = await provider.getTransactionReceipt(sourceTxHash);
|
||||
|
||||
if (!receipt) {
|
||||
return {
|
||||
sourceTxHash,
|
||||
status: 'pending',
|
||||
step: 'Waiting for source chain confirmation',
|
||||
};
|
||||
}
|
||||
|
||||
// Simplified - in production, you'd check bridge provider for destination chain status
|
||||
return {
|
||||
sourceTxHash,
|
||||
status: receipt.status === 1 ? 'processing' : 'failed',
|
||||
step: receipt.status === 1
|
||||
? 'Processing bridge - waiting for destination chain'
|
||||
: 'Bridge transaction failed',
|
||||
sourceBlockNumber: receipt.blockNumber,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check finality threshold for Chain 138
|
||||
*/
|
||||
export const FINALITY_THRESHOLD_BLOCKS = 1; // Adjust based on Chain 138's finality rules
|
||||
|
||||
/**
|
||||
* Check if bridge transaction has reached finality
|
||||
*/
|
||||
export async function checkFinality(
|
||||
blockNumber: number,
|
||||
currentBlockNumber: number
|
||||
): Promise<boolean> {
|
||||
return currentBlockNumber >= blockNumber + FINALITY_THRESHOLD_BLOCKS;
|
||||
}
|
||||
5
packages/bridge/src/index.ts
Normal file
5
packages/bridge/src/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export * from './types';
|
||||
export * from './routes';
|
||||
export * from './tokenLists';
|
||||
export * from './quote';
|
||||
export * from './execution';
|
||||
75
packages/bridge/src/quote.ts
Normal file
75
packages/bridge/src/quote.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import type { providers } from 'ethers';
|
||||
import { ethers } from 'ethers';
|
||||
import type { BridgeQuote, BridgeableToken } from './types';
|
||||
import { isRouteSupported } from './routes';
|
||||
import { chain138 } from '@dbis-thirdweb/chain';
|
||||
|
||||
/**
|
||||
* Generate bridge quote
|
||||
* Note: This is a simplified implementation. In production, you'd call a bridge provider API
|
||||
*/
|
||||
export async function generateBridgeQuote(params: {
|
||||
fromChainId: number;
|
||||
toChainId: number;
|
||||
token: BridgeableToken;
|
||||
amount: bigint;
|
||||
provider?: providers.Provider;
|
||||
slippageBps?: number; // Basis points (e.g., 100 = 1%)
|
||||
}): Promise<BridgeQuote> {
|
||||
const { fromChainId, toChainId, token, amount, slippageBps = 50 } = params;
|
||||
|
||||
// Validate route
|
||||
if (!isRouteSupported(fromChainId, toChainId)) {
|
||||
throw new Error(`Bridge route from ${fromChainId} to ${toChainId} is not supported`);
|
||||
}
|
||||
|
||||
// Simplified quote calculation
|
||||
// In production, this would call a bridge provider API (e.g., thirdweb Bridge API, Socket, etc.)
|
||||
const bridgeFeeBps = 10; // 0.1% fee (simplified)
|
||||
const fee = (amount * BigInt(bridgeFeeBps)) / BigInt(10000);
|
||||
const estimatedOutput = amount - fee;
|
||||
|
||||
// Apply slippage protection
|
||||
const minimumOutput = (estimatedOutput * BigInt(10000 - slippageBps)) / BigInt(10000);
|
||||
|
||||
// Estimated time (simplified - actual time depends on bridge provider and chains)
|
||||
const estimatedTimeSeconds = 300; // 5 minutes default
|
||||
|
||||
return {
|
||||
fromChainId,
|
||||
toChainId,
|
||||
token,
|
||||
amount,
|
||||
estimatedOutput,
|
||||
fee,
|
||||
estimatedTimeSeconds,
|
||||
minimumOutput,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate bridge quote
|
||||
*/
|
||||
export function validateQuote(quote: BridgeQuote): boolean {
|
||||
const amount = typeof quote.amount === 'bigint' ? quote.amount : BigInt(quote.amount.toString());
|
||||
const estimatedOutput = typeof quote.estimatedOutput === 'bigint' ? quote.estimatedOutput : BigInt(quote.estimatedOutput.toString());
|
||||
const minimumOutput = typeof quote.minimumOutput === 'bigint' ? quote.minimumOutput : BigInt(quote.minimumOutput.toString());
|
||||
|
||||
if (amount <= 0n) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (estimatedOutput <= 0n) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (minimumOutput > estimatedOutput) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isRouteSupported(quote.fromChainId, quote.toChainId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
59
packages/bridge/src/routes.ts
Normal file
59
packages/bridge/src/routes.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { chain138 } from '@dbis-thirdweb/chain';
|
||||
import type { BridgeRoute, BridgeableToken } from './types';
|
||||
|
||||
/**
|
||||
* Native token for Chain 138
|
||||
*/
|
||||
export const chain138NativeToken: BridgeableToken = {
|
||||
address: '0x0000000000000000000000000000000000000000',
|
||||
symbol: 'ETH',
|
||||
name: 'Ether',
|
||||
decimals: 18,
|
||||
isNative: true,
|
||||
};
|
||||
|
||||
/**
|
||||
* Canonical chain mapping: supported source chains for bridging to Chain 138
|
||||
* Add chain IDs that you want to support bridging FROM
|
||||
*/
|
||||
export const SUPPORTED_SOURCE_CHAINS = [
|
||||
1, // Ethereum Mainnet
|
||||
5, // Goerli
|
||||
137, // Polygon
|
||||
80001, // Mumbai
|
||||
// Add more chains as needed
|
||||
];
|
||||
|
||||
/**
|
||||
* Get supported bridge routes TO Chain 138
|
||||
*/
|
||||
export function getSupportedRoutes(): BridgeRoute[] {
|
||||
return SUPPORTED_SOURCE_CHAINS.map((fromChainId) => ({
|
||||
fromChainId,
|
||||
toChainId: chain138.chainId,
|
||||
tokens: [chain138NativeToken], // Add more tokens as needed
|
||||
active: true,
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a route is supported
|
||||
*/
|
||||
export function isRouteSupported(fromChainId: number, toChainId: number): boolean {
|
||||
return (
|
||||
toChainId === chain138.chainId &&
|
||||
SUPPORTED_SOURCE_CHAINS.includes(fromChainId)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get route for specific chain pair
|
||||
*/
|
||||
export function getRoute(fromChainId: number, toChainId: number): BridgeRoute | null {
|
||||
if (!isRouteSupported(fromChainId, toChainId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const routes = getSupportedRoutes();
|
||||
return routes.find((r) => r.fromChainId === fromChainId) || null;
|
||||
}
|
||||
50
packages/bridge/src/tokenLists.ts
Normal file
50
packages/bridge/src/tokenLists.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import type { BridgeableToken } from './types';
|
||||
import { chain138NativeToken } from './routes';
|
||||
|
||||
/**
|
||||
* Token lists for Chain 138 bridging
|
||||
*/
|
||||
|
||||
/**
|
||||
* Native and wrapped tokens
|
||||
*/
|
||||
export const nativeTokens: BridgeableToken[] = [
|
||||
chain138NativeToken,
|
||||
// Add WETH if it exists on Chain 138
|
||||
// {
|
||||
// address: '0x...',
|
||||
// symbol: 'WETH',
|
||||
// name: 'Wrapped Ether',
|
||||
// decimals: 18,
|
||||
// isNative: false,
|
||||
// },
|
||||
];
|
||||
|
||||
/**
|
||||
* Stablecoins (add actual addresses when available)
|
||||
*/
|
||||
export const stablecoins: BridgeableToken[] = [
|
||||
// Example structure - fill in with actual Chain 138 token addresses
|
||||
// {
|
||||
// address: '0x...',
|
||||
// symbol: 'USDC',
|
||||
// name: 'USD Coin',
|
||||
// decimals: 6,
|
||||
// isNative: false,
|
||||
// },
|
||||
];
|
||||
|
||||
/**
|
||||
* All bridgeable tokens for Chain 138
|
||||
*/
|
||||
export function getAllBridgeableTokens(): BridgeableToken[] {
|
||||
return [...nativeTokens, ...stablecoins];
|
||||
}
|
||||
|
||||
/**
|
||||
* Find token by address
|
||||
*/
|
||||
export function findToken(address: string): BridgeableToken | undefined {
|
||||
const tokens = getAllBridgeableTokens();
|
||||
return tokens.find((token) => token.address.toLowerCase() === address.toLowerCase());
|
||||
}
|
||||
141
packages/bridge/src/types.ts
Normal file
141
packages/bridge/src/types.ts
Normal file
@@ -0,0 +1,141 @@
|
||||
import type { BigNumberish } from 'ethers';
|
||||
|
||||
/**
|
||||
* Supported token for bridging
|
||||
*/
|
||||
export interface BridgeableToken {
|
||||
/**
|
||||
* Token address (native token uses zero address)
|
||||
*/
|
||||
address: string;
|
||||
|
||||
/**
|
||||
* Token symbol
|
||||
*/
|
||||
symbol: string;
|
||||
|
||||
/**
|
||||
* Token name
|
||||
*/
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* Token decimals
|
||||
*/
|
||||
decimals: number;
|
||||
|
||||
/**
|
||||
* Token logo URL (optional)
|
||||
*/
|
||||
logoURI?: string;
|
||||
|
||||
/**
|
||||
* Whether this is the native token
|
||||
*/
|
||||
isNative: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bridge route between two chains
|
||||
*/
|
||||
export interface BridgeRoute {
|
||||
/**
|
||||
* Source chain ID
|
||||
*/
|
||||
fromChainId: number;
|
||||
|
||||
/**
|
||||
* Destination chain ID (should be 138 for Chain 138)
|
||||
*/
|
||||
toChainId: number;
|
||||
|
||||
/**
|
||||
* Supported tokens on this route
|
||||
*/
|
||||
tokens: BridgeableToken[];
|
||||
|
||||
/**
|
||||
* Whether route is currently active
|
||||
*/
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bridge quote
|
||||
*/
|
||||
export interface BridgeQuote {
|
||||
/**
|
||||
* Source chain ID
|
||||
*/
|
||||
fromChainId: number;
|
||||
|
||||
/**
|
||||
* Destination chain ID
|
||||
*/
|
||||
toChainId: number;
|
||||
|
||||
/**
|
||||
* Token being bridged
|
||||
*/
|
||||
token: BridgeableToken;
|
||||
|
||||
/**
|
||||
* Amount to bridge (in token units)
|
||||
*/
|
||||
amount: BigNumberish;
|
||||
|
||||
/**
|
||||
* Estimated output amount (in token units)
|
||||
*/
|
||||
estimatedOutput: BigNumberish;
|
||||
|
||||
/**
|
||||
* Bridge fee (in token units)
|
||||
*/
|
||||
fee: BigNumberish;
|
||||
|
||||
/**
|
||||
* Estimated time in seconds
|
||||
*/
|
||||
estimatedTimeSeconds: number;
|
||||
|
||||
/**
|
||||
* Minimum output amount (slippage protection)
|
||||
*/
|
||||
minimumOutput: BigNumberish;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bridge transaction status
|
||||
*/
|
||||
export interface BridgeStatus {
|
||||
/**
|
||||
* Transaction hash on source chain
|
||||
*/
|
||||
sourceTxHash: string;
|
||||
|
||||
/**
|
||||
* Transaction hash on destination chain (when available)
|
||||
*/
|
||||
destinationTxHash?: string;
|
||||
|
||||
/**
|
||||
* Current status
|
||||
*/
|
||||
status: 'pending' | 'processing' | 'completed' | 'failed';
|
||||
|
||||
/**
|
||||
* Current step description
|
||||
*/
|
||||
step: string;
|
||||
|
||||
/**
|
||||
* Block number on source chain
|
||||
*/
|
||||
sourceBlockNumber?: number;
|
||||
|
||||
/**
|
||||
* Block number on destination chain (when available)
|
||||
*/
|
||||
destinationBlockNumber?: number;
|
||||
}
|
||||
12
packages/bridge/tsconfig.json
Normal file
12
packages/bridge/tsconfig.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"composite": false
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"references": [
|
||||
{ "path": "../chain" }
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user