Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(backend): remaining tickets for event #8

Merged
merged 3 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions .github/workflows/elixir.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,6 @@ jobs:
--health-timeout 5s
--health-retries 5

env:
MIX_ENV: test

steps:
- name: Checkout
Expand All @@ -78,6 +76,14 @@ jobs:
mix ecto.drop
mix ecto.create
mix ecto.migrate

- name: Create .env file
run: |
echo "PRIVATE_KEY=${{ secrets.PRIVATE_KEY }}" >> .env
echo "ADDRESS=${{ secrets.ADDRESS }}" >> .env
echo "PROVIDER_URL=${{ secrets.PROVIDER_URL }}" >> .env
echo "CONTRACT_ADDRESS=${{ secrets.CONTRACT_ADDRESS }}" >> .env
echo "CHAIN_ID=${{ secrets.CHAIN_ID }}" >> .env
- name: Run tests
env:
MIX_ENV: test
Expand Down
1 change: 1 addition & 0 deletions backend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ erl_crash.dump
# Ignore package tarball (built via "mix hex.build").
peach-*.tar

.env
9 changes: 9 additions & 0 deletions backend/config/runtime.exs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ if System.get_env("PHX_SERVER") do
config :peach, PeachWeb.Endpoint, server: true
end

env = Dotenv.load()

config :peach, Peach.Config,
private_key: env.values["PRIVATE_KEY"],
address: env.values["ADDRESS"],
provider_url: env.values["PROVIDER_URL"],
contract_address: env.values["CONTRACT_ADDRESS"],
chain_id: env.values["CHAIN_ID"]

if config_env() == :prod do
database_url =
System.get_env("DATABASE_URL") ||
Expand Down
87 changes: 87 additions & 0 deletions backend/lib/peach/calldata_builder.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
defmodule Peach.CalldataBuilder do
@moduledoc """
Converts an Event struct into a calldata list for the smart contract.
"""

import Bitwise

def build_calldata(event) do
# 1. Event ID as u64
event_id = to_u64(event.id)

# 2. Ticket Tiers
ticket_tiers_params = build_ticket_tiers_params(event.ticket_tiers)

# Combine all parts into the calldata list
["0x" <> Integer.to_string(event_id, 16)] ++
Enum.map(ticket_tiers_params, &("0x" <> Integer.to_string(&1, 16))) ++
[event.treasury]
end

defp to_u64(value) do
value
|> to_integer()
|> check_integer_size(64)
end

defp build_ticket_tiers_params(ticket_tiers) do
# Number of ticket tiers
len = length(ticket_tiers)

# Flattened list of ticket tier parameters
tiers_params =
ticket_tiers
|> Enum.flat_map(&ticket_tier_to_params/1)

[len] ++ tiers_params
end

defp ticket_tier_to_params(tier) do
# Convert price to u256 (two u128)
{price_low, price_high} = to_u256(tier.price)

# Convert max_supply to u32
max_supply = tier.max_supply |> to_integer() |> check_integer_size(32)

[price_low, price_high, max_supply]
end

defp to_u256(value) do
# Convert the value to a big integer
bigint = to_integer(value)

# Split into low and high 128 bits
price_low = bigint &&& (1 <<< 128) - 1
price_high = bigint >>> 128

{price_low, price_high}
end

defp check_integer_size(value, bits) do
max_value = (1 <<< bits) - 1

if value < 0 or value > max_value do
raise ArgumentError, "Value #{value} does not fit in #{bits} bits"

Check warning on line 64 in backend/lib/peach/calldata_builder.ex

View check run for this annotation

Codecov / codecov/patch

backend/lib/peach/calldata_builder.ex#L64

Added line #L64 was not covered by tests
else
value
end
end

defp to_integer(value) do
cond do
is_integer(value) ->
value

is_binary(value) ->
try do
String.to_integer(value)

Check warning on line 77 in backend/lib/peach/calldata_builder.ex

View check run for this annotation

Codecov / codecov/patch

backend/lib/peach/calldata_builder.ex#L75-L77

Added lines #L75 - L77 were not covered by tests
rescue
ArgumentError ->
reraise ArgumentError, "Cannot parse integer from string: #{value}"

