diff --git a/.gitignore b/.gitignore
index 149b429a..b33c754b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,8 @@
scripts/node_modules
-scripts/.env
\ No newline at end of file
+scripts/package-lock.json
+scripts/.env
+
+example/node_modules
+example/package-lock.json
+example/.env
+example/infra/.env
\ No newline at end of file
diff --git a/README.md b/README.md
index 291db1f9..5511b0e9 100644
--- a/README.md
+++ b/README.md
@@ -125,6 +125,10 @@ Constructors parameters can be specified in the `contract_config.json`.
The rust repository is strictly used for tests purposes.
+## Example
+
+A fully runnable example lives in the [`example`](example/README.md) folder.
+
## 📖 License
This project is licensed under the **MIT license**. See [LICENSE](LICENSE) for more information.
diff --git a/example/.env.example b/example/.env.example
new file mode 100644
index 00000000..c4f622f6
--- /dev/null
+++ b/example/.env.example
@@ -0,0 +1,13 @@
+# Starknet RPC URL
+STARKNET_RPC_URL=https://starknet-sepolia.public.blastapi.io/rpc/v0_8
+# Starknet Sepolia account address
+ACCOUNT_ADDRESS=
+# fees beneficiary address
+BENEFICIARY_ADDRESS=
+# Starknet Sepolia network
+NETWORK=starknet_sepolia
+# Starknet Sepolia private key
+PRIVATE_KEY=
+
+# gnosis chiado private key
+GNOSIS_CHIADO_PK=
diff --git a/example/README.md b/example/README.md
new file mode 100644
index 00000000..fa02baaf
--- /dev/null
+++ b/example/README.md
@@ -0,0 +1,241 @@
+# Hyperlane - Starknet Sepolia <> Gnosis Chiado - Warp Route Deployment Guide
+
+We use the Hyperlane protocol to deploy a Warp route between Starknet and Gnosis Chiado. This allows us to transfer tokens between these two networks efficiently.
+
+## Table of Contents
+
+- [Setup](#setup)
+- [Deploy Warp Route on Starknet and Gnosis Chiado](#deploy-warp-route-on-starknet-and-gnosis-chiado)
+- [Set Up the Relayer](#set-up-the-relayer)
+- [Start the Relayer](#start-the-relayer)
+- [Testing the Warp Route](#testing-the-warp-route)
+- [🚩 Pitfalls](#🚩-pitfalls)
+
+## Setup
+
+To set up the project, follow these steps:
+
+### Install Dependencies
+
+1. Make sure dependencies are installed on both the `/example` and `/scripts` directory:
+
+```bash
+npm i
+cd ../scripts && npm i && cd ../example
+```
+
+2. Build the Cairo contracts:
+
+```bash
+cd ../cairo && scarb build && cd ../example
+```
+
+### Setup Environment Variables
+
+1. Copy the `.env.example` file from `/example` directory to `.env` and populate the necessary variables
+
+2. Copy the `.env.example` file from `/scripts` directory to `.env` and populate the necessary variables
+
+### Gas Fees
+
+Ensure you have enough gas fees in your Starknet Sepolia account. You can use the [Starknet Faucet](https://starknet-faucet.vercel.app/) to get some test STRK.
+
+### Hyperlane CLI
+
+Install the [Hyperlane CLI](https://docs.hyperlane.xyz/docs/reference/developer-tools/cli) globally:
+
+```bash
+npm install -g @hyperlane-xyz/cli
+```
+
+## Deploy Warp Route on Starknet and Gnosis Chiado
+
+This guide will help you deploy the Warp route on Starknet and Gnosis Chiado.
+
+1. **Deploy Starknet Core Contracts**
+
+ ```bash
+ npm run deploy
+ ```
+
+ A file with all deployed contracts will be generated at the following path `./scripts/deployments/starknet_sepolia/deployments.json`.
+
+2. **Generate `starknetsepolia-deploy.yaml` file:**
+
+ ```bash
+ hyperlane warp init
+ ```
+
+
+ The prompts should be similar to the following: (click to expand)
+
+ ? Select chains to connect gnosischiadotestnet, starknetsepolia
+ ? Is this chain selection correct?: gnosischiadotestnet, starknetsepolia yes
+ gnosischiadotestnet: Configuring warp route...
+ ? Enter the desired owner address: 0x_your_gnosischiado_owner_address
+ ? Use an existing Proxy Admin contract for the warp route deployment on chain "gnosischiadotestnet"? no
+ ? Do you want to use a trusted ISM for warp route? yes
+ ? Select gnosischiadotestnet's token type collateral
+ ? Enter the existing token address on chain gnosischiadotestnet 0x_gnosischiado_token_you_want_to_bridge_address
+ starknetsepolia: Configuring warp route...
+ ? Enter the desired owner address: 0x_your_starknetsepolia_owner_address
+ ? Use an existing Proxy Admin contract for the warp route deployment on chain "starknetsepolia"? no
+ ? Do you want to use a trusted ISM for warp route? yes
+ ? Select starknetsepolia's token type synthetic
+ Warp Route config is valid, writing to file undefined:
+
+
+
+
+3. **Build the Warp route contract:**
+
+ ```bash
+ npm run deploy-route
+ ```
+
+ This will deploy the Warp route contract on Starknet and print the deployed contract address.
+
+
+ Example output:
+
+ Declaring contract HypERC20...
+ Contract HypERC20 declared with class hash: 0x_class_hash
+ HypERC20 contract deployed at address: 0x_deployed_contract_address
+
+
+
+
+4. **Replace the `starknetsepolia` section in `starknetsepolia-deploy.yaml`**
+
+ ```yaml
+ starknetsepolia:
+ foreignDeployment: 0x # deployed contract
+ remoteDecimals: 18 # Chiado token decimals
+ mailbox: 0x # mailbox contract
+ interchainSecurityModule: 0x
+ owner: 0x # your address
+ type: synthetic
+ ```
+
+ the file is located at `~/.hyperlane/deployments/warp_routes/$COLLATERAL_TICKER`
+
+
+ Example starknetsepolia-deploy.yaml
+
+ ```yaml
+ gnosischiadotestnet:
+ interchainSecurityModule:
+ modules:
+ - relayer: '0x_your_relayer_address'
+ type: trustedRelayerIsm
+ - domains: {}
+ owner: '0x_your_gnosischiado_ism_owner_address'
+ type: defaultFallbackRoutingIsm
+ threshold: 1
+ type: staticAggregationIsm
+ owner: '0x_your_gnosischiado_owner_address'
+ token: '0x_gnosischiado_token_you_want_to_bridge_address'
+ type: collateral
+ starknetsepolia:
+ foreignDeployment: '0x_deployed_contract_address_from_step_3' # address from step 3
+ remoteDecimals: 18 # decimals of the token on Gnosis Chiado
+ mailbox: '0x_your_starknetsepolia_mailbox_address'
+ interchainSecurityModule: '0x_your_starknetsepolia_ism_address'
+ type: synthetic # the type of contract deployed on Starknet, here it is synthetic of the original token
+ owner: '0x_your_starknetsepolia_owner_address'
+ ```
+
+
+
+
+5. **Deploy the Warp route to Gnosis Chiado:**
+
+ ```bash
+ hyperlane warp deploy
+ ```
+
+ Select your token route from the list, and confirm the deployment.
+ Once it's done, you will see something like this:
+ `Successfully deployed contracts on gnosischiadotestnet`
+
+ Then `Error: address bytes must not be empty`, it's a known issue, you can ignore it as we enroll the router on next step.
+
+6. **Enroll the remote router on Starknet**
+
+ Go to `enroll-remote-router.ts` and update `REMOTE_TOKEN_ADDRESS`, `REMOTE_DOMAIN_ID`, and `STARKNET_SYNTHETIC_ADDRESS` with the values from the previous steps.
+
+ then run the script:
+
+ ```bash
+ npm run enroll-remote-router
+ ```
+
+7. **Update the hooks on Starknet**
+
+ You can update the hooks on Starknet using the following command:
+
+ ```bash
+ npm run update-hooks -- -r $REQUIRED_HOOK -d $DEFAULT_HOOK
+ ```
+
+ Replace `$REQUIRED_HOOK` and `$DEFAULT_HOOK` with the desired hook addresses.
+
+ Example:
+
+ ```bash
+ npm run update-hooks -- -r 0x_required_hook_address -d 0x_default_hook_address
+ ```
+
+
+
+You are now ready to use the Warp route on both Starknet and Gnosis Chiado 🎉
+
+
+
+## Set Up the Relayer
+
+### Configure Environment Variables
+
+To set up the Hyperlane relayer, configure the environment variables. Copy the example environment file and modify it as needed:
+
+```bash
+cp infra/.env.example infra/.env
+```
+
+### Update the config.json file
+
+Update the `infra/config.json` file with the contract addresses from the deployments.json file generated in step 1.
+
+## Start the Relayer
+
+At this point, you can start the Hyperlane relayer to handle messages between the two networks:
+
+```bash
+cd infra && docker-compose -f docker-compose.relayer.yml up --force-recreate --remove-orphans
+```
+
+## Testing the Warp Route
+
+The Hyperlane commands cannot send messages to non-EVM chains.
+
+## Validator
+
+The validator is not implemented in this example
+
+### Starknet ➡ Gnosis Chiado
+
+```bash
+npm run bridge:to:starknet
+```
+
+### Gnosis Chiado ➡ Starknet
+
+```bash
+npm run bridge:to:gnosis
+```
+
+## 🚩 Pitfalls
+
+- Ensure the ISMs (Interchain Security Modules) are correctly set up on both networks. Or that the NoopIsm is used for testing purposes.
+- Approve HypERC20Collateral spender on collateral token (Original ERC20).
+- To bridge from Starknet, do not forget to mention the `new CairoOption(CairoOptionVariant.None)` for `hook`and `hook_metadata`
diff --git a/example/infra/.env.example b/example/infra/.env.example
new file mode 100644
index 00000000..e74d0bbf
--- /dev/null
+++ b/example/infra/.env.example
@@ -0,0 +1,14 @@
+# ---------- Relayer signer (can be same key on both chains) ----------
+HYP_CHAINS_CHIADO_SIGNER_KEY=
+HYP_CHAINS_STARKNETSEPOLIA_SIGNER_KEY=
+HYP_CHAINS_STARKNETSEPOLIA_SIGNER_ADDRESS=
+HYP_CHAINS_STARKNETSEPOLIA_SIGNER_IMPLEMENTATION=argent
+
+# Checkpoint storage (both agents)
+HYP_CHECKPOINTSYNCER_TYPE=localStorage
+HYP_CHECKPOINTSYNCER_PATH=/hyperlane_db
+
+
+# VALIDATORS PRIVATE KEYS
+VALIDATOR_PK_CHIADO=
+VALIDATOR_PK_STARK=
\ No newline at end of file
diff --git a/example/infra/config.json b/example/infra/config.json
new file mode 100644
index 00000000..d50f6b02
--- /dev/null
+++ b/example/infra/config.json
@@ -0,0 +1,112 @@
+{
+ "relayChains": "starknetsepolia,chiado",
+ "chains": {
+ "starknetsepolia": {
+ "signer": {
+ "type": "starkKey"
+ },
+ "gasCurrencyCoinGeckoId": "starknet",
+ "name": "starknetsepolia",
+ "displayName": "Starknet Sepolia",
+ "protocol": "starknet",
+ "chainId": "0x534e5f5345504f4c4941",
+ "domainId": 23448591,
+ "isTestnet": true,
+ "blocks": {
+ "confirmations": 1,
+ "estimateBlockTime": 30,
+ "reorgPeriod": 1
+ },
+
+ "rpcUrls": [{ "http": "https://starknet-sepolia.public.blastapi.io/rpc/v0_8" }],
+
+ "blockExplorers": [
+ {
+ "name": "Starknet Sepolia Explorer",
+ "family": "voyager",
+ "url": "https://sepolia.voyager.online",
+ "apiUrl": "https://sepolia.voyager.online/api"
+ }
+ ],
+
+ "nativeToken": {
+ "name": "Stark",
+ "symbol": "STRK",
+ "decimals": 18,
+ "denom": "0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d"
+ },
+ "merkleRootMultisigIsm": "0x048...",
+ "messageIdMultisigIsm": "0x0499...",
+ "domainRoutingIsm": "0x05d2...",
+ "noopIsm": "0x02dc...",
+ "pausableIsm": "0x07ff...",
+ "aggregation": "0x031e...",
+ "protocolFee": "0x0466...",
+ "hook": "0x0462...",
+ "mailbox": "0x02e5...",
+ "merkleTreeHook": "0x06ac...",
+ "defaultFallbackRoutingIsm": "0x0474...",
+ "trustedRelayerIsm": "0x027d...",
+ "domainRoutingHook": "0x025d...",
+ "validator_announce": "0x025d...",
+ "interchainGasPaymaster": "0x0000000000000000000000000000000000000000",
+ "index": {
+ "from": 1250000
+ }
+ },
+ "chiado": {
+ "blockExplorers": [
+ {
+ "apiUrl": "https://gnosis-chiado.blockscout.com/api",
+ "family": "blockscout",
+ "name": "Chiado Explorer",
+ "url": "https://gnosis-chiado.blockscout.com"
+ }
+ ],
+ "blocks": {
+ "confirmations": 1,
+ "estimateBlockTime": 5,
+ "reorgPeriod": 5
+ },
+ "chainId": 10200,
+ "deployer": {
+ "name": "k4ctuss",
+ "url": "https://github.com/k4ctuss"
+ },
+ "displayName": "Gnosis Chiado Testnet",
+ "domainId": 10200,
+ "isTestnet": true,
+ "name": "chiado",
+ "nativeToken": {
+ "decimals": 18,
+ "name": "xDai",
+ "symbol": "xDai"
+ },
+ "protocol": "ethereum",
+ "rpcUrls": [
+ {
+ "http": "https://gnosis-chiado-rpc.publicnode.com"
+ }
+ ],
+ "technicalStack": "other",
+ "domainRoutingIsmFactory": "0x7c09b92a4818B08b8aA47a11698652fC6Aee11F0",
+ "interchainAccountIsm": "0xCbCDfC039ED11e0ff461dDDDe0825E4fd454c2b7",
+ "interchainAccountRouter": "0x4e93f12403eA3AA3e467b2e02044b42912e3cAb0",
+ "mailbox": "0x7323E06E40B0Fb82Dc26D309AaF14e49D266A6a7",
+ "merkleTreeHook": "0x9Da0eb6667033e01936cD914b46E32d9560D848B",
+ "proxyAdmin": "0x75A7BFEd3c9de77c5C27205D6Ddf772744ca48fc",
+ "staticAggregationHookFactory": "0x50fDFa26b9e9cCFf4Af8d6A0F95FBdc0F78F6E99",
+ "staticAggregationIsmFactory": "0xe95E3EF49b0fB2151301480d1845E760B5575A39",
+ "staticMerkleRootMultisigIsmFactory": "0xcE41FAc8bfAcb89f6AF3978E655C8839Ee21803a",
+ "staticMerkleRootWeightedMultisigIsmFactory": "0x32Ada46916c814704378af86163D1861aD8d4f29",
+ "staticMessageIdMultisigIsmFactory": "0xEB94C84C1aDB6aBA8866EC457d85374642937E2d",
+ "staticMessageIdWeightedMultisigIsmFactory": "0xEc02d931299Dc41342fD655f62b79345bA646714",
+ "testRecipient": "0x4e2d35b59FaC85FEFE30416745e743F9c7CaCB02",
+ "validatorAnnounce": "0xEb079356da4fE505ebA2018EB4D3B2db036B8Da5",
+ "interchainGasPaymaster": "0x0000000000000000000000000000000000000000",
+ "index": {
+ "from": 16990000
+ }
+ }
+ }
+}
diff --git a/example/infra/docker-compose.relayer.yml b/example/infra/docker-compose.relayer.yml
new file mode 100644
index 00000000..0bb1b3ec
--- /dev/null
+++ b/example/infra/docker-compose.relayer.yml
@@ -0,0 +1,22 @@
+services:
+ relayer:
+ platform: linux/amd64
+ # b4048d0-20250606-201810 = main branch on the 09 June 2025
+ image: gcr.io/abacus-labs-dev/hyperlane-agent:b4048d0-20250606-201810
+ container_name: hl-relayer
+ command: ['./relayer']
+ environment:
+ CONFIG_FILES: /etc/hyperlane/overrides.json
+ HYP_CHAINS_CHIADO_SIGNER_KEY: ${HYP_CHAINS_CHIADO_SIGNER_KEY}
+ HYP_CHAINS_STARKNETSEPOLIA_SIGNER_KEY: ${HYP_CHAINS_STARKNETSEPOLIA_SIGNER_KEY}
+ HYP_CHAINS_STARKNETSEPOLIA_SIGNER_ADDRESS: ${HYP_CHAINS_STARKNETSEPOLIA_SIGNER_ADDRESS}
+
+ volumes:
+ - relayer_db:/hyperlane_db
+ # mount the custom domain-list read-only **exactly** at the path above
+ - ./config.json:/etc/hyperlane/overrides.json:ro
+
+ restart: unless-stopped
+
+volumes:
+ relayer_db: {}
diff --git a/example/package.json b/example/package.json
new file mode 100644
index 00000000..ff1a8a48
--- /dev/null
+++ b/example/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "example",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "deploy": "cd ../scripts && npm run deploy && cd ../example",
+ "deploy-route": "ts-node scripts/deploy-route.ts",
+ "update-hooks": "cd ../scripts && npm run update-hooks --",
+ "enroll-remote-router": "ts-node scripts/enroll-remote-router",
+ "bridge:to:starknet": "ts-node scripts/bridge.ts chiado",
+ "bridge:to:gnosis": "ts-node scripts/bridge.ts starknetsepolia"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "^22.0.2",
+ "ethers": "^6.14.3",
+ "dotenv": "^16.4.5",
+ "fs": "^0.0.1-security",
+ "starknet": "^7.6.4",
+ "ts-node": "^10.9.2"
+ }
+}
diff --git a/example/scripts/bridge.ts b/example/scripts/bridge.ts
new file mode 100644
index 00000000..bed7d853
--- /dev/null
+++ b/example/scripts/bridge.ts
@@ -0,0 +1,150 @@
+/****************************************************************
+ * bridge.ts – Hyperlane Warp-Route helper
+ *
+ * Usage:
+ * ts-node bridge.ts chiado # lock on Chiado, mint on Starknet
+ * ts-node bridge.ts starknet # burn on Starknet, unlock on Chiado
+ *
+ ****************************************************************/
+
+import 'dotenv/config';
+import { ethers } from 'ethers';
+import { Account, BigNumberish, cairo, CairoOption, CairoOptionVariant, Contract, json } from 'starknet';
+
+import dotenv from 'dotenv';
+import { buildAccount } from '../../scripts/utils'; // ← your helper
+
+import fs from 'fs';
+
+type Order = {
+ p1: BigNumberish;
+ p2: BigNumberish;
+};
+
+dotenv.config();
+
+const TOKEN_PATH = '../cairo/target/dev/token';
+
+function findContractFile(name: string, suffix: string): string {
+ const tokenPath = `${TOKEN_PATH}_${name}${suffix}`;
+
+ if (fs.existsSync(tokenPath)) {
+ return tokenPath;
+ }
+
+ throw new Error(`Contract file not found for ${name} with suffix ${suffix} at ${tokenPath}`);
+}
+
+export function getCompiledContract(name: string): any {
+ const contractPath = findContractFile(name, '.contract_class.json');
+ return json.parse(fs.readFileSync(contractPath).toString('ascii'));
+}
+
+/****************************************************************
+ * ENV
+ ****************************************************************/
+
+// ---------- Chiado ----------
+const CHIADO_RPC = 'https://gnosis-chiado-rpc.publicnode.com';
+const GNOSIS_CHIADO_PK = process.env.GNOSIS_CHIADO_PK as string; // EOA that holds the token
+const CHIADO_DOMAIN_ID = '10200'; // Chiado domain-id
+const CHIADO_RECIPIENT = '0x_your_gnosischiado_recipient_address'; // recipient on Gnosis Chiado
+
+// the token address on Gnosis Chiado we want to bridge
+const collateralAddress = '0x_gnosischiado_token_you_want_to_bridge_address';
+// the HypERC20Collateral contract on Gnosis Chiado chain
+const hypERC20CollateralAddress = '0x_deployed_hyper20_collateral_address'; // HypERC20Collateral on Chiado
+
+// ---------- Starknet ----------
+const STARKNET_HYPERC20_ADDRESS = '0x_synthetic_contract_address'; // synthetic HypERC20 on Starknet
+const STARKNET_RECIPIENT = '0x_starknet_recipient'; // felt recipient on Starknet
+const STARKNET_DOMAIN_ID = '23448591'; // Starknet domain-id (fixed in the protocol)
+
+if (!hypERC20CollateralAddress || !STARKNET_HYPERC20_ADDRESS) {
+ throw new Error('Missing HypERC20Collateral or STARKNET_HYPERC20_ADDRESS');
+}
+
+/****************************************************************
+ * MAIN
+ ****************************************************************/
+async function bridge(origin: 'chiado' | 'starknetsepolia') {
+ if (origin === 'chiado') {
+ /*----------------------------------------------------------
+ * Chiado ➜ Starknet (lock & mint)
+ *---------------------------------------------------------*/
+ if (!GNOSIS_CHIADO_PK || !STARKNET_RECIPIENT) {
+ throw new Error('Set GNOSIS_CHIADO_PK and STARKNET_RECIPIENT in .env');
+ }
+ const provider = new ethers.JsonRpcProvider(CHIADO_RPC);
+ const wallet = new ethers.Wallet(GNOSIS_CHIADO_PK, provider);
+
+ const collateralAbi = ['function approve(address spender, uint256 amount) public returns (bool)'];
+
+ // Minimal ABI: approve + transferRemote
+ const hypERC20Abi = [
+ 'function transferRemote(uint32 domain, bytes32 recipient, uint256 amount) public returns (bool)'
+ ];
+
+ const hypERC20CollateralContract = new ethers.Contract(hypERC20CollateralAddress, hypERC20Abi, wallet);
+ const collateralContract = new ethers.Contract(collateralAddress, collateralAbi, wallet);
+
+ const amount = ethers.parseUnits('0.1', 18); // 0.1 token
+
+ console.log('🔐 approving collateral contract…');
+ const tx1 = await collateralContract.approve(hypERC20CollateralAddress, amount);
+ await tx1.wait();
+ console.log(`✔️ approve() → ${tx1.hash}`);
+
+ console.log('🚚 sending transferRemote…');
+ const tx2 = await hypERC20CollateralContract.transferRemote(
+ Number(STARKNET_DOMAIN_ID),
+ ethers.zeroPadValue(STARKNET_RECIPIENT, 32), // bytes32 recipient
+ amount
+ );
+ await tx2.wait();
+ console.log(`✅ transferRemote() → ${tx2.hash}`);
+ console.log('⏳ Relayer will mint on Starknet once signatures arrive.');
+ } else if (origin === 'starknetsepolia') {
+ /*------------------------------------------------------------
+ * Starknet ➜ Chiado (burn & unlock)
+ *-----------------------------------------------------------*/
+ const account: Account = await buildAccount();
+
+ const compiledContract = getCompiledContract('HypErc20');
+ const tokenContract = new Contract(compiledContract.abi, STARKNET_HYPERC20_ADDRESS, account);
+
+ const amount = 1;
+ const formattedAmount = cairo.uint256(amount * 10 ** 18);
+ const recipient = '0x' + CHIADO_RECIPIENT!.slice(2).padStart(64, '0');
+
+ console.log('🔥 burning synthetic token on Starknet…');
+ const txRes = await tokenContract.invoke('transfer_remote', [
+ // gnosis chain domain id
+ Number(CHIADO_DOMAIN_ID),
+ recipient, // gnosis chiado recipient
+ formattedAmount, // amount
+ 0, // value
+ new CairoOption(CairoOptionVariant.None), // hook_metadata
+ new CairoOption(CairoOptionVariant.None) // hook
+ ]);
+
+ await account.waitForTransaction(txRes.transaction_hash);
+
+ console.log(`✅ burn sent → ${txRes.transaction_hash}`);
+ console.log('⏳ Relayer will unlock on Chiado once signatures arrive.');
+ }
+}
+
+/****************************************************************
+ * Run
+ ****************************************************************/
+const arg = process.argv[2] as 'chiado' | 'starknetsepolia';
+if (!arg || !['chiado', 'starknetsepolia'].includes(arg)) {
+ console.error('Usage: ts-node bridge.ts ');
+ process.exit(1);
+}
+
+bridge(arg).catch((err) => {
+ console.error(err);
+ process.exit(1);
+});
diff --git a/example/scripts/deploy-route.ts b/example/scripts/deploy-route.ts
new file mode 100644
index 00000000..e7004b5c
--- /dev/null
+++ b/example/scripts/deploy-route.ts
@@ -0,0 +1,93 @@
+import dotenv from 'dotenv';
+
+import fs from 'fs';
+import { Account, byteArray, cairo, CallData, ContractFactory, ContractFactoryParams, json } from 'starknet';
+import TESTNET_DEPLOYMENTS from '../../scripts/deployments/STARKNET_SEPOLIA/deployments.json';
+import { buildAccount } from '../../scripts/utils';
+
+dotenv.config();
+
+const TOKEN_PATH = '../cairo/target/dev/token';
+
+function findContractFile(name: string, suffix: string): string {
+ const tokenPath = `${TOKEN_PATH}_${name}${suffix}`;
+
+ if (fs.existsSync(tokenPath)) {
+ return tokenPath;
+ }
+
+ throw new Error(`Contract file not found for ${name} with suffix ${suffix} at ${tokenPath}`);
+}
+
+export function getCompiledContract(name: string): any {
+ const contractPath = findContractFile(name, '.contract_class.json');
+ return json.parse(fs.readFileSync(contractPath).toString('ascii'));
+}
+
+export function getCompiledContractCasm(name: string): any {
+ const contractPath = findContractFile(name, '.compiled_contract_class.json');
+ return json.parse(fs.readFileSync(contractPath).toString('ascii'));
+}
+
+export async function declareContract(account: Account, name: string): Promise {
+ console.log(`Declaring contract ${name}...`);
+ const compiledContract = getCompiledContract(name);
+ const casm = getCompiledContractCasm(name);
+
+ const declareResponse = await account.declareIfNot({
+ contract: compiledContract,
+ casm
+ });
+
+ console.log(`Contract ${name} declared with class hash:`, declareResponse.class_hash);
+
+ return declareResponse.class_hash;
+}
+
+/**
+ * We deploy an HypERC20 contract to the Starknet network.
+ */
+async function main() {
+ const account = await buildAccount();
+
+ if (!TESTNET_DEPLOYMENTS.mailbox || !TESTNET_DEPLOYMENTS.hook || !TESTNET_DEPLOYMENTS.aggregation) {
+ throw new Error(
+ 'Missing required deployment addresses in TESTNET_DEPLOYMENTS. \n> Please run make deploy-starknet-contracts first'
+ );
+ }
+
+ // to update as you need
+ const constructor = {
+ decimals: 18,
+ mailbox: TESTNET_DEPLOYMENTS.mailbox as string,
+ total_supply: cairo.uint256('0'),
+ name: byteArray.byteArrayFromString('yourDesiredTokenName'),
+ symbol: byteArray.byteArrayFromString('yourDesiredTokenSymbol'),
+ hook: TESTNET_DEPLOYMENTS.hook as string,
+ // please not that we use noop_ism for testing purposes
+ // in production you should use the actual ISM contract address
+ interchain_security_module: TESTNET_DEPLOYMENTS.noop_ism as string,
+ owner: account.address as string
+ };
+
+ const contractName = 'HypERC20';
+
+ const classHash = await declareContract(account, contractName);
+
+ const compiledContract = getCompiledContract(contractName);
+ const casm = getCompiledContractCasm(contractName);
+ const constructorCalldata = CallData.compile(constructor);
+ const params: ContractFactoryParams = {
+ compiledContract,
+ account,
+ casm,
+ classHash
+ };
+
+ const contractFactory = new ContractFactory(params);
+ const contract = await contractFactory.deploy(constructorCalldata);
+
+ console.log(`HypERC20 contract deployed at address: ${contract.address}`);
+}
+
+main();
diff --git a/example/scripts/enroll-remote-router.ts b/example/scripts/enroll-remote-router.ts
new file mode 100644
index 00000000..6b9d8a37
--- /dev/null
+++ b/example/scripts/enroll-remote-router.ts
@@ -0,0 +1,67 @@
+import dotenv from 'dotenv';
+
+import fs from 'fs';
+import { CallData, Contract, json, num, uint256 } from 'starknet';
+import TESTNET_DEPLOYMENTS from '../../scripts/deployments/STARKNET_SEPOLIA/deployments.json';
+import { buildAccount } from '../../scripts/utils';
+
+dotenv.config();
+
+// the hypERC20Collateral contract on Gnosis Chiado chain
+const REMOTE_TOKEN_ADDRESS = '0x_your_gnosischiado_token_you_want_to_bridge_address';
+// the domain ID of the Gnosis chain
+const REMOTE_DOMAIN_ID = '10200'; // Gnosis Chiado chain domain ID
+// synthetic on starknet
+const STARKNET_SYNTHETIC_ADDRESS = '0x_your_synthetic_hyper20_address'; // synthetic HypERC20 on Starknet
+
+function findContractFile(name: string, suffix: string): string {
+ const TOKEN_PATH = '../cairo/target/dev/token';
+ const tokenPath = `${TOKEN_PATH}_${name}${suffix}`;
+ console.log('tokenPath:', tokenPath);
+ if (fs.existsSync(tokenPath)) {
+ return tokenPath;
+ }
+
+ throw new Error(`Contract file not found for ${name} with suffix ${suffix} at ${tokenPath}`);
+}
+
+function getCompiledContract(name: string): any {
+ const contractPath = findContractFile(name, '.contract_class.json');
+ return json.parse(fs.readFileSync(contractPath).toString('ascii'));
+}
+/**
+ * We deploy an HypERC20 contract to the Starknet network.
+ */
+async function main() {
+ const account = await buildAccount();
+
+ if (!TESTNET_DEPLOYMENTS.mailbox || !TESTNET_DEPLOYMENTS.hook || !TESTNET_DEPLOYMENTS.aggregation) {
+ throw new Error('Missing required deployment addresses in TESTNET_DEPLOYMENTS');
+ }
+
+ const constructor = {
+ // gnosis chain domain id
+ domain: REMOTE_DOMAIN_ID,
+ router: uint256.bnToUint256(num.toBigInt(REMOTE_TOKEN_ADDRESS))
+ };
+
+ const constructorCalldata = CallData.compile(constructor);
+
+ const compiledContract = getCompiledContract('HypErc20');
+
+ const contract = new Contract(compiledContract.abi, STARKNET_SYNTHETIC_ADDRESS);
+
+ console.log('Enrolling remote router for HypERC20 contract...');
+
+ const tx = await account.execute({
+ contractAddress: contract.address,
+ entrypoint: 'enroll_remote_router',
+ calldata: constructorCalldata
+ });
+
+ await account.waitForTransaction(tx.transaction_hash);
+
+ console.log(`✅ Remote router enrolled successfully for HypERC20 contract at address ${STARKNET_SYNTHETIC_ADDRESS}`);
+}
+
+main();
diff --git a/example/tsconfig.json b/example/tsconfig.json
new file mode 100644
index 00000000..b6ae8e73
--- /dev/null
+++ b/example/tsconfig.json
@@ -0,0 +1,15 @@
+{
+ "compilerOptions": {
+ "module": "commonjs",
+ "target": "es6",
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "forceConsistentCasingInFileNames": true,
+ "strict": true,
+ "moduleResolution": "node",
+ "resolveJsonModule": true,
+ "outDir": "./dist"
+ },
+ "include": ["./**/*.ts"],
+ "exclude": ["node_modules"]
+ }
\ No newline at end of file
diff --git a/scripts/deployments/STARKNET_SEPOLIA/deployments.json b/scripts/deployments/STARKNET_SEPOLIA/deployments.json
index b558d4cc..9911ae3c 100644
--- a/scripts/deployments/STARKNET_SEPOLIA/deployments.json
+++ b/scripts/deployments/STARKNET_SEPOLIA/deployments.json
@@ -1,16 +1,16 @@
{
- "merkleroot_multisig_ism": "0x07041a9408d8ee588a48e858fbc59dbb6232b0fe5b4c315d0cab192d151b81ff",
- "messageid_multisig_ism": "0x05273d583af7d0721a8ba770206f86ecac7c61fcbaf937e7412d7d04c59b7b3c",
- "domain_routing_ism": "0x079956e5090451e3ba0aae43ac1fedd2ed378ad234f4c95b3a4bafe708357603",
- "noop_ism": "0x0328e944edb4b1c1e625b9cbb94d6188078e61db33363fb937a2e06ff38b963f",
- "pausable_ism": "0x02337f6a5aac407fd2d5ed8e1acd5df2be12633e79c38aacfc8ded680e05dfe1",
- "aggregation": "0x075c85b618e5c0469596759578db275a526dce21a6c252b48437f65da799e3af",
- "protocol_fee": "0x001e485245b14127de40c9c35281e919acae751e8136bcefcc8033468d6c966d",
- "hook": "0x072471e42a5368856ecd5aad27ec0259e3d34c63495c04124e6a3ce55c46cbd5",
- "mailbox": "0x07df35d3796603c930747bfc8409173aefc2c4c2c9cae4c0ea4908ea9a3baef2",
- "merkle_tree_hook": "0x03577d5fcfa888ee618cfab4563029f2f22db76f8a03e7ed96afa9410e2d0af5",
- "default_fallback_routing_ism": "0x045a69c3f44acd7fffb81a64bf00de9d0b1c19ee2fa333ea4b3887fbea44de47",
- "trusted_relayer_ism": "0x0168a0e02bba6a10f7fedd211884d2a1d57e28729380161e720d6aa7b1e5ce3e",
- "validator_announce": "0x07ae6742b4cd35ee92d6399c046ca2594e8374bb466d8e25aa7f88e2f68f8d9d",
- "domain_routing_hook": "0x06b60a487e0cd8419fee8e1cd06091aeddb55e358aedf96ea77ef49296d3ddbe"
+ "merkleroot_multisig_ism": "0x055e9b55c578eb298550a3d383d4bed3e7f1ea7d0388b572f7cd853ff2f224cb",
+ "messageid_multisig_ism": "0x03e15ccc5e88083aa0489c151660ab3d393bb11dcd57e2a0515ca51b71e459ba",
+ "domain_routing_ism": "0x06e916f9a76a45388895740549ef8e183ece59aa7956a102f8d250daebdc076e",
+ "noop_ism": "0x05e5406d8deaf2968b45bf6146f2f150833842add925a100d1fe3a08a85889bb",
+ "pausable_ism": "0x02c81e4c5d0eb3c19f37be03ea8c60664dfd6a5477e6be94fcff3f78435e5cb4",
+ "aggregation": "0x034e92b39e161461f0de92111261c864f748a661c5f16a87d5187a4d496531e8",
+ "protocol_fee": "0x005162b1d0da6e8b3c051d90fdca92f7b128407b9b53f656a066176204b284bd",
+ "hook": "0x034bbfd915737767338aac7e87b01b26e0a1323bc0015a8f69746ade378be661",
+ "mailbox": "0x022dd896eb97a118a4d0198a534bd13924e748a5447077beee1ef4746f4cc0c5",
+ "merkle_tree_hook": "0x01b91e252ec2f94833d5d74a34474fdf4b0d8912cf4931b0710e0dc7e1a204e3",
+ "default_fallback_routing_ism": "0x05d278142e85b2556a339f130844acfabc44f07ef3a8571bb78a17023337518c",
+ "trusted_relayer_ism": "0x05128cfd9a6a26dd7f02e22ff2ead3719d818a57f92e93a94411ecd7b1089f15",
+ "validator_announce": "0x033a88e24d37d397d017ef2302add746d823f6e2ef875aea96f5ccc0ecc118ed",
+ "domain_routing_hook": "0x05fec9deb9eee6fe6466951022666e860bd9a45906fcb802f85889091630e96e"
}
\ No newline at end of file
diff --git a/scripts/package.json b/scripts/package.json
index f33951f5..d64777f1 100644
--- a/scripts/package.json
+++ b/scripts/package.json
@@ -15,6 +15,6 @@
"commander": "^12.1.0",
"dotenv": "^16.4.5",
"fs": "^0.0.1-security",
- "starknet": "^6.11.0"
+ "starknet": "^7.6.4"
}
}
diff --git a/scripts/update-hooks.ts b/scripts/update-hooks.ts
index f13094db..e3ca0fbd 100644
--- a/scripts/update-hooks.ts
+++ b/scripts/update-hooks.ts
@@ -6,6 +6,9 @@ import { Command } from "commander";
dotenv.config();
+// example usage:
+// npm run update-hooks -- -r 0x_required_hook_address -d 0x_default_hook_address
+
// Initialize commander
const program = new Command();