Skip to main content

Deployment Guide

This guide covers how to deploy the Open Pay smart contracts to BSC Testnet (or Mainnet) and configure them for your environment.

Current Testnet Deployment

All contracts are live on BSC Testnet (Chain ID 97):
ContractAddressPurpose
OpenPayEscrow0xe50464081b781AFE101EB40bC7e68Fd017c5e8f2Payment escrow
OpenPayPriceFeed0x1f34e070D4BB1eD3AaF37D8E3297b0a9A12a3399Price oracle aggregator
MockUSDTTestnet ERC-20Test USDT token
MockUSDCTestnet ERC-20Test USDC token
MockPriceFeedTestnet contractSimulated Chainlink oracle
These testnet contracts use mock tokens and mock price feeds. For production, you would use real token addresses and Chainlink oracle feeds.

Deploy Your Own

Prerequisites

Environment Setup

1

Clone the Repository

git clone https://github.com/theetaz/open-pay.git
cd open-pay/contracts
2

Install Dependencies

npm install
3

Configure Environment Variables

Create a .env file in the contracts directory:
# Private key of the deployer wallet (without 0x prefix)
DEPLOYER_PRIVATE_KEY=your_private_key_here

# BSC Testnet RPC URL
BSC_TESTNET_RPC_URL=https://data-seed-prebsc-1-s1.bnbchain.org:8545

# BSC Mainnet RPC URL (for production)
BSC_MAINNET_RPC_URL=https://bsc-dataseed1.bnbchain.org

# BscScan API key for contract verification
BSCSCAN_API_KEY=your_bscscan_api_key

# Platform fee in basis points (200 = 2%)
PLATFORM_FEE_BPS=200

# Fee recipient address
FEE_RECIPIENT=0xYourFeeRecipientAddress
Never commit your .env file to version control. The deployer private key controls the contract and all escrowed funds.
4

Deploy Contracts

Deploy to BSC Testnet:
npx hardhat run scripts/deploy.ts --network bscTestnet
The deployment script deploys contracts in this order:
  1. MockPriceFeed — simulated Chainlink oracle (testnet only)
  2. MockUSDT — test USDT token (testnet only)
  3. MockUSDC — test USDC token (testnet only)
  4. OpenPayPriceFeed — price aggregator
  5. OpenPayEscrow — payment escrow
After deployment, the script configures:
  • Token price feeds on OpenPayPriceFeed
  • Supported tokens on OpenPayEscrow
  • Price feed address on OpenPayEscrow
5

Verify on BscScan

Verify each contract on BscScan for public source code access:
# Verify OpenPayEscrow
npx hardhat verify --network bscTestnet \
  0xe50464081b781AFE101EB40bC7e68Fd017c5e8f2 \
  200 \
  0xYourFeeRecipientAddress

# Verify OpenPayPriceFeed
npx hardhat verify --network bscTestnet \
  0x1f34e070D4BB1eD3AaF37D8E3297b0a9A12a3399
Verification makes your contract source code publicly readable on BscScan, which builds trust with users and merchants.

Adding New Tokens

To support a new ERC-20 token for payments:
1

Configure the Price Feed

Register the token with a Chainlink price feed on the OpenPayPriceFeed contract:
// For a volatile token (e.g., WETH), provide a Chainlink feed
priceFeed.configureToken(
    0xTokenAddress,     // ERC-20 token address
    0xChainlinkFeed,    // Chainlink price feed address
    false               // Not a stablecoin
);

// For a stablecoin (e.g., DAI), no feed needed
priceFeed.configureToken(
    0xDAIAddress,       // DAI token address
    address(0),         // No feed required
    true                // Is a stablecoin ($1.00)
);
2

Enable on Escrow

Mark the token as supported on the OpenPayEscrow contract:
escrow.setSupportedToken(0xTokenAddress, true);
3

Update Backend

Add the token configuration to the Open Pay backend so the API knows about the new payment option. Update the token list in the service configuration.

Switching to Mainnet

When deploying to BSC Mainnet (Chain ID 56), make these changes:
1

Use Real Token Addresses

Replace mock tokens with mainnet addresses:
TokenMainnet Address
USDT (BEP-20)0x55d398326f99059fF775485246999027B3197955
USDC (BEP-20)0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d
WBNB0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c
2

Use Real Chainlink Feeds

Replace mock price feeds with mainnet Chainlink oracles:
PairMainnet Feed Address
BNB/USD0x0567F2323251f0Aab15c8dFb1967E4e8A7D42aeE
BTC/USD0x264990fbd0A4796A3E3d8E37C4d5F87a3aCa5Ebf
ETH/USD0x9ef1B8c0E4F7dc8bF5719Ea496883DC6401d5b2e
Find all Chainlink BSC feeds at data.chain.link.
3

Deploy to Mainnet

npx hardhat run scripts/deploy.ts --network bscMainnet
4

Verify and Configure

  • Verify contracts on BscScan Mainnet
  • Configure all token price feeds
  • Set the fee recipient to your production address
  • Update the platform backend with the new contract addresses

Hardhat Configuration

Example hardhat.config.ts for BSC networks:
import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";
import * as dotenv from "dotenv";

dotenv.config();

const config: HardhatUserConfig = {
  solidity: {
    version: "0.8.24",
    settings: {
      optimizer: {
        enabled: true,
        runs: 200,
      },
    },
  },
  networks: {
    bscTestnet: {
      url: process.env.BSC_TESTNET_RPC_URL || "https://data-seed-prebsc-1-s1.bnbchain.org:8545",
      chainId: 97,
      accounts: process.env.DEPLOYER_PRIVATE_KEY
        ? [process.env.DEPLOYER_PRIVATE_KEY]
        : [],
    },
    bscMainnet: {
      url: process.env.BSC_MAINNET_RPC_URL || "https://bsc-dataseed1.bnbchain.org",
      chainId: 56,
      accounts: process.env.DEPLOYER_PRIVATE_KEY
        ? [process.env.DEPLOYER_PRIVATE_KEY]
        : [],
    },
  },
  etherscan: {
    apiKey: {
      bscTestnet: process.env.BSCSCAN_API_KEY || "",
      bsc: process.env.BSCSCAN_API_KEY || "",
    },
  },
};

export default config;

Troubleshooting

The Chainlink oracle data is older than the staleness threshold (default 1 hour). On testnet, mock feeds may not auto-update. Call the mock feed’s updateAnswer() to refresh the price, or increase the staleness threshold.
The token has not been registered with the price feed contract. Call priceFeed.configureToken() to register it.
The payer has not approved the escrow contract to spend their tokens. The payer must call token.approve(escrowAddress, amount) before creating a payment.
Ensure you are using the exact same compiler version and optimization settings as the deployment. Check that constructor arguments match exactly.