Table of contents
- Ethereum Scaling
- StarkNet and STARKs
- StarkNet Endpoints with Infura
- How To Launch a Project on StarkNet with Infura
- Create an Infura account to access the StarkNet network
- Building a project on StarkNet with Infura
- Project setup and installation
- Download the account contract ABI
- Setup provider
- Generate private and public key pairs
- Deploy a new account on StarkNet
- Fund the new account
- Deploy ERC-20 contract
- Minting tokens
- Transfer tokens
- Conclusion
- Resources
Over the past year, we have seen an increase in layer two scaling solutions for Ethereum, with ZK-rollups being some of the most promising.
Thanks to the Stark rollup, any decentralized application can achieve a limitless computing scale, which keeps the Ethereum network secure and stable. This is made feasible by the fact that the STARK cryptographic proof system, which is the most flexible and scalable of all cryptographic proof systems, is used by StarkNet.
And Infura, Web3’s top node provider, now offers StarkNet compatibility through its node-providing service! By providing StarkNet RPC endpoints, Infura helps bridge the gap for Ethereum developers and facilitates a composable Web3 ecosystem.
This article covers what StarkNet is, how it contributes to Ethereum scalability, and how developers can get started by launching a smart contract to the network using Infura. You also learn how to access StarkNet with Infura RPC endpoints and deploy your own ERC-20 tokens on the network.
Ethereum Scaling
Adding a bridge to a layer two network is one of the most effective strategies to maximize the value of crypto assets. Lower transaction fees, more throughput, and improved customer experience are all benefits of doing this.
Rollups are some of the most widely used options for layer two scaling solutions. They function by processing transactions off-chain. They can also guarantee transaction data storage on the Ethereum chain, boosting layer one network security.
Zero-knowledge rollups create cryptographic proofs that demonstrate the legitimacy of a transaction. It's interesting to note that each batch of transactions would have a unique "validity proof" filed on the main chain. Optimistic rollups, on the other hand, assume that all transactions are valid and can submit batches of transactions without any calculations.
The ZK rollups' ability to assess transaction integrity would rely on cryptographic proofs. Conversely, the time between submitting the rollup and its acceptance on the base chain is difficult for optimistic rollups.
StarkNet and STARKs
StarkNet is a permissionless decentralized ZK-rollup. It functions as an L2 network on top of Ethereum, allowing any dapp to reach an infinite scale for processing without affecting Ethereum's security and composability.
By utilizing quasi-linear probabilistically checkable proofs (PCPs), interactive oracle proofs (IOPs), and fast algebraic local coding protocols like FRI, StarkNet's cryptographic hashing functions are the quickest. They outperform all other ZK technology.
The Solidity to Cairo compiler also enables faster builds for Solidity developers using the ZK rollup, facilitating the use of established Solidity tools in the developer’s stack.
StarkNet Endpoints with Infura
Infura, a Web3 Infrastructure-as-a-Service (IaaS) provider, offers various services and tools for blockchain developers. The Infura Application Programming Interface (API) suite is part of this.
Any project pushing a lot of traffic through the Ethereum network, or one that plans to, can benefit from Infura's modular scaling solutions. The network of hosted Ethereum clients, which supports mainnet and testnets via client-compatible JSON-RPC over HTTPS and WSS, is the most well-known component of the Infura infrastructure.
StarkNet endpoints are now widely available and supported by Infura. StarkNet enables indefinite scalability of any commercial use case, backed by STARKs, the most potent zero-knowledge proofs in the Ethereum ecosystem.
How To Launch a Project on StarkNet with Infura
Prerequisites
Before getting started, we need the following prerequisites:
Node.js and its package manager NPM.
Verify we have Node.js installed by using the following terminal command:
node -v && npm -v
- An Infura account
- Basic understanding of JavaScript
Create an Infura account to access the StarkNet network
To access a StarkNet node and execute requests, we will need an Infura endpoint.
Visit the Infura website to sign up for a new account or log in if you already have one.
After successfully signing up, the page redirects to our dashboard, where we can create a new key, as shown below.
Click the "Create a New Key" button and fill in the required information.
Next, click the "MANAGE KEY" button, scroll down to the Starknet section, select Goerli network, and copy the URL.
Building a project on StarkNet with Infura
In this section, we will build our application from scratch to implement StarkNet account creation, deployment, minting and transferring tokens.
Project setup and installation
We'll create an empty project and install the starknet.js library dependency using the following commands.
mkdir deploy-erc-20-token-with-starknet
cd deploy-erc-20-token-with-starknet
npm init -y
npm install starknet dotenv
touch index.js
Next, create a .env
file and paste the StarkNet URL we copied from our Infura dashboard.
STARKNET_TESTNET_ENDPOINT=https://starknet-goerli.infura.io/v3/<API-KEY>
Download the account contract ABI
Head over to the contract folder in this repository to download the contract ABI for ERC-20 and OZAccount.
Create a new folder called contracts
. Then, create two new files inside the folder, ERC20.json
and OZAccount.json
.
Next, we will paste the contract ABI we copied into those files. Our project folder structure should look similar to what we have below.
Setup provider
We may communicate with the StarkNet network through the Provider API without signing any transactions or messages. We will set up a provider via the Infura API endpoint, but in some cases, we may also want to use the default provider.
Inside the index.js
file, add the following code snippet.
import dotenv from "dotenv";
import fs from "fs";
import { Account, Contract, ec, json, stark, Provider, number } from "starknet";
import readline from "readline";
dotenv.config();
// Initialize provider
const url = process.env.STARKNET_TESTNET_ENDPOINT;
console.log("Using Infura Starknet provider: ", url);
const provider = new Provider({
rpc: {
nodeUrl: url,
},
});
In the code snippet above, we
- Imported the
starknet
,fs
, anddotenv
libraries - Initialized the provider URL
- Created a new instance of the
Provider
from thestarknet
library
Generate private and public key pairs
Since StarkNet does not support Externally Owned Accounts (EOA), all accounts on its network are contracts. In contrast to Ethereum, where public and private key pairs facilitate accounts, StarkNet accounts are the only means to sign transactions and communications and confirm signatures. Consequently, a Contract-Account interface is required.
Let us generate public and private key pairs with the following code snippet.
// Generate public and private key pair.
const privateKey = stark.randomAddress();
const starkKeyPair = ec.genKeyPair(privateKey);
const starkKeyPub = ec.getStarkKey(starkKeyPair);
// Log the Public and Private key pair.
console.log(`Private key: ${privateKey}`);
console.log(`Public key: ${starkKeyPub}`);
To test our application in the next step and every step afterwards, use the following command:
node index
We will use the command above each time we need to run the project locally.
Note: If you are getting a SyntaxError stating you can’t use the import statement outside of a module, add "type": "module" to your package.json file.
We should have something similar to the output below.
Write down both keys in case we need to use them again for other procedures in the future. Never share your private keys with anyone, and keep them secure.
Deploy a new account on StarkNet
By utilizing the deployContract
provider method and passing the previously produced public key as input, we can deploy the pre-compiled account contract to StarkNet.
Add the following code snippet to deploy the account contract, and we will wait for it to be verified on StarkNet.
//...
// Deploy the Account contract and wait for it to be verified on StarkNet.
console.log(
`////////////////////////////////////////////////////////////////////////////////
Deployment Tx - Account Contract to StarkNet...
////////////////////////////////////////////////////////////////////////////////`
);
const compiledOZAccount = json.parse(
fs.readFileSync("./contracts/OZAccount.json").toString("ascii")
);
const accountResponse = await provider.deployContract({
contract: compiledOZAccount,
constructorCalldata: [starkKeyPub],
addressSalt: starkKeyPub,
});
console.log("Account address ", accountResponse.contract_address);
console.log(
`See account on the explorer: https://goerli.voyager.online/contract/${accountResponse.contract_address}`
);
console.log(
`Follow the tx status on: https://goerli.voyager.online/tx/${accountResponse.transaction_hash}`
);
console.log(
`////////////////////////////////////////////////////////////////////////////////
Waiting for Tx to be Accepted on Starknet - OpenZeppelin Account Deployment...
////////////////////////////////////////////////////////////////////////////////`
);
await provider.waitForTransaction(accountResponse.transaction_hash);
console.log("Account contract deployed successfully!");
This operation may require a few minutes to complete, but we can always track its progress via the explorer URL logged on the console. To avoid creating multiple accounts each time we run the command to start the project, be sure to fill out the rest of the code in the project.
Fund the new account
Before anything else, we must add some code to pause the execution and resume it after all of the above actions are completed. This pause is necessary because our function, once launched, won't stop; we’ll need time to fund our account and for the transaction to be confirmed.
Hence, we will include the following method in the index.js
file.
//…
function askQuestion(query) {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
return new Promise((resolve) =>
rl.question(query, (ans) => {
rl.close();
resolve(ans);
})
);
}
const ans = await askQuestion(
"Did you add funds to your Account? Hit enter if yes"
);
Note: Before running the program again, be sure to fill out the rest of the code in the project. This will avoid having to fund multiple accounts.
After running the code again, we should have a prompt similar to what we have below, pausing the execution while adding some faucets to our account.
We need to put some gas in our tank before executing transactions on the StarkNet network (just like we can on Ethereum). Using the official StarkNet Goerli Faucet, we can fund our account by pasting in the account address from the terminal output.
Once the transaction is approved, hit enter to continue with the rest of the code.
Deploy ERC-20 contract
We have successfully created a provider, generated private and public key pairs for our Contract-Account interface, deployed account contracts, and funded our account in the previous steps.
In this section, we will deploy the ERC-20 contract using the following code snippet.
//...
// Use your new account address
const account = new Account(
provider,
accountResponse.contract_address,
starkKeyPair
);
console.log("Reading ERC20 Contract...");
const compiledErc20 = json.parse(
fs.readFileSync("./contracts/ERC20.json").toString("ascii")
);
// Deploy an ERC20 contract and wait for it to be verified on StarkNet.
console.log(
`////////////////////////////////////////////////////////////////////////////////
Deployment Tx - ERC20 Contract to StarkNet...
////////////////////////////////////////////////////////////////////////////////`
);
const erc20Response = await provider.deployContract({
contract: compiledErc20,
});
// Wait for the deployment transaction to be accepted on StarkNet
console.log("Waiting for Tx to be Accepted on Starknet - ERC20 Deployment...");
await provider.waitForTransaction(erc20Response.transaction_hash);
// Get the erc20 contract address
const erc20Address = erc20Response.contract_address;
console.log("ERC20 Address: ", erc20Address);
// Create a new erc20 contract object
const erc20 = new Contract(compiledErc20.abi, erc20Address, provider);
erc20.connect(account);
Minting tokens
Next, we will mint our tokens utilizing the mint transaction, which will take the account address of the recipient and the number of tokens.
Let us implement that functionality using the following code snippet.
//...
// Mint 500 tokens to account address
console.log(
`////////////////////////////////////////////////////////////////////////////////
Invoke Tx - Minting 500 tokens to ${account.address}...
////////////////////////////////////////////////////////////////////////////////`
);
const { transaction_hash: mintTxHash } = await erc20.mint(
account.address,
"500",
{
// transaction can be rejected if maxFee is lower than actual
// Error: REJECTED: FEE_TRANSFER_FAILURE
// Actual fee exceeded max fee.
maxFee: "999999995330000",
}
);
// Wait for the invoke transaction to be accepted on StarkNet
console.log(`Waiting for Tx to be Accepted on Starknet - Minting...`);
await provider.waitForTransaction(mintTxHash);
// Check balance - should be 500
console.log(`Calling StarkNet for account balance...`);
const balanceBeforeTransfer = await erc20.balance_of(account.address);
console.log(
`account Address ${account.address} has a balance of:`,
number.toBN(balanceBeforeTransfer.res, 16).toString()
);
Transfer tokens
Now that we have minted tokens let’s transfer some to confirm they exist and are transferable.
//...
// Execute transfer of ERC20 tokens
console.log(`Invoke Tx - Transfer 20 tokens back to erc20 contract...`);
const { code, transaction_hash: transferTxHash } = await account.execute(
{
contractAddress: erc20Address,
entrypoint: "transfer",
calldata: [erc20Address, "20"],
},
undefined,
{
maxFee: "999999995330000",
}
);
// Wait for the invoke transaction to be accepted on StarkNet
console.log(
`////////////////////////////////////////////////////////////////////////////////
Waiting for Tx to be Accepted on Starknet - Transfer...
////////////////////////////////////////////////////////////////////////////////`
);
await provider.waitForTransaction(transferTxHash);
// Check balance after transfer - should be 480
console.log(`Calling StarkNet for account balance...`);
const balanceAfterTransfer = await erc20.balance_of(account.address);
console.log(
`account Address ${account.address} has a balance of:`,
number.toBN(balanceAfterTransfer.res, 16).toString()
);
In the last section of the code, we check the balance of our account. As you can see from the output, our transactions were successful, and we hold a balance of our tokens.
Voila! 🥳 We have minted our first ERC-20 tokens on StarkNet with Infura!
To follow up, you can find the entire code for this project on a GitHub repository here.
Conclusion
In this article, you learned about the StarkNet layer two ZK-rollup, how to access it with Infura RPC endpoints, and how to create and deploy your own ERC-20 tokens on the network. By providing these endpoints, Infura helps bridge the gap for Ethereum developers and facilitates a composable Web3 ecosystem.
To learn more about StarkNet and how to build on it using Infura, visit the following resources: