Initial commit

This commit is contained in:
Yaser
2026-08-13 19:50:53 +03:30
commit 38084458fe
879 changed files with 95198 additions and 0 deletions

View File

@@ -0,0 +1,60 @@
# encoding: UTF-8
# frozen_string_literal: true
module API
module V2
module Public
class Currencies < Grape::API
helpers ::API::V2::ParamHelpers
desc 'Get a currency' do
success Entities::Currency
end
params do
requires :id,
type: String,
values: { value: -> { Currency.visible.codes(bothcase: true) }, message: 'public.currency.doesnt_exist'},
desc: -> { API::V2::Entities::Currency.documentation[:id][:desc] }
end
get '/currencies/:id', requirements: { id: /[\w\.\-]+/ } do
present Currency.find(params[:id]), with: API::V2::Entities::Currency
end
desc 'Get list of currencies',
is_array: true,
success: Entities::Currency
params do
use :pagination
optional :type,
type: String,
values: { value: %w[fiat coin], message: 'public.currency.invalid_type' },
desc: -> { API::V2::Entities::Currency.documentation[:type][:desc] }
optional :search, type: JSON, default: {} do
optional :code,
type: String,
desc: 'Search by currency code using SQL LIKE'
optional :name,
type: String,
desc: 'Search by currency name using SQL LIKE'
end
end
get '/currencies' do
search_attrs = { m: 'or',
code_cont: params.dig(:search, :code),
name_cont: params.dig(:search, :name) }
present paginate(Rails.cache.fetch("currencies_#{params}", expires_in: 600) do
currencies = Currency.visible.ordered
currencies = currencies.where(type: params[:type]).includes(:blockchain) if params[:type] == 'coin'
currencies = currencies.where(type: params[:type]) if params[:type] == 'fiat'
search = currencies.ransack(search_attrs)
search.result.load.to_a
end), with: API::V2::Entities::Currency
end
end
end
end
end

View File

@@ -0,0 +1,36 @@
# frozen_string_literal: true
module API
module V2
module Public
class DepositLimits < Grape::API
helpers ::API::V2::Admin::Helpers
desc 'Returns deposit limits table as paginated collection',
is_array: true,
success: API::V2::Entities::DepositLimit
params do
optional :group,
type: String,
desc: -> { API::V2::Entities::DepositLimit.documentation[:group][:desc] },
coerce_with: ->(c) { c.strip.downcase }
optional :kyc_level,
type: String,
desc: -> { API::V2::Entities::DepositLimit.documentation[:kyc_level][:desc] }
use :pagination
use :ordering
end
get '/deposit_limits' do
ransack_params = API::V2::Admin::Helpers::RansackBuilder.new(params)
.eq(:group, :kyc_level)
.build
search = DepositLimit.ransack(ransack_params)
search.sorts = "#{params[:order_by]} #{params[:ordering]}"
present paginate(Rails.cache.fetch("deposit_limits_#{params}", expires_in: 600) { search.result.load.to_a }), with: API::V2::Entities::DepositLimit
end
end
end
end
end

View File

