Skip to content

Commit

Permalink
Merge pull request #20 from Near-One/rename-deployer
Browse files Browse the repository at this point in the history
Fixes and refactoring for bridge clients
  • Loading branch information
kiseln authored Jan 13, 2025
2 parents bb4c4d6 + 382bf13 commit a25f781
Show file tree
Hide file tree
Showing 8 changed files with 134 additions and 153 deletions.
36 changes: 18 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ yarn add omni-bridge-sdk
The SDK currently provides a split interface for cross-chain transfers:

- `omniTransfer`: A unified interface for initiating transfers from any supported chain
- Chain-specific deployers: Required for finalizing transfers on destination chains
- Chain-specific clients: Required for finalizing transfers on destination chains

> [!NOTE]
> We're working on unifying this into a single interface that will handle the complete transfer lifecycle. For now, you'll need to use both `omniTransfer` and chain-specific deployers as shown below.
> We're working on unifying this into a single interface that will handle the complete transfer lifecycle. For now, you'll need to use both `omniTransfer` and chain-specific clients as shown below.
Here's a complete example:

Expand All @@ -53,7 +53,7 @@ import {
ChainKind,
omniAddress,
OmniBridgeAPI,
getDeployer,
getClient,
} from "omni-bridge-sdk";
import { connect } from "near-api-js";

