Розробка системи крипто-зарплати (crypto payroll)

Проєктуємо та розробляємо блокчейн-рішення повного циклу: від архітектури смарт-контрактів до запуску DeFi-протоколів, NFT-маркетплейсів та криптобірж. Аудит безпеки, токеноміка, інтеграція з наявною інфраструктурою.
Показано 1 з 1Усі 1306 послуг
Розробка системи крипто-зарплати (crypto payroll)
Складний
від 2 тижнів до 3 місяців
Часті запитання

Напрямки блокчейн-розробки

Етапи блокчейн-розробки

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1311
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1222
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    922
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1151
  • image_logo-advance_0.webp
    Розробка логотипу компанії B2B Advance
    614
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    887

Розробка системи крипто-зарплат

Ключова проблема крипто-пейролу — це не технологія, а операційна складність: різні сотрудники хочуть різні токени в різних мережах, податкове законодавство вимагає фіксації фіатного еквіваленту в момент виплати, compliance вимагає KYC, а фінансовий директор хочет планові видатки у стабільному виразі. Система повинна вирішити все це одночасно — і робити це автоматично.

Самостійна розробка має сенс коли: команда > 20 чоловік, є нестандартні вимоги до токенів/мереж, потрібна інтеграція з HR-системою або бухгалтерським обліком, або готові рішення (Request Finance, Superfluid, Deel Crypto) не закривають вашу специфіку.

Архітектура системи

HR System / Manual Input
        ↓
Payroll Engine (розрахунок, конвертація)
        ↓
Approval Workflow (multi-sig авторизація)
        ↓
Disbursement Module (батчові виплати)
        ↓
Accounting Module (податковий облік, звіти)

Модель даних

interface Employee {
    id: string
    legalName: string
    taxId?: string        // для звітності
    walletAddresses: {
        chain: string
        address: string
        verified: boolean // підтверджений через sign message
    }[]
    paymentPreferences: PaymentPreference[]
    kycStatus: 'pending' | 'approved' | 'rejected'
}

interface PaymentPreference {
    percentage: number        // % від зарплати у цьому токені
    token: string             // contract address
    chain: string
    minAmount?: Decimal       // мінімум для виплати (інакше накопичувати)
}

interface PayrollRun {
    id: string
    periodStart: Date
    periodEnd: Date
    paymentDate: Date
    currency: 'USD' | 'EUR' | string   // base currency для розрахунку
    employees: PayrollEntry[]
    status: 'draft' | 'approved' | 'processing' | 'completed' | 'failed'
    approvals: Approval[]
}

interface PayrollEntry {
    employeeId: string
    grossAmountFiat: Decimal    // у base currency
    deductions: Deduction[]
    netAmountFiat: Decimal
    payments: CryptoPayment[]  // розбивка по токенам/мережам
    exchangeRates: ExchangeRateSnapshot[]  // зафіксовані курси
}

Розрахунок виплат та конвертація курсів

Найболючіше питання — який курс використовувати. Три варіанти:

Spot rate у момент виплати — найпростіший. Роботодавець бере курс у момент відправки транзакції. Волатильність несе сотрудник.

Фіксований курс за N днів до виплати — знижує волатильність, але вимагає попередньої планування та може розходитися з ринком у момент виплати.

TWA (Time-Weighted Average) за розрахунковий період — стандарт у традиційних FX-розрахунках, найсправедливіший, але складніше пояснити сотрудникам.

class ExchangeRateService {
    // Джерела курсів з fallback
    private sources = [
        new ChainlinkPriceFeed(),  // on-chain, manipulation-resistant
        new CoinGeckoAPI(),        // CoinGecko з API key
        new BinanceAPI(),          // CEX spot price
    ]

