Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: config starknet chain #5228

Open
wants to merge 43 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
dedaa34
merge with main repo latest changes
mortezashojaei Oct 16, 2024
4f541e3
Merge remote-tracking branch 'origin-root/main'
mortezashojaei Oct 22, 2024
f10e542
Merge remote-tracking branch 'origin-root/main'
mortezashojaei Oct 25, 2024
36e2b00
Merge remote-tracking branch 'origin-root/main'
mortezashojaei Oct 28, 2024
182fc36
feat: add Starknet protocol support to SDK
ljankovic-txfusion Nov 21, 2024
c6cc7e9
Merge remote-tracking branch 'origin-root/main'
mortezashojaei Nov 27, 2024
e13e08a
Merge branch 'hyperlane-xyz:main' into main
mshojaei-txfusion Nov 28, 2024
0546e10
Merge branch 'hyperlane-xyz:main' into main
mshojaei-txfusion Nov 28, 2024
2ee37d4
Merge branch 'hyperlane-xyz:main' into main
mshojaei-txfusion Nov 28, 2024
3f58da3
fix: exclude starknet on widget protocol type
mortezashojaei Nov 29, 2024
7770bd7
Merge branch 'hyperlane-xyz:main' into main
mshojaei-txfusion Nov 29, 2024
7d6a156
Merge branch 'hyperlane-xyz:main' into main
mshojaei-txfusion Dec 5, 2024
b90d0f7
Merge branch 'hyperlane-xyz:main' into main
mshojaei-txfusion Dec 5, 2024
c33d90f
Merge branch 'hyperlane-xyz:main' into main
mshojaei-txfusion Dec 6, 2024
e3fba6a
Merge branch 'hyperlane-xyz:main' into main
mshojaei-txfusion Dec 9, 2024
a45c50a
Merge branch 'hyperlane-xyz:main' into main
ljankovic-txfusion Dec 10, 2024
576744b
Merge branch 'hyperlane-xyz:main' into main
ljankovic-txfusion Dec 11, 2024
8f7d04d
Merge branch 'hyperlane-xyz:main' into main
ljankovic-txfusion Dec 11, 2024
e16b0e3
Merge branch 'hyperlane-xyz:main' into main
ljankovic-txfusion Dec 12, 2024
5f380e5
Merge branch 'hyperlane-xyz:main' into main
ljankovic-txfusion Dec 13, 2024
8ac3b22
Merge branch 'hyperlane-xyz:main' into main
ljankovic-txfusion Dec 17, 2024
074e7d4
Merge branch 'hyperlane-xyz:main' into main
ljankovic-txfusion Dec 19, 2024
b48be06
Merge branch 'hyperlane-xyz:main' into main
mshojaei-txfusion Jan 13, 2025
ed0f5a9
Merge branch 'hyperlane-xyz:main' into main
ljankovic-txfusion Jan 13, 2025
8090e97
Merge branch 'hyperlane-xyz:main' into main
mshojaei-txfusion Jan 13, 2025
af971af
Merge branch 'hyperlane-xyz:main' into main
mshojaei-txfusion Jan 15, 2025
814bdb0
Merge branch 'hyperlane-xyz:main' into main
mshojaei-txfusion Jan 16, 2025
a2f1528
Merge branch 'main' into feat/add-starknet-provider
mortezashojaei Jan 16, 2025
8e6e976
refactor: extract NonStarknetProtocol type in widget protocol handling
mortezashojaei Jan 16, 2025
7d4f834
feat(sdk): add Starknet token standards and type mappings
mortezashojaei Jan 16, 2025
af1cede
feat(sdk): add Starknet token standards test
mortezashojaei Jan 16, 2025
c945d53
chore: remove unused requireindex dependency from yarn.lock
mortezashojaei Jan 16, 2025
a63d411
refactor(sdk): reorder token standards to group by protocol
mortezashojaei Jan 16, 2025
f5d8d47
minor: cleanup
mortezashojaei Jan 16, 2025
7130424
docs(changeset): Added Starknet protocol support
mortezashojaei Jan 16, 2025
ecb73ff
feat: add starknet address utils and validation functions
mortezashojaei Jan 17, 2025
7702247
minor: fix starknet transaction import
mortezashojaei Jan 20, 2025
ee40565
Merge branch 'feat/add-starknet-provider' into feat/address-checker-s…
mortezashojaei Jan 21, 2025
52f0d55
feat: add Starknet protocol support to chain config CLI
mortezashojaei Jan 21, 2025
1ad1470
docs(changeset): Add Starknet protocol support to the chain configura…
mortezashojaei Jan 21, 2025
055b2fd
feat: enforce Starknet address validation and make native token confi…
mortezashojaei Jan 21, 2025
51be725
minor: cleanup
mshojaei-txfusion Jan 22, 2025
32b0a7f
Merge branch 'feat/address-checker-starknet' into feat/config-starkne…
mshojaei-txfusion Jan 22, 2025
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
7 changes: 7 additions & 0 deletions .changeset/few-crabs-occur.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@hyperlane-xyz/utils': minor
'@hyperlane-xyz/sdk': minor
'@hyperlane-xyz/widgets': patch
---