Expand Down Expand Up @@ -97,8 +97,8 @@ do {

if (status === "ready_for_finalize") {
// 4. Finalize transfer on destination chain
const ethDeployer = getDeployer(ChainKind.Eth, ethWallet);
await ethDeployer.finalizeTransfer(transferMessage, signature);
const ethClient = getClient(ChainKind.Eth, ethWallet);
await ethClient.finalizeTransfer(transferMessage, signature);
break;
}

Expand Down Expand Up @@ -131,16 +131,16 @@ const status = await api.getTransferStatus(chain, nonce);

### 3. Transfer Finalization

When status is "ready_for_finalize", use chain-specific deployers to complete the transfer:
When status is "ready_for_finalize", use chain-specific clients to complete the transfer:

```typescript
// Finalize on Ethereum/EVM chains
const evmDeployer = getDeployer(ChainKind.Eth, ethWallet);
await evmDeployer.finalizeTransfer(transferMessage, signature);
const evmClient = getClient(ChainKind.Eth, ethWallet);
await evmClient.finalizeTransfer(transferMessage, signature);

// Finalize on NEAR
const nearDeployer = getDeployer(ChainKind.Near, nearAccount);
await nearDeployer.finalizeTransfer(
const nearClient = getClient(ChainKind.Near, nearAccount);
await nearClient.finalizeTransfer(
token,
recipientAccount,
storageDeposit,
Expand All @@ -149,8 +149,8 @@ await nearDeployer.finalizeTransfer(
);

// Finalize on Solana
const solDeployer = getDeployer(ChainKind.Sol, provider);
await solDeployer.finalizeTransfer(transferMessage, signature);
const solClient = getClient(ChainKind.Sol, provider);
await solClient.finalizeTransfer(transferMessage, signature);
```

## Core Concepts
Expand Down Expand Up @@ -245,20 +245,20 @@ const result = await omniTransfer(provider, transfer);

### Deploying Tokens

Token deployment uses chain-specific deployers through a unified interface:
Token deployment uses chain-specific clients through a unified interface:

```typescript
import { getDeployer } from "omni-bridge-sdk";
import { getClient } from "omni-bridge-sdk";

// Initialize deployer for source chain
const deployer = getDeployer(ChainKind.Near, wallet);
// Initialize client for source chain
const client = getClient(ChainKind.Near, wallet);

// Example: Deploy NEAR token to Ethereum
const txHash = await deployer.logMetadata("near:token.near");
const txHash = await client.logMetadata("near:token.near");
console.log(`Metadata logged with tx: ${txHash}`);

// Deploy token with signed MPC payload
const result = await deployer.deployToken(signature, {
const result = await client.deployToken(signature, {
token: "token.near",
name: "Token Name",
symbol: "TKN",
Expand Down
48 changes: 24 additions & 24 deletions docs/token-deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,30 +17,30 @@ Important: To deploy a token on any chain, it must first exist on NEAR. You cann
### NEAR Chain Deployment

```typescript
import { NearDeployer, ChainKind } from "omni-bridge-sdk";
import { NearBridgeClient, ChainKind } from "omni-bridge-sdk";
import { connect } from "near-api-js";

// Setup NEAR connection
const near = await connect({
networkId: "testnet",
nodeUrl: "https://rpc.testnet.near.org",
});
const account = await near.account("deployer.near");
const account = await near.account("client.near");

// Initialize deployer
const deployer = new NearDeployer(account);
// Initialize client
const client = new NearBridgeClient(account);

// 1. Log metadata for existing NEAR token
const logTxHash = await deployer.logMetadata("near:token.near");
const logTxHash = await client.logMetadata("near:token.near");

// 2. Deploy to destination chain (e.g., Ethereum)
const deployTxHash = await deployer.deployToken(
const deployTxHash = await client.deployToken(
ChainKind.Eth,
vaa // Wormhole VAA containing deployment approval
);

// 3. For tokens being deployed TO NEAR, bind them after deployment
await deployer.bindToken(
await client.bindToken(
ChainKind.Eth, // Source chain
vaa, // Optional: Wormhole VAA
evmProof // Optional: EVM proof (for EVM chains)
Expand All @@ -50,21 +50,21 @@ await deployer.bindToken(
### EVM Chain Deployment (Ethereum/Base/Arbitrum)

```typescript
import { EVMDeployer, ChainKind } from "omni-bridge-sdk";
import { EvmBridgeClient, ChainKind } from "omni-bridge-sdk";
import { ethers } from "ethers";

// Setup EVM wallet
const provider = new ethers.providers.Web3Provider(window.ethereum);
const wallet = provider.getSigner();

// Initialize deployer for specific chain
const deployer = new EVMDeployer(wallet, ChainKind.Eth);
// Initialize client for specific chain
const client = new EvmBridgeClient(wallet, ChainKind.Eth);

// 1. Log metadata for existing token
const logTxHash = await deployer.logMetadata("eth:0x123...");
const logTxHash = await client.logMetadata("eth:0x123...");

// 2. Deploy token using MPC signature
const { txHash, tokenAddress } = await deployer.deployToken(
const { txHash, tokenAddress } = await client.deployToken(
signature, // MPC signature authorizing deployment
{
token: "token_id",
Expand All @@ -78,27 +78,27 @@ const { txHash, tokenAddress } = await deployer.deployToken(
### Solana Deployment

```typescript
import { SolanaDeployer } from "omni-bridge-sdk";
import { SolanaBridgeClient } from "omni-bridge-sdk";
import { Connection, PublicKey, Keypair } from "@solana/web3.js";

// Setup Solana connection
const connection = new Connection("https://api.testnet.solana.com");
const payer = Keypair.generate();

// Initialize deployer
const deployer = new SolanaDeployer(
// Initialize client
const client = new SolanaBridgeClient(
provider,
new PublicKey("wormhole_program_id")
);

// 1. Log metadata for existing SPL token
const logTxHash = await deployer.logMetadata(
const logTxHash = await client.logMetadata(
tokenPubkey,
payer // Optional payer for transaction
);

// 2. Deploy token using MPC signature
const { txHash, tokenAddress } = await deployer.deployToken(
const { txHash, tokenAddress } = await client.deployToken(
signature,
{
token: "token_id",
Expand All @@ -116,7 +116,7 @@ Each deployment step can encounter different types of errors that need handling:

```typescript
try {
await deployer.logMetadata("near:token.near");
await client.logMetadata("near:token.near");
} catch (error) {
if (error.message.includes("Token metadata not provided")) {
// Handle missing metadata
Expand Down Expand Up @@ -164,12 +164,12 @@ console.log(status); // "pending" | "ready_for_finalize" | "finalized" | "ready_

```typescript
// For EVM chains
const evmDeployer = new EVMDeployer(wallet, ChainKind.Eth);
const nearTokenAddress = await evmDeployer.factory.nearToEthToken("token.near");
const evmClient = new EvmClient(wallet, ChainKind.Eth);
const nearTokenAddress = await evmClient.factory.nearToEthToken("token.near");

// For Solana
const solDeployer = new SolanaDeployer(provider, wormholeProgramId);
const isBridgedToken = await solDeployer.isBridgedToken(tokenPubkey);
const solClient = new SolanaClient(provider, wormholeProgramId);
const isBridgedToken = await solClient.isBridgedToken(tokenPubkey);
```

## Security Considerations
Expand All @@ -193,7 +193,7 @@ const isBridgedToken = await solDeployer.isBridgedToken(tokenPubkey);

```typescript
// Check required balances on NEAR
const { regBalance, initBalance, storage } = await deployer.getBalances();
const { regBalance, initBalance, storage } = await client.getBalances();
const requiredBalance = regBalance + initBalance;
```

Expand Down Expand Up @@ -222,7 +222,7 @@ if (!signature.isValidFor(ChainKind.Eth)) {
while ((await api.getDeploymentStatus(txHash)).status !== "ready_for_bind") {
await new Promise((r) => setTimeout(r, 1000));
}
await deployer.bindToken(sourceChain, vaa);
await client.bindToken(sourceChain, vaa);
```

## Chain Support Matrix
Expand Down
30 changes: 9 additions & 21 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,51 +2,39 @@ import { AnchorProvider as SolWallet } from "@coral-xyz/anchor"
import { PublicKey } from "@solana/web3.js"
import { Wallet as EthWallet } from "ethers"
import { Account as NearAccount } from "near-api-js"
import { EVMDeployer } from "./deployer/evm"
import { NearDeployer } from "./deployer/near"
import { SolanaDeployer } from "./deployer/solana"
import { EvmBridgeClient } from "./clients/evm"
import { NearBridgeClient } from "./clients/near"
import { SolanaBridgeClient } from "./clients/solana"
import { ChainKind, type OmniTransferMessage, type OmniTransferResult } from "./types"

export async function omniTransfer(
wallet: EthWallet | NearAccount | SolWallet,
transfer: OmniTransferMessage,
): Promise<OmniTransferResult> {
if (wallet instanceof NearAccount) {
const deployer = new NearDeployer(wallet, "omni-locker.testnet") // TODO: Get from config
const { nonce, hash } = await deployer.initTransfer(
transfer.tokenAddress,
transfer.recipient,
transfer.amount,
)
const client = new NearBridgeClient(wallet)
const { nonce, hash } = await client.initTransfer(transfer)
return {
txId: hash,
nonce: BigInt(nonce),
}
}

if (wallet instanceof EthWallet) {
const deployer = new EVMDeployer(wallet, ChainKind.Eth)
const { hash, nonce } = await deployer.initTransfer(
transfer.tokenAddress,
transfer.recipient,
transfer.amount,
)
const client = new EvmBridgeClient(wallet, ChainKind.Eth)
const { hash, nonce } = await client.initTransfer(transfer)
return {
txId: hash,
nonce: BigInt(nonce),
}
}

if (wallet instanceof SolWallet) {
const deployer = new SolanaDeployer(
const client = new SolanaBridgeClient(
wallet,
new PublicKey("3u8hJUVTA4jH1wYAyUur7FFZVQ8H635K3tSHHF4ssjQ5"),
) // TODO: Get from config
const { nonce, hash } = await deployer.initTransfer(
transfer.tokenAddress,
transfer.recipient,
transfer.amount,
)
const { nonce, hash } = await client.initTransfer(transfer)
return {
txId: hash,
nonce: BigInt(nonce),
Expand Down
32 changes: 14 additions & 18 deletions src/deployer/evm.ts → src/clients/evm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import type {
ChainKind,
MPCSignature,
OmniAddress,
OmniTransferMessage,
TokenMetadata,
TransferMessagePayload,
U128,
} from "../types"
import { getChain } from "../utils"

Expand Down Expand Up @@ -68,15 +68,15 @@ const FACTORY_ADDRESSES: Record<ChainTag<EVMChainKind>, string | undefined> = {
}

/**
* EVM blockchain implementation of the token deployer
* EVM blockchain implementation of the bridge client
*/
export class EVMDeployer {
export class EvmBridgeClient {
private factory: ethers.Contract
private chainKind: EVMChainKind
private chainTag: ChainTag<EVMChainKind>

/**
* Creates a new EVM token deployer instance
* Creates a new EVM bridge client instance
* @param wallet - Ethereum signer instance for transaction signing
* @param chain - The EVM chain to deploy to (Ethereum, Base, or Arbitrum)
* @throws {Error} If factory address is not configured for the chain or if chain is not EVM
Expand Down Expand Up @@ -109,7 +109,7 @@ export class EVMDeployer {
async logMetadata(tokenAddress: OmniAddress): Promise<string> {
const sourceChain = getChain(tokenAddress)

// Validate source chain matches the deployer's chain
// Validate source chain matches the client's chain
if (!ChainUtils.areEqual(sourceChain, this.chainKind)) {
throw new Error(`Token address must be on ${this.chainTag}`)
}
Expand Down Expand Up @@ -168,27 +168,23 @@ export class EVMDeployer {
* @throws {Error} If token address is not on the correct EVM chain
* @returns Promise resolving to object containing transaction hash and nonce
*/
async initTransfer(
token: OmniAddress,
recipient: OmniAddress,
amount: U128,
): Promise<{ hash: string; nonce: number }> {
const sourceChain = getChain(token)

// Validate source chain matches the deployer's chain
async initTransfer(transfer: OmniTransferMessage): Promise<{ hash: string; nonce: number }> {
const sourceChain = getChain(transfer.tokenAddress)

// Validate source chain matches the client's chain
if (!ChainUtils.areEqual(sourceChain, this.chainKind)) {
throw new Error(`Token address must be on ${this.chainTag}`)
}

const [_, tokenAccountId] = token.split(":")
const [_, tokenAccountId] = transfer.tokenAddress.split(":")

try {
const tx = await this.factory.initTransfer(
tokenAccountId,
amount.valueOf(),
0,
0,
recipient,
transfer.amount,
transfer.fee,
transfer.nativeFee,
transfer.recipient,
"",
)
return {
Expand Down
Loading

0 comments on commit a25f781

Please sign in to comment.