Check warning on line 80 in backend/lib/peach/calldata_builder.ex

View check run for this annotation

Codecov / codecov/patch

backend/lib/peach/calldata_builder.ex#L80

Added line #L80 was not covered by tests
end

true ->
raise ArgumentError, "Cannot convert #{inspect(value)} to integer"

Check warning on line 84 in backend/lib/peach/calldata_builder.ex

View check run for this annotation

Codecov / codecov/patch

backend/lib/peach/calldata_builder.ex#L83-L84

Added lines #L83 - L84 were not covered by tests
end
end
end
27 changes: 27 additions & 0 deletions backend/lib/peach/config.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
defmodule Peach.Config do
@moduledoc """
Provides access to application configuration.
"""

@app :peach

def private_key do
Application.fetch_env!(@app, __MODULE__)[:private_key]
end

def address do
Application.fetch_env!(@app, __MODULE__)[:address]
end

def provider_url do
Application.fetch_env!(@app, __MODULE__)[:provider_url]
end

def contract_address do
Application.fetch_env!(@app, __MODULE__)[:contract_address]
end

def chain_id do
Application.fetch_env!(@app, __MODULE__)[:chain_id]
end
end
41 changes: 38 additions & 3 deletions backend/lib/peach/events.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,55 @@ defmodule Peach.Events do
@moduledoc """
Manages the events for the peach app
"""
alias Peach.CalldataBuilder
alias Peach.Config
alias Peach.Event
alias Peach.Repo
alias Peach.TicketTiers
import Ecto.Query

@default_limit 50
@default_event_id 0

@selector "0x005b3134506a8ff22ce883984545296af6e65577777882051fa04dc6ecb84e99"

@doc """
Creates an event with the given attributes.
"""
def create_event(event \\ %{}) do
%Event{}
|> Event.changeset(event)
|> Repo.insert()
event =
%Event{}
|> Event.changeset(event)
|> Repo.insert()

case event do
{:ok, real_event} ->
calls = {
Config.contract_address(),
@selector,
CalldataBuilder.build_calldata(real_event)
}

Starknet.execute_tx(
Config.provider_url(),
Config.private_key(),
Config.address(),
Config.chain_id(),
[calls]
)

err ->
err
end

event
end

def remaining_event_tickets(event_id) do
Enum.map(TicketTiers.event_ticket_tiers(event_id), fn ticket_tier ->
{:ok, remaining} = TicketTiers.remaining_tickets(ticket_tier.id)
remaining
end)
end

