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: starknet core package #5229

Open
wants to merge 52 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 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
827fb7c
Merge branch 'hyperlane-xyz:main' into main
mshojaei-txfusion Jan 16, 2025
ecb73ff
feat: add starknet address utils and validation functions
mortezashojaei Jan 17, 2025
ae3acc4
Merge branch 'hyperlane-xyz:main' into main
mshojaei-txfusion Jan 20, 2025
7702247
minor: fix starknet transaction import
mortezashojaei Jan 20, 2025
19a4cc2
Merge branch 'hyperlane-xyz:main' into main
mshojaei-txfusion Jan 21, 2025
ee40565
Merge branch 'feat/add-starknet-provider' into feat/address-checker-s…
mortezashojaei Jan 21, 2025
aec3579
Merge branch 'hyperlane-xyz:main' into main
mshojaei-txfusion 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
9f42411
Merge branch 'hyperlane-xyz:main' into main
mshojaei-txfusion Jan 21, 2025
08ed064
feat: implement starknet workspace in monorepo
mortezashojaei Jan 21, 2025
57eba2c
docs(changeset): Added new Starknet workspace to the monorepo. This p…
mortezashojaei Jan 21, 2025
37fe32f
fix: eslint version
mortezashojaei Jan 21, 2025
59165bb
Merge branch 'hyperlane-xyz:main' into main
mshojaei-txfusion Jan 22, 2025
bc1aec6
fix: yarn.lock
mshojaei-txfusion Jan 22, 2025
08fe83f
merge: latest main changes
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
5 changes: 5 additions & 0 deletions .changeset/nervous-ads-draw.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@hyperlane-xyz/starknet-core': minor
---

Added new Starknet workspace to the monorepo. This package provides core Cairo contract bindings and utilities for Hyperlane's Starknet integration
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@
},
"workspaces": [
"solidity",
"typescript/*"
"typescript/*",
"starknet"
],
"resolutions": {
"async": "^2.6.4",
Expand Down
6 changes: 6 additions & 0 deletions starknet/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"rules": {
"no-console": ["off"],
"no-restricted-imports": ["off"]
}
}
3 changes: 3 additions & 0 deletions starknet/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.env
dist
release
44 changes: 44 additions & 0 deletions starknet/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"name": "@hyperlane-xyz/starknet-core",
"description": "Core cairo contracts for Hyperlane",
"version": "0.2.2",
"type": "module",
"homepage": "https://www.hyperlane.xyz",
"license": "Apache-2.0",
"scripts": {
"prepare-contracts": "tsx ./scripts/fetch-contracts-if-missing.ts",
"build-contracts": "mkdir -p dist/target && cp release/* dist/target/",
"build": "tsc && yarn prepare-contracts && yarn build-contracts",
"clean": "rm -rf ./dist ./release",
"lint": "eslint src --ext .ts",
"prettier": "prettier --write ./src ./scripts ./package.json"
},
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
},
"types": "./dist/index.d.ts",
"files": [
"/dist"
],
"keywords": [
"Hyperlane",
"Cairo",
"Starknet"
],
"engines": {
"node": ">=16"
},
"dependencies": {
"starknet": "6.11.0"
},
"devDependencies": {
"@eslint/js": "^9.15.0",
"eslint-config-prettier": "^9.1.0",
"prettier": "^2.8.8",
"tsx": "^4.7.1",
"typescript": "5.3.3"
}
}
23 changes: 23 additions & 0 deletions starknet/scripts/fetch-contracts-if-missing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { execSync } from 'child_process';
import { existsSync } from 'fs';
import { join } from 'path';

const RELEASE_DIR = join(process.cwd(), 'release');

try {
if (existsSync(RELEASE_DIR)) {
console.log(
'[INFO] Contracts already present in src/release, skipping fetch',
);
process.exit(0);
}

console.log('[INFO] Fetching contracts...');
execSync('./scripts/fetch-contracts-release.sh', {
stdio: 'inherit',
cwd: join(process.cwd()),
});
} catch (error) {
console.error('[ERROR]', (error as Error).message);
process.exit(1);
}
151 changes: 151 additions & 0 deletions starknet/scripts/fetch-contracts-release.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
#!/usr/bin/env bash

