Contract Deployment and Scripting
Learn how to deploy smart contracts and write scripts that interact with Prividium™ chains.
Table of Contents
- Obtaining Authentication Tokens (SIWE Flow)
- Scripting with Viem and Ethers
- Sample Contract
- Deploying Smart Contracts
- Configuring Contract Function Permissions
- Full Examples
- Next Steps
Overview
Prividium™ chains enforce authentication on all RPC interactions. Unlike public blockchains where anyone can read state or submit transactions, Prividium™ requires valid JWT tokens for both read and write operations.
Scripting libraries (Ethers, Viem) support custom HTTP headers, so you can add authentication tokens to requests. This works for deployment scripts, backend services, and test suites.
This guide covers:
- Obtaining authentication tokens via the SIWE (Sign-In With Ethereum) flow
- Configuring Ethers and Viem to use authenticated RPC endpoints
- Three deployment methods for smart contracts
- Configuring contract permissions after deployment
Obtaining Authentication Tokens (SIWE Flow)
The first stop is to get a valid token. You can follow this guide to programmatically get and refresh valid tokens.
Once you have a valid auth token, you can use it to interact with your Prividium™ chain.
Scripting with Viem and Ethers
Scripting tools inject custom headers into HTTP requests, enabling direct access to the Prividium™ RPC endpoint via
token-based authentication (Authorization: Bearer {token} headers).
Use scripts for:
- Backend services
- Contract deployments
- Automated transactions
Viem can use the authenticated transport returned by createPrividiumSiweChain():
import { createPrividiumClient } from 'prividium';
import { createPrividiumSiweChain } from 'prividium/siwe'; const prividium = createPrividiumSiweChain({
account,
chain,
prividiumApiBaseUrl,
domain: process.env.DOMAIN ?? 'localhost:3000'
});
await prividium.authorize();
const client = createPrividiumClient({
chain: prividium.chain,
transport: prividium.transport,
account: prividium.address
});
const balance = await client.getBalance({ address: prividium.address });See Full Examples for the complete runnable Viem script.
Sample Contract
The examples in this guide use a minimal Greeter contract. Create a file called Greeter.sol:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
contract Greeter {
string private greeting;
constructor(string memory _initialGreeting) {
greeting = _initialGreeting;
}
function getGreeting() external view returns (string memory) {
return greeting;
}
function updateGreeting(string memory _newGreeting) external {
greeting = _newGreeting;
}
}Compile it with Foundry to get the deployment bytecode:
forge build
export GREETER_BYTECODE=$(jq -r '.bytecode.object' out/Greeter.sol/Greeter.json)The GREETER_BYTECODE value is used by the deployment scripts in the Full Examples section.
Deploying Smart Contracts
Prerequisites
Contract deployment requires:
- User account with a role that has the appropriate system permission (see below)
- Authentication token
Deployment attempts without these permissions fail with an error.
Required System Permissions
To deploy contracts, your user must have a role with one of the following system permissions:
| Permission | Key | Scope |
|---|---|---|
| Contract Deployment | contract_deployment | Grants only contract deployment capability |
| Full Sequencer RPC Access | full_sequencer_rpc_access | Grants full access including deployment |
Deployment Options
Choose one of three deployment methods:
Option 1: Use the Prividium™ Local Proxy
The Prividium™ NPM package provides a CLI tool that runs a local proxy. The proxy automatically injects authentication headers into your RPC requests.
Steps:
-
Start the local proxy. For browser-based login (interactive):
npx prividium proxy -u {{USER_PANEL_URL}}Or for non-interactive use (CI, scripts), authenticate with a private key:
npx prividium proxy --api-url {{PRIVIDIUM_API_URL}} --private-key 0xYOUR_KEY -
The proxy will:
- Discover the API URL from the User Panel (browser mode) or use the provided
--api-url(private key mode) - Authenticate via browser sign-in or SIWE
- Start a local proxy server that injects auth headers
- Discover the API URL from the User Panel (browser mode) or use the provided
-
Deploy contracts using the local endpoint. The proxy injects authentication headers and forwards requests to the Prividium™ RPC.
Example usage:
# After starting the proxy, use the local endpoint
forge script DeployScript \
--rpc-url http://127.0.0.1:24101 \
--broadcastRegistering Deployed Contracts Programmatically
Deployment alone is not enough: until a contract is registered with the permissions API, all of its functions remain
Forbidden. Chain administrators can register contracts manually in the Admin Panel (see
next section), but a service holding admin credentials can also register
them in-process via the SDK's admin namespace — useful for fully-automated deployment pipelines.
import { createPrividiumSiweChain } from 'prividium/siwe'; const admin = createPrividiumSiweChain({
account: privateKeyToAccount(ADMIN_PRIVATE_KEY),
chain: { id: CHAIN_ID, name: 'Prividium™ Chain' },
prividiumApiBaseUrl: PRIVIDIUM_API_BASE_URL,
domain: DOMAIN
});
await admin.authorize();
// Whitelist the deployed contract against a permissions template.
// The template controls which methods are exposed and to whom.
const response = await admin.admin.contracts.create({
contractAddress: DEPLOYED_CONTRACT_ADDRESS,
templateKey: 'erc20-token',
abi: JSON.stringify(erc20Abi),
name: null,
description: null,
discloseErc20TotalSupply: false,
discloseBytecode: false,
disclosureStartBlock: '0x0'
});Configuring Contract Function Permissions
All contract functions default to "Forbidden" until configured. Chain administrators must register new contracts and configure function permissions in the Prividium™ Admin Panel after deployment. An example of configuring contract permissions programmatically can be found in the developer quickstart.
-
Register the contract: Provide the contract name, address, and ABI in the admin panel
-
View Function List: Review all functions from the contract ABI
-
Edit Permissions: For each function:
- Select permission type (Forbidden, All Users, Check Role, etc.)
- Configure role requirements (if using role-based permissions)
- Set argument restrictions (if needed)
- Save changes
-
Verify Configuration: Permissions activate immediately. Test by calling functions from your application.
Permission Enforcement
Read Functions (view/pure)
- Permission check occurs during
eth_call - Unauthorized calls return an error
Write Functions (nonpayable/payable)
- Permission checks occur twice:
- Simulation phase (
eth_callfor gas estimation) - Execution phase (
eth_sendRawTransaction)
- Simulation phase (
- Both checks must pass for transaction success
Full Examples
Authenticated Balance Script
import { createPrividiumClient } from 'prividium';
import { createPrividiumSiweChain } from 'prividium/siwe';
import { defineChain, formatEther, type Hex } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
const PRIVIDIUM_API_BASE_URL = process.env.PRIVIDIUM_API_BASE_URL ?? 'http://localhost:8000';
const PRIVATE_KEY = process.env.PRIVATE_KEY;
const DOMAIN = process.env.DOMAIN ?? 'localhost:3000';
const CHAIN_ID = Number(process.env.CHAIN_ID ?? '6565');
const CHAIN_NAME = process.env.CHAIN_NAME ?? 'Prividium Local';
const TARGET_ADDRESS = process.env.TARGET_ADDRESS ?? '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266';
if (!PRIVATE_KEY) throw new Error('MISSING PRIVATE KEY');
export const account = privateKeyToAccount(PRIVATE_KEY as Hex);
export const chain = defineChain({
id: CHAIN_ID,
name: CHAIN_NAME,
nativeCurrency: {
name: 'Ether',
symbol: 'ETH',
decimals: 18
},
rpcUrls: {
default: {
http: [new URL('/rpc', PRIVIDIUM_API_BASE_URL).toString()]
}
}
});
export const prividium = createPrividiumSiweChain({
account,
chain,
prividiumApiBaseUrl: PRIVIDIUM_API_BASE_URL,
domain: DOMAIN
});
export async function main() {
await prividium.authorize();
const client = createPrividiumClient({
chain: prividium.chain,
transport: prividium.transport,
account: prividium.address
});
const balance = await client.getBalance({ address: TARGET_ADDRESS });
console.log(`Balance: ${formatEther(balance)} ETH`);
}
main().catch(console.error);Contract Deployment Script
import { createPrividiumClient } from 'prividium';
import { createPrividiumSiweChain } from 'prividium/siwe';
import { createWalletClient, defineChain, type Hex } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
const PRIVIDIUM_API_BASE_URL = process.env.PRIVIDIUM_API_BASE_URL ?? 'http://localhost:8000';
const PRIVATE_KEY = process.env.PRIVATE_KEY;
const DOMAIN = process.env.DOMAIN ?? 'localhost:3000';
const CHAIN_ID = Number(process.env.CHAIN_ID ?? '6565');
const CHAIN_NAME = process.env.CHAIN_NAME ?? 'Prividium Local';
const GREETER_BYTECODE = process.env.GREETER_BYTECODE;
const greeterAbi = [
{
type: 'constructor',
inputs: [{ name: 'initialGreeting', type: 'string' }],
stateMutability: 'nonpayable'
}
];
if (!PRIVATE_KEY) throw new Error('MISSING PRIVATE KEY');
if (!GREETER_BYTECODE) throw new Error('MISSING GREETER_BYTECODE');
export async function main() {
const account = privateKeyToAccount(PRIVATE_KEY as Hex);
const chain = defineChain({
id: CHAIN_ID,
name: CHAIN_NAME,
nativeCurrency: {
name: 'Ether',
symbol: 'ETH',
decimals: 18
},
rpcUrls: {
default: {
http: [new URL('/rpc', PRIVIDIUM_API_BASE_URL).toString()]
}
}
});
const prividium = createPrividiumSiweChain({
account,
chain,
prividiumApiBaseUrl: PRIVIDIUM_API_BASE_URL,
domain: DOMAIN
});
const rpcClient = createPrividiumClient({
chain: prividium.chain,
transport: prividium.transport,
account: account.address
});
await prividium.authorize();
const walletClient = createWalletClient({
account,
chain: prividium.chain,
transport: prividium.transport
});
const hash = await walletClient.deployContract({
abi: greeterAbi,
bytecode: GREETER_BYTECODE,
args: ['Hello from Prividium']
});
const receipt = await rpcClient.waitForTransactionReceipt({ hash });
console.log(`Contract deployed at: ${receipt.contractAddress}`);
return receipt.contractAddress;
}
main().catch(console.error);Next Steps
- Building Web Applications - Build frontend applications that interact with Prividium™ chains