@doc """
Expand Down
7 changes: 4 additions & 3 deletions backend/lib/peach/ticket_tier.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ defmodule Peach.TicketTier do
use Ecto.Schema
import Ecto.Changeset

@derive {Jason.Encoder, only: [:id, :name, :description, :max_supply]}
@derive {Jason.Encoder, only: [:id, :name, :description, :max_supply, :price]}
schema "ticket_tiers" do
field :name, :string
field :description, :string
field :max_supply, :integer
field :price, :integer

belongs_to :event, Peach.Event

Expand All @@ -19,7 +20,7 @@ defmodule Peach.TicketTier do
@doc false
def changeset(ticket_tier, attrs) do
ticket_tier
|> cast(attrs, [:name, :description, :max_supply])
|> validate_required([:name, :description, :max_supply])
|> cast(attrs, [:name, :description, :max_supply, :price])
|> validate_required([:name, :description, :max_supply, :price])
end
end
1 change: 1 addition & 0 deletions backend/lib/peach/ticket_tiers.ex
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ defmodule Peach.TicketTiers do
id: ticket_tier.id,
name: ticket_tier.name,
description: ticket_tier.description,
price: ticket_tier.price,
remaining: ticket_tier.max_supply - sold_tickets,
max_supply: ticket_tier.max_supply
}}
Expand Down
16 changes: 15 additions & 1 deletion backend/lib/peach_web/controllers/event_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ defmodule PeachWeb.EventController do
end

@doc """
Updates the name of an event.
Updates an event.
"""
def update(conn, %{"id" => id, "event" => event_params}) do
case Events.update_event(id, event_params) do
Expand All @@ -55,4 +55,18 @@ defmodule PeachWeb.EventController do
|> json(%{errors: errors})
end
end

def remaining_event_tickets(conn, %{"id" => id}) do
case Events.remaining_event_tickets(id) do
[] ->
conn
|> put_status(:not_found)
|> json(%{errors: "Event not found"})

ticket_tier ->
conn
|> put_status(:ok)
|> json(%{tickets: ticket_tier})
end
end
end
1 change: 1 addition & 0 deletions backend/lib/peach_web/controllers/ticket_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ defmodule PeachWeb.TicketController do
%{
"tier_id" => tier.id,
"name" => tier.name,
"price" => tier.price,
"description" => tier.description,
"ticket_ids" => Enum.map(tickets, & &1.id) |> Enum.sort()
}
Expand Down
1 change: 1 addition & 0 deletions backend/lib/peach_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ defmodule PeachWeb.Router do
get "/tickets/:address", TicketController, :index
get "/events/:id/ticket_tiers", TicketTierController, :index
get "/ticket_tiers/:id", TicketTierController, :show
get "/events/:id/remaining_tickets", EventController, :remaining_event_tickets
end

# Enable LiveDashboard and Swoosh mailbox preview in development
Expand Down
10 changes: 10 additions & 0 deletions backend/lib/starknet.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
defmodule Starknet do
@moduledoc """
Binding to call the starknet rust sdk to execute a transaction
"""
use Rustler, otp_app: :peach, crate: "starknet"

# Fallback function in case the NIF is not loaded
def execute_tx(_provider_url, _private_key, _address, _chain_id, _calls),
do: :erlang.nif_error(:nif_not_loaded)

Check warning on line 9 in backend/lib/starknet.ex

View check run for this annotation

Codecov / codecov/patch

backend/lib/starknet.ex#L9

Added line #L9 was not covered by tests
end
4 changes: 3 additions & 1 deletion backend/mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ defmodule Peach.MixProject do
{:dns_cluster, "~> 0.1.1"},
{:bandit, "~> 1.5"},
{:credo, "~> 1.7", only: [:dev, :test], runtime: false},
{:excoveralls, "~> 0.18", only: [:test]}
{:excoveralls, "~> 0.18", only: [:test]},
{:dotenv, "~> 3.0.0"},
{:rustler, "~> 0.35.0"}
]
end

Expand Down
4 changes: 4 additions & 0 deletions backend/mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"db_connection": {:hex, :db_connection, "2.7.0", "b99faa9291bb09892c7da373bb82cba59aefa9b36300f6145c5f201c7adf48ec", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dcf08f31b2701f857dfc787fbad78223d61a32204f217f15e881dd93e4bdd3ff"},
"decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"},
"dns_cluster": {:hex, :dns_cluster, "0.1.3", "0bc20a2c88ed6cc494f2964075c359f8c2d00e1bf25518a6a6c7fd277c9b0c66", [:mix], [], "hexpm", "46cb7c4a1b3e52c7ad4cbe33ca5079fbde4840dedeafca2baf77996c2da1bc33"},
"dotenv": {:hex, :dotenv, "3.0.0", "52a28976955070d8312a81d59105b57ecf5d6a755c728b49c70a7e2120e6cb40", [:mix], [], "hexpm", "f8a7d800b6b419a8d8a8bc5b5cd820a181c2b713aab7621794febe934f7bd84e"},
"ecto": {:hex, :ecto, "3.12.3", "1a9111560731f6c3606924c81c870a68a34c819f6d4f03822f370ea31a582208", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9efd91506ae722f95e48dc49e70d0cb632ede3b7a23896252a60a14ac6d59165"},
"ecto_sql": {:hex, :ecto_sql, "3.12.0", "73cea17edfa54bde76ee8561b30d29ea08f630959685006d9c6e7d1e59113b7d", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.12", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.19 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dc9e4d206f274f3947e96142a8fdc5f69a2a6a9abb4649ef5c882323b6d512f0"},
"excoveralls": {:hex, :excoveralls, "0.18.3", "bca47a24d69a3179951f51f1db6d3ed63bca9017f476fe520eb78602d45f7756", [:mix], [{:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "746f404fcd09d5029f1b211739afb8fb8575d775b21f6a3908e7ce3e640724c6"},
Expand All @@ -29,11 +30,14 @@
"plug": {:hex, :plug, "1.16.1", "40c74619c12f82736d2214557dedec2e9762029b2438d6d175c5074c933edc9d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a13ff6b9006b03d7e33874945b2755253841b238c34071ed85b0e86057f8cddc"},
"plug_crypto": {:hex, :plug_crypto, "2.1.0", "f44309c2b06d249c27c8d3f65cfe08158ade08418cf540fd4f72d4d6863abb7b", [:mix], [], "hexpm", "131216a4b030b8f8ce0f26038bc4421ae60e4bb95c5cf5395e1421437824c4fa"},
"postgrex": {:hex, :postgrex, "0.19.1", "73b498508b69aded53907fe48a1fee811be34cc720e69ef4ccd568c8715495ea", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "8bac7885a18f381e091ec6caf41bda7bb8c77912bb0e9285212829afe5d8a8f8"},
"req": {:hex, :req, "0.5.6", "8fe1eead4a085510fe3d51ad854ca8f20a622aae46e97b302f499dfb84f726ac", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "cfaa8e720945d46654853de39d368f40362c2641c4b2153c886418914b372185"},
"rustler": {:hex, :rustler, "0.35.0", "1e2e379e1150fab9982454973c74ac9899bd0377b3882166ee04127ea613b2d9", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:req, "~> 0.5", [hex: :req, repo: "hexpm", optional: false]}, {:toml, "~> 0.6", [hex: :toml, repo: "hexpm", optional: false]}], "hexpm", "a176bea1bb6711474f9dfad282066f2b7392e246459bf4e29dfff6d828779fdf"},
"swoosh": {:hex, :swoosh, "1.17.1", "01295a82bddd2c6cac1e65856e29444d7c23c4501e0ebc69cea8a82018227e25", [:mix], [{:bandit, ">= 1.0.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mua, "~> 0.2.3", [hex: :mua, repo: "hexpm", optional: true]}, {:multipart, "~> 0.4", [hex: :multipart, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:req, "~> 0.5 or ~> 1.0", [hex: :req, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3b20d25e580cb79af631335a1bdcfbffd835c08ebcdc16e98577223a241a18a1"},
"telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"},
"telemetry_metrics": {:hex, :telemetry_metrics, "1.0.0", "29f5f84991ca98b8eb02fc208b2e6de7c95f8bb2294ef244a176675adc7775df", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f23713b3847286a534e005126d4c959ebcca68ae9582118ce436b521d1d47d5d"},
"telemetry_poller": {:hex, :telemetry_poller, "1.1.0", "58fa7c216257291caaf8d05678c8d01bd45f4bdbc1286838a28c4bb62ef32999", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9eb9d9cbfd81cbd7cdd24682f8711b6e2b691289a0de6826e58452f28c103c8f"},
"thousand_island": {:hex, :thousand_island, "1.3.5", "6022b6338f1635b3d32406ff98d68b843ba73b3aa95cfc27154223244f3a6ca5", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2be6954916fdfe4756af3239fb6b6d75d0b8063b5df03ba76fd8a4c87849e180"},
"toml": {:hex, :toml, "0.7.0", "fbcd773caa937d0c7a02c301a1feea25612720ac3fa1ccb8bfd9d30d822911de", [:mix], [], "hexpm", "0690246a2478c1defd100b0c9b89b4ea280a22be9a7b313a8a058a2408a2fa70"},
"websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"},
"websock_adapter": {:hex, :websock_adapter, "0.5.7", "65fa74042530064ef0570b75b43f5c49bb8b235d6515671b3d250022cb8a1f9e", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "d0f478ee64deddfec64b800673fd6e0c8888b079d9f3444dd96d2a98383bdbd1"},
}
5 changes: 5 additions & 0 deletions backend/native/starknet/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[target.'cfg(target_os = "macos")']
rustflags = [
"-C", "link-arg=-undefined",
"-C", "link-arg=dynamic_lookup",
]
1 change: 1 addition & 0 deletions backend/native/starknet/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/target
Loading
Loading