# Strict mode configuration
set -euo pipefail
IFS=$'\n\t'

# Constants
readonly REPO="astraly-labs/hyperlane_starknet"
readonly GITHUB_RELEASES_API="https://api.github.com/repos/${REPO}/releases"
readonly TARGET_DIR="./release"

# Color definitions
declare -r COLOR_GREEN='\033[0;32m'
declare -r COLOR_RED='\033[0;31m'
declare -r COLOR_RESET='\033[0m'

log_error() {
echo -e "${COLOR_RED}Error: $1${COLOR_RESET}" >&2
}

log_success() {
echo -e "${COLOR_GREEN}$1${COLOR_RESET}"
}

check_dependencies() {
local -r required_tools=("curl" "jq" "unzip")

for tool in "${required_tools[@]}"; do
if ! command -v "$tool" &> /dev/null; then
log_error "$tool is not installed"
exit 1
fi
done
}

get_package_version() {
local package_version
if ! package_version=$(jq -r '.version' ./package.json 2>/dev/null); then
log_error "Failed to read version from package.json"
exit 1
fi
echo "v${package_version}"
}

verify_version_exists() {
local version=$1
if ! curl --output /dev/null --silent --head --fail "${GITHUB_RELEASES_API}/tags/${version}"; then
log_error "Version ${version} does not exist"
exit 1
fi
}

get_release_info() {
local version=$1
local release_info

release_info=$(curl -sf "${GITHUB_RELEASES_API}/tags/${version}") || {
log_error "Failed to fetch release information for version ${version}"
exit 1
}
echo "$release_info"
}

download_and_extract() {
local version=$1
local download_url=$2
local base_url="${download_url%/*}"
local filename="${download_url##*/}"

if ! mkdir -p "$TARGET_DIR"; then
log_error "Failed to create target directory"
exit 1
fi

log_success "Downloading version ${version}..."

if ! curl -L "$download_url" -o "${TARGET_DIR}/release.zip"; then
log_error "Download failed"
exit 1
fi

if ! verify_checksum "${TARGET_DIR}/release.zip" "$base_url" "$filename"; then
rm -f "${TARGET_DIR}/release.zip"
exit 1
fi

if ! unzip -o "${TARGET_DIR}/release.zip" -d "${TARGET_DIR}"; then
log_error "Extraction failed"
exit 1
fi
}

verify_checksum() {
local file_path="$1"
local base_url="$2"
local filename="$3"
local checksum_filename
checksum_filename="${filename%.zip}.CHECKSUM"

local downloaded_checksum
downloaded_checksum="$(sha256sum "$file_path" | cut -d' ' -f1)"
log_success "File checksum: ${downloaded_checksum}"

local expected_checksum
if ! expected_checksum="$(curl -sL "${base_url}/${checksum_filename}")"; then
log_error "Failed to fetch checksum file"
return 1
fi

if [[ "${downloaded_checksum}" != "$(echo "${expected_checksum}" | awk '{print $1}')" ]]; then
log_error "Checksum verification failed"
return 1
fi

return 0
}

