Skip to content

Instantly share code, notes, and snippets.

@brunodb3
Last active November 2, 2021 11:48
Show Gist options
  • Save brunodb3/090a6f3847237959b453b1b9395085f2 to your computer and use it in GitHub Desktop.
Save brunodb3/090a6f3847237959b453b1b9395085f2 to your computer and use it in GitHub Desktop.
Rell Example
// notes:
// - I like the "query" notation of `@` and `@*`. It's short and simple, and makes so much sense. `user at name equals 'bruno'` is nice
// - on an actual project, could be difficult to differentiate `val` and `var` in a glance. I wonder if the linting is smart enough to see that `val` cannot be reassigned (I assume yes)
// - I like that Rell doesn't need type declarations, but that it offers it. makes code better to follow and understand for devs
// - I don't like that the web UI doesn't have autocomplete or type-check. It's nice when errors are found while I'm typing
//
entity user {
key pubkey;
key username: text;
}
entity channel {
key name;
admin: user;
}
entity channel_member {
key channel, member: user;
}
entity message {
key channel, timestamp; // only one message at a given timestamp to one channel, as `key` is unique
index posted_by: user;
text;
}
entity balance {
key user;
mutable amount: integer;
}
// ----------
operation init (founder_pubkey: pubkey) {
require( (user@*{} limit 1).size() == 0 );
// nice that Rell assigns values to attributes based on the type (if you have only one attribute of the same type)
// so you don't need to do `create user (pubkey = founder_pubkey, username = 'admin');`
val founder = create user (founder_pubkey, 'admin');
create balance (founder, 1000000);
}
operation register_user (
existing_user_pubkey: pubkey,
new_user_pubkey: pubkey,
new_user_username: text,
transfer_amount: integer
) {
require ( is_signer(existing_user_pubkey) );
val existing_user = user@{existing_user_pubkey};
require ( transfer_amount > 0);
val new_user = create user (new_user_pubkey, new_user_username);
pay_fee(existing_user, 100);
create balance(new_user, 0);
transfer_balance(existing_user, new_user, transfer_amount);
// I like that if anything fails, it just rolls back and transaction is rejected. no need to check for balance of existing_user :)
}
operation create_channel (admin_pubkey: pubkey, name) {
require ( is_signer(admin_pubkey) );
val admin_user = user@{admin_pubkey};
pay_fee(admin_user, 100);
val channel = create channel(admin_user, name);
create channel_member(channel, admin_user);
}
operation add_channel_member (admin_pubkey: pubkey, channel_name: name, member_username: text) {
require( is_signer(admin_pubkey) );
val admin_user = user@{admin_pubkey};
pay_fee(admin_user, 1);
val channel = channel@{channel_name, .admin==admin_user}; // here the docs say `.admin==user@{admin_pubkey}`. Wouldn't that trigger another "query"?
create channel_member(channel, member=user@{.username == member_username});
}
operation post_message (channel_name: name, pubkey, message: text) {
require ( is_signer(pubkey) );
val channel = channel@{channel_name};
val member = user@{pubkey};
require ( channel_member@?{channel, member} );
pay_fee(member, 1);
create message (channel, member, text=message, op_context.last_block_time); // op_context.last_block_time for timestamp. Could be better explained in the docs
}
// ---------
function transfer_balance (from: user, to: user, amount: integer) {
require( balance@{from}.amount >= amount );
update balance@{from} (amount -= amount);
update balance@{to} (amount += amount);
}
function pay_fee(user, deduct_amount: integer) {
if(user.username != 'admin') {
transfer_balance(user, user@{.username == 'admin'}, deduct_amount);
}
}
// ---------
query get_channels(pubkey): list<(name: text, admin: text)> {
return channel_member@*{.member == user@{pubkey} }
(name = .channel.name, admin = .channel.admin.username);
}
query get_balance(pubkey) {
return balance@{ user@{pubkey} }.amount;
}
query get_last_messages(channel_name: name): list<(text: text, poster: text, timestamp: timestamp)> {
return message@*{ channel@{channel_name} }
(.text, poster=.posted_by.username, @sort .timestamp); // `sort` seems to be deprecated, running the node asks to use @sort instead
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment