Step 2: Start Solidity Contract
2.1 Create Your Project Directory
mkdir typescript-wasm-project
cd typescript-wasm-project
npm init -y
2.2 Install Dependencies
npm install --save-dev typescript ts-node hardhat hardhat-deploy ethers dotenv @nomicfoundation/hardhat-toolbox @typechain/ethers-v6 @typechain/hardhat @types/node
pnpm install
npx hardhat
# Follow the prompts to create a basic Hardhat project.
2.3 Configure TypeScript and Hardhat
2.3.1 Update Hardhat Configuration
hardhat.config.ts
import { HardhatUserConfig } from "hardhat/types";
import "hardhat-deploy";
import "@nomicfoundation/hardhat-toolbox";
import "./tasks/greeting"
require("dotenv").config();
const DEPLOYER_PRIVATE_KEY = process.env.DEPLOYER_PRIVATE_KEY || "";
const config: HardhatUserConfig = {
defaultNetwork: "dev",
networks: {
dev: {
url: "https://rpc.dev.gblend.xyz/",
accounts: [DEPLOYER_PRIVATE_KEY],
chainId : 20993,
},
},
solidity: {
version: "0.8.24",
settings: {
optimizer: {
enabled: true,
runs: 200,
},
},
},
namedAccounts: {
deployer: {
default: 0,
},
},
};
export default config;
2.3.2 Update package.json
package.json
{
"name": "blendedapp",
"version": "1.0.0",
"description": "Blended Hello, World",
"main": "index.js",
"scripts": {
"compile": "npx hardhat compile",
"deploy": "npx hardhat deploy"
}
,
"devDependencies": {
"@nomicfoundation/hardhat-ethers": "^3.0.0",
"@nomicfoundation/hardhat-toolbox": "^5.0.0",
"@nomicfoundation/hardhat-verify": "^2.0.0",
"@openzeppelin/contracts": "^5.0.2",
"@typechain/ethers-v6": "^0.5.0",
"@typechain/hardhat": "^9.0.0",
"@types/node": "^20.12.12",
"dotenv": "^16.4.5",
"hardhat": "^2.22.4",
"hardhat-deploy": "^0.12.4",
"ts-node": "^10.9.2",
"typescript": "^5.4.5"
},
"dependencies": {
"ethers": "^6.12.2",
"fs": "^0.0.1-security"
}
}
2.4 Set Up Environment Variables
- Create a
.envfile:
DEPLOYER_PRIVATE_KEY=your-private-key-here
- Replace
your-private-key-herewith your actual private key.
2.5 Write the Solidity Contracts
ℹ️ Note
In this section, we'll create two Solidity smart contracts:
IFluentGreetingGreetingWithWorldThe interface contract allows the Solidity contract to call the Rust function, demonstrating interoperability between Solidity and Rust within a single execution environment. The final contract
GreetingWithWorldprovides a composable solution that combines the outputs of both the Rust and Solidity contracts.
- Create a
contractsdirectory and add the following:
2.5.1 Define the Interface
contracts/IFluentGreeting.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
interface IFluentGreeting {
function greeting() external view returns (string memory);
}
Detailed Code Explanation
Interface Definition:
The IFluentGreeting interface declares a single function greeting() that is external and viewable, meaning it does not modify the state of the blockchain and returns a string. This function will be implemented by another contract and is used to interact with the Rust smart contract.
Interaction with Rust Code:
The greeting function defined in this interface matches the Rust function that returns a greeting message. The Solidity interface allows the Solidity contract to call the Rust smart contract's function.
2.5.2 Implement the Greeting Contract
contracts/GreetingWithWorld.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "./IFluentGreeting.sol";
contract GreetingWithWorld {
IFluentGreeting public fluentGreetingContract;
constructor(address _fluentGreetingContractAddress) {
fluentGreetingContract = IFluentGreeting(_fluentGreetingContractAddress);
}
function getGreeting() external view returns (string memory) {
string memory greeting = fluentGreetingContract.greeting();
return string(abi.encodePacked(greeting, " World"));
}
}
Detailed Code Explanation
Import Statement: Imports the IFluentGreeting interface defined earlier.
Contract Definition: Defines a contract GreetingWithWorld.
State Variable: Declares a state variable fluentGreetingContract of type IFluentGreeting. This variable will hold the address of the deployed Rust smart contract.
Constructor:
- Takes an address
_fluentGreetingContractAddressas a parameter. - Initializes the
fluentGreetingContractwith the provided address.
- Function
getGreeting:- Calls the
greetingfunction of thefluentGreetingContractto get the greeting message from the Rust contract. - Concatenates the greeting message with ", World" using
abi.encodePackedand returns the resulting string.
- Calls the
Interaction with Rust Code:
- The
GreetingWithWorldcontract interacts with the Rust smart contract by calling thegreetingfunction via theIFluentGreetinginterface. - When
getGreetingis called, it fetches the greeting message ("Hello") from the Rust contract, concatenates it with ", World", and returns the complete greeting ("Hello, World").
How Solidity and Rust Interact:
- Rust Smart Contract Deployment: The Rust smart contract is compiled to Wasm and deployed to the blockchain. It contains a function that returns the greeting "Hello".
- Solidity Interface (
IFluentGreeting): The Solidity interface declares agreetingfunction that matches the function in the Rust contract. - Solidity Implementation (
GreetingWithWorld):- The
GreetingWithWorldcontract uses theIFluentGreetinginterface to interact with the Rust contract. - It initializes with the address of the deployed Rust contract.
- It calls the
greetingfunction of the Rust contract to fetch the greeting message. - It concatenates the Rust greeting with ", World" and returns the result.
- The