    async getRate(
        fromCurrency: string,
        toCurrency: string,
        method: 'spot' | 'twap_7d' | 'twap_30d' = 'spot'
    ): Promise<{ rate: Decimal; source: string; timestamp: Date }> {

        if (method === 'spot') {
            for (const source of this.sources) {
                try {
                    const rate = await source.getSpotRate(fromCurrency, toCurrency)
                    return { rate, source: source.name, timestamp: new Date() }
                } catch (e) {
                    console.warn(`${source.name} failed:`, e)
                }
            }
            throw new Error(`Cannot get spot rate for ${fromCurrency}/${toCurrency}`)
        }

        // TWAP: усереднення по історичним цінам
        const days = method === 'twap_7d' ? 7 : 30
        const historicalRates = await this.getHistoricalRates(fromCurrency, toCurrency, days)
        const avgRate = historicalRates.reduce((sum, r) => sum.plus(r), new Decimal(0))
            .div(historicalRates.length)

        return { rate: avgRate, source: 'twap', timestamp: new Date() }
    }

    async snapshotForPayroll(run: PayrollRun): Promise<ExchangeRateSnapshot[]> {
        // Зафіксувати всі потрібні курси та зберегти у БД для аудиту
        const tokens = new Set(
            run.employees.flatMap(e => e.payments.map(p => p.token))
        )

        const snapshots = await Promise.all(
            [...tokens].map(async (token) => {
                const rate = await this.getRate(run.currency, token, 'spot')
                return { token, ...rate, payrollRunId: run.id }
            })
        )

        await this.db.insertRateSnapshots(snapshots)
        return snapshots
    }
}

Батчові виплати: оптимізація газу

На Ethereum mainnet виплата кожному сотруднику окремою транзакцією — дорого. Батчові виплати через мультисенд контракт знижують вартість у 3-5 разів:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract PayrollDispatcher is Ownable {
    event PaymentDispatched(
        bytes32 indexed payrollRunId,
        address indexed recipient,
        address indexed token,
        uint256 amount
    );

    struct Payment {
        address recipient;
        address token;   // address(0) для native ETH/BNB
        uint256 amount;
    }

    function dispatchPayroll(
        bytes32 payrollRunId,
        Payment[] calldata payments
    ) external onlyOwner {
        for (uint256 i = 0; i < payments.length; i++) {
            Payment calldata p = payments[i];

            if (p.token == address(0)) {
                (bool success,) = p.recipient.call{value: p.amount}("");
                require(success, "ETH transfer failed");
            } else {
                require(
                    IERC20(p.token).transferFrom(msg.sender, p.recipient, p.amount),
                    "Token transfer failed"
                );
            }

            emit PaymentDispatched(payrollRunId, p.recipient, p.token, p.amount);
        }
    }

    receive() external payable {}
}

Для стейблкоінів (USDC, USDT) використовуємо transferFrom — джерело коштів залишається на multisig гаманці, контракт тільки спрямовує виплати. Це важливо для безпеки: контракт не зберігає коштів.

Мультичейн disbursement

Якщо сотрудники отримують зарплату у різних мережах — потрібен окремий disbursement модуль для кожної мережі. Паралельне виконання з агрегацією статусів:

class MultiChainDisbursementService {
    private dispatchers: Map<string, ChainDispatcher>

    async executePayrollRun(run: PayrollRun): Promise<DisbursementResult> {
        // Групуємо платежі по цепочкам
        const byChain = groupBy(
            run.employees.flatMap(e => e.payments),
            p => p.chain
        )

        // Паралельно запускаємо disbursement у кожній цепочці
        const results = await Promise.allSettled(
            Object.entries(byChain).map(([chain, payments]) =>
                this.dispatchers.get(chain)!.dispatch(run.id, payments)
            )
        )

        // Обробляємо часткові сбої
        const failures = results.filter(r => r.status === 'rejected')
        if (failures.length > 0) {
            await this.handlePartialFailure(run.id, failures)
        }

        return this.aggregateResults(results)
    }
}

Multi-sig авторизація

Для фондів з AML-вимогами недостатньо однієї підписи. Стандартна схема: CFO + CEO + фінансовий директор, 2-of-3.

Gnosis Safe (тепер Safe{Wallet}) — стандарт для мультисиг операцій. Інтеграція через Safe SDK:

import Safe, { EthersAdapter } from '@safe-global/protocol-kit'
import SafeApiKit from '@safe-global/api-kit'

