Skip to content

Commit

Permalink
refactor: use clientConfig ethUrl for evmSlotValue and ozVotesStorage…
Browse files Browse the repository at this point in the history
…Proof (#62)

Predefined mapping was used initially, but as each networkConfig
will have each own ethUrl we can use it as well (Starknet Goerli will
be mapped to Ethereum goerli etc.).
  • Loading branch information
Sekhmet authored Feb 19, 2024
1 parent 88e1980 commit ddcdc7f
Show file tree
Hide file tree
Showing 7 changed files with 43 additions and 50 deletions.
5 changes: 5 additions & 0 deletions .changeset/slow-stingrays-smash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@snapshot-labs/sx": patch
---

use clientConfig.ethUrl instead of predefined RPC APIs for starknet strategies
1 change: 0 additions & 1 deletion apps/ui/.env
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
VITE_ENABLED_NETWORKS=
VITE_ETH_RPC_URL=https://rpc.snapshotx.xyz
VITE_MANA_URL=https://mana.pizza
VITE_HIGHLIGHT_URL=
VITE_IPFS_GATEWAY=snapshot.4everland.link
Expand Down
4 changes: 1 addition & 3 deletions apps/ui/src/networks/starknet/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,11 @@ export function createActions(
networkId: NetworkID,
starkProvider: RpcProvider,
helpers: NetworkHelpers,
{ l1ChainId }: { l1ChainId: number }
{ l1ChainId, ethUrl }: { l1ChainId: number; ethUrl: string }
): NetworkActions {
const networkConfig = CONFIGS[networkId];
if (!networkConfig) throw new Error(`Unsupported network ${networkId}`);

const ethUrl: string = import.meta.env.VITE_ETH_RPC_URL;

const l1Provider = getProvider(l1ChainId);

const clientConfig = {
Expand Down
12 changes: 10 additions & 2 deletions apps/ui/src/networks/starknet/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type Metadata = {
baseChainId: number;
baseNetworkId: NetworkID;
rpcUrl: string;
ethRpcUrl: string;
explorerUrl: string;
apiUrl: string;
};
Expand All @@ -29,6 +30,7 @@ export const METADATA: Partial<Record<NetworkID, Metadata>> = {
baseChainId: 1,
baseNetworkId: 'eth',
rpcUrl: `https://starknet-mainnet.infura.io/v3/${import.meta.env.VITE_INFURA_API_KEY}`,
ethRpcUrl: `https://mainnet.infura.io/v3/${import.meta.env.VITE_INFURA_API_KEY}`,
apiUrl: 'https://api-1.snapshotx.xyz',
explorerUrl: 'https://starkscan.co'
},
Expand All @@ -38,6 +40,7 @@ export const METADATA: Partial<Record<NetworkID, Metadata>> = {
baseChainId: 5,
baseNetworkId: 'gor',
rpcUrl: `https://starknet-goerli.infura.io/v3/${import.meta.env.VITE_INFURA_API_KEY}`,
ethRpcUrl: `https://goerli.infura.io/v3/${import.meta.env.VITE_INFURA_API_KEY}`,
apiUrl: 'https://testnet-api-1.snapshotx.xyz',
explorerUrl: 'https://testnet.starkscan.co'
},
Expand All @@ -47,6 +50,7 @@ export const METADATA: Partial<Record<NetworkID, Metadata>> = {
baseChainId: 11155111,
baseNetworkId: 'sep',
rpcUrl: `https://starknet-sepolia.infura.io/v3/${import.meta.env.VITE_INFURA_API_KEY}`,
ethRpcUrl: `https://sepolia.infura.io/v3/${import.meta.env.VITE_INFURA_API_KEY}`,
apiUrl: 'https://testnet-api-1.snapshotx.xyz',
explorerUrl: 'https://sepolia.starkscan.co'
}
Expand All @@ -56,7 +60,8 @@ export function createStarknetNetwork(networkId: NetworkID): Network {
const metadata = METADATA[networkId];
if (!metadata) throw new Error(`Unsupported network ${networkId}`);

const { name, chainId, baseChainId, baseNetworkId, rpcUrl, apiUrl, explorerUrl } = metadata;
const { name, chainId, baseChainId, baseNetworkId, rpcUrl, ethRpcUrl, apiUrl, explorerUrl } =
metadata;

const provider = createProvider(rpcUrl);
const api = createApi(apiUrl, networkId);
Expand Down Expand Up @@ -138,7 +143,10 @@ export function createStarknetNetwork(networkId: NetworkID): Network {
hasReceive: true,
supportsSimulation: true,
managerConnectors: STARKNET_CONNECTORS,
actions: createActions(networkId, provider, helpers, { l1ChainId: baseChainId }),
actions: createActions(networkId, provider, helpers, {
l1ChainId: baseChainId,
ethUrl: ethRpcUrl
}),
api,
constants,
helpers
Expand Down
23 changes: 13 additions & 10 deletions packages/sx.js/src/strategies/starknet/evmSlotValue.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
/* eslint-disable @typescript-eslint/no-unused-vars */

import { Contract, CallData } from 'starknet';
import { StaticJsonRpcProvider } from '@ethersproject/providers';
import EVMSlotValue from './abis/EVMSlotValue.json';
import SpaceAbi from '../../clients/starknet/starknet-tx/abis/Space.json';
import { getUserAddressEnum } from '../../utils/starknet-enums';
import { getEthProvider, getSlotKey, getBinaryTree } from './utils';
import { getSlotKey, getBinaryTree } from './utils';
import type { ClientConfig, Envelope, Strategy, Propose, Vote } from '../../types';

export default function createEvmSlotValueStrategy({
Expand All @@ -17,9 +18,10 @@ export default function createEvmSlotValueStrategy({
voterAddress: string,
slotIndex: number,
blockNumber: number,
ethUrl: string,
chainId: number
) {
const provider = getEthProvider(chainId);
const provider = new StaticJsonRpcProvider(ethUrl, chainId);

const proof = await provider.send('eth_getProof', [
l1TokenAddress,
Expand Down Expand Up @@ -59,17 +61,15 @@ export default function createEvmSlotValueStrategy({

const voteEnvelope = envelope as Envelope<Vote>;

const spaceContract = new Contract(
SpaceAbi,
voteEnvelope.data.space,
clientConfig.starkProvider
);
const { starkProvider, ethUrl, networkConfig } = clientConfig;

const spaceContract = new Contract(SpaceAbi, voteEnvelope.data.space, starkProvider);
const proposalStruct = (await spaceContract.call('proposals', [
voteEnvelope.data.proposal
])) as any;
const startTimestamp = proposalStruct.start_timestamp;

const { herodotusAccumulatesChainId: chainId } = clientConfig.networkConfig;
const { herodotusAccumulatesChainId: chainId } = networkConfig;
const { contractAddress, slotIndex } = metadata;

const tree = await getBinaryTree(deployedOnChain, startTimestamp, chainId);
Expand All @@ -80,6 +80,7 @@ export default function createEvmSlotValueStrategy({
signerAddress,
slotIndex,
l1BlockNumber,
ethUrl,
chainId
);

Expand All @@ -100,11 +101,12 @@ export default function createEvmSlotValueStrategy({
const isEthereumAddress = voterAddress.length === 42;
if (!isEthereumAddress) return 0n;

const { herodotusAccumulatesChainId: chainId } = clientConfig.networkConfig;
const { ethUrl, networkConfig } = clientConfig;
const { herodotusAccumulatesChainId: chainId } = networkConfig;
const { contractAddress, slotIndex } = metadata;

if (!timestamp) {
const provider = getEthProvider(chainId);
const provider = new StaticJsonRpcProvider(clientConfig.ethUrl, chainId);
const storage = await provider.getStorageAt(
contractAddress,
getSlotKey(voterAddress, slotIndex)
Expand All @@ -123,6 +125,7 @@ export default function createEvmSlotValueStrategy({
voterAddress,
slotIndex,
l1BlockNumber,
ethUrl,
chainId
);

Expand Down
30 changes: 14 additions & 16 deletions packages/sx.js/src/strategies/starknet/ozVotesStorageProof.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@

import { Contract, CallData } from 'starknet';
import { Contract as EvmContract } from '@ethersproject/contracts';
import { StaticJsonRpcProvider } from '@ethersproject/providers';
import { keccak256 } from '@ethersproject/keccak256';
import OzVotesToken from './abis/OzVotesToken.json';
import OZVotesStorageProof from './abis/OZVotesStorageProof.json';
import SpaceAbi from '../../clients/starknet/starknet-tx/abis/Space.json';
import { getUserAddressEnum } from '../../utils/starknet-enums';
import { getEthProvider, getSlotKey, getBinaryTree } from './utils';
import { getSlotKey, getBinaryTree } from './utils';
import type { ClientConfig, Envelope, Strategy, Propose, Vote } from '../../types';

export default function createOzVotesStorageProofStrategy({
Expand All @@ -21,9 +22,10 @@ export default function createOzVotesStorageProofStrategy({
numCheckpoints: number,
slotIndex: number,
blockNumber: number,
ethUrl: string,
chainId: number
) {
const provider = getEthProvider(chainId);
const provider = new StaticJsonRpcProvider(ethUrl, chainId);

const checkpointSlotKey =
BigInt(keccak256(getSlotKey(voterAddress, slotIndex))) + BigInt(numCheckpoints) - BigInt(1);
Expand Down Expand Up @@ -72,21 +74,18 @@ export default function createOzVotesStorageProofStrategy({
if (signerAddress.length !== 42) throw new Error('Not supported for non-Ethereum addresses');
if (!metadata) throw new Error('Invalid metadata');

const { herodotusAccumulatesChainId: chainId } = clientConfig.networkConfig;
const { starkProvider, ethUrl, networkConfig } = clientConfig;
const { herodotusAccumulatesChainId: chainId } = networkConfig;
const { contractAddress, slotIndex } = metadata;

const provider = getEthProvider(chainId);
const provider = new StaticJsonRpcProvider(ethUrl, chainId);
const tokenContract = new EvmContract(contractAddress, OzVotesToken, provider);
const numCheckpoints: number = await tokenContract.numCheckpoints(signerAddress);
if (numCheckpoints === 0) throw new Error('No checkpoints found');

const voteEnvelope = envelope as Envelope<Vote>;

const spaceContract = new Contract(
SpaceAbi,
voteEnvelope.data.space,
clientConfig.starkProvider
);
const spaceContract = new Contract(SpaceAbi, voteEnvelope.data.space, starkProvider);
const proposalStruct = (await spaceContract.call('proposals', [
voteEnvelope.data.proposal
])) as any;
Expand All @@ -101,6 +100,7 @@ export default function createOzVotesStorageProofStrategy({
numCheckpoints,
slotIndex,
l1BlockNumber,
ethUrl,
chainId
);

Expand All @@ -123,21 +123,18 @@ export default function createOzVotesStorageProofStrategy({
const isEthereumAddress = voterAddress.length === 42;
if (!isEthereumAddress) return 0n;

const { herodotusAccumulatesChainId: chainId } = clientConfig.networkConfig;
const { starkProvider, ethUrl, networkConfig } = clientConfig;
const { herodotusAccumulatesChainId: chainId } = networkConfig;
const { contractAddress, slotIndex } = metadata;
const provider = getEthProvider(chainId);
const provider = new StaticJsonRpcProvider(ethUrl, chainId);

const tokenContract = new EvmContract(contractAddress, OzVotesToken, provider);
if (!timestamp) return tokenContract.getVotes(voterAddress);

const numCheckpoints: number = await tokenContract.numCheckpoints(voterAddress);
if (numCheckpoints === 0) return 0n;

const contract = new Contract(
OZVotesStorageProof,
strategyAddress,
clientConfig.starkProvider
);
const contract = new Contract(OZVotesStorageProof, strategyAddress, starkProvider);

const tree = await getBinaryTree(deployedOnChain, timestamp, chainId);
const l1BlockNumber = tree.path[1].blockNumber;
Expand All @@ -148,6 +145,7 @@ export default function createOzVotesStorageProofStrategy({
numCheckpoints,
slotIndex,
l1BlockNumber,
ethUrl,
chainId
);

Expand Down
18 changes: 0 additions & 18 deletions packages/sx.js/src/strategies/starknet/utils.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,6 @@
import { keccak256 } from '@ethersproject/keccak256';
import { StaticJsonRpcProvider } from '@ethersproject/providers';
import fetch from 'cross-fetch';

export function getEthRpcUrl(chainId: number) {
const apiKey = '46a5dd9727bf48d4a132672d3f376146';

// TODO: ideally we would use rpc.snapshotx.xyz here but those don't support eth_getProof with past lookup
if (chainId === 1) return `https://mainnet.infura.io/v3/${apiKey}`;
if (chainId === 5) return `https://goerli.infura.io/v3/${apiKey}`;
if (chainId === 11155111) return `https://sepolia.infura.io/v3/${apiKey}`;
if (chainId === 137) return `https://polygon-mainnet.infura.io/v3/${apiKey}`;
if (chainId === 42161) return `https://arbitrum-mainnet.infura.io/v3/${apiKey}`;

throw new Error(`Unsupported chainId ${chainId}`);
}

export function getEthProvider(chainId: number) {
return new StaticJsonRpcProvider(getEthRpcUrl(chainId), chainId);
}

export function getSlotKey(voterAddress: string, slotIndex: number) {
return keccak256(
`0x${voterAddress.slice(2).padStart(64, '0')}${slotIndex.toString(16).padStart(64, '0')}`
Expand Down

0 comments on commit ddcdc7f

Please sign in to comment.