Learning RALPH a smart contract language for Alephium
A Journey through RALPH
RALPH is the smart contract programming language for the blockchain Alephium. Alephium while not describing it’s Luke Sykwalker origin story is a layer one (POW) ~ Proof of Work Blockchain. You can think of Alephium like ETH in it’s early days when it was POW… although there is much that seperates Alephium from Ethereum. While both programmable Alephium follows the UTXO model similar to Bitcoin. So you can think of Alephium like Bitcoin with smart contracts and a 1/10 the energy consumption due to the algorithm Alephium uses. Alephium is also the first to pioneer the concept (POLW) Proof of Less Work…
Let’s talk about RALPH now.
Simple Ralph Counter Contract
I will choose rust here for the syntax and styling but Ralph is not really Rust. Rust is hard and Ralph… well Ralph is easy.
Contract Counter (
mut count: U256
) {
// more on this later
// ...
pub fn increment() -> () {
count = count + 1
}
}
So this is it. Mic Drop. 🎤
A really simple smart counter contract.
- the code is readable
- the code is compact
- the code is sec.. wait were missing one of Ralph’s best feature(s)
Permission Flags
Let’s take a look at the counter contract with these.
Contract Counter (
mut count: U256
) {
// added!
@using(checkExternalCaller = false, updateFields = true)
pub fn increment() -> () {
// we now have given permission to update the count
count = count + 1
}
}
Here are the permission flags:
- checkExternalCaller
- updateFields
- preapprovedAssets
- assetsInContract
- payToContractOnly
You can think of these as giving the function certain permissions over user assets, contract assets or contract variables.
For instance payToContractOnly would allow you to only send assets to the contract and not from the contract for that function.
PreapprovedAssets
Contract Foo (
mut alph: U256
) {
// added!
@using(checkExternalCaller = false, updateFields = true, preapprovedAssets = true)
pub fn sendALPHToContract(amount: U256) -> () {
// fetch caller
let caller = callerAddress!()
// send alph to contract
// from , to , token , amount
transferToken!(caller, selfAddress!(), ALPH, amount)
}
}
So, we set preapprovedAssets = true
and we sent ALPH transferToken!(caller, selfAddress!(), ALPH, amount)
for sending the Alephium token we can use the build in variable ALPH which correlates to the Alephium token’s id.
So we sent Alephium but how would we request the usage of ALPH for say a mapping in Alephium.
Mappings and Deposits
Mappings in Alephium require a base storage fee of 0.1 alph for each mapping. Therefore the following would require 1 alph since we performed 10 id.insert!()
; if we wanted our ALPH back all we have to do is call id.remove!()
and provide the address to return the deposit too and the key value we would like to remove.
Contract Foo () {
// mapping should be at top of your contract
mapping [U256; U256] ids // key , value
@using(checkExternalCaller = false, preapprovedAssets = true)
pub fn map10() -> () {
let caller = callerAddress!()
// creates 10 mappings
for (let mut i = 0; i < 10; i = i + 1)
{
ids.insert!(caller, i, i)
}
}
// removing mappings
@using(checkExternalCaller = false, preapprovedAssets = false, assetsInContract = true)
pub fn remove10() -> () {
let caller = callerAddress!()
// removes 10 mappings
for (let mut i = 0; i < 10; i = i + 1)
{
ids.remove!(caller, i, i)
}
}
}
Notice the section function uses some different permission settings. We are not creating mappings so there is no need to set preapprovedAssets
to being true, we are however withdrawing assets (mapping deposits being reclaimed) so assetsInContract = true
.
Alephium creates the need for a deposit so you are incentivized to come and reclaim your deposit as a reward. This helps maintain a more thoughtful chain state.
Events
Just like EVM.. RALPH also has events however contracts should follow the following template
Contract Foo() {
// mappings
mapping [Address; Bool] interacted
// events
event SaidHi(user: Address)
// enums
enum ErrorCodes {
1 = Invalid
2 = Confirmed
3 = Thermal Exhaust Port
}
// functions
@using(checkExternalCaller = false, preapprovedAssets = true)
pub fn bar() -> () {
let caller = callerAddress!()
assert!(interacted.contains!(caller) == false, ErrorCodes.Invalid)
interacted.insert!(caller, caller, true)
emit SaidHi(caller)
}
}
This is the general structure all RALPH contracts should follow for a successful compile.
Next Article
We will look into how RALPH can be used across contracts and how to make subContracts and tokens on the Alephium blockchain.
Ending Note
To readers who know me well, I like to think I represent every type of builder. The little guy, the hobbyist, and many more like minded individuals who wanna have fun and build something on chain. I love cypherpunk energy and good vibes.
Some people want vc table round meetings, audits and a suit job where they hop on discord. I want to chill and build awesome things with people and see what we can do.
I hope people follow who they believe in, take part in projects you like, review the code either with AI or with your own ability and form genuine discussion because this discussion and flow of knowledge is greater than any such audit that a service would provide you.
Open Source
Sincerely,
Benjamin