Кейс: как мы решили проблему batch-выплат для стартапа на 30 человек
Стартап с распределённой командой хотел платить в USDC на Polygon, но каждая выплата вручную через мультисиг отнимала часы. Для команды из 30 человек экономия на газе при использовании батчевых выплат составляет до $1500 в месяц. Батчевые выплаты через мультисенд контракт снижают стоимость в 3-5 раз. Самостоятельная разработка имеет смысл, когда в команде больше 20 человек, есть нестандартные требования к токенам или необходимость интеграции с HR-системой. Готовые решения вроде Request Finance или Superfluid не всегда покрывают специфику, поэтому мы предлагаем кастомную разработку под ключ.
Мы используем проверенный стек: Solidity 0.8.x для смарт-контрактов, Foundry для тестирования, TypeScript для бэкенда, Safe SDK для мультисига. На счету — более 10 проектов по автоматизации выплат в криптовалюте, гарантируем соответствие стандартам безопасности и compliance.
Как выбрать метод конвертации курса?
Самый болезненный вопрос — какой курс использовать. Три варианта:
| Метод | Описание | Риски | Применимость |
|---|---|---|---|
| Spot rate на момент выплаты | Курс биржи в момент отправки транзакции | Волатильность на сотруднике | Простой, для частых выплат |
| Фиксированный курс за N дней до выплаты | Замораживается заранее | Может отличаться от рынка | Планирование бюджета |
| TWA (Time-Weighted Average) | Среднее за период | Сложнее в расчётах | Справедливый, для крупных сумм |
Spot rate на момент выплаты — самый простой. Работодатель берёт курс в момент отправки транзакции. Риск волатильности несёт сотрудник.
Фиксированный курс за N дней до выплаты — снижает волатильность, но требует advance planning и может расходиться с рынком на момент выплаты.
TWA (Time-Weighted Average) за расчётный период — стандарт в традиционных FX-расчётах, наиболее справедлив, но сложнее объяснить сотрудникам.
class ExchangeRateService { private sources = [ new ChainlinkPriceFeed(), new CoinGeckoAPI(), new BinanceAPI(), ] 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}`) } 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 выплата каждому сотруднику отдельной транзакцией — это дорого. Например, для команды из 30 человек экономия на газе при использовании батчевых выплат может составлять до $1500 в месяц. Батчевые выплаты через мультисенд контракт снижают стоимость в 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; 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 ) 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.
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' }) 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
Каждая выплата должна быть зафиксирована с:
- Фиатным эквивалентом на момент выплаты (для НДФЛ/income tax)
- Источником курса (для аудита)
- Идентификатором транзакции 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) 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 — доход начисляется непрерывно.
Что входит в работу
Разработка системы под ключ включает:
- Аудит текущих процессов выплат и требований compliance
- Проектирование архитектуры и модели данных
- Разработка смарт-контрактов и бэкенда
- Интеграция с мультисиг кошельками (Safe) и оракулами (Chainlink)
- Настройка налогового модуля и экспорта в бухгалтерские системы
- Тестирование (unit, integration, audit)
- Документация, обучение команды, поддержка после запуска
| Этап | Срок |
|---|---|
| Аналитика и проектирование | 1–2 недели |
| Разработка и тестирование | 2–4 недели |
| Интеграция и деплой | 1 неделя |
| Поддержка и доработки | по соглашению |
Полная система с мультичейн поддержкой, Safe интеграцией, налоговым модулем и HR-интеграцией — от 3 до 6 недель разработки в зависимости от числа поддерживаемых сетей и требований к compliance. Стоимость рассчитывается индивидуально после аудита.
Если вам нужна система крипто-зарплат под ключ, свяжитесь с нами — мы оценим ваш проект и предложим решение. Наш опыт — более 5 лет в блокчейн-разработке, сертифицированные специалисты по Solidity и Rust, гарантия безопасности и соответствия регуляторам. Получите консультацию — обсудим детали вашего проекта.







