Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/backend/src/common/constants/campaign.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ export const QUEST_POINTS_ACCOUNT_FIRST_TX = 100;
export const QUEST_POINTS_SUCCESSFUL_TX = 50;

// Supported chain IDs
// 421614 = Arbitrum Sepolia (testnet only; account contract is the Stylus port).
export const SUPPORTED_CHAIN_IDS = [2651420, 84532, 26514, 8453, 421614];
// 421614 = Arbitrum Sepolia, 42161 = Arbitrum One (account contract is the Stylus port).
export const SUPPORTED_CHAIN_IDS = [2651420, 84532, 26514, 8453, 421614, 42161];

// External APIs
export const COINGECKO_API_URL =
Expand Down
1 change: 1 addition & 0 deletions packages/backend/src/common/constants/proof.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ export const DOMAIN_ID_BY_CHAIN_ID = {
421614: 4, // Arbitrum Sepolia (zkVerify testnet domain)
26514: 3, // Horizen mainnet
8453: 2, // Base mainnet
42161: 10, // Arbitrum One (zkVerify System Domain, verified docs.zkverify.io)
} as const;
3 changes: 2 additions & 1 deletion packages/hardhat/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ const config: HardhatUserConfig = {
accounts: [deployerPrivateKey],
},
arbitrum: {
url: `https://arb-mainnet.g.alchemy.com/v2/${providerApiKey}`,
// Default Alchemy key is IP-whitelisted; use the public RPC unless overridden.
url: process.env.ARBITRUM_RPC || "https://arb1.arbitrum.io/rpc",
accounts: [deployerPrivateKey],
},
arbitrumSepolia: {
Expand Down
15 changes: 6 additions & 9 deletions packages/nextjs/components/NewAccount/ChooseNetwork.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import React from "react";
import Image from "next/image";
import { CHAIN_IDS } from "@polypay/shared";
import { getDefaultChainId, getNetworkMeta } from "~~/utils/network";
import { notification } from "~~/utils/scaffold-eth";

Expand All @@ -14,10 +15,7 @@ interface ChooseNetworkProps {
isWalletConnected: boolean;
}

const HORIZEN_MAINNET = 26514;
const BASE_MAINNET = 8453;
// Arbitrum is testnet-only (zkVerify has no Arbitrum One mainnet verifier yet).
const ARBITRUM_SEPOLIA = 421614;
const { HORIZEN_MAINNET, HORIZEN_TESTNET, BASE_MAINNET, BASE_SEPOLIA, ARBITRUM_ONE, ARBITRUM_SEPOLIA } = CHAIN_IDS;

const ChooseNetwork: React.FC<ChooseNetworkProps> = ({
className,
Expand All @@ -29,13 +27,12 @@ const ChooseNetwork: React.FC<ChooseNetworkProps> = ({
}) => {
const defaultChainId = getDefaultChainId();

const isTestnet = defaultChainId === 2651420;
const isTestnet = defaultChainId === HORIZEN_TESTNET;

const networks = [
{ chainId: HORIZEN_MAINNET, fallbackChainId: 2651420 },
{ chainId: BASE_MAINNET, fallbackChainId: 84532 },
// Arbitrum has no mainnet entry; only surfaced on testnet.
...(isTestnet ? [{ chainId: ARBITRUM_SEPOLIA, fallbackChainId: ARBITRUM_SEPOLIA }] : []),
{ chainId: HORIZEN_MAINNET, fallbackChainId: HORIZEN_TESTNET },
{ chainId: BASE_MAINNET, fallbackChainId: BASE_SEPOLIA },
{ chainId: ARBITRUM_ONE, fallbackChainId: ARBITRUM_SEPOLIA },
].map(n => {
// If we are on testnet env, use testnet ids instead
const chainId = isTestnet ? n.fallbackChainId : n.chainId;
Expand Down
3 changes: 1 addition & 2 deletions packages/nextjs/components/Sidebar/NetworkChooserSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ interface NetworkChooserSidebarProps {
onSelectNetwork: (chainId: number | null) => void;
}

const NETWORKS_MAINNET = [26514, 8453];
// Arbitrum Sepolia (421614) is testnet-only — no Arbitrum One mainnet entry.
const NETWORKS_MAINNET = [26514, 8453, 42161];
const NETWORKS_TESTNET = [2651420, 84532, 421614];

export default function NetworkChooserSidebar({ isOpen, accounts, onSelectNetwork }: NetworkChooserSidebarProps) {
Expand Down
3 changes: 1 addition & 2 deletions packages/nextjs/scaffold.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,9 @@ const scaffoldConfig = {
// The networks on which your DApp is live
// targetNetworks: [chains.sepolia],
// targetNetworks: [chains.hardhat],
// Arbitrum is testnet-only: zkVerify has a verifier on Arbitrum Sepolia but not Arbitrum One.
targetNetworks:
process.env.NEXT_PUBLIC_NETWORK === NetworkValue.mainnet
? [horizenMainnet, chains.base]
? [horizenMainnet, chains.base, chains.arbitrum]
: [horizenTestnet, chains.baseSepolia, chains.arbitrumSepolia],
// The interval at which your front-end polls the RPC servers for new data (it has no effect if you only target the local network (default is 4000))
pollingInterval: RPC_POLLING_INTERVAL,
Expand Down
7 changes: 6 additions & 1 deletion packages/nextjs/utils/network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,12 @@ const NETWORK_META_BY_CHAIN_ID: Record<number, NetworkMeta> = {
icon: "/token/base.svg",
badge: "/token/base.svg",
},
// Arbitrum (testnet only). Add /token/arbitrum.svg asset for the icon/badge.
// Arbitrum
42161: {
name: "Arbitrum",
icon: "/token/arbitrum.svg",
badge: "/token/arbitrum.svg",
},
421614: {
name: "Arbitrum Sepolia",
icon: "/token/arbitrum.svg",
Expand Down
6 changes: 6 additions & 0 deletions packages/shared/src/chains/arbitrumOne.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { arbitrum } from "viem/chains";

// Re-export viem's Arbitrum One (42161) chain definition for consistency with
// the other chains module. On Arbitrum the account contract is the Stylus
// (Rust/WASM) port of MetaMultiSigWallet (same as Arbitrum Sepolia).
export { arbitrum as arbitrumOne };
4 changes: 4 additions & 0 deletions packages/shared/src/chains/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import { horizenMainnet } from "./horizenMainnet";
import { baseSepolia } from "./baseSepolia";
import { baseMainnet } from "./baseMainnet";
import { arbitrumSepolia } from "./arbitrumSepolia";
import { arbitrumOne } from "./arbitrumOne";

export {
horizenTestnet,
horizenMainnet,
baseSepolia,
baseMainnet,
arbitrumSepolia,
arbitrumOne,
};

export type NetworkType = "testnet" | "mainnet";
Expand Down Expand Up @@ -38,6 +40,8 @@ export const getChainById = (chainId: number) => {
return baseMainnet;
case arbitrumSepolia.id:
return arbitrumSepolia;
case arbitrumOne.id:
return arbitrumOne;
default:
throw new Error(`Unsupported chainId: ${chainId}`);
}
Expand Down
12 changes: 12 additions & 0 deletions packages/shared/src/constants/chains.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Single source of truth for the EVM chain IDs PolyPay supports.
// Reuse these everywhere instead of redeclaring magic numbers.
export const CHAIN_IDS = {
HORIZEN_MAINNET: 26514,
HORIZEN_TESTNET: 2651420,
BASE_MAINNET: 8453,
BASE_SEPOLIA: 84532,
ARBITRUM_ONE: 42161,
ARBITRUM_SEPOLIA: 421614,
} as const;

export type SupportedChainId = (typeof CHAIN_IDS)[keyof typeof CHAIN_IDS];
1 change: 1 addition & 0 deletions packages/shared/src/constants/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from "./socket-events";
export * from "./room";
export * from "./chains";
export * from "./token";
export * from "./campaign";
export * from "./contract";
21 changes: 14 additions & 7 deletions packages/shared/src/constants/token.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { CHAIN_IDS } from "./chains";

export type TokenAddresses = Record<number, string>;

export interface Token {
Expand All @@ -15,20 +17,23 @@ export interface ResolvedToken extends Omit<Token, "addresses"> {

export const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";

// Chain IDs
const HORIZEN_MAINNET = 26514;
const HORIZEN_TESTNET = 2651420;
const BASE_MAINNET = 8453;
const BASE_SEPOLIA = 84532;
// Arbitrum is testnet-only in PolyPay (zkVerify has no Arbitrum One verifier).
const ARBITRUM_SEPOLIA = 421614;
// Chain IDs (single source of truth in ./chains).
const {
HORIZEN_MAINNET,
HORIZEN_TESTNET,
BASE_MAINNET,
BASE_SEPOLIA,
ARBITRUM_SEPOLIA,
ARBITRUM_ONE,
} = CHAIN_IDS;

const ALL_CHAIN_IDS = [
HORIZEN_MAINNET,
HORIZEN_TESTNET,
BASE_MAINNET,
BASE_SEPOLIA,
ARBITRUM_SEPOLIA,
ARBITRUM_ONE,
];

export const NATIVE_ETH: Token = {
Expand Down Expand Up @@ -80,6 +85,8 @@ export const USDC_TOKEN: Token = {
[BASE_SEPOLIA]: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
// Circle official USDC on Arbitrum Sepolia (source: developers.circle.com).
[ARBITRUM_SEPOLIA]: "0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d",
// Circle native USDC on Arbitrum One (NOT bridged USDC.e; source: developers.circle.com).
[ARBITRUM_ONE]: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
},
symbol: "USDC",
name: "USD Coin",
Expand Down
1 change: 1 addition & 0 deletions packages/shared/src/contracts/MetaMultiSigWalletStylus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
// Chains whose account contract is the Stylus port instead of the EVM .sol one.
export const STYLUS_CHAIN_IDS: readonly number[] = [
421614, // Arbitrum Sepolia
42161, // Arbitrum One
];

export const isStylusChain = (chainId: number): boolean =>
Expand Down
17 changes: 15 additions & 2 deletions packages/shared/src/contracts/contracts-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,26 @@ export const CONTRACT_CONFIG_BY_CHAIN_ID = {
// Current build: original STATICCALL-based Poseidon (uses the on-chain
// poseidon-solidity PoseidonT3 library at `poseidonT3Address`). The
// in-process Rust Poseidon experiment was reverted — see NOTES.md.
stylusImplAddress: "0x0395b99f3a45bd08d018d3d3060a0e2bf8dc8978",
stylusImplAddress: "0x61fddf7cde02d4527b7d1086671d3f948e59f1d1",
// Stylus/Rust EIP-1167 factory (packages/stylus-factory) bound to the
// STATICCALL-Poseidon impl above. Emits byte-identical proxy bytecode to
// the previous Solidity factory, so accounts created here are
// indistinguishable on-chain from accounts created against the legacy
// factory.
stylusFactoryAddress: "0xc35c0693286ebdc18bdf257f102dec9632a7ce77",
stylusFactoryAddress: "0x73d33f803600087ed1259035f9ff46f16f15c11a",
},
42161: {
// Arbitrum One mainnet. Account contract is the Stylus (Rust/WASM) port,
// same as Arbitrum Sepolia. zkVerify aggregation + vkHash match the other
// mainnets (Horizen/Base); PoseidonT3 is the deterministic CREATE2 address.
zkVerifyAddress: "0xCb47A3C3B9Eb2E549a3F2EA4729De28CafbB2b69",
vkHash:
"0xb3c5381523a496996868370791ec7ae490be7e2c996296fb67708daed8a6ea38",
poseidonT3Address: "0x3333333C0A88F9BE4fd23ed0536F9B6c427e3B93",
// Deployed in Phase 2 via cargo stylus on Arbitrum One. Zero-address
// sentinel until then (getStylusFactoryAddress throws on zero).
stylusImplAddress: "0x49e772bd7efd483c043402331fbf03533852850f",
stylusFactoryAddress: "0x740b6a46585474eb113f81999c1117e69d4be1be",
},
} as const;

Expand Down
Loading
Loading