@@ -0,0 +1,197 @@
# encoding: UTF-8
# frozen_string_literal: true
module API
module V2
module Public
class Markets < Grape::API
helpers ::API::V2::OrderHelpers
helpers ::API::V2::ParamHelpers
class OrderBook < Struct.new(:asks, :bids); end
resource :markets do
desc 'Get all available markets.',
is_array: true,
success: API::V2::Entities::Market
params do
use :pagination
optional :ordering,
values: { value: %w(asc desc), message: 'public.markets.invalid_ordering' },
default: 'asc',
desc: 'If set, returned values will be sorted in specific order, defaults to \'asc\'.'
optional :order_by,
values: { value: %w(id position), message: 'public.markets.invalid_order_by' },
default: 'position',
desc: 'Name of the field, which result will be ordered by.'
optional :base_unit,
type: String,
values: { value: -> (v){ ::Currency.exists?(v) },
message: 'public.markets.base_unit_doesnt_exist' },
desc: 'Strict filter for base unit'
optional :quote_unit,
type: String,
values: { value: -> (v){ ::Currency.exists?(v) },
message: 'public.markets.quote_unit_doesnt_exist' },
desc: 'Strict filter for quote unit'
optional :search, type: JSON, default: {} do
optional :base_code,
type: String,
desc: 'Search base currency code using LIKE'
optional :quote_code,
type: String,
desc: 'Search qoute currency code using LIKE'
optional :base_name,
type: String,
desc: 'Search base currency name using LIKE'
optional :quote_name,
type: String,
desc: 'Search quote currency name using LIKE'
end
end
get "/" do
search_params = params[:search]
.slice(:base_code, :quote_code, :base_name, :quote_name)
.transform_keys {|k| "#{k}_cont"}
.merge(m: 'or')
search = ::Market.active
.where(params.slice(:base_unit, :quote_unit))
.ransack(search_params)
# Add default ordering (position asc) for cases markets where unit position is same.
search.sorts = ["#{params[:order_by]} #{params[:ordering]}", "position asc"]
present paginate(Rails.cache.fetch("markets_#{params}", expires_in: 600) { search.result.load.to_a }),
with: API::V2::Entities::Market,
extended: !!params[:extended]
end
desc 'Get the order book of specified market.',
is_array: true,
success: API::V2::Entities::OrderBook
params do
requires :market,
type: String,
values: { value: -> { ::Market.active.ids }, message: 'public.market.doesnt_exist' },
desc: -> { V2::Entities::Market.documentation[:id] }
optional :asks_limit,
type: { value: Integer, message: 'public.order_book.non_integer_ask_limit' },
values: { value: 1..200, message: 'public.order_book.invalid_ask_limit' },
default: 20,
desc: 'Limit the number of returned sell orders. Default to 20.'
optional :bids_limit,
type: { value: Integer, message: 'public.order_book.non_integer_bid_limit' },
values: { value: 1..200, message: 'public.order_book.invalid_bid_limit' },
default: 20,
desc: 'Limit the number of returned buy orders. Default to 20.'
end
get ":market/order-book", requirements: { market: /[\w\.\-]+/ } do
asks = OrderAsk.active.with_market(params[:market]).matching_rule.limit(params[:asks_limit])
bids = OrderBid.active.with_market(params[:market]).matching_rule.limit(params[:bids_limit])
book = OrderBook.new asks, bids
present book, with: API::V2::Entities::OrderBook
end
desc 'Get recent trades on market, each trade is included only once. Trades are sorted in reverse creation order.',
is_array: true,
success: API::V2::Entities::Trade
params do
requires :market,
type: String,
values: { value: -> { ::Market.active.ids }, message: 'public.market.doesnt_exist' },
desc: -> { V2::Entities::Market.documentation[:id] }
optional :limit,
type: { value: Integer, message: 'public.trade.non_integer_limit' },
values: { value: 1..1000, message: 'public.trade.invalid_limit' },
default: 100,
desc: 'Limit the number of returned trades. Default to 100.'
optional :timestamp,
type: { value: Integer, message: 'public.trade.non_integer_timestamp' },
desc: "An integer represents the seconds elapsed since Unix epoch."\
"If set, only trades executed before the time will be returned."
optional :order_by,
type: String,
values: { value: %w(asc desc), message: 'public.trade.invalid_order_by' },
default: 'desc',
desc: "If set, returned trades will be sorted in specific order, default to 'desc'."
end
get ":market/trades", requirements: { market: /[\w\.\-]+/ } do
present Trade.public_from_influx(params[:market], params[:limit]), with: API::V2::Entities::PublicTrade
end
desc 'Get depth or specified market. Both asks and bids are sorted from highest price to lowest.'
params do
requires :market,
type: String,
values: { value: -> { ::Market.active.ids }, message: 'public.market.doesnt_exist' },
desc: -> { V2::Entities::Market.documentation[:id] }
optional :limit,
type: { value: Integer, message: 'public.market_depth.non_integer_limit' },
values: { value: 1..1000, message: 'public.market_depth.invalid_limit' },
default: 300,
desc: 'Limit the number of returned price levels. Default to 300.'
end
get ":market/depth", requirements: { market: /[\w\.\-]+/ } do
asks = OrderAsk.get_depth(params[:market])[0, params[:limit]]
bids = OrderBid.get_depth(params[:market])[0, params[:limit]]
{ timestamp: Time.now.to_i, asks: asks, bids: bids }
end
desc 'Get OHLC(k line) of specific market.'
params do
requires :market,
type: String,
values: { value: -> { ::Market.active.ids }, message: 'public.market.doesnt_exist' },
desc: -> { V2::Entities::Market.documentation[:id] }
optional :period,
type: { value: Integer, message: 'public.k_line.non_integer_period' },
values: { value: KLineService::AVAILABLE_POINT_PERIODS, message: 'public.k_line.invalid_period' },
default: 1,
desc: "Time period of K line, default to 1. You can choose between #{KLineService::AVAILABLE_POINT_PERIODS.join(', ')}"
optional :time_from,
type: { value: Integer, message: 'public.k_line.non_integer_time_from' },
allow_blank: { value: false, c_name: 'k_line' },
desc: "An integer represents the seconds elapsed since Unix epoch. If set, only k-line data after that time will be returned."
optional :time_to,
type: { value: Integer, message: 'public.k_line.non_integer_time_to' },
allow_blank: { value: false, c_name: 'k_line' },
desc: "An integer represents the seconds elapsed since Unix epoch. If set, only k-line data till that time will be returned."
optional :limit,
type: { value: Integer, message: 'public.k_line.non_integer_limit' },
values: { value: KLineService::AVAILABLE_POINT_LIMITS, message: 'public.k_line.invalid_limit' },
default: 30,
desc: "Limit the number of returned data points default to 30. Ignored if time_from and time_to are given."
end
get ":market/k-line", requirements: { market: /[\w\.\-]+/ } do
KLineService[params[:market], params[:period]]
.get_ohlc(params.slice(:limit, :time_from, :time_to).merge(offset: true))
end
desc 'Get ticker of all markets (For response doc see /:market/tickers/ response).'
get "/tickers" do
Rails.cache.fetch(:markets_tickers, expires_in: 60) do
::Market.active.ordered.inject({}) do |h, m|
h[m.id] = format_ticker TickersService[m].ticker
h
end
end
end
desc 'Get ticker of specific market.',
success: API::V2::Entities::Ticker
params do
requires :market,
type: String,
values: { value: -> { ::Market.active.ids }, message: 'public.market.doesnt_exist' },
desc: -> { V2::Entities::Market.documentation[:id] }
end
get "/:market/tickers/", requirements: { market: /[\w\.\-]+/ } do
present format_ticker(TickersService[params[:market]].ticker),
with: API::V2::Entities::Ticker
end
end
end
end
end
end