cleanup() {
rm -f "$TARGET_DIR/release.zip"
rm -f "$TARGET_DIR"/*.md5
rm -f "$TARGET_DIR"/*.sha256
}

main() {
trap cleanup EXIT

check_dependencies

local version
version=$(get_package_version)
log_success "Using version ${version} from package.json"
verify_version_exists "$version"

local release_info
release_info=$(get_release_info "$version")

local download_url
download_url=$(echo "$release_info" | jq -r '.assets[] | select(.name | startswith("hyperlane-starknet") and endswith(".zip")) | .browser_download_url')

if [[ -z "$download_url" ]]; then
log_error "Could not find ZIP download URL for release"
exit 1
fi

# Process download and file checksum verification and extraction
download_and_extract "$version" "$download_url"

log_success "Successfully downloaded and extracted version ${version}"
}

main
11 changes: 11 additions & 0 deletions starknet/src/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export const CONFIG = {
COMPILED_CONTRACTS_DIR: 'target',
CONTRACT_FILE_SUFFIXES: {
SIERRA_JSON: '.contract_class.json', // Sierra is the high-level representation
ASSEMBLY_JSON: '.compiled_contract_class.json', // Cairo assembly (CASM) is the low-level bytecode
},
CONTRACT_ERROR_CODES: {
FILE_NOT_FOUND: 'FILE_NOT_FOUND',
PARSE_ERROR: 'PARSE_ERROR',
},
} as const;
6 changes: 6 additions & 0 deletions starknet/src/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export class ContractError extends Error {
constructor(public readonly code: string, public readonly details?: unknown) {
super(`[${code}] ${details ? JSON.stringify(details) : ''}`);
this.name = 'ContractError';
}
}
98 changes: 98 additions & 0 deletions starknet/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { existsSync, readFileSync } from 'fs';
import { dirname, join } from 'path';
import { CairoAssembly, CompiledContract } from 'starknet';
import { fileURLToPath } from 'url';

import { CONFIG } from './config.js';
import { ContractError } from './errors.js';

const currentDirectory = dirname(fileURLToPath(import.meta.url));
const TARGET_DEV_PATH = join(currentDirectory, CONFIG.COMPILED_CONTRACTS_DIR);

/**
* @notice Retrieves and parses the standard compiled contract data
* @param name The name of the contract to retrieve
* @returns {CompiledContract} The parsed contract data
* @throws {ContractError} If the file is not found, cannot be parsed
*/
export const getCompiledContract = (
name: string,
contractType?: ContractType,
): CompiledContract => {
try {
return JSON.parse(
readFileSync(
findContractFile(name, 'SIERRA_JSON', contractType),
'utf-8',
),
);
} catch (error: unknown) {
if (error instanceof ContractError) throw error;
throw new ContractError(CONFIG.CONTRACT_ERROR_CODES.PARSE_ERROR, {
name,
error: (error as Error).message,
});
}
};

/**
* @notice Retrieves and parses the CASM compiled contract data
* @param name The name of the contract to retrieve
* @returns {CairoAssembly} The parsed CASM contract data
* @throws {ContractError} If the file is not found, cannot be parsed
*/
export const getCompiledContractCasm = (
name: string,
contractType?: ContractType,
): CairoAssembly => {
try {
return JSON.parse(
readFileSync(
findContractFile(name, 'ASSEMBLY_JSON', contractType),
'utf-8',
),
);
} catch (error: unknown) {
if (error instanceof ContractError) throw error;
throw new ContractError(CONFIG.CONTRACT_ERROR_CODES.PARSE_ERROR, {
name,
error: (error as Error).message,
});
}
};

/**
* @notice Contract file type enum
*/
export enum ContractType {
CONTRACT = 'contracts_',
TOKEN = 'token_',
MOCK = 'mock_',
}

/**
* @notice Finds the path to a contract file based on predefined patterns
* @param name The base name of the contract to find
* @param suffix The type of contract file to look for (from CONFIG.CONTRACT_FILE_SUFFIXES)
* @param type Optional contract type prefix (defaults to CONTRACT)
* @returns {string} The full path to the contract file
* @throws {ContractError} If file is not found or the contract name is invalid
*/
function findContractFile(
name: string,
suffix: keyof typeof CONFIG.CONTRACT_FILE_SUFFIXES,
type: ContractType = ContractType.CONTRACT,
): string {
const suffixPath = CONFIG.CONTRACT_FILE_SUFFIXES[suffix];
const path = `${TARGET_DEV_PATH}/${type}${name}${suffixPath}`;

if (!existsSync(path)) {
throw new ContractError(CONFIG.CONTRACT_ERROR_CODES.FILE_NOT_FOUND, {
name,
suffix,
path,
});
}

return path;
}
15 changes: 15 additions & 0 deletions starknet/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"importHelpers": true,
"noEmitHelpers": true,
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*"],
"ts-node": {
"experimentalSpecifierResolution": "node",
"experimentalResolver": true,
"files": true
}
}
Loading
Loading