PolarLoop-rb

Ruby gem для взаимодействия со смарт-контрактом PolarLoop на EVM-чейнах. Управление мандатами, списание, парсинг событий и генерация подписи оператора для бэкенда AWB.

Оглавление

Установка

gem install polarloop

Или добавить в Gemfile:

gem "polarloop"

Конфигурация

PolarLoop.configure do |c|
  c.mnemonic = "your twelve word mnemonic phrase here ..."
  c.index = 0  # BIP-44 индекс деривации (m/44'/60'/0'/0/N)

  c.register(:bsc,
    rpc_url: "https://bsc-dataseed.binance.org",
    contract_address: "0x...",
    chain_id: 56,
    gas_multiplier: 1.2  # опционально, по умолчанию 1.2
  )

  c.register(:polygon,
    rpc_url: "https://polygon-rpc.com",
    contract_address: "0x...",
    chain_id: 137
  )
end

Использование

client = PolarLoop.client(:bsc)

View-методы (без газа)

# Получить мандат по ID
mandate = client.get_mandate("0xabc...")
mandate.payer        # => "0x..."
mandate.amount       # => 1000000
mandate.interval     # => 86400
mandate.active       # => true
mandate.reference_id # => "0x..."

# Получить мандат по вашему reference ID
mandate_id, mandate = client.get_mandate_by_reference_id("0xref...")

# Проверить готовность к списанию
result = client.charge_ready?("0xabc...")
result.ready   # => true
result.reason  # => ""

# Пакетная проверка
results = client.batch_charge_ready?(["0xabc...", "0xdef..."])

# Все мандаты плательщика
ids = client.get_payer_mandates("0xpayer...")

# Состояние контракта
client.min_interval  # => 86400
client.paused?       # => false

Write-методы (отправка транзакций)

# Списание по мандату
result = client.charge("0xabc...")
result.tx_hash      # => "0x..."
result.block_number # => 12345678
result.gas_used     # => 71356
result.status       # => true

# Пакетное списание
result = client.batch_charge(["0xabc...", "0xdef..."])
result.batch_results[0].mandate_id # => "0xabc..."
result.batch_results[0].success    # => true
result.batch_results[1].mandate_id # => "0xdef..."
result.batch_results[1].success    # => false
result.batch_results[1].reason     # => "Interval not elapsed"

# Отзыв мандата
result = client.revoke_mandate("0xabc...")
result = client.batch_revoke_mandate(["0xabc...", "0xdef..."])

# Админские методы
client.pause
client.unpause
client.grant_operator_role("0xaddress...")
client.revoke_operator_role("0xaddress...")
client.rescue_eth
client.rescue_erc20("0xtoken...", amount)

Подпись оператора (off-chain, без газа)

Генерация ECDSA-подписи для createMandate на фронтенде:

signature = client.sign_create_mandate(
  payer: "0xpayer...",
  merchant: "0xmerchant...",
  token: "0xtoken...",
  amount: 1_000_000,
  interval: 86_400,
  max_total_amount: 10_000_000,
  reference_id: "0xref..."
)
# => "abcdef1234..." (hex-подпись для dApp)

События

# MandateCreated — создание мандата
events = client.mandate_created_events(from_block: 1000, to_block: "latest")
events.each do |e|
  e.mandate_id   # => "0x..."
  e.payer        # => "0x..."
  e.merchant     # => "0x..."
  e.token        # => "0x..."
  e.amount       # => 1000000
  e.interval     # => 86400
  e.max_total_amount # => 10000000
  e.reference_id # => "0x..."
  e.block_number # => 12345
  e.tx_hash      # => "0x..."
end

# MandateCharged — списание по мандату
events = client.mandate_charged_events(from_block: 1000)
events.each do |e|
  e.mandate_id   # => "0x..."
  e.payer        # => "0x..."
  e.merchant     # => "0x..."
  e.amount       # => 1000000
  e.timestamp    # => 1773521124
  e.block_number # => 12345
  e.tx_hash      # => "0x..."
end

# MandateRevoked — отзыв мандата
events = client.mandate_revoked_events(from_block: 1000)
events.each do |e|
  e.mandate_id   # => "0x..."
  e.payer        # => "0x..."
  e.block_number # => 12345
  e.tx_hash      # => "0x..."
end

# События конкретной транзакции
events = client.events_for_tx("0xtxhash...")

Адрес кошелька

PolarLoop.address  # => "0x..." (из мнемоники + индекса)

Обработка ошибок

begin
  client.charge("0xabc...")
rescue PolarLoop::ContractRevertError => e
  # Контракт отклонил вызов — декодированная ошибка из ABI
  # например: "AccessControlUnauthorizedAccount(0x..., 0x...)"
  # например: "Interval not elapsed"
  puts e.message
rescue PolarLoop::RpcError => e
  # Ошибка соединения или протокола RPC
  puts e.message
end

Мульти-чейн

Регистрируйте несколько чейнов и получайте отдельные клиенты:

bsc_client = PolarLoop.client(:bsc)
polygon_client = PolarLoop.client(:polygon)

Один и тот же адрес кошелька на всех EVM-чейнах (BIP-44 деривация).

Разработка

bundle install
bundle exec rspec

Лицензия

MIT