View File

@@ -0,0 +1,17 @@
# encoding: UTF-8
# frozen_string_literal: true
module API
module V2
module Public
class MemberLevels < Grape::API
desc 'Returns hash of minimum levels and the privileges they provide.'
get '/member-levels' do
{ deposit: { minimum_level: ENV.fetch('MINIMUM_MEMBER_LEVEL_FOR_DEPOSIT').to_i },
withdraw: { minimum_level: ENV.fetch('MINIMUM_MEMBER_LEVEL_FOR_WITHDRAW').to_i },
trading: { minimum_level: ENV.fetch('MINIMUM_MEMBER_LEVEL_FOR_TRADING').to_i } }
end
end
end
end
end

View File

@@ -0,0 +1,21 @@
# frozen_string_literal: true
module API
module V2
module Public
class Mount < Grape::API
before { set_ets_context! }
mount Public::Currencies
mount Public::Markets
mount Public::MemberLevels
mount Public::Tools
mount Public::TradingFees
mount Public::Webhooks
mount Public::WithdrawLimits
mount Public::DepositLimits
end
end
end
end

View File

@@ -0,0 +1,39 @@
# encoding: UTF-8
# frozen_string_literal: true
module API
module V2
module Public
class Tools < Grape::API
desc 'Get server current time, in seconds since Unix epoch.'
get "/timestamp" do
::Time.now.iso8601
end
desc 'Get running Dena version and build details.'
get "/version" do
{
git_tag: Peatio::Application::GIT_TAG,
git_sha: Peatio::Application::GIT_SHA,
build_date: Peatio::Application::BUILD_DATE,
version: Peatio::Application::VERSION
}.tap do |v|
present OpenStruct.new(v), with: Entities::Version
end
end
resource :health do
desc 'Get application liveness status'
get "/alive" do
status Services::HealthChecker.alive? ? 200 : 503
end
desc 'Get application readiness status'
get "/ready" do
status Services::HealthChecker.ready? ? 200 : 503
end
end
end
end
end
end