class PayrollApprovalService {
    async proposePayrollTransaction(
        safeAddress: string,
        payrollData: PayrollRun,
        payments: BatchPayment[]
    ): Promise<string> {
        const safeSDK = await Safe.create({ ethAdapter, safeAddress })
        const apiKit = new SafeApiKit({ txServiceUrl: 'https://safe-transaction-mainnet.safe.global' })

        // Кодуємо виклик PayrollDispatcher.dispatchPayroll()
        const data = payrollDispatcher.interface.encodeFunctionData(
            'dispatchPayroll',
            [payrollData.id, payments]
        )

        const safeTransaction = await safeSDK.createTransaction({
            transactions: [{ to: PAYROLL_DISPATCHER_ADDRESS, data, value: '0' }]
        })

        const safeTxHash = await safeSDK.getTransactionHash(safeTransaction)
        const senderSignature = await safeSDK.signTransactionHash(safeTxHash)

        await apiKit.proposeTransaction({
            safeAddress,
            safeTransactionData: safeTransaction.data,
            safeTxHash,
            senderAddress: await signer.getAddress(),
            senderSignature: senderSignature.data,
        })

        return safeTxHash
    }
}

Податковий облік та compliance

Кожна виплата повинна бути зафіксована з:

  • Фіатним еквівалентом у момент виплати (для податків)
  • Джерелом курсу (для аудиту)
  • ID транзакції on-chain
  • Період за який виплачено
CREATE TABLE payroll_transactions (
    id              BIGSERIAL PRIMARY KEY,
    payroll_run_id  UUID NOT NULL REFERENCES payroll_runs(id),
    employee_id     UUID NOT NULL REFERENCES employees(id),
    payment_date    DATE NOT NULL,
    period_start    DATE NOT NULL,
    period_end      DATE NOT NULL,

    -- Крипто деталі
    token_address   VARCHAR(42) NOT NULL,
    token_symbol    VARCHAR(20) NOT NULL,
    chain           VARCHAR(50) NOT NULL,
    crypto_amount   NUMERIC(36, 18) NOT NULL,
    tx_hash         VARCHAR(66),

    -- Фіатний еквівалент для податків
    fiat_currency   VARCHAR(3) NOT NULL,
    fiat_amount     NUMERIC(20, 2) NOT NULL,
    exchange_rate   NUMERIC(20, 8) NOT NULL,
    rate_source     VARCHAR(100) NOT NULL,
    rate_timestamp  TIMESTAMPTZ NOT NULL,

    -- Статус
    status          VARCHAR(20) NOT NULL DEFAULT 'pending',
    confirmed_at    TIMESTAMPTZ,
    block_number    BIGINT
);

Експорт для бухгалтерії: CSV з розбивкою по сотрудникам та періодам, сумісний з 1C або міжнародними стандартами (IAS 19 для employee benefits).

Streaming payments: Superfluid інтеграція

Для DAOs та компаній з реальним-таймовим потоком зарплати — інтеграція з Superfluid Protocol. Замість періодичних виплат — неперервний потік токенів по секунді:

import { Framework } from '@superfluid-finance/sdk-core'

async function createSalaryStream(
    employeeAddress: string,
    tokenAddress: string,
    monthlyAmountWei: bigint
): Promise<void> {
    const sf = await Framework.create({ chainId: 137, provider })
    const superToken = await sf.loadSuperToken(tokenAddress)

    // flowRate = wei/second
    const flowRate = monthlyAmountWei / BigInt(30 * 24 * 3600)

    const createFlowOp = superToken.createFlow({
        sender: companyAddress,
        receiver: employeeAddress,
        flowRate: flowRate.toString(),
    })

    await createFlowOp.exec(signer)
}

Streaming payments знімають проблему періодичності та знижують операційну нагрузку, але вимагають обеспечення ліквідності (sufficient buffer) та ускладнюють tax accounting — дохід нараховується неперервно.

Повна система з мультичейн поддержкою, Safe інтеграцією, податковим модулем та HR-інтеграцією — від 3 до 6 тижнів розробки залежно від числа поддержуваних мереж та вимог до compliance.