Added Starknet protocol support
5 changes: 5 additions & 0 deletions .changeset/five-rice-sit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@hyperlane-xyz/cli': minor
---

Add Starknet protocol support to the chain configuration
1 change: 1 addition & 0 deletions typescript/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"chalk": "^5.3.0",
"ethers": "^5.7.2",
"latest-version": "^8.0.0",
"starknet": "6.17.0",
"terminal-link": "^3.0.0",
"tsx": "^4.19.1",
"yaml": "2.4.5",
Expand Down
173 changes: 130 additions & 43 deletions typescript/cli/src/config/chain.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { confirm, input, select } from '@inquirer/prompts';
import { ethers } from 'ethers';
import { keccak256 } from 'ethers/lib/utils.js';
import {
Provider as StarknetProvider,
provider as starknetProvider,
} from 'starknet';
import { stringify as yamlStringify } from 'yaml';

import {
Expand All @@ -10,13 +15,22 @@ import {
ExplorerFamily,
ZChainName,
} from '@hyperlane-xyz/sdk';
import { ProtocolType } from '@hyperlane-xyz/utils';
import { ProtocolType, assert, isAddressStarknet } from '@hyperlane-xyz/utils';

import { CommandContext } from '../context/types.js';
import { errorRed, log, logBlue, logGreen } from '../logger.js';
import { indentYamlOrJson, readYamlOrJson } from '../utils/files.js';
import { detectAndConfirmOrPrompt } from '../utils/input.js';

// Add constants at the top of the file
const SUPPORTED_PROTOCOLS = [
ProtocolType.Ethereum,
ProtocolType.Starknet,
] as const;

// Add type for supported protocols
type SupportedProtocol = (typeof SUPPORTED_PROTOCOLS)[number];

export function readChainConfigs(filePath: string) {
log(`Reading file configs in ${filePath}`);
const chainMetadata = readYamlOrJson<ChainMetadata>(filePath);
Expand Down Expand Up @@ -49,16 +63,25 @@ export async function createChainConfig({
}) {
logBlue('Creating a new chain config');

const protocol = (await select({
message: 'Select the chain protocol type:',
choices: SUPPORTED_PROTOCOLS.map((value) => ({ value })),
pageSize: SUPPORTED_PROTOCOLS.length,
})) as SupportedProtocol;

assert(
SUPPORTED_PROTOCOLS.includes(protocol),
`Protocol type ${protocol} not supported. Supported protocols: ${SUPPORTED_PROTOCOLS.join(
', ',
)}`,
);

const rpcUrl = await detectAndConfirmOrPrompt(
async () => {
await new ethers.providers.JsonRpcProvider().getNetwork();
return ethers.providers.JsonRpcProvider.defaultUrl();
},
createProtocolDefaultProviderDetector(protocol),
'Enter http or https',
'rpc url',
'JSON RPC provider',
);
const provider = new ethers.providers.JsonRpcProvider(rpcUrl);

const name = await input({
message: 'Enter chain name (one word, lower case)',
Expand All @@ -70,55 +93,58 @@ export async function createChainConfig({
default: name[0].toUpperCase() + name.slice(1),
});

const chainId = parseInt(
const chainId = formatChainIdBasedOnProtocol(
await detectAndConfirmOrPrompt(
async () => {
const network = await provider.getNetwork();
return network.chainId.toString();
},
'Enter a (number)',
createProtocolChainIdDetector(protocol, rpcUrl),
protocol === ProtocolType.Starknet ? 'Enter a (hex)' : 'Enter a (number)',
'chain id',
'JSON RPC provider',
),
10,
protocol,
);

const isTestnet = await confirm({
message:
'Is this chain a testnet (a chain used for testing & development)?',
});

const technicalStack = (await select({
choices: Object.entries(ChainTechnicalStack).map(([_, value]) => ({
value,
})),
message: 'Select the chain technical stack',
pageSize: 10,
})) as ChainTechnicalStack;

const arbitrumNitroMetadata: Pick<ChainMetadata, 'index'> = {};
if (technicalStack === ChainTechnicalStack.ArbitrumNitro) {
const indexFrom = await detectAndConfirmOrPrompt(
async () => {
return (await provider.getBlockNumber()).toString();
},
`Enter`,
'starting block number for indexing',
'JSON RPC provider',
);
let technicalStack: ChainTechnicalStack | undefined;
let arbitrumNitroMetadata: Pick<ChainMetadata, 'index'> = {};

arbitrumNitroMetadata.index = {
from: parseInt(indexFrom),
};
if (protocol === ProtocolType.Ethereum) {
technicalStack = (await select({
choices: Object.entries(ChainTechnicalStack).map(([_, value]) => ({
value,
})),
message: 'Select the chain technical stack',
pageSize: 10,
})) as ChainTechnicalStack;

if (technicalStack === ChainTechnicalStack.ArbitrumNitro) {
const provider = new ethers.providers.JsonRpcProvider(rpcUrl);
const indexFrom = await detectAndConfirmOrPrompt(
async () => {
return (await provider.getBlockNumber()).toString();
},
`Enter`,
'starting block number for indexing',
'JSON RPC provider',
);

arbitrumNitroMetadata.index = {
from: parseInt(indexFrom),
};
}
}

const metadata: ChainMetadata = {
name,
displayName,
chainId,
domainId: chainId,
protocol: ProtocolType.Ethereum,
technicalStack,
domainId:
typeof chainId === 'string' ? stringChainIdToDomainId(chainId) : chainId,
protocol: protocol,
...(technicalStack && { technicalStack }),
rpcUrls: [{ http: rpcUrl }],
isTestnet,
...arbitrumNitroMetadata,
Expand Down Expand Up @@ -268,12 +294,16 @@ async function addGasConfig(metadata: ChainMetadata): Promise<void> {
}

async function addNativeTokenConfig(metadata: ChainMetadata): Promise<void> {
const wantNativeConfig = await confirm({
default: false,
message:
'Do you want to set native token properties for this chain config (defaults to ETH)',
});
let symbol, name, decimals;
const isStarknet = metadata.protocol === ProtocolType.Starknet;
const wantNativeConfig =
isStarknet ||
(await confirm({
default: false,
message:
'Do you want to set native token properties for this chain config (defaults to ETH)',
}));

let symbol, name, decimals, denom;
if (wantNativeConfig) {
symbol = await input({
message: "Enter the native token's symbol:",
Expand All @@ -284,11 +314,68 @@ async function addNativeTokenConfig(metadata: ChainMetadata): Promise<void> {
decimals = await input({
message: "Enter the native token's decimals:",
});

if (isStarknet) {
denom = await input({
message: "Enter the native token's address:",
validate: (value) => isAddressStarknet(value),
});
}
}
Comment on lines +318 to 324
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there any way to fetch this for the user without requiring input?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On starknet mainnet there are two native tokens for fees - STRK and ETH, and AFAIK Paradex chain has an native token ETH address completely different from starknet mainnet @mshojaei-txfusion correct me if I am wrong

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We did not find any address on starknet.js to import, but we may be able to add a default native token address for starknet, and ask user to change it in case it was different.


metadata.nativeToken = {
symbol: symbol ?? 'ETH',
name: name ?? 'Ether',
decimals: decimals ? parseInt(decimals, 10) : 18,
...(denom && { denom }), // Only include denom if it was provided
};
}

// Update function signature
function createProtocolDefaultProviderDetector(protocol: SupportedProtocol) {
switch (protocol) {
case ProtocolType.Ethereum:
return async () => {
return ethers.providers.JsonRpcProvider.defaultUrl();
};
case ProtocolType.Starknet:
return async () => {
return starknetProvider.getDefaultNodeUrl();
};
}
}

// Add return type annotations
function createProtocolChainIdDetector(
protocol: SupportedProtocol,
rpcUrl: string,
) {
return async () => {
switch (protocol) {
case ProtocolType.Ethereum: {
const network = await new ethers.providers.JsonRpcProvider(
rpcUrl,
).getNetwork();
return network.chainId.toString();
}
case ProtocolType.Starknet:
return new StarknetProvider({ nodeUrl: rpcUrl }).getChainId();
}
};
}

function formatChainIdBasedOnProtocol(chainId: string, protocol: ProtocolType) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: formatChainIdForProtocol

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and should that be SupportedProtocol as well?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right better to be SupportedProtocol

if (protocol === ProtocolType.Starknet) return chainId;
return parseInt(chainId, 10);
}

/**
* Converts a string chain ID to a numeric domain ID using keccak256
* @param chainId The chain ID string to convert
* @returns A numeric domain ID derived from the first 4 bytes of the hash
*/
function stringChainIdToDomainId(chainId: string): number {
const hash = keccak256(chainId);
// keccak256 should never return null for a string input
return parseInt(hash.slice(2, 10), 16); // Take first 4 bytes after '0x'
}
1 change: 1 addition & 0 deletions typescript/sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"cross-fetch": "^3.1.5",
"ethers": "^5.7.2",
"pino": "^8.19.0",
"starknet": "6.17.0",
"viem": "^2.21.45",
"zod": "^3.21.2"
},
Expand Down
10 changes: 10 additions & 0 deletions typescript/sdk/src/providers/MultiProtocolProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
ProviderMap,
ProviderType,
SolanaWeb3Provider,
StarknetJsProvider,
TypedProvider,
TypedTransaction,
ViemProvider,
Expand Down Expand Up @@ -205,6 +206,15 @@ export class MultiProtocolProvider<
);
}

getStarknetProvider(
chainNameOrId: ChainNameOrId,
): StarknetJsProvider['provider'] {
return this.getSpecificProvider<StarknetJsProvider['provider']>(
chainNameOrId,
ProviderType.Starknet,
);
}

setProvider(
chainNameOrId: ChainNameOrId,
provider: TypedProvider,
Expand Down
Loading
Loading