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 package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"vite": "^5.0.3"
},
"dependencies": {
"@openzeppelin/defender-sdk": "^2.4.0",
"@openzeppelin/defender-sdk": "^2.5.0",
"@remixproject/plugin": "^0.3.38",
"@remixproject/plugin-api": "^0.3.38",
"@remixproject/plugin-iframe": "^0.3.38",
Expand All @@ -37,4 +37,4 @@
"superchain-registry": "github:ethereum-optimism/superchain-registry"
},
"version": "0.0.1"
}
}
15 changes: 11 additions & 4 deletions src/lib/defender/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,19 @@ export const listApiKeyPermissions = async (credentials: Credentials) => {

export const listNetworks = async (credentials: Credentials) => {
const client = getClient(credentials);
const [networks, forkedNetworks, privateNetworks] = await Promise.all([
client.network.listSupportedNetworks(),

const [nativeNetworks, forkedNetworks, privateNetworks] = (await Promise.all([
client.network.listSupportedNetworks({ networkType: ["deploy"], includeDefinition: true }),
client.network.listForkedNetworks(),
client.network.listPrivateNetworks(),
]);
return [...networks, ...forkedNetworks, ...privateNetworks];
]))

return [
nativeNetworks
.map((network) => ({...network, networkType: "native"} as const)),
forkedNetworks,
privateNetworks
].flat()
}

export const listApprovalProcesses = async (credentials: Credentials) => {
Expand Down
11 changes: 5 additions & 6 deletions src/lib/ethereum/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { type Eip1193Provider, BrowserProvider, ContractFactory } from 'ethers';
import { chainIds, type TenantNetworkResponse } from "$lib/models/network";
import { type NetworkResponse, type TenantNetworkResponse } from "$lib/models/network";
import type { DeployContractResult } from '$lib/models/ethereum';
import { log } from '$lib/remix/logger';

Expand All @@ -13,21 +13,20 @@ function getEthereum(): Eip1193Provider {
*
* @param network target network to switch to.
*/
export async function switchToNetwork(network: string | TenantNetworkResponse) {
const chainId = typeof network === 'string' ? chainIds[network] : network.chainId;
if (!chainId) throw new Error(`Invalid network: ${network}`);
export async function switchToNetwork(network: NetworkResponse | TenantNetworkResponse) {
if (!network.chainId) throw new Error(`Invalid network: ${network}`);

const ethereum = getEthereum();

// ignore if user is already connected to target network.
const current = await ethereum.request({ method: 'eth_chainId' });
if (parseInt(current, 16) === chainId) return;
if (parseInt(current, 16) === network.chainId) return;

log("[Defender Deploy] Switching network...");

await ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: `0x${chainId.toString(16)}` }],
params: [{ chainId: `0x${network.chainId.toString(16)}` }],
});
};

Expand Down
3 changes: 1 addition & 2 deletions src/lib/models/approval-process.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type { TenantNetworkResponse } from "./network";
import type { GlobalState } from "./ui";

