{"id":13651173,"url":"https://github.com/s6ruby/redpaper","last_synced_at":"2025-04-22T22:30:35.669Z","repository":{"id":97968481,"uuid":"169785060","full_name":"s6ruby/redpaper","owner":"s6ruby","description":"Red Paper ++ sruby - Small, Smart, Secure, Safe, Solid \u0026 Sound (S6) Ruby - The Ruby Programming Language for Contract / Transaction Scripts on the Blockchain World Computer - Yes, It's Just Ruby","archived":false,"fork":false,"pushed_at":"2023-10-02T18:52:48.000Z","size":44,"stargazers_count":23,"open_issues_count":0,"forks_count":0,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-11-10T02:34:04.355Z","etag":null,"topics":["blockchain","contract","contracts","rubidity","solidity","sruby","universum","vyper","yul"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"cc0-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/s6ruby.png","metadata":{"files":{"readme":"README.V0.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2019-02-08T19:06:20.000Z","updated_at":"2024-09-10T13:40:28.000Z","dependencies_parsed_at":"2024-01-14T14:29:26.839Z","dependency_job_id":"658abb1c-7d1e-48a8-9c48-958fcf484fcc","html_url":"https://github.com/s6ruby/redpaper","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/s6ruby%2Fredpaper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/s6ruby%2Fredpaper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/s6ruby%2Fredpaper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/s6ruby%2Fredpaper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/s6ruby","download_url":"https://codeload.github.com/s6ruby/redpaper/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250333862,"owners_count":21413470,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["blockchain","contract","contracts","rubidity","solidity","sruby","universum","vyper","yul"],"created_at":"2024-08-02T02:00:46.007Z","updated_at":"2025-04-22T22:30:35.381Z","avatar_url":"https://github.com/s6ruby.png","language":null,"funding_links":[],"categories":["Alternative Contract-Oriented Programming Languages"],"sub_categories":["Higher-Level"],"readme":"_The Ruby Programming Language for Contract / Transaction Scripts on the Blockchain World Computer - Yes, It's Just Ruby_\n\n# sruby - Small, Smart, Secure, Safe, Solid \u0026 Sound (S6) Ruby\n\nsruby is a subset of mruby [1] that is a subset of \"classic\" ruby [2].\n\n\n## What's missing and why?\n\n\nLess is more. The golden rule of secure code is keep it simple, stupid.\n\n- NO inheritance\n- NO recursion\n- NO re-entrance - auto-magic protection on function calls\n- NO floating point numbers or arithmetic\n- NO overflow \u0026 underflow in numbers - auto-magic \"safe-math\" protection\n- NO null (`nil`) - all variables, structs and hash mappings have default (zero) values \n- and much much more\n\n\n\n## What's the upside?\n\nYou can cross-compile (transpile) contract scripts (*) to:\n\n- Solidity [3] - JavaScript-like contract scripts\n- Vyper [4] - Python-like contract scripts\n- Yul [5] - EVM (Ethereum Virtual Machine) Assembly-like intermediate contract scripts \n- Liquidity [6] - OCaml-like (or ReasonML-like) contract scripts\n- and much much more\n\n\n(*) in the future.\n\n\n## Yes, yes, yes - It's just \"plain-vanilla\" ruby\n\nRemember - the code is and always will be just \"plain-vanilla\" ruby\nthat runs with \"classic\" ruby or mruby \"out-of-the-box\".\n\n\n\n## Contract Samples\n\n\n**Hello, World! - Greeter**\n\n``` ruby\n############################\n# Greeter Contract \n\ndef setup( greeting )\n  @owner    = msg.sender\n  @greeting = greeting\nend\n\ndef greet\n  @greeting\nend\n\ndef kill\n  selfdestruct( msg.sender )  if msg.sender == @owner\nend\n```\n\n\n**Mint Your Own Money - Minimal Viable Token**\n\n``` ruby\n#######################\n# Token Contract\n\ndef setup( initial_supply )\n  @balance_of = Mapping.of( Address =\u003e Money ).new   # or Mapping‹Address→Money›.new \n  @balance_of[ msg.sender] = initial_supply\nend\n\ndef transfer( to, value )\n  assert @balance_of[ msg.sender ] \u003e= value\n  assert @balance_of[ to ] + value \u003e= @balance_of[ to ]\n\n  @balance_of[ msg.sender ] -= value\n  @balance_of[ to ]         += value\n\n  true\nend\n```\n\n\n**Win x65 000 - Roll the (Satoshi) Dice**\n\n``` ruby\n################################\n# Satoshi Dice Contract\n\nstruct :Bet,\n         user:   Address(0), \n         block:  0, \n         cap:    0, \n         amount: 0\n\n## Fee (Casino House Edge) is 1.9%, that is, 19 / 1000\nFEE_NUMERATOR   = 19\nFEE_DENOMINATOR = 1000\n\nMAXIMUM_CAP = 2**16   # 65_536 = 2^16 = 2 byte/16 bit\nMAXIMUM_BET = 100_000_000\nMINIMUM_BET = 100\n\nevent :BetPlaced, :id, :user, :cap, :amount\nevent :Roll,      :id, :rolled\n\ndef setup\n  @owner   = msg.sender\n  @counter = 0\n  @bets    = Mapping.of( Integer =\u003e Bet ).new   # or Mapping‹Integer→Bet›.new\nend\n\ndef bet( cap )\n  assert cap \u003e= 1 \u0026\u0026 cap \u003c= MAXIMUM_CAP\n  assert msg.value \u003e= MINIMUM_BET \u0026\u0026 msg.value \u003c= MAXIMUM_BET\n\n  @counter += 1\n  @bets[@counter] = Bet.new( msg.sender, block.number+3, cap, msg.value )\n  log BetPlaced.new( @counter, msg.sender, cap, msg.value )\nend\n\ndef roll( id )\n  bet = @bets[id]\n\n  assert msg.sender == bet.user\n  assert block.number \u003e= bet.block\n  assert block.number \u003c= bet.block + 255\n\n  ## \"provable\" fair - random number depends on\n  ##  - blockhash (of block in the future - t+3)\n  ##  - nonce (that is, bet counter id)\n  hex = sha256( \"#{blockhash( bet.block )} #{id}\" )\n  ## get first 2 bytes (4 chars in hex string) and convert to integer number\n  ##   results in a number between 0 and 65_535\n  rolled = hex_to_i( hex[0,4] )\n\n  if rolled \u003c bet.cap\n     payout = bet.amount * MAXIMUM_CAP / bet.cap\n     fee = payout * FEE_NUMERATOR / FEE_DENOMINATOR\n     payout -= fee\n\n     msg.sender.transfer( payout )\n  end\n\n  log Roll.new( id, rolled )\n  @bets.delete( id )\nend\n\ndef fund\nend\n\ndef kill\n  assert msg.sender == @owner\n  selfdestruct( @owner )\nend\n```\n\n\n**Kick Start Your Project with a Crowd Funder**\n\n``` ruby\n##############################\n# Crowd Funder Contract\n\nenum :State, :fundraising, :expired_refund, :successful\n\nstruct :Contribution,\n         amount:      0, \n         contributor: Address(0)\n\nevent :FundingReceived, :address, :amount, :current_total\nevent :WinnerPaid,      :winner_address\n\n\ndef setup(\n      time_in_hours_for_fundraising,\n      campaign_url,\n      fund_recipient,\n      minimum_to_raise )\n\n  @creator          = msg.sender\n  @fund_recipient   = fund_recipient   # note: creator may be different than recipient\n  @campaign_url     = campaign_url\n  @minimum_to_raise = minimum_to_raise # required to tip, else everyone gets refund\n  @raise_by         = block.timestamp + (time_in_hours_for_fundraising * 1.hour )\n\n  @state            = State.fundraising\n  @total_raised     = 0\n  @complete_at      = 0\n  @contributions    = Array.of( Contribution ).new  # or Array‹Contribution›.new or Contribution[].new\nend\n\n\n\ndef pay_out\n  assert @state.successful?\n\n  @fund_recipient.transfer( this.balance )\n  log WinnerPaid.new( @fund_recipient )\nend\n\ndef check_if_funding_complete_or_expired\n  if @total_raised \u003e @minimum_to_raise\n    @state = State.successful\n    pay_out()\n  elsif block.timestamp \u003e @raise_by\n    # note: backers can now collect refunds by calling refund(id)\n    @state = State.expired_refund\n    @complete_at = block.timestamp\n  end\nend\n\n\ndef contribute\n  assert @state.fundraising?\n\n  @contributions.push( Contribution.new( msg.value, msg.sender ))\n  @total_raised += msg.value\n\n  log FundingReceived.new( msg.sender, msg.value, @total_raised )\n\n  check_if_funding_complete_or_expired()\n\n  @contributions.size - 1   # return (contribution) id\nend\n\n\ndef refund( id )\n  assert @state.expired_refund?\n  assert @contributions.size \u003e id \u0026\u0026 id \u003e= 0 \u0026\u0026 @contributions[id].amount != 0\n\n  amount_to_refund = @contributions[id].amount\n  @contributions[id].amount = 0\n\n  @contributions[id].contributor.transfer( amount_to_refund )\n\n  true\nend\n\ndef kill\n  assert msg.sender == @creator\n  # wait 24 weeks after final contract state before allowing contract destruction\n  assert (@state.expired_refund? || @state.successful?) \u0026\u0026 @complete_at + 24.weeks \u003c block.timestamp\n\n  # note: creator gets all money that hasn't be claimed\n  selfdestruct( msg.sender )\nend\n```\n\n\n**Liquid / Delegative Democracy - Let's Vote (or Delegate Your Vote) on a Proposal**\n\n``` ruby\n#########################\n# Ballot Contract\n\nstruct :Voter,\n          weight:   0,\n          voted:    false,\n          vote:     0,\n          delegate: Address(0)\n\nstruct :Proposal, \n          vote_count: 0\n\n## Create a new ballot with $(num_proposals) different proposals.\ndef setup( num_proposals )\n  @chairperson = msg.sender\n  @voters      = Mapping.of( Address =\u003e Voter ).new       # or Mapping‹Address→Voter›.new\n  @proposals   = Array.of( Proposal, num_proposals ).new  # or Proposal[ num_proposals ].new\n\n  @voters[@chairperson].weight = 1\nend\n\n## Give $(to_voter) the right to vote on this ballot.\n## May only be called by $(chairperson).\ndef give_right_to_vote( to_voter ) \n   assert msg.sender == @chairperson \u0026\u0026 @voters[to_voter].voted? == false\n   @voters[to_voter].weight = 1\nend\n\n## Delegate your vote to the voter $(to).\ndef delegate( to )\n  sender = @voters[msg.sender]  # assigns reference\n  assert sender.voted? == false\n\n  while @voters[to].delegate != Address(0) \u0026\u0026 @voters[to].delegate != msg.sender do\n    to = @voters[to].delegate\n  end\n  assert to != msg.sender\n\n  sender.voted    = true\n  sender.delegate = to\n  delegate_to = @voters[to]\n  if delegate_to.voted\n    @proposals[delegate_to.vote].vote_count += sender.weight\n  else\n    delegate_to.weight += sender.weight\n  end\nend\n\n## Give a single vote to proposal $(to_proposal).\ndef vote( to_proposal )\n  sender = @voters[msg.sender]\n  assert sender.voted? == false \u0026\u0026 to_proposal \u003c @proposals.length\n  sender.voted = true\n  sender.vote  = to_proposal\n  @proposals[to_proposal].vote_count += sender.weight\nend\n\ndef winning_proposal\n  winning_vote_count = 0 \n  winning_proposal   = 0\n  @proposals.each_with_index do |proposal,i|\n    if proposal.vote_count \u003e winning_vote_count\n      winning_vote_count = proposal.vote_count\n      winning_proposal   = i\n    end\n  end\n  winning_proposal\nend\n```\n\n\n\n## Types\n\n[Value Types](#value-types) •\n[Reference Types](#reference-types)\n\n\n\n### Value Types\n\nBool •\nInteger (Money • Timestamp • Timedelta • Enum) •\nAddress •\nString •\nByte Array / Bytes\n\n\n#### Bool\n\nClass: `Bool`\n\nValues: true | false\n\nZero: false\n\n``` ruby\nBool.zero   #=\u003e false\n```\n\n#### Integer\n\nClass: `Integer`\n\nZero: 0\n\n``` ruby\nInteger.zero   #=\u003e false\n```\n\n**Integer Types**\n\nMoney • Timestamp • Timedelta • Enum\n\n#### Money (Integer)\n\nMoney Units\n\n#### Timestamp (Integer)\n\nTime Units\n\n``` ruby\nTimestamp.now   #=\u003e 1551122309\n```\n\n#### Timedelta (Integer)\n\n\n#### Enum (Integer)\n\n\n\n\n#### Address\n\nClass: `Address`\n\nZero: `0x0000` or `0x0` or `Address(0)`\n\n\nExample:\n\n``` ruby\nowner = '0x0000'  # or\nowner = Address(0)\n```\n\n\n#### String\n\n#### Byte Array / Bytes\n\n\n\n\n\n### Reference Types\n\n[Array](#array)  •\n[Struct](#struct)  •\n[Mapping](#mapping)\n\n\n\n#### Array\n\nClass Builder: `Safe::SafeArray`\n\nExample:\n\n``` ruby\nArray.of()\n```\n\n#### Struct\n\nClass Builder: `Safe::SafeStruct` or `Safe::Struct`\n\n#### Mapping\n\nClass Builder: `Safe::SafeHash`\n\n\n\n\n## Event Logging\n\n...\n\n\n## Storage\n\nThe contract's state gets stored in contract storage.\n\n...\n\n\n\n\n\n\n## Request for Comments (RFC)\n\nSend your questions and comments to the ruby-talk mailing list. Thanks!\n\n\n\n## References\n\n1. mruby programming language, see \u003chttps://mruby.org\u003e\n2. ruby programming language, see \u003chttps://www.ruby-lang.org\u003e \n3. solidity programming language, see \u003chttps://solidity.readthedocs.io\u003e\n4. vyper programming language, see \u003chttps://vyper.readthedocs.io\u003e\n5. yul intermediate language for the EVM (ethereum virtual machine) assembly code, see the Yul section in the solidity programming language reference\n6. liquidity programing language, see \u003chttp://www.liquidity-lang.org\u003e\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fs6ruby%2Fredpaper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fs6ruby%2Fredpaper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fs6ruby%2Fredpaper/lists"}