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
$ git clone https://github.com/prtldotfun/portal-labs.git
$ cd portal-labs
$ anchor build
$ anchor testBuild the SDK
$ cd sdk
$ npm install
$ npm run buildBuild the CLI
$ cd cli
$ npm install
$ npm run build
$ portal --helpWrapper 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
WrapperMetaaccount linking it to the source chain asset - Burning wrapper tokens is required to initiate cross-chain settlement
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
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);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.
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.
Ethereum
BNB Chain
Polygon
Arbitrum
Base
Avalanche
Optimism
Solana
SDK Installation
$ npm install @portal-labs/sdkThe SDK provides PortalClient for high-level operations and PortalInstructions for building raw transaction instructions.
Exports
| Category | Exports |
|---|---|
| Client | PortalClient, PortalInstructions |
| Types | BridgeConfig, ChainEntry, WrapperMeta, IntentRecord, AgentProfile |
| PDA Utils | deriveBridgeConfigPda, deriveAgentPda, deriveIntentPda, ... |
| Chain Utils | chainIdToName, encodeAddress, decodeAddress, ... |
PortalClient
High-level client that handles PDA derivation, instruction building, and transaction signing automatically.
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
| Method | Access | Description |
|---|---|---|
registerAgent(agent, alias) | Agent | Create agent profile |
getAgentProfile(pubkey) | Public | Read agent profile |
wrapTokens(relayer, config) | Relayer | Mint wrapper tokens |
unwrapTokens(agent, config) | Agent | Burn wrapper tokens |
submitIntent(agent, config) | Agent | Submit settlement intent |
cancelIntent(agent, intentId, mint) | Agent | Cancel expired intent |
getBridgeConfig() | Public | Read bridge config |
getChainRegistry() | Public | Read chain registry |
SDK Examples
Full Bridge Flow
Complete example: register agent, wrap SOL, submit intent.
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
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
$ cd portal-labs/cli
$ npm install
$ npm run build
$ portal --helpCLI Commands
portal register
Create an on-chain agent profile.
$ portal register --alias "sol-arb-bot"portal wrap
Mint wrapper tokens after verifying a source chain deposit. Relayer-only.
$ portal wrap \
--mint <WRAPPER_MINT> \
--agent <AGENT_PUBKEY> \
--amount 5000000000 \
--source-tx <SOURCE_TX_HASH>portal bridge
Submit a cross-chain settlement intent.
$ portal bridge \
--mint <WRAPPER_MINT> \
--amount 3000000000 \
--dest-chain 42161 \
--dest-address 0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18portal status
$ portal status intent 0
$ portal status bridge
$ portal status agentportal config
$ portal config --rpc https://api.mainnet-beta.solana.com
$ portal config --keypair ~/.config/solana/id.jsonProgram Instructions
The on-chain program (PRT1xBridge1111111111111111111111111111111) exposes the following instructions.
| Instruction | Access | Description |
|---|---|---|
initialize | Admin | Initialize bridge config and chain registry |
register_chain | Admin | Register a new destination chain |
create_wrapper | Admin | Create a wrapper token mint |
wrap_tokens | Relayer | Mint wrapper tokens on deposit verification |
unwrap_tokens | Agent | Burn wrapper tokens for redemption |
submit_intent | Agent | Lock tokens into settlement intent |
settle_intent | Relayer | Mark intent settled with tx proof |
cancel_intent | Agent | Cancel expired intent, reclaim tokens |
register_agent | Agent | Create on-chain agent profile |
update_agent | Agent | Update agent alias |
update_bridge_config | Admin | Modify 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.
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 LabsMIT License