/**
Expand All @@ -9,7 +8,7 @@ export type ApprovalProcess = {
createdAt: string;
name: string;
component?: ComponentType;
network?: string | TenantNetworkResponse;
network?: string;
via?: string;
viaType?: 'EOA' | 'Contract' | 'Multisig' | 'Gnosis Safe' | 'Safe' | 'Gnosis Multisig' | 'Relayer' | 'Relayer Group' | 'Unknown' | 'Relayer Group' | 'Timelock Controller' | 'ERC20' | 'Governor' | 'Fireblocks';
multisigSender?: string;
Expand Down
4 changes: 2 additions & 2 deletions src/lib/models/auth.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { ApprovalProcess } from "./approval-process";
import type { BlockExplorerKey } from "./block-explorer-key";
import type { TenantNetworkResponse } from "./network";
import type { NetworkResponse, TenantNetworkResponse } from "./network";
import type { Relayer } from "./relayer";

/**
Expand All @@ -13,7 +13,7 @@ export type ApiKeyCapability = 'create-admin-proposals' | 'manage-relayers' | 'm
export type AuthenticationResponse = {
credentials: Credentials,
permissions: ApiKeyCapability[],
networks: (string | TenantNetworkResponse)[],
networks: (NetworkResponse | TenantNetworkResponse)[],
approvalProcesses: ApprovalProcess[],
relayers: Relayer[],
blockExplorerKeys: BlockExplorerKey[]
Expand Down
160 changes: 21 additions & 139 deletions src/lib/models/network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,150 +11,32 @@ export interface TenantNetworkResponse {
isProduction?: boolean;
}

export const productionNetworks = new Set([
'arbitrum',
'arbitrum-nova',
'aurora',
'avalanche',
'base',
'bsc',
'celo',
'fantom',
'fuse',
'hedera',
'japan',
'linea',
'mainnet',
'mantle',
'matic',
'matic-zkevm',
'meld',
'moonbeam',
'moonriver',
'optimism',
'scroll',
'unichain',
'xdai',
'zksync'
]);
export interface NetworkResponse {
name: string;
displayName?: string;
symbol: string;
chainId: number;
networkType: 'native';
isProduction: boolean;
}


export function isProductionNetwork(network: string | TenantNetworkResponse): boolean {
if (typeof network === 'string') {
return productionNetworks.has(network);
}
export function isProductionNetwork(network: NetworkResponse | TenantNetworkResponse): boolean {
return network.isProduction ?? false;
}

export function getNetworkLiteral(network: string | TenantNetworkResponse): string {
return typeof network === 'string' ? network : network.name;
export function getNetworkLiteral(network: NetworkResponse | TenantNetworkResponse): string {
return network.name;
}

export const chainIds: { [key in string]: number } = {
'alfajores': 44787,
'amoy': 80002,
'arbitrum': 42161,
'arbitrum-nova': 42170,
'arbitrum-sepolia': 421614,
'aurora': 1313161554,
'auroratest': 1313161555,
'avalanche': 43114,
'base': 8453,
'base-sepolia': 84532,
'bsc': 56,
'bsctest': 97,
'celo': 42220,
'fantom': 250,
'fantomtest': 4002,
'fuji': 43113,
'fuse': 122,
'hedera': 295,
'hederatest': 296,
'holesky': 17000,
'japan': 81,
'japan-testnet': 10081,
'linea': 59144,
'linea-goerli': 59140,
'linea-sepolia': 59141,
'mainnet': 1,
'mantle': 5000,
'mantle-sepolia': 5003,
'matic': 137,
'matic-zkevm': 1101,
'matic-zkevm-testnet': 1442,
'meld': 333000333,
'meld-kanazawa': 222000222,
'moonbase': 1287,
'moonbeam': 1284,
'moonriver': 1285,
'mumbai': 80001,
'optimism': 10,
'optimism-sepolia': 11155420,
'scroll': 534352,
'scroll-sepolia': 534351,
'sepolia': 11155111,
'sokol': 77,
'unichain': 130,
'unichain-sepolia': 1301,
'x-dfk-avax-chain': 53935,
'x-dfk-avax-chain-test': 335,
'x-security-alliance': 888,
'xdai': 100,
'zksync': 324,
'zksync-sepolia': 300,
};

export function isTenantNetwork(network: NetworkResponse | TenantNetworkResponse): network is TenantNetworkResponse {
return "tenantNetworkId" in network
}

export function isNativeNetwork(network: NetworkResponse | TenantNetworkResponse): network is NetworkResponse {
return network.networkType === "native"
}

export const chainDisplayNames: { [key in string]: string } = {
'alfajores': 'Celo Alfajores',
'amoy': 'Polygon Amoy',
'arbitrum': 'Arbitrum',
'arbitrum-nova': 'Arbitrum Nova',
'arbitrum-sepolia': 'Arbitrum Sepolia',
'aurora': 'Aurora',
'auroratest': 'Aurora Testnet',
'avalanche': 'Avalanche',
'base': 'Base',
'base-sepolia': 'Base Sepolia',
'bsc': 'Binance Smart Chain',
'bsctest': 'Binance Smart Chain Testnet',
'celo': 'Celo',
'fantom': 'Fantom',
'fantomtest': 'Fantom Testnet',
'fuji': 'Avalanche Fuji',
'fuse': 'Fuse',
'hedera': 'Hedera',
'hederatest': 'Hedera Testnet',
'holesky': 'Holesky',
'japan': 'Japan Open Chain',
'japan-testnet': 'Japan Open Chain Testnet',
'linea': 'Linea',
'linea-sepolia': 'Linea Sepolia',
'mainnet': 'Ethereum Mainnet',
'mantle': 'Mantle',
'mantle-sepolia': 'Mantle Sepolia',
'matic': 'Polygon',
'matic-zkevm': 'Polygon ZK-EVM',
'matic-zkevm-testnet': 'Polygon ZK-EVM Testnet',
'matic-cardona-zkevm-testnet': 'Polygon Cardona ZK-EVM Testnet',
'meld': 'Meld',
'meld-kanazawa': 'Meld Kanazawa',
'moonbase': 'Moonbase',
'moonbeam': 'Moonbeam',
'moonriver': 'Moonriver',
'mumbai': 'Polygon Mumbai',
'optimism': 'OP Mainnet',
'optimism-sepolia': 'OP Sepolia',
'scroll': 'Scroll',
'scroll-sepolia': 'Scroll Sepolia',
'sepolia': 'Sepolia',
'sokol': 'Sokol',
'unichain': 'Unichain',
'unichain-sepolia': 'Unichain Sepolia',
'x-dfk-avax-chain': 'Avalanche X-DFK',
'x-dfk-avax-chain-test': 'Avalanche X-DFK Testnet',
'x-security-alliance': 'X Security Alliance',
'xdai': 'Gnosis Chain',
'zksync': 'zkSync',
'zksync-sepolia': 'zkSync Sepolia',
};
export function getNetworkDisplayName(network: NetworkResponse | TenantNetworkResponse) {
return (isNativeNetwork(network) ? network.displayName : network.name) || network.name
}
6 changes: 3 additions & 3 deletions src/lib/models/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { CompilationFileSources, CompilationResult, SourceWithTarget } from
import type { Relayer } from "./relayer";
import type { ApiKeyCapability, Credentials } from "./auth";
import type { ApprovalProcess, ApprovalProcessToCreate } from "./approval-process";
import type { TenantNetworkResponse } from "./network";
import type { NetworkResponse, TenantNetworkResponse } from "./network";
import type { BlockExplorerKey } from "./block-explorer-key";

export type DropdownItem<TValue = any> = {
Expand All @@ -20,7 +20,7 @@ export type GlobalState = {
successMessage?: string;
credentials: Credentials;
permissions: ApiKeyCapability[];
networks: (string | TenantNetworkResponse)[];
networks: (NetworkResponse | TenantNetworkResponse)[];
approvalProcesses: ApprovalProcess[];
relayers: Relayer[];
blockExplorerKeys: BlockExplorerKey[]
Expand All @@ -33,7 +33,7 @@ export type GlobalState = {
groupNetworksBy?: 'superchain',
}
form: {
network?: string | TenantNetworkResponse;
network?: NetworkResponse | TenantNetworkResponse;
approvalProcessSelected?: ApprovalProcess;
approvalProcessToCreate?: ApprovalProcessToCreate;
approvalType?: SelectedApprovalProcessType;
Expand Down
2 changes: 1 addition & 1 deletion src/lib/remix/components/ApprovalProcess.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@

// Relayer selection logic
const relayerByNetwork = (relayer: Relayer) =>
relayer.network === globalState.form.network;
relayer.network === globalState.form.network?.name;
const relayerToDropdownItem = (relayer: Relayer) => ({
label: `${relayer.name} (${abbreviateAddress(relayer.address)})`,
value: relayer,
Expand Down
16 changes: 3 additions & 13 deletions src/lib/remix/components/Deploy.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,17 @@
import { onDestroy } from "svelte";

// Lib
import { updateSelectedApprovalProcessWithExisting, clearErrorBanner, globalState, setDeploymentCompleted, setErrorBanner } from "$lib/state/state.svelte";
import { updateSelectedApprovalProcessWithExisting, clearErrorBanner, globalState, setDeploymentCompleted, setErrorBanner, findDeploymentEnvironment } from "$lib/state/state.svelte";
import { log, logError, logSuccess, logWarning } from "$lib/remix/logger";
import { deployContract, switchToNetwork } from "$lib/ethereum";
import { API } from "$lib/api";

// Utils
import { attempt } from "$lib/utils/attempt";
import { isDeploymentEnvironment, isSameNetwork } from "$lib/utils/helpers";
import { getContractFeatures, getConstructorInputs, encodeConstructorArgs, getContractBytecode, createArtifactPayload } from "$lib/utils/contracts";

// Models
import { getNetworkLiteral, isProductionNetwork, type TenantNetworkResponse } from "$lib/models/network";
import { getNetworkLiteral, isProductionNetwork } from "$lib/models/network";
import type { ApprovalProcess, CreateApprovalProcessRequest} from "$lib/models/approval-process";
import type { DeployContractRequest, UpdateDeploymentRequest } from "$lib/models/deploy";
import type { APIResponse, HTMLInputElementEvent } from "$lib/models/ui";
Expand Down Expand Up @@ -95,16 +94,7 @@
);
}
});

function findDeploymentEnvironment(via?: string, network?: string) {
if (!via || !network) return undefined;
return globalState.approvalProcesses.find((ap) =>
ap.network &&
isDeploymentEnvironment(ap) &&
isSameNetwork(ap.network, network) &&
ap.via?.toLocaleLowerCase() === via.toLocaleLowerCase()
);
}


async function getOrCreateApprovalProcess(): Promise<ApprovalProcess | undefined> {
const ap = globalState.form.approvalProcessToCreate;
Expand Down
Loading