View File

@@ -0,0 +1,39 @@
# encoding: UTF-8
# frozen_string_literal: true
module API
module V2
module Public
class TradingFees < Grape::API
helpers ::API::V2::Admin::Helpers
desc 'Returns trading_fees table as paginated collection',
is_array: true,
success: API::V2::Entities::TradingFee
params do
optional :group,
type: String,
desc: -> { API::V2::Entities::TradingFee.documentation[:group][:desc] },
coerce_with: ->(c) { c.strip.downcase }
optional :market_id,
type: String,
desc: -> { API::V2::Entities::TradingFee.documentation[:market_id][:desc] },
values: { value: -> { ::Market.ids.append(::TradingFee::ANY) },
message: 'public.trading_fee.market_doesnt_exist' }
use :pagination
use :ordering
end
get '/trading_fees' do
ransack_params = API::V2::Admin::Helpers::RansackBuilder.new(params)
.eq(:group, :market_id)
.build
search = TradingFee.ransack(ransack_params)
search.sorts = "#{params[:order_by]} #{params[:ordering]}"
present paginate(Rails.cache.fetch("trading_fees_#{params}", expires_in: 600) { search.result.load.to_a }), with: API::V2::Entities::TradingFee
end
end
end
end
end

View File

@@ -0,0 +1,55 @@
# frozen_string_literal: true
module API
module V2
module Public
class Webhooks < Grape::API
helpers ::API::V2::WebhooksHelpers
desc 'Bitgo Webhook'
params do
requires :event,
type: String,
desc: 'Name of event can be deposit or withdraw',
values: { value: -> { %w[deposit withdraw] }, message: 'public.webhook.invalid_event' }
requires :type,
type: String,
desc: 'Type of event.'
given type: ->(val) { val == 'transfer' } do
requires :hash,
type: String,
desc: 'Transfer txid.'
requires :transfer,
type: String,
desc: 'Transfer id.'
requires :coin,
type: String,
desc: 'Currency code.'
requires :wallet,
type: String,
desc: 'Wallet id.'
end
given type: ->(val) { val == 'address_confirmation' } do
requires :hash,
type: String,
desc: 'Address txid.'
requires :address,
type: String,
desc: 'User Address.'
requires :walletId,
type: String,
desc: 'Wallet id.'
end
end
post '/webhooks/:event' do
proces_webhook_event(params)
status 200
rescue StandardError => e
Rails.logger.warn { "Cannot perform webhook: #{params}. Error: #{e}." }
body errors: ["public.webhook.cannot_perfom_#{params['type']}"]
status 422
end
end
end
end
end

View File

@@ -0,0 +1,36 @@
# frozen_string_literal: true
module API
module V2
module Public
class WithdrawLimits < Grape::API
helpers ::API::V2::Admin::Helpers
desc 'Returns withdraw limits table as paginated collection',
is_array: true,
success: API::V2::Entities::WithdrawLimit
params do
optional :group,
type: String,
desc: -> { API::V2::Entities::WithdrawLimit.documentation[:group][:desc] },
coerce_with: ->(c) { c.strip.downcase }
optional :kyc_level,
type: String,
desc: -> { API::V2::Entities::WithdrawLimit.documentation[:kyc_level][:desc] }
use :pagination
use :ordering
end
get '/withdraw_limits' do
ransack_params = API::V2::Admin::Helpers::RansackBuilder.new(params)
.eq(:group, :kyc_level)
.build
search = WithdrawLimit.ransack(ransack_params)
search.sorts = "#{params[:order_by]} #{params[:ordering]}"
present paginate(Rails.cache.fetch("withdraw_limits_#{params}", expires_in: 600) { search.result.load.to_a }), with: API::V2::Entities::WithdrawLimit
end
end
end
end
end