Documentation Index
Fetch the complete documentation index at: https://mintlify.com/covenant-gov/pacto-app/llms.txt
Use this file to discover all available pages before exploring further.
Pacto’s embedded EVM wallet follows a strict read / write split. Chain reads (balances, contract state, Safe reads) happen in the TypeScript frontend via viem public clients, using RPC URLs resolved through getEffectiveRpcUrlsForChain. All signing and broadcasting — native ETH transfers, ERC-20 transfers, and contract calls — run in the Rust backend using Alloy, where the private key is decrypted only for the duration of an approved operation. The commands on this page are the Tauri surface for that Rust send-plane. Frontend helpers live in src/lib/wallet/backend-wallet.ts; background receipt polling uses src/lib/evm/on-chain-background.ts.
Import
import { invoke } from '@tauri-apps/api/core';
Balance and summary commands
get_wallet_summary
Fetch per-network native balances and any watched ERC-20 balances for the active EVM account. USD values for ETH, USDC, and USDT are computed from Chainlink on-chain spot prices fetched at call time. The result is cached per account as pacto_wallet_summary_cache_v1_<npub> (keyed by a fingerprint of the watched-token list).
| Parameter | Type | Description |
|---|
watchedErc20s | WatchedErc20Input[] | ERC-20 tokens to include in the summary beyond the defaults |
WatchedErc20Input shape:
| Field | Type | Description |
|---|
network | string | Network key, e.g. "arbitrum" |
symbol | string | Token symbol, e.g. "USDC" |
address | string | ERC-20 contract address (0x…) |
decimals | number | Token decimals |
Returns: WalletSummary
// Wallet summary type (camelCase from Rust serde)
type WalletSummary = {
networks: Array<{
network: string;
chainId: number;
assets: Array<{
symbol: string;
balanceRaw: string;
balanceDecimal: string;
usdValue: number | null;
}>;
}>;
totalUsdApprox: number;
prices: { ethUsd: number; usdcUsd: number; usdtUsd: number };
};
const summary = await invoke('get_wallet_summary', { watchedErc20s: [] });
console.log('Total USD:', summary.totalUsdApprox);
The last successful get_wallet_summary response is stored per account as pacto_wallet_summary_cache_v1_<npub> (includes a fingerprint of the watched-token list). It is read immediately on login to avoid a blank WalletBar before the first live fetch. The cache is cleared on logout via clearAccountState.
wallet_get_usd_spot_prices
Fetch live USD spot rates for ETH, USDC, and USDT directly from Chainlink on-chain feeds on Ethereum mainnet. Used by get_wallet_summary internally; call this separately when you need prices without a full balance summary.
Parameters: none
Returns: { ethUsd: number, usdcUsd: number, usdtUsd: number }
const prices = await invoke('wallet_get_usd_spot_prices');
console.log('ETH/USD:', prices.ethUsd);
There are no static price fallbacks. If the Chainlink eth_call fails (e.g. RPC unavailable), the command returns an error. Configure ALCHEMY_RPC_KEY in .env to avoid public-endpoint rate limits.
Transaction commands
wallet_build_and_send_transaction
Sign and broadcast an EVM transaction (native ETH transfer or ERC-20 transfer). Requires the active account to be a squad-purpose signer; advanced-purpose keys are rejected. By default returns as soon as the transaction is submitted to the mempool (waitForConfirmation: false), so the UI is never blocked on receipt polling.
| Parameter | Type | Description |
|---|
toNpub | string | Recipient npub (resolved to EVM address via stored dm_peer_evm or profile metadata) |
network | string | Network key: "mainnet", "sepolia", "arbitrum", "optimism", "gnosis" |
asset | string | Asset to send: "ETH", "USDC", or "USDT" |
amount | string | Decimal amount string, e.g. "10.00" |
erc20Transfer | { address: string, decimals: number } | null | Override ERC-20 contract for non-standard tokens |
toAddressEvm | string | null | Raw 0x recipient address — when set, bypasses npub resolution entirely |
waitForConfirmation | boolean | null | If true, block until receipt (use sparingly); default false |
Returns: WalletSendResult
type WalletSendResult = {
txHash: string;
network: string;
chainId: number;
blockNumber?: string; // only present when waitForConfirmation: true
};
// Broadcast immediately (non-blocking, recommended)
const result = await invoke('wallet_build_and_send_transaction', {
toNpub: 'npub1alice...',
network: 'arbitrum',
asset: 'USDC',
amount: '10.00',
erc20Transfer: null,
toAddressEvm: null,
waitForConfirmation: false
});
console.log('Tx hash:', result.txHash);
Key type enforcement: wallet_build_and_send_transaction requires a squad-purpose signer. Calling it while an advanced-purpose key is active returns a SQUAD_SIGNER_REQUIRED error. Use evm_send_advanced_contract_call for advanced-key flows.
wallet_wait_for_transaction
Poll for a transaction receipt on the given network. Runs independently of the send command so the UI remains unblocked. Times out after 180 seconds (RECEIPT_WAIT_TIMEOUT); on timeout returns a RECEIPT_TIMEOUT error string with the txHash included so callers can surface an explorer link.
| Parameter | Type | Description |
|---|
txHash | string | 0x-prefixed 32-byte hex transaction hash |
network | string | Network key matching the one used for the send |
Returns: WalletSendResult — includes blockNumber on success.
const receipt = await invoke('wallet_wait_for_transaction', {
txHash: '0xabc123...',
network: 'arbitrum'
});
console.log('Confirmed in block:', receipt.blockNumber);
evm_send_advanced_contract_call
Execute an opaque contract call using the advanced-purpose signer only. Exposed through Settings → Advanced (WalletAdvancedPanel.svelte). Refuses squad-purpose signers.
| Parameter | Type | Description |
|---|
network | string | Network key |
to | string | Target contract address (0x…) |
valueWei | string | Native value in wei to attach, e.g. "0" for pure calldata sends |
dataHex | string | Hex-encoded calldata (ABI-encoded function call) |
waitForConfirmation | boolean | null | Default false |
Returns: WalletSendResult
Key type enforcement: evm_send_advanced_contract_call refuses squad-purpose signers. Squad paths in turn refuse advanced addresses. Calling with the wrong key type returns an error — switch the active account in Settings first.
evm_send_squad_allowlisted_contract_call
Execute a contract call from a squad-purpose signer against an explicitly allowlisted target address plus implicit deploy infrastructure. Used from the Dashboard → Settings → Smart contract security panel.
| Parameter | Type | Description |
|---|
parentId | string | Squad parent ID used to look up the contract allowlist |
network | string | Network key |
to | string | Allowlisted target contract address (0x…) |
valueWei | string | Native value in wei to attach, e.g. "0" for pure calldata sends |
dataHex | string | Hex-encoded calldata (ABI-encoded function call) |
waitForConfirmation | boolean | null | Default false |
Returns: WalletSendResult
EVM account commands
list_evm_accounts
List all EVM accounts associated with the current Nostr identity — HD-derived accounts and any imported accounts.
Parameters: none
Returns: EvmAccountRow[] — each row includes address, purpose ("squad" | "advanced"), and display metadata.
const accounts = await invoke('list_evm_accounts');
const squadAccounts = accounts.filter(a => a.purpose === 'squad');
set_active_evm_account
Set the active EVM signing account for squad-purpose sends.
| Parameter | Type | Description |
|---|
accountId | string | Account ID from list_evm_accounts |
Returns: void
set_active_advanced_evm_account
Set the active EVM signing account for advanced-purpose sends.
| Parameter | Type | Description |
|---|
accountId | string | Account ID from list_evm_accounts |
Returns: void
Commons commands
These commands manage public Commons broadcasts — time-bounded public Nostr events (Kind 30078) used for squad and user discoverability. They are wallet-adjacent in that Commons activity is visible on the same trusted relay set.
commons_publish_broadcast
Publish a public broadcast for a user or squad. Enforces tag normalization, duration validation, and a one-active-broadcast-per-subject cooldown.
| Parameter | Type | Description |
|---|
input.subject | string | "user" or "squad" |
input.message | string | Broadcast message text (required) |
input.durationHours | number | TTL: 24, 48, 72, 168, 336, or 720 |
input.tags | string[] | 1–3 lowercase tag strings |
input.audience | string | null | "new_user" or "active_user" (user subjects only) |
input.squad | object | null | Squad metadata (id, name, kind, iconUrl) — required for squad broadcasts |
Returns: CommonsBroadcastDto — the published broadcast record.
const broadcast = await invoke('commons_publish_broadcast', {
input: {
subject: 'user',
message: 'Building wallet tooling; happy to DM.',
durationHours: 48,
tags: ['neo'],
audience: null,
squad: null
}
});
commons_fetch_broadcasts
Sync active broadcasts from trusted relays and return the deduplicated feed, pruned of expired entries.
| Parameter | Type | Description |
|---|
limit | number | null | Maximum results (default 100, clamped to 1–500) |
Returns: CommonsBroadcastDto[]
const feed = await invoke('commons_fetch_broadcasts', { limit: 50 });
commons_get_local_active
Return the current user’s own active broadcast for a given subject/subject ID pair without hitting the network.
| Parameter | Type | Description |
|---|
subject | string | "user" or "squad" |
subjectId | string | User npub (for user) or squad group_id (for squad) |
Returns: CommonsBroadcastDto | null — null if no active broadcast or if the newest event is a cancellation.
const active = await invoke('commons_get_local_active', {
subject: 'user',
subjectId: 'npub1me...'
});
if (active) {
console.log('Active until:', new Date(active.expiresAt * 1000));
}
commons_cancel_broadcast
Retract an active broadcast by publishing a replacement event with cancelled: true (NIP-33 same d tag). Other clients drop the entry from their feed immediately; the author’s cooldown lifts so they can rebroadcast right away.
| Parameter | Type | Description |
|---|
subject | string | "user" or "squad" |
subjectId | string | Subject ID matching the broadcast to cancel |
Returns: void
await invoke('commons_cancel_broadcast', {
subject: 'user',
subjectId: 'npub1me...'
});
Transaction lifecycle example
The recommended pattern is broadcast-first, poll in the background — never block the UI on receipt confirmation.
import { invoke } from '@tauri-apps/api/core';
// Step 1: Broadcast (returns immediately after mempool submission)
const { txHash, network } = await invoke('wallet_build_and_send_transaction', {
toNpub: 'npub1alice...',
network: 'arbitrum',
asset: 'USDC',
amount: '10.00',
erc20Transfer: null,
toAddressEvm: null,
waitForConfirmation: false
});
// Step 2: Close the modal, show optimistic pending state
console.log('Submitted:', txHash);
showPendingBadge(txHash);
// Step 3: Poll for receipt in the background (180s timeout)
try {
const receipt = await invoke('wallet_wait_for_transaction', {
txHash,
network
});
// Receipt succeeded
showConfirmedBadge(txHash, receipt.blockNumber);
} catch (err) {
if (String(err).includes('RECEIPT_TIMEOUT')) {
// Transaction may still confirm — show explorer link
showTimeoutBadge(txHash);
} else {
// Transaction reverted
showFailedBadge(txHash);
}
}
Never set waitForConfirmation: true in UI-critical paths. Receipt polling can take minutes on congested networks. Use the two-step pattern above and handle RECEIPT_TIMEOUT with an explorer link rather than claiming success or failure.
Balance cache: The last successful get_wallet_summary result is stored per account under pacto_wallet_summary_cache_v1_<npub>, keyed by the watched-token fingerprint. On login it is loaded immediately so the WalletBar has data while a live fetch runs. The cache is cleared alongside all other npub-scoped keys when clearAccountState runs on logout.