Let's start with some examples from Solidity's introduction.
This basic Solidity contract sets the value of a variable and then exposes it to others to access:
contract SimpleStorage {
uint storedData;
function set(uint x) public {
storedData = x;
}
function get() public view returns (uint) {
return storedData;
}
}Here is how this logic translates to Rubidity:
class Contracts::SimpleStorage < Contract
uint256 :storedData
function :set, { x: :uint256 }, :public do
s.storedData = x
end
function :get, {}, :public, :view, returns: :uint256 do
return s.storedData
end
constructor() {}
endLooking point by point, here's what the Solidity version does and how Rubidity translates it.
-
Solidity:
contract SimpleStorage { ... } -
Rubidity:
class Contracts::SimpleStorage < ContractIn Solidity, you define a contract using the
contractkeyword. In Rubidity, you define a contract as a Ruby class that inherits from a base class calledContract.
State Variable
-
Solidity:
uint storedData; -
Rubidity:
uint256 :storedDataSolidity uses the
uintkeyword to define a state variable. Rubidity uses a Ruby symbol to define a state variable along with its type, in this case,uint256.
Solidity:\
function set(uint x) public {
storedData = x;
}Rubidity:\
function :set, { x: :uint256 }, :public do
s.storedData = x
endThe set function is public in both languages and takes an unsigned integer as an argument. In Rubidity, the function signature also specifies its visibility (:public) and arguments ({ x: :uint256 }).
Solidity:
function get() public view returns (uint) {
return storedData;
}Rubidity:
function :get, {}, :public, :view, returns: :uint256 do
return s.storedData
endThe get function is public and has a view property in both languages. The Solidity version uses the returns keyword to specify the return type, while Rubidity does it via the returns: :uint256 option.
Constructor
-
Solidity: No explicit constructor.
-
Rubidity:
constructor() {}Both Solidity and Rubidity examples don't utilize a constructor for any initial setup, but the Rubidity code explicitly includes an empty constructor for clarity.
In Rubidity, state variables are accessed using the s. prefix, as seen in s.storedData = x and return s.storedData. Writing to state using an unprefixed variable is not possible in Ruby and the explicitness of s. is nice anyway.
Let's break down this OpenMintToken Rubidity contract line-by-line, focusing on how it might translate to a Solidity contract. The contract is an ERC20 token with a capped supply and additional limitations on individual mints.
class Contracts::OpenMintToken < Contract
is :ERC20
uint256 :public, :maxSupply
uint256 :public, :perMintLimit
constructor(
name: :string,
symbol: :string,
maxSupply: :uint256,
perMintLimit: :uint256,
decimals: :uint256
) {
ERC20(name: name, symbol: symbol, decimals: decimals)
s.maxSupply = maxSupply
s.perMintLimit = perMintLimit
}
function :mint, { amount: :uint256 }, :public do
require(amount > 0, 'Amount must be positive')
require(amount <= s.perMintLimit, 'Exceeded mint limit')
require(s.totalSupply + amount <= s.maxSupply, 'Exceeded max supply')
_mint(to: msg.sender, amount: amount)
end
function :airdrop, { to: :addressOrDumbContract, amount: :uint256 }, :public do
require(amount > 0, 'Amount must be positive')
require(amount <= s.perMintLimit, 'Exceeded mint limit')
require(s.totalSupply + amount <= s.maxSupply, 'Exceeded max supply')
_mint(to: to, amount: amount)
end
end-
Inheritance:
is :ERC20- This line means that the
OpenMintTokencontract inherits from an existing ERC20 contract, inheriting its state variables, functions, and logic.
- This line means that the
-
State Variables:
uint256 :public, :maxSupplyanduint256 :public, :perMintLimit- These lines define two public state variables,
maxSupplyandperMintLimit. Public state variables are accessible to external contracts and can also have getter methods generated automatically.
- These lines define two public state variables,
-
Constructor:
ruby constructor( name: :string, symbol: :string, maxSupply: :uint256, perMintLimit: :uint256, decimals: :uint256 )
- The constructor function initializes the contract. It takes the token's name, symbol, maximum supply, per-mint limit, and decimals as parameters.
-
State Variable Initialization:
s.maxSupply = maxSupplyands.perMintLimit = perMintLimit- These lines initialize the state variables using the
s.prefix, which is specific to Rubidity for accessing and manipulating state variables.
- These lines initialize the state variables using the
-
Mint Function:
function :mint, { amount: :uint256 }, :public do require(amount > 0, 'Amount must be positive') require(amount <= s.perMintLimit, 'Exceeded mint limit') require(s.totalSupply + amount <= s.maxSupply, 'Exceeded max supply') _mint(to: msg.sender, amount: amount) end
- This is a public function to mint new tokens. The
requirestatements serve as checks that theamountis positive, within the per-mint limit, and won't exceed the max supply._mintis a likely internal function inherited from the ERC20 contract that actually mints the tokens.
- This is a public function to mint new tokens. The
-
Airdrop Function:
function :airdrop, { to: :addressOrDumbContract, amount: :uint256 }, :public do require(amount > 0, 'Amount must be positive') require(amount <= s.perMintLimit, 'Exceeded mint limit') require(s.totalSupply + amount <= s.maxSupply, 'Exceeded max supply') _mint(to: to, amount: amount) end
- Similar to the
mintfunction but allows specifying a recipient addressto. It performs the samerequirechecks and then invokes_mintto create the tokens for the target address.
- Similar to the
The Rubidity code uses specific language constructs that mirror Solidity but in a Ruby-like syntax, making it more expressive while maintaining similar logic and functionalities.