Portal Labsdocs
Built on Solana

Portal Labs
Documentation

Permissionless interchain layer for autonomous agents. Enable AI agents to operate across multiple blockchains through Solana-native wrapper tokens and intent-based cross-chain settlements.

Permissionless

Any agent can register. No approvals, no KYC.

Single-Chain UX

Operate entirely on Solana with wrapper tokens.

Intent-Based

Submit intent, relayer handles cross-chain settlement.

Introduction

The Problem

Autonomous agents managing cross-chain portfolios face compounding friction: separate gas budgets on each chain, separate key management systems, and bridge fees stacking on every operation. For an agent operating across Ethereum, Base, Arbitrum, and Avalanche, that means 4x the complexity for every transaction.

What Portal Does

Portal introduces wrapper tokens — Solana-native SPL tokens that represent cross-chain assets. An agent holding pSOL on Solana holds a claim on SOL locked in Portal's bridge contract. The agent can trade, transfer, and compose with pSOL across DeFi without ever touching the source chain. When ready to settle, it submits an intent that the relayer network fulfills on the destination chain.

How It Works

Four steps from cross-chain asset to single-chain operation. Click each step to see details.

Deposit: Agent deposits SOL or tokens into Portal's bridge contract on the source chain. The relayer network observes and verifies the deposit.

Key Properties

  • Permissionless — any agent can register and operate
  • Single-chain UX — agents hold/trade wrapper tokens entirely on Solana
  • Intent-based settlement — no direct bridge calls from the agent
  • Fee transparency — flat basis-point fee, configurable per bridge
  • Composability — wrapper tokens are standard SPL tokens, work with any Solana program

Quick Start

Get up and running with Portal Labs.

Clone & Build

terminal
$ git clone https://github.com/prtldotfun/portal-labs.git
$ cd portal-labs
$ anchor build
$ anchor test

Build the SDK

terminal
$ cd sdk
$ npm install
$ npm run build

Build the CLI

terminal
$ cd cli
$ npm install
$ npm run build
$ portal --help
Prerequisites: Rust 1.78+, Anchor CLI, Node.js 18+, Solana CLI

Wrapper Tokens

Wrapper tokens (pTokens) are standard SPL tokens on Solana that represent 1:1 claims on assets locked in Portal's bridge contracts. For example, pSOL represents SOL locked on another chain.

  • Standard SPL tokens — fully composable with Solana DeFi
  • Relayer-gated minting — only the authorized relayer can mint (prevents inflation)
  • Each wrapper has a WrapperMeta account linking it to the source chain asset
  • Burning wrapper tokens is required to initiate cross-chain settlement
wrap-example.ts
import { PortalClient } from "@portal-labs/sdk";
import BN from "bn.js";

const client = new PortalClient({
  rpcUrl: "https://api.mainnet-beta.solana.com",
});

// Wrap: Relayer mints pSOL after verifying deposit
const result = await client.wrapTokens(relayer, {
  wrapperMint: pSolMint,
  agent: agentPubkey,
  amount: new BN(2_000_000_000), // 2 SOL
  sourceTxHash: "0xabc...def",
});

console.log("Wrapped. Signature:", result.signature);

Intents & Settlement

Instead of directly calling bridge contracts, agents submit intents. An intent burns wrapper tokens and creates an on-chain record. The relayer network observes the intent and fulfills it on the destination chain.

Intent Lifecycle

Click each state to explore transitions.

Wrapper tokens burned, awaiting relayer settlement on destination chain.

Transitions to:

Submit an Intent

submit-intent.ts
import { PortalClient, encodeAddress } from "@portal-labs/sdk";
import BN from "bn.js";

const intent = await client.submitIntent(agent, {
  wrapperMint: pSolMint,
  amount: new BN(1_000_000_000),     // 1 pSOL
  destinationChainId: 1,              // Ethereum
  destinationAddress: encodeAddress(
    "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18"
  ),
});

console.log("Intent #" + intent.intentId + " submitted");
console.log("Signature:", intent.signature);

// Check status later
const status = await client.getIntentStatus(intent.intentId);
console.log("Status:", status);
Intents expire after ~24 hours (216,000 slots). Expired intents can be cancelled to reclaim wrapper tokens.

Agent Profiles

Any wallet can register as an agent on Portal. Agent profiles are on-chain accounts that store the owner key, alias, status, and cumulative trading volume.

register-agent.ts
import { PortalClient } from "@portal-labs/sdk";
import { Keypair } from "@solana/web3.js";

const client = new PortalClient({
  rpcUrl: "https://api.mainnet-beta.solana.com",
});

const agent = Keypair.generate();

// Register
await client.registerAgent(agent, "sol-arb-bot");

// Check profile
const profile = await client.getAgentProfile(agent.publicKey);
console.log("Alias:", profile.alias);
console.log("Status:", profile.status);
console.log("Volume:", profile.totalVolume.toString(), "lamports");

Supported Chains

Portal supports bridging from any of these EVM chains to Solana. Each chain is registered on-chain with its chain ID, required confirmations, and bridge contract address.

E

Ethereum

ID: 1ETH
B

BNB Chain

ID: 56BNB
M

Polygon

ID: 137MATIC
E

Arbitrum

ID: 42161ETH
E

Base

ID: 8453ETH
A

Avalanche

ID: 43114AVAX
E

Optimism

ID: 10ETH

Solana

Portal LayerpTokens

SDK Installation

terminal
$ npm install @portal-labs/sdk

The SDK provides PortalClient for high-level operations and PortalInstructions for building raw transaction instructions.

Exports

CategoryExports
ClientPortalClient, PortalInstructions
TypesBridgeConfig, ChainEntry, WrapperMeta, IntentRecord, AgentProfile
PDA UtilsderiveBridgeConfigPda, deriveAgentPda, deriveIntentPda, ...
Chain UtilschainIdToName, encodeAddress, decodeAddress, ...

PortalClient

High-level client that handles PDA derivation, instruction building, and transaction signing automatically.

client-init.ts
import { PortalClient } from "@portal-labs/sdk";

const client = new PortalClient({
  rpcUrl: "https://api.mainnet-beta.solana.com",
  // Optional: custom program ID
  // programId: new PublicKey("PRT1x..."),
});

Methods

MethodAccessDescription
registerAgent(agent, alias)AgentCreate agent profile
getAgentProfile(pubkey)PublicRead agent profile
wrapTokens(relayer, config)RelayerMint wrapper tokens
unwrapTokens(agent, config)AgentBurn wrapper tokens
submitIntent(agent, config)AgentSubmit settlement intent
cancelIntent(agent, intentId, mint)AgentCancel expired intent
getBridgeConfig()PublicRead bridge config
getChainRegistry()PublicRead chain registry

SDK Examples

Full Bridge Flow

Complete example: register agent, wrap SOL, submit intent.

full-flow.ts
import { PortalClient, encodeAddress } from "@portal-labs/sdk";
import { Keypair } from "@solana/web3.js";
import BN from "bn.js";

const client = new PortalClient({
  rpcUrl: "https://api.mainnet-beta.solana.com",
});

// 1. Register agent
const agent = Keypair.generate();
await client.registerAgent(agent, "sol-bridge-bot");

// 2. After depositing SOL on source chain,
//    relayer mints pSOL on Solana
const wrap = await client.wrapTokens(relayer, {
  wrapperMint: pSolMint,
  agent: agent.publicKey,
  amount: new BN(5_000_000_000), // 5 SOL
  sourceTxHash: "0xabc123...",
});

// 3. Agent operates with pSOL on Solana
//    (trade, LP, transfer — standard SPL ops)

// 4. Submit settlement intent
const intent = await client.submitIntent(agent, {
  wrapperMint: pSolMint,
  amount: new BN(3_000_000_000), // 3 pSOL
  destinationChainId: 42161,     // Arbitrum
  destinationAddress: encodeAddress(
    "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18"
  ),
});

console.log("Intent submitted:", intent.intentId);

Query Bridge State

query-state.ts
const config = await client.getBridgeConfig();
console.log("Fee:", config.feeBps, "bps");
console.log("Total wrapped:", config.totalWrapped.toString());
console.log("Total intents:", config.totalIntents.toString());

const chains = await client.getChainRegistry();
chains.forEach(chain => {
  console.log(chain.name, "— ID:", chain.chainId);
});

const profile = await client.getAgentProfile(agent.publicKey);
console.log("Volume:", profile.totalVolume.toString());

CLI Installation

terminal
$ cd portal-labs/cli
$ npm install
$ npm run build
$ portal --help

CLI Commands

portal register

Create an on-chain agent profile.

terminal
$ portal register --alias "sol-arb-bot"

portal wrap

Mint wrapper tokens after verifying a source chain deposit. Relayer-only.

terminal
$ portal wrap \
  --mint <WRAPPER_MINT> \
  --agent <AGENT_PUBKEY> \
  --amount 5000000000 \
  --source-tx <SOURCE_TX_HASH>

portal bridge

Submit a cross-chain settlement intent.

terminal
$ portal bridge \
  --mint <WRAPPER_MINT> \
  --amount 3000000000 \
  --dest-chain 42161 \
  --dest-address 0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18

portal status

terminal
$ portal status intent 0
$ portal status bridge
$ portal status agent

portal config

terminal
$ portal config --rpc https://api.mainnet-beta.solana.com
$ portal config --keypair ~/.config/solana/id.json

Program Instructions

The on-chain program (PRT1xBridge1111111111111111111111111111111) exposes the following instructions.

InstructionAccessDescription
initializeAdminInitialize bridge config and chain registry
register_chainAdminRegister a new destination chain
create_wrapperAdminCreate a wrapper token mint
wrap_tokensRelayerMint wrapper tokens on deposit verification
unwrap_tokensAgentBurn wrapper tokens for redemption
submit_intentAgentLock tokens into settlement intent
settle_intentRelayerMark intent settled with tx proof
cancel_intentAgentCancel expired intent, reclaim tokens
register_agentAgentCreate on-chain agent profile
update_agentAgentUpdate agent alias
update_bridge_configAdminModify fees, pause, treasury, relayer

Account Structure

Five on-chain accounts power the protocol. Click to explore each.

Central configuration — authority, fees, treasury, relayer, and global stats.

struct BridgeConfig { authority: Pubkey, fee_bps: u16, total_wrapped: u64, total_unwrapped: u64, total_intents: u64, paused: bool, treasury: Pubkey, relayer: Pubkey, }

Security

Authority-gated admin

Only bridge authority can register chains, create wrappers, modify config.

Relayer-gated minting

Only the authorized relayer can mint wrapper tokens — prevents inflation.

Intent expiry

Unsettled intents expire after ~24h. Agents can cancel and recover tokens.

Pausable

Bridge authority can pause all operations in emergency.

Overflow protection

All arithmetic uses checked operations.

Permissionless agents

Any wallet can register. No gatekeeping.

Portal LabsPortal Labs

MIT License