Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/s6ruby/redpaper
Red Paper ++ sruby - Small, Smart, Secure, Safe, Solid & Sound (S6) Ruby - The Ruby Programming Language for Contract / Transaction Scripts on the Blockchain World Computer - Yes, It's Just Ruby
https://github.com/s6ruby/redpaper
blockchain contract contracts rubidity solidity sruby universum vyper yul
Last synced: 3 days ago
JSON representation
Red Paper ++ sruby - Small, Smart, Secure, Safe, Solid & Sound (S6) Ruby - The Ruby Programming Language for Contract / Transaction Scripts on the Blockchain World Computer - Yes, It's Just Ruby
- Host: GitHub
- URL: https://github.com/s6ruby/redpaper
- Owner: s6ruby
- License: cc0-1.0
- Created: 2019-02-08T19:06:20.000Z (almost 6 years ago)
- Default Branch: master
- Last Pushed: 2023-10-02T18:52:48.000Z (about 1 year ago)
- Last Synced: 2024-08-03T02:05:34.494Z (3 months ago)
- Topics: blockchain, contract, contracts, rubidity, solidity, sruby, universum, vyper, yul
- Homepage:
- Size: 43 KB
- Stars: 22
- Watchers: 5
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.V0.md
- License: LICENSE.md
Awesome Lists containing this project
README
_The Ruby Programming Language for Contract / Transaction Scripts on the Blockchain World Computer - Yes, It's Just Ruby_
# sruby - Small, Smart, Secure, Safe, Solid & Sound (S6) Ruby
sruby is a subset of mruby [1] that is a subset of "classic" ruby [2].
## What's missing and why?
Less is more. The golden rule of secure code is keep it simple, stupid.
- NO inheritance
- NO recursion
- NO re-entrance - auto-magic protection on function calls
- NO floating point numbers or arithmetic
- NO overflow & underflow in numbers - auto-magic "safe-math" protection
- NO null (`nil`) - all variables, structs and hash mappings have default (zero) values
- and much much more## What's the upside?
You can cross-compile (transpile) contract scripts (*) to:
- Solidity [3] - JavaScript-like contract scripts
- Vyper [4] - Python-like contract scripts
- Yul [5] - EVM (Ethereum Virtual Machine) Assembly-like intermediate contract scripts
- Liquidity [6] - OCaml-like (or ReasonML-like) contract scripts
- and much much more(*) in the future.
## Yes, yes, yes - It's just "plain-vanilla" ruby
Remember - the code is and always will be just "plain-vanilla" ruby
that runs with "classic" ruby or mruby "out-of-the-box".## Contract Samples
**Hello, World! - Greeter**
``` ruby
############################
# Greeter Contractdef setup( greeting )
@owner = msg.sender
@greeting = greeting
enddef greet
@greeting
enddef kill
selfdestruct( msg.sender ) if msg.sender == @owner
end
```**Mint Your Own Money - Minimal Viable Token**
``` ruby
#######################
# Token Contractdef setup( initial_supply )
@balance_of = Mapping.of( Address => Money ).new # or Mapping‹Address→Money›.new
@balance_of[ msg.sender] = initial_supply
enddef transfer( to, value )
assert @balance_of[ msg.sender ] >= value
assert @balance_of[ to ] + value >= @balance_of[ to ]@balance_of[ msg.sender ] -= value
@balance_of[ to ] += valuetrue
end
```**Win x65 000 - Roll the (Satoshi) Dice**
``` ruby
################################
# Satoshi Dice Contractstruct :Bet,
user: Address(0),
block: 0,
cap: 0,
amount: 0## Fee (Casino House Edge) is 1.9%, that is, 19 / 1000
FEE_NUMERATOR = 19
FEE_DENOMINATOR = 1000MAXIMUM_CAP = 2**16 # 65_536 = 2^16 = 2 byte/16 bit
MAXIMUM_BET = 100_000_000
MINIMUM_BET = 100event :BetPlaced, :id, :user, :cap, :amount
event :Roll, :id, :rolleddef setup
@owner = msg.sender
@counter = 0
@bets = Mapping.of( Integer => Bet ).new # or Mapping‹Integer→Bet›.new
enddef bet( cap )
assert cap >= 1 && cap <= MAXIMUM_CAP
assert msg.value >= MINIMUM_BET && msg.value <= MAXIMUM_BET@counter += 1
@bets[@counter] = Bet.new( msg.sender, block.number+3, cap, msg.value )
log BetPlaced.new( @counter, msg.sender, cap, msg.value )
enddef roll( id )
bet = @bets[id]assert msg.sender == bet.user
assert block.number >= bet.block
assert block.number <= bet.block + 255## "provable" fair - random number depends on
## - blockhash (of block in the future - t+3)
## - nonce (that is, bet counter id)
hex = sha256( "#{blockhash( bet.block )} #{id}" )
## get first 2 bytes (4 chars in hex string) and convert to integer number
## results in a number between 0 and 65_535
rolled = hex_to_i( hex[0,4] )if rolled < bet.cap
payout = bet.amount * MAXIMUM_CAP / bet.cap
fee = payout * FEE_NUMERATOR / FEE_DENOMINATOR
payout -= feemsg.sender.transfer( payout )
endlog Roll.new( id, rolled )
@bets.delete( id )
enddef fund
enddef kill
assert msg.sender == @owner
selfdestruct( @owner )
end
```**Kick Start Your Project with a Crowd Funder**
``` ruby
##############################
# Crowd Funder Contractenum :State, :fundraising, :expired_refund, :successful
struct :Contribution,
amount: 0,
contributor: Address(0)event :FundingReceived, :address, :amount, :current_total
event :WinnerPaid, :winner_addressdef setup(
time_in_hours_for_fundraising,
campaign_url,
fund_recipient,
minimum_to_raise )@creator = msg.sender
@fund_recipient = fund_recipient # note: creator may be different than recipient
@campaign_url = campaign_url
@minimum_to_raise = minimum_to_raise # required to tip, else everyone gets refund
@raise_by = block.timestamp + (time_in_hours_for_fundraising * 1.hour )@state = State.fundraising
@total_raised = 0
@complete_at = 0
@contributions = Array.of( Contribution ).new # or Array‹Contribution›.new or Contribution[].new
enddef pay_out
assert @state.successful?@fund_recipient.transfer( this.balance )
log WinnerPaid.new( @fund_recipient )
enddef check_if_funding_complete_or_expired
if @total_raised > @minimum_to_raise
@state = State.successful
pay_out()
elsif block.timestamp > @raise_by
# note: backers can now collect refunds by calling refund(id)
@state = State.expired_refund
@complete_at = block.timestamp
end
enddef contribute
assert @state.fundraising?@contributions.push( Contribution.new( msg.value, msg.sender ))
@total_raised += msg.valuelog FundingReceived.new( msg.sender, msg.value, @total_raised )
check_if_funding_complete_or_expired()
@contributions.size - 1 # return (contribution) id
enddef refund( id )
assert @state.expired_refund?
assert @contributions.size > id && id >= 0 && @contributions[id].amount != 0amount_to_refund = @contributions[id].amount
@contributions[id].amount = 0@contributions[id].contributor.transfer( amount_to_refund )
true
enddef kill
assert msg.sender == @creator
# wait 24 weeks after final contract state before allowing contract destruction
assert (@state.expired_refund? || @state.successful?) && @complete_at + 24.weeks < block.timestamp# note: creator gets all money that hasn't be claimed
selfdestruct( msg.sender )
end
```**Liquid / Delegative Democracy - Let's Vote (or Delegate Your Vote) on a Proposal**
``` ruby
#########################
# Ballot Contractstruct :Voter,
weight: 0,
voted: false,
vote: 0,
delegate: Address(0)struct :Proposal,
vote_count: 0## Create a new ballot with $(num_proposals) different proposals.
def setup( num_proposals )
@chairperson = msg.sender
@voters = Mapping.of( Address => Voter ).new # or Mapping‹Address→Voter›.new
@proposals = Array.of( Proposal, num_proposals ).new # or Proposal[ num_proposals ].new@voters[@chairperson].weight = 1
end## Give $(to_voter) the right to vote on this ballot.
## May only be called by $(chairperson).
def give_right_to_vote( to_voter )
assert msg.sender == @chairperson && @voters[to_voter].voted? == false
@voters[to_voter].weight = 1
end## Delegate your vote to the voter $(to).
def delegate( to )
sender = @voters[msg.sender] # assigns reference
assert sender.voted? == falsewhile @voters[to].delegate != Address(0) && @voters[to].delegate != msg.sender do
to = @voters[to].delegate
end
assert to != msg.sendersender.voted = true
sender.delegate = to
delegate_to = @voters[to]
if delegate_to.voted
@proposals[delegate_to.vote].vote_count += sender.weight
else
delegate_to.weight += sender.weight
end
end## Give a single vote to proposal $(to_proposal).
def vote( to_proposal )
sender = @voters[msg.sender]
assert sender.voted? == false && to_proposal < @proposals.length
sender.voted = true
sender.vote = to_proposal
@proposals[to_proposal].vote_count += sender.weight
enddef winning_proposal
winning_vote_count = 0
winning_proposal = 0
@proposals.each_with_index do |proposal,i|
if proposal.vote_count > winning_vote_count
winning_vote_count = proposal.vote_count
winning_proposal = i
end
end
winning_proposal
end
```## Types
[Value Types](#value-types) •
[Reference Types](#reference-types)### Value Types
Bool •
Integer (Money • Timestamp • Timedelta • Enum) •
Address •
String •
Byte Array / Bytes#### Bool
Class: `Bool`
Values: true | false
Zero: false
``` ruby
Bool.zero #=> false
```#### Integer
Class: `Integer`
Zero: 0
``` ruby
Integer.zero #=> false
```**Integer Types**
Money • Timestamp • Timedelta • Enum
#### Money (Integer)
Money Units
#### Timestamp (Integer)
Time Units
``` ruby
Timestamp.now #=> 1551122309
```#### Timedelta (Integer)
#### Enum (Integer)
#### Address
Class: `Address`
Zero: `0x0000` or `0x0` or `Address(0)`
Example:
``` ruby
owner = '0x0000' # or
owner = Address(0)
```#### String
#### Byte Array / Bytes
### Reference Types
[Array](#array) •
[Struct](#struct) •
[Mapping](#mapping)#### Array
Class Builder: `Safe::SafeArray`
Example:
``` ruby
Array.of()
```#### Struct
Class Builder: `Safe::SafeStruct` or `Safe::Struct`
#### Mapping
Class Builder: `Safe::SafeHash`
## Event Logging
...
## Storage
The contract's state gets stored in contract storage.
...
## Request for Comments (RFC)
Send your questions and comments to the ruby-talk mailing list. Thanks!
## References
1. mruby programming language, see
2. ruby programming language, see
3. solidity programming language, see
4. vyper programming language, see
5. yul intermediate language for the EVM (ethereum virtual machine) assembly code, see the Yul section in the solidity programming language reference
6. liquidity programing language, see