Introduction
What is Rosettanet?
Rosettanet is a middleware architecture that reads and executes Starknet transactions like Ethereum transactions. It makes it possible with having its own node and its own account contracts. Anyone can deploy the Rosettanet node without any trusted setup. Any Ethereum tooling or wallet can keep working since it is connected to the Rosettanet node with its own custom chain ID.
What Rosettanet brings to Starknet?
- Any Starknet protocols can start supporting Ethereum wallets such as Metamask, etc.
- Existing Ethereum toolings like Web3js and ethers can be used on Starknet.
- Allows sending & executing transactions with Ethereum signatures.
- Seamless migration for users from Ethereum or any other EVM chain.
Project structure and related repositories
- Rosettanet: Rosettanet node written in Typescript
- Rosettanet Accounts: Starknet account smart contracts that work with Ethereum-signed transactions.
- Rosettanetjs: Javascript library that includes functions to integrate Rosettanet to frontend.
Community Channels
For Users
Connecting Starknet via Rosettanet
Rosettanet is not a chain itself. It is a gateway between client and Starknet. In order to connect to the Starknet via Rosettanet, you have to connect your web3 wallet to any Rosettanet node.
Add Rosettanet as a new network
Mainnet: 0x525354 (RST) Sepolia: 0x52535453 (RSTS)
These are the chain IDs you have to use to connect your targeted Starknet chain.
Currency: STRK
Decimals: 18
Explorer: Voyager or Starkscan
Public endpoints: TBD
Fee token
Fees are always paid as STRKs currently. You have to deposit STRKs to your new Wallet in order to broadcast transactions through your account. Please follow next page for more details.
Do not send tokens directly to your Ethereum address from CEXes.
Notice
Rosettanet account contracts are not audited and still under development. DYOR.
First deposit
In order to use Rosettanet, your account has to be funded with STRKs and deployed on chain. So first you have to learn your Ethereum address actual Starknet contract address equivalent. This can be done through the Rosettanet dashboard or manually retrieved via the Rosettanet registry contract.
How to find the actual Starknet address?
To find your Starknet address, you need to interact with the Rosettanet smart contract. This process does not require you to pay any fees or connect your wallet.
Currently, the contract is deployed only on the Sepolia testnet. You can access it via the following block explorers:
Under the "Read Contract" section, you will find a function named precalculate_starknet_account
. To retrieve your Starknet address, simply enter your Ethereum address as the input to this function and click the "Query" button.
The returned value will be your corresponding Starknet address.
Alternatively, you can also use the get_starknet_address
or get_starknet_address_with_fallback
functions — don’t worry, all of them will return the same result. If the result of the get_starknet_address
function is 0x0
, this means your account has not been deployed yet. In that case, you can use the fallback or precalculate version. Once your account is deployed, the get_starknet_address
function will also return your Starknet address correctly.
How to deploy my account?
Your account will be automatically deployed when you send your first transaction. Rosettanet intercepts this initial transaction and sends an account deployment transaction beforehand. Based on the response from the deployment transaction, it adjusts the nonce and other relevant fields of your original transaction accordingly, then submits it to the network.
As a user, you only need to send a regular transaction — no additional steps are required for account deployment. Rosettanet takes care of the entire deployment process behind the scenes.
What happens next?
Once your first transaction is completed. You will be able to use your wallet with your favourite Starknet protocols.
For Developers
Integration of Rosettanet into Starknet DApps
Rosettanet integration is getting added to starknet.js
(6.24.1) and get-starknet
(v4). With these integrations Rosettanet will work as WalletAccount
in starknet.js
.
Connecting to Wallet
DApps can connect to wallets using the connect()
function from get-starknet
:
import { connect } from "@starknet-io/get-starknet";
const [selectedAccount, setSelectedAccount] = useState(null);
function handleConnect() {
return async () => {
const res = await connect();
setSelectedAccount(res);
};
}
return <button onClick={handleConnect()}>Connect With get-starknet</button>;
This is a standard implementation of get-starknet
in DApps. To use the wallet with starknet.js
, we first need to determine whether the user's wallet is a Starknet wallet or an EVM wallet. We can do this by checking selectedAccount from get-starknet is a EVM Wallet or Starknet Wallet. You can distinguish EVM wallets by checking if the window object includes the methods send
and sendAsync
, which are not present in Starknet wallets.
Ensure the node
variable points to a valid Rosettanet node.
import { RosettanetAccount, WalletAccount } from "starknet";
const userWalletAccount = await (typeof selectedAccount.sendAsync ===
"function" && typeof selectedAccount.send === "function"
? RosettanetAccount.connect({ nodeUrl: node }, selectedAccount)
: WalletAccount.connect({ nodeUrl: node }, selectedAccount));
This detection method will be available in the rosettanetjs npm library for easier usage in the near future.
Sending Requests
Once connected, the DApp can send transactions and requests through the user's wallet. Use the execute()
method from starknet.js
to send transactions. RosettanetAccount
supports nearly all WalletAccount
methods, except for declare()
and deploy()
. You can check what is available for WalletAccount
from starknet.js
docs.
const starknetCalldata = [
{
contractAddress:'contractAddress',
entrypoint: 'approve',
// You can use entrypoint as function name or entry point selector hash
calldata: "calldata",
},
{
contractAddress:'contractAddress',
// You can use entrypoint as function name or entry point selector hash
entrypoint: '0x25356d5707a314336daf6636019fcd414e2403787a6dfb3eacc0c8450b341c8',
calldata: "calldata",
},
];
const response = await userWalletAccount.execute(starknetCalldata);
If your DApp needs the wallet address, userWalletAccount.address
will return the address. For EVM wallets, this will be an Ethereum address. You can call a helper contract to obtain the corresponding Starknet address from the Ethereum address:
import { RpcProvider, Contract } from "starknet";
export async function getStarknetAddress(address) {
const starknetProvider = new RpcProvider({
nodeUrl: nodeUrl,
});
const rosettanetContractAddress = "Rosettanet Contract Address";
const { abi: rosettanetContractAbi } = await starknetProvider.getClassAt(
contractAddress
);
if (contractAbi === undefined) {
throw new Error("no contract abi, check contract address");
}
const rosettanetContract = new Contract(
rosettanetContractAbi,
rosettanetContractAddress,
starknetProvider
);
const starknetAddress =
await rosettanetContract.get_starknet_address_with_fallback(address);
return "0x" + starknetAddress.toString(16);
}
This method will also be included in the rosettanetjs npm library for convenience.
Rosettanet Account can get information both Ethereum and Starknet requests. For example blockNumberRosettanet
uses eth_blockNumber
method and returns Starknet block number, getBlockNumber
uses starknet_blockNumber
method and returns Starknet block number.
const userWalletAccount = await RosettanetAccount.connect(
{ nodeUrl: node },
selectedAccount
);
const ethereumMethod = await userWalletAccount.blockNumberRosettanet();
const starknetMethod = await userWalletAccount.getBlockNumber();
Available Ethereum Requests
These methods are using Ethereum requests but retrieve data from Starknet Chain.
sendTransactionRosettanet
Sends transaction request to wallet. Uses eth_sendTransaction
method.
Parameters:
Name | Description | Type |
---|---|---|
params | Valid Ethereum Transaction Object | Ethereum Transaction Object |
const userWalletAccount = await RosettanetAccount.connect(
{ nodeUrl: node },
selectedAccount
);
const ethereumMethod = await userWalletAccount.sendTransactionRosettanet(
params
);
chainIdRosettanet
Request the current chain ID from the wallet. Uses eth_chainId
method.
const userWalletAccount = await RosettanetAccount.connect(
{ nodeUrl: node },
selectedAccount
);
const ethereumMethod = await userWalletAccount.requestChainId();
blockNumberRosettanet
Request latest block number in Starknet. Uses eth_blockNumber
method.
const userWalletAccount = await RosettanetAccount.connect(
{ nodeUrl: node },
selectedAccount
);
const ethereumMethod = await userWalletAccount.blockNumberRosettanet();
callRosettanet
Call request. Uses eth_call
method.
Parameters:
Name | Description | Type |
---|---|---|
tx | Valid Ethereum Transaction Object | Ethereum Transaction Object |
const userWalletAccount = await RosettanetAccount.connect(
{ nodeUrl: node },
selectedAccount
);
const ethereumMethod = await userWalletAccount.callRosettanet(tx);
estimateGasRosettanet
Estimated gas fee for the transaction. Uses eth_estimateGas
method
Parameters:
Name | Description | Type |
---|---|---|
tx | Valid Ethereum Transaction Object | Ethereum Transaction Object |
const userWalletAccount = await RosettanetAccount.connect(
{ nodeUrl: node },
selectedAccount
);
const ethereumMethod = await userWalletAccount.estimateGasRosettanet(tx);
gasPriceRosettanet
Latest gas price in network. Uses eth_gasPrice
method.
const userWalletAccount = await RosettanetAccount.connect(
{ nodeUrl: node },
selectedAccount
);
const ethereumMethod = await userWalletAccount.gasPriceRosettanet();
getBalanceRosettanet
STRK balance of given address. Uses eth_getBalance
method.
Parameters:
Name | Description | Type |
---|---|---|
address | Ethereum Address to check balance | string |
block | Block number or hash (optional) | string |
const userWalletAccount = await RosettanetAccount.connect(
{ nodeUrl: node },
selectedAccount
);
const ethereumMethod = await userWalletAccount.getBalanceRosettanet(
address,
block
);
getBlockByHashRosettanet
Block by given block hash. Uses eth_getBlockByHash
method.
Parameters:
Name | Description | type |
---|---|---|
blockHash | Ethereum Block hash. | string |
hydratedTx | Hydrated transactions (optional) | boolean |
const userWalletAccount = await RosettanetAccount.connect(
{ nodeUrl: node },
selectedAccount
);
const ethereumMethod = await userWalletAccount.getBlockByHashRosettanet(
blockHash,
hydratedTx
);
getBlockByNumberRosettanet
Block by given block number. Uses eth_getBlockByNumber
method.
Parameters:
Name | Description | type |
---|---|---|
blockNumber | Block number or block tag. | string |
hydratedTx | Hydrated transactions (optional) | boolean |
const userWalletAccount = await RosettanetAccount.connect(
{ nodeUrl: node },
selectedAccount
);
const ethereumMethod = await userWalletAccount.getBlockByHashRosettanet(
blockNumber,
hydratedTx
);
getBlockTransactionCountByHashRosettanet
Transaction count of given block hash. Uses eth_getBlockTransactionCountByHash
method.
Parameters:
Name | Description | type |
---|---|---|
blockHash | Block hash | string |
const userWalletAccount = await RosettanetAccount.connect(
{ nodeUrl: node },
selectedAccount
);
const ethereumMethod = await userWalletAccount.getBlockByHashRosettanet(
blockHash
);
getBlockTransactionCountByNumberRosettanet
Transaction count of given block number. Uses eth_getBlockTransactionCountByNumber
method.
Parameters:
Name | Description | type |
---|---|---|
blockNumber | Block number | string |
const userWalletAccount = await RosettanetAccount.connect(
{ nodeUrl: node },
selectedAccount
);
const ethereumMethod =
await userWalletAccount.getBlockTransactionCountByNumberRosettanet(
blockNumber
);
getTransactionCountRosettanet
Transaction count of given address. Uses eth_getTransactionCount
method.
Parameters:
Name | Description | type |
---|---|---|
address | Ethereum address | string |
const userWalletAccount = await RosettanetAccount.connect(
{ nodeUrl: node },
selectedAccount
);
const ethereumMethod = await userWalletAccount.getTransactionCountRosettanet(
address
);
getTransactionReceiptRosettanet
Transaction receipt of given transaction hash. Uses eth_getTransactionReceipt
method.
Parameters:
Name | Description | type |
---|---|---|
txHash | Ethereum transaction hash | string |
const userWalletAccount = await RosettanetAccount.connect(
{ nodeUrl: node },
selectedAccount
);
const ethereumMethod = await userWalletAccount.getTransactionReceiptRosettanet(
txHash
);
Using Ethereum Based Toolings
With the Rosettanet Node API, developers can use Ethereum tooling such as ethers.js, Wagmi, Reown (Wallet Connect) without any modifications. This means DApps built with these toolings can be deployed and run on Starknet seamlessly with little to no changes to the frontend code.
In Wagmi and Reown (Wallet Connect) you may need to add Rosettanet as a chain.
Ethers
Using ethers.js
with Rosettanet is straightforward, it works just like it does on Ethereum, with only a few small adjustments. Below is a simple example of how to read data from a contract using ethers.Contract
as you would in any typical Ethereum DApp:
const TestAccount1 = "account-address";
const STRKToken = "token-address";
// Connect to Rosettanet-compatible Starknet RPC
const provider = new ethers.JsonRpcProvider("Rosettanet-RPC-URL");
// A minimal ERC-20 ABI for demonstration purposes
const ERC20_ABI = [
'function balanceOf(address owner) view returns (uint256)',
'function decimals() view returns (uint8)'
];
// Instantiate the token contract
const tokenContract = new ethers.Contract(STRKToken, ERC20_ABI, provider);
// Read the token balance
const balance = await tokenContract.balanceOf(TestAccount1);
// Read the token decimals
const decimals = await tokenContract.decimals();
This code behaves the same way as it would on Ethereum. The Rosettanet Node takes care of translating the Ethereum-compatible calls to Starknet under the hood. No custom provider wrappers or contract interaction logic is required just plug in your Rosettanet RPC URL and use ethers
as usual.
Sending Transactions With Wallet
Class
When using Rosettanet with ethers.js
, the only modification required is in how you send transactions using the Wallet
class.
Unfortunately, calling wallet.sendTransaction(...)
will not work as expected. This is because ethers
internally computes the transaction hash and expects the RPC node to return the exact same hash. If there's a mismatch, ethers
will reject the transaction and throw an error.
Since Rosettanet executes transactions on the Starknet network, it cannot provide the Ethereum-style precalculated transaction hash that ethers
expects. As a result, direct usage of sendTransaction
is incompatible.
✅ Recommended Workaround To send a transaction successfully, you can follow these three steps:
-
Populate the transaction using
wallet.populateTransaction(...)
-
Sign the transaction with
wallet.signTransaction(...)
-
Send the raw signed transaction using
provider.send('eth_sendRawTransaction', [signedTx])
Here’s a full example:
const provider = new ethers.JsonRpcProvider("Rosettanet-RPC-URL");
const privateKey = 'account-priv-key';
// Creating new Wallet class
const wallet = new ethers.Wallet(privateKey, provider);
const toAddress = 'to-address';
// We cant directly send transaction with wallet.sendTransaction.
// Because it expects precalculated tx hash from RPC.
// And it reverts if hash is different.
// Populating Transaction
const txRequest = await wallet.populateTransaction({
to: toAddress,
value: ethers.parseEther('1.0')
});
// Signing Transaction
const signedTx = await wallet.signTransaction(txRequest);
// Sending transaction to blockchain network
await provider.send('eth_sendRawTransaction', [signedTx])
// Checking toAddress for incoming funds
// A minimal ERC-20 ABI for demonstration purposes
const ERC20_ABI = [
'function balanceOf(address owner) view returns (uint256)',
'function decimals() view returns (uint8)'
];
const STRKToken = "token-address";
// Instantiate the token contract
const strkContract = new ethers.Contract(STRKToken, ERC20_ABI, provider);
// Read the token balance
const balance = await strkContract.balanceOf(toAddress);
Sending Transactions With Browser Wallet (Metamask)
You can send transactions through Browser Wallet using ethers.js
with little to no modification. Here's a basic example to get started.
This time, we'll use ethers.BrowserProvider
as the provider
, since we're interacting with a wallet injected into the browser (like MetaMask). Unlike with wallet.sendTransaction()
, there's no need for any workaround here you can directly use signer.sendTransaction()
.
const provider = new ethers.BrowserProvider(window.ethereum);
// Request wallet connection
const signer = await provider.getSigner();
// Now you can send transactions using signer.sendTransaction()
const tx = {
to:"toAddress",
value:ethers.parseEther(1) // 1 STRK
};
const txResponse = await signer.sendTransaction(tx);
Transactions with data
If your transaction includes a data
payload, you first need to register your smart contract with the Rosettanet registry. You can do this by calling the register_contract
function on the Rosettanet Sepolia smart contract.
After registration, you can retrieve the corresponding Ethereum-compatible address of your Starknet contract using the get_ethereum_address
function.
When constructing your transaction:
-
The
to
field should be set to the Ethereum address returned from the registry. -
The
data
field must be manually encoded.
To encode the data
correctly, create a new instance of the ethers.Interface
class using your contract’s ABI. This allows you to encode function calls as shown below (using a minimal ERC-20 ABI for demonstration):
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const ERC20_ABI = [
'function balanceOf(address owner) view returns (uint256)',
'function decimals() view returns (uint8)',
'function transfer(address to, uint256 amount) public returns (bool)'
];
const iface = new ethers.Interface(ERC20_ABI);
const calldata = iface.encodeFunctionData('transfer', [
'transfer-to-address',
ethers.parseUnits('1', 18)
]);
const tx = {
to:"ethereum address from get_ethereum_address()",
data: calldata,
chainId:1381192787 // Rosettanet Sepolia Chain Id
};
const txResponse = await signer.sendTransaction(tx);
Using multicall
If you want to use multicall
feature from starknet, you need to pass to
field as 0x0000000000000000000000004645415455524553
. Below is an example of how to transfer ERC-20 tokens to two different accounts using a multicall
:
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const MULTICALL_ABI = [
'function multicall((uint256,uint256,uint256[])[])'
];
const iface = new ethers.Interface(MULTICALL_ABI);
const receiver1SnAddress = await precalculateStarknetAddress('reciever-1-eth-address');
const receiver2SnAddress = await precalculateStarknetAddress('reciever-2-eth-address');
const data = iface.encodeFunctionData('multicall', [[
[
ERC20_TOKEN_ADDRESS,
'0x0083afd3f4caedc6eebf44246fe54e38c95e3179a5ec9ea81740eca5b482d12e', // transfer function entrypoint
[receiver1SnAddress,
'1000', // amount.low
'0' // amount.high
]
],
[
ERC20_TOKEN_ADDRESS,
'0x0083afd3f4caedc6eebf44246fe54e38c95e3179a5ec9ea81740eca5b482d12e', // transfer function entrypoint
[receiver2SnAddress,
'2000', // amount.low
'0'// amount.high
]
]
]]);
const tx = {
to:"0x0000000000000000000000004645415455524553",
data: calldata,
chainId:1381192787 // Rosettanet Sepolia Chain Id
};
const txResponse = await signer.sendTransaction(tx);
Wagmi
Integrating wagmi
with Rosettanet is straightforward and works almost exactly as it does on Ethereum with just a few minor adjustments.
You can continue to use familiar hooks and patterns from wagmi
, and in some cases, you may also need to use utility functions from the viem
library to handle lower-level tasks or data formatting as needed.
Connecting to Rosettanet Chain;
First, you need to define a custom Rosettanet Chain
and then create a corresponding Wagmi configuration.
You can use the code snippet below as-is, just replace the placeholder with a valid Rosettanet Sepolia RPC URL
.
The custom Rosettanet chain will be added to the list of chains recognized by viem
.
import { defineChain } from 'viem'
import { http, createConfig } from '@wagmi/core'
import { sepolia } from '@wagmi/core/chains'
import { injected } from 'wagmi/connectors'
const rosettanetSepolia = defineChain({
id: 1381192787,
caipNetworkId: 'eip155:1381192787',
chainNamespace: 'eip155',
name: 'Rosettanet',
nativeCurrency: {
decimals: 18,
name: 'Starknet Token',
symbol: 'STRK',
},
rpcUrls: {
default: {
http: ['Rosettanet Sepolia RPC URL'],
},
},
blockExplorers: {
default: { name: 'Voyager Sepolia', url: 'https://sepolia.voyager.online' },
},
contracts: {
},
})
export const config = createConfig({
chains: [rosettanetSepolia, sepolia],
connectors:[injected()]
transports: {
[rosettanetSepolia.id]: http(),
[sepolia.id]:http(),
},
});
Now that the configuration is complete, the next step is to wrap your application with the required providers:
-
Wagmi Provider
fromwagmi
-
QueryClientProvider
fromTanstack Query
This ensures that both the wagmi hooks and query caching mechanisms function properly throughout your app.
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { WagmiProvider } from 'wagmi'
const queryClient = new QueryClient()
function App() {
return (
<WagmiProvider config={config}>
<QueryClientProvider client={queryClient}>
{/** ... */}
</QueryClientProvider>
</WagmiProvider>
)
}
Once the providers are set up, your DApp is fully connected and ready to use wagmi
throughout the application.
To connect browser-injected wallets (such as MetaMask), you can use the useConnect
hook provided by wagmi
:
Interacting with a Smart Contract
Using wagmi
with Rosettanet is seamless and mirrors the typical Ethereum development experience, with only minimal adjustments.
Below is a simple example of how to read data from a smart contract using the useReadContract()
hook, exactly as you would in a standard Ethereum-based DApp:
const TestAccount1 = "account-address";
const TestContract = "contract-address";
// A minimal ERC-20 ABI for demonstration purposes
const ERC20_ABI = [
'function balanceOf(address owner) view returns (uint256)',
'function decimals() view returns (uint8)'
];
// Checking decimals of ERC20 contract
const result = useReadContract({
ERC20_ABI,
address: TestContract,
functionName: 'decimals',
})
---
// Checking balance of TestAccount1
const result = useReadContract({
ERC20_ABI,
address: TestContract,
functionName: 'balanceOf',
account: TestAccount1,
args: [TestAccount1]
})
Sending Transactions
To send a transaction with wagmi
, you can use the useSendTransaction
hook. Below is an example demonstrating how to use it.
import { useSendTransaction, useAccount } from 'wagmi'
import { parseEther } from 'viem'
import { config } from "path-to-config"
const { sendTransaction } = useSendTransaction()
const { address } = useAccount()
const toAddress = "to-address"
const transaction = await sendTransaction(config, {
to: toAddress
value: parseEther(1) // 1 STRK
chainId: 1381192787,
account: address,
})
Transactions with data
If your transaction includes a data
payload, you first need to register your smart contract with the Rosettanet registry. You can do this by calling the register_contract
function on the Rosettanet Sepolia smart contract.
After registration, you can retrieve the corresponding Ethereum-compatible address of your Starknet contract using the get_ethereum_address
function.
When constructing your transaction:
-
The
to
field should be set to the Ethereum address returned from the registry. -
The
data
field must be manually encoded.
This encoding can be done using encodeFunctionData()
, a function available in the viem
library. (using a minimal ERC-20 ABI for demonstration):
import { useSendTransaction, useAccount } from 'wagmi'
import { parseEther , encodeFunctionData } from 'viem'
import { config } from "path-to-config"
const { sendTransaction } = useSendTransaction()
const { address } = useAccount()
const ERC20_ABI = [
'function balanceOf(address owner) view returns (uint256)',
'function decimals() view returns (uint8)',
'function transfer(address to, uint256 amount) public returns (bool)'
];
const calldata = encodeFunctionData({
abi: ERC20_ABI,
functionName: "transfer",
args: ["transfer-to-address", parseEther(1)]
})
const transaction = await sendTransaction(config, {
to: "ethereum address from get_ethereum_address()"
chainId: 1381192787,
account: address,
data: calldata
})
Using multicall
If you want to use multicall
feature from starknet, you need to pass to
field as 0x0000000000000000000000004645415455524553
. Below is an example of how to transfer ERC-20 tokens to two different accounts using a multicall
:
import { useSendTransaction, useAccount } from 'wagmi'
import { parseEther , encodeFunctionData } from 'viem'
import { config } from "path-to-config"
const { sendTransaction } = useSendTransaction()
const { address } = useAccount()
const MULTICALL_ABI = [
'function multicall((uint256,uint256,uint256[])[])'
];
const receiver1SnAddress = await precalculateStarknetAddress('reciever-1-eth-address');
const receiver2SnAddress = await precalculateStarknetAddress('reciever-2-eth-address');
const calldata = encodeFunctionData({
abi: MULTICALL_ABI,
functionName: "multicall",
args: [[
[
ERC20_TOKEN_ADDRESS,
'0x0083afd3f4caedc6eebf44246fe54e38c95e3179a5ec9ea81740eca5b482d12e', // transfer function entrypoint
[receiver1SnAddress,
'1000', // amount.low
'0' // amount.high
]
],
[
ERC20_TOKEN_ADDRESS,
'0x0083afd3f4caedc6eebf44246fe54e38c95e3179a5ec9ea81740eca5b482d12e', // transfer function entrypoint
[receiver2SnAddress,
'2000', // amount.low
'0'// amount.high
]
]
]]
})
const transaction = await sendTransaction(config, {
to: "0x0000000000000000000000004645415455524553"
chainId: 1381192787,
account: address,
data: calldata
})
Reown (Wallet Connect)
We recommend using the wagmi
library alongside Reown (WalletConnect)
for better integration and support. Below is an example of the necessary configuration and adjustments when working with Reown.
import { createAppKit } from '@reown/appkit/react';
import { WagmiProvider } from 'wagmi';
import { defineChain, sepolia } from '@reown/appkit/networks';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { WagmiAdapter } from '@reown/appkit-adapter-wagmi';
const queryClient = new QueryClient();
const projectId = 'Project-id-from-reown';
const metadata = {
name: 'DApp-name',
description: 'DApp-desc',
url: 'DApp-URL',
icons: ['DApp-icon-url'],
};
const rosettanetSepolia = defineChain({
id: 1381192787,
caipNetworkId: 'eip155:1381192787',
chainNamespace: 'eip155',
name: 'Rosettanet',
nativeCurrency: {
decimals: 18,
name: 'Starknet Token',
symbol: 'STRK',
},
rpcUrls: {
default: {
http: ['Rosettanet Sepolia RPC URL'],
},
},
blockExplorers: {
default: { name: 'Voyager Sepolia', url: 'https://sepolia.voyager.online' },
},
contracts: {
},
})
const networks = [rosettanetSepolia, sepolia];
const wagmiAdapter = new WagmiAdapter({
networks,
projectId,
});
export const reownConfig = wagmiAdapter.wagmiConfig;
createAppKit({
adapters: [wagmiAdapter],
networks,
projectId,
metadata,
});
export function App({ children }) {
return (
<WagmiProvider config={wagmiAdapter.wagmiConfig}>
<QueryClientProvider client={queryClient}>{/** ... */}</QueryClientProvider>
</WagmiProvider>
);
}
Using multicall with Rosettanet
As Rosettanet aims to provide EVM experience on Starknet, we tried our best to not compromising from Starknet native features. Rosettanet account contracts supports multicall transactions, raw Starknet calldata transaction and upgrades. We call this type of calls to account contracts as feature calls.
Sending transaction with raw starknet calldata
If you re willing to send a transaction to interact with a contract that has starknet specific type (such like felt252) as input parameter. We recommend to send this transaction as a multicall transaction. Multicall usage will be explained below.
Sending multicall transaction
Rosettanet account contracts will act differently depends on target parameter of calldata. Also our account abstractions interface is different than actual Starknet accounts. Please check our Account interface.
To interact with account contracts features, your transaction target (to address) must be 0x4645415455524553. This address may be changed before Alpha release. However, you can find latest address here.
The calldata of multicall transaction must be encoded like calling multicall((uint256,uint256,uint256[])[]) function in EVM. We recommend to use our prepareMulticallCalldata function in RosettanetJS.
Once you broadcast this transaction. Your calls will be executed in the same order.
Sending upgrade transaction
Rosettanet account contracts can be upgraded to latest Rosettanet class, which is received from Rosettanet registry. To achieve this all you need to do is, sending a transaction with to address as our feature address (0x4645415455524553), and 0x74d0bb9d as calldata (upgrade selector)
General Overview
Rosettanet Node
Rosettanet Node is a middleware of the Rosettanet architecture. Main idea behind Rosettanet is making Starknet RPCs work like Ethereums. With that we will be able to use existing libraries and toolings with Starknet. So Rosettanet Node is the main entrypoint for interacting with Starknet as like Ethereum.
How does it works?
Node simply supports all JSON-RPC methods that any public Ethereum RPC supports. You can use Rosettanet endpoint as like Ethereum RPC endpoint. Inputs and outputs will be same as Ethereum. You can track our progress on JSON-RPC methods here.
Requirements
Rosettanet Node is not a resource consuming application. Written in Typescript and Rust version will be developed soon. We recommend to run Rosettanet Node on the same server with Starknet RPC running. Rosettanet Node needs to connect a Starknet RPC without any IP or request limit. Using Rosettanet with public Starknet RPCs may cause request limit reaches. Node can make up to 10 RPC calls for a simple eth_call request.
Current supported methods & behaviors
Every method is working right now, there might be some differences. TBD
Rosettanet Accounts
Rosettanet has its own account smart contracts. These account contracts designed to validate and execute transactions signed as Legacy or EIP1559 transaction types. Every Ethereum account is matched with actual starknet account addresses, however Rosettanet will handle the transaction so users won't notice the difference.
What is difference between Ethereum address and Starknet contract address?
While Ethereum and Starknet both use addresses to identify accounts and contracts, they operate under different account models and deployment requirements.
1. Type of Account
-
Ethereum:
Ethereum has two types of accounts:- Externally Owned Accounts (EOAs): Controlled by private keys. These are standard user wallets like MetaMask.
- Contract Accounts: Deployed with bytecode and controlled by code.
-
Starknet:
On Starknet, all accounts are smart contracts. There are no EOAs. This means even user wallets are implemented as contract instances with programmable logic. This model allows for advanced features like multisig, session keys, and account abstraction directly at the protocol level. Every interaction with Starknet, including signing and transaction validation, is routed through these account contracts.
2. Deployment
-
Ethereum:
EOAs exist by default once a user generates a public/private key pair. No on-chain deployment is required. -
Starknet:
Starknet accounts must be explicitly deployed to the network before they can be used. An address on Starknet does not become active until its associated contract is deployed. This allows the account to include custom verification logic or wallet behavior as part of the contract's code.
How should I transfer my first funds into Rosettanet account?
Before transferring any funds, you need to know your Rosettanet Starknet address. We've explained how to find it in this section.
Once you have your Starknet address, you can safely transfer funds to it from another Starknet wallet or faucet. Please avoid sending funds directly from centralized exchanges (CEXs).
If you have another wallet that already holds a Rosettanet account, you can also transfer funds using your Ethereum address directly. For example, open MetaMask, click the “Send” button, and paste your Ethereum wallet address into the "To" field.
Note: This method only works if both wallets are connected to the Rosettanet network and the accounts have already been deployed.
IRosettaAccount
#![allow(unused)] fn main() { pub struct RosettanetCall { pub tx_type: u8, // 0: Legacy, 1: Eip2930, 2: Eip1559 pub to: EthAddress, pub nonce: u64, pub max_priority_fee_per_gas: u128, pub max_fee_per_gas: u128, pub gas_price: u128, pub gas_limit: u64, pub value: u256, pub calldata: Span<u128> } #[starknet::interface] pub trait IRosettaAccount<TState> { fn __execute__(ref self: TState, call: RosettanetCall) -> Array<Span<felt252>>; fn __validate__(self: @TState, call: RosettanetCall) -> felt252; fn is_valid_signature(self: @TState, hash: u256, signature: Array<felt252>) -> felt252; fn supports_interface(self: @TState, interface_id: felt252) -> bool; fn __validate_declare__(self: @TState, class_hash: felt252) -> felt252; fn __validate_deploy__( self: @TState, class_hash: felt252, contract_address_salt: felt252, eth_address: EthAddress, registry: ContractAddress, ) -> felt252; fn get_ethereum_address(self: @TState) -> EthAddress; fn rosettanet(self: @TState) -> ContractAddress; fn native_currency(self: @TState) -> ContractAddress; fn nonce(self: @TState) -> u64; fn isValidSignature(self: @TState, hash: u256, signature: Array<felt252>) -> felt252; fn getEthereumAddress(self: @TState) -> EthAddress; fn nativeCurrency(self: @TState) -> ContractAddress; } }
RosettanetAccount methods
RosettanetJS
Rosettanet JS is a javascript library that includes functionalities to integrate Rosettanet into your project.
Deployed Contracts
Sepolia
Account Class: Rosettanet Registry:
Mainnet
Will be deployed
Security
Audits & Bug bounties
Rosettanet Account contracts are currently under security review. We will update this page with audit report once it is completed.