Розробка мультисиг-контрактів
Мультисиг — це не про параноїдальну безпеку. Це про те, що ніхто у команді не повинен мати можливість в односторонньому порядку вивести скарбницю, оновити контракт або змінити критичний параметр протоколу. При $3M у скарбниці достатньо одного скомпрометованого Ledger або утекшого приватного ключа — і recovery неможлива.
Gnosis Safe vs кастомний мультисиг
Більшість завдань вирішує Safe (Gnosis Safe) — аудований контракт з 4+ роками на mainnet, $100B+ під управлінням, підтримкою всіх EVM-мереж. Під капотом — пороговова схема M-of-N: M підписів з N авторизованих owners. Safe зберігає транзакції на chain, кожен owner підписує офчейн (EIP-712 структуровані дані), при збиранні M підписів — будь-хто може виконати.
Коли потрібен кастомний мультисиг:
- нестандартна логіка авторизації (timelock + multisig + DAO vote)
- on-chain голосування з вагами (weighted multisig)
- специфічні умови виконання (лише після оракульної події)
- TON, Solana, Aptos — де Safe не існує
Що стоїть за "M-of-N підписів"
Реалізація мультисига на Solidity — хрестоматійний матеріал для аудиторів, тому що вони регулярно знаходять один і той же клас проблем.
Replay attack. Контракт приймає список підписів і перевіряє їх. Якщо nonce не включений у підписаний хеш — та ж транзакція може бути переіграна. Правильна структура: keccak256(abi.encode(to, value, data, nonce, chainId)). ChainId обов'язков — інакше підпис, дійсний на Ethereum mainnet, приймається на Polygon.
Signature malleability. ECDSA має два дійсні підписи для одного повідомлення (s і -s mod n). OpenZeppelin ECDSA.recover обробляє це правильно з версії 4.x, перевіряючи s <= secp256k1n / 2. Самописний ecrecover без цієї перевірки дозволяє дублювати підписи одного owner-а.
Duplicate signer. Контракт збирає N підписів, перевіряє кожен проти списку owners, але не перевіряє унікальність підписантів. Атака: один owner надає M підписів одного й того ж повідомлення — транзакція виконується з M/N = 1 фактичним учасником. Захист: require(signer > lastSigner) — сортування адрес у порядку зростання гарантує унікальність.
Ось мінімальний правильний чеклист верифікації підписів:
function _verifySignatures(
bytes32 txHash,
bytes[] calldata signatures
) internal view {
require(signatures.length >= threshold, "Below threshold");
address lastSigner = address(0);
for (uint256 i = 0; i < signatures.length; i++) {
address signer = ECDSA.recover(txHash, signatures[i]);
require(isOwner[signer], "Not an owner");
require(signer > lastSigner, "Duplicate signer"); // ключева перевірка
lastSigner = signer;
}
}
Timelock як обов'язковий шар
Мультисиг 2-of-3 з трьома ключами в одному офісі — не мультисиг у смислі безпеки. Реальна захист — комбінація мультисига з TimelockController (OpenZeppelin). Транзакція, підтримана M підписами, ставиться в чергу з затримкою (зазвичай 24-72 години). В цьому вікні community може помітити вредоносне дію.
Для протоколів з TVL > $1M TimelockController з затримкою 48 годин — baseline. Параметр minDelay задається при деплої і не може бути зменшений самим timelock-ом (лише через governance vote або multisig).
Weighted multisig і DAO-інтеграція
Якщо підписанти мають різний вес (VC фонд з 40% voting power vs окремі контрибьютори з 5% кожен) — потрібна взважена схема. Реалізуємо через mapping weights[address] і перевірку totalWeight >= requiredWeight замість threshold за кількістю.
Інтеграція з Governor (OpenZeppelin) дозволяє будувати гібридні схеми: дрібні операції (до X ETH) — через мультисиг, крупні — через DAO vote з timelock.
Multisig на Solana і TON
На Solana використовуємо Squads Protocol — аналог Safe для Solana. Squads v4 підтримує програмовані spending limits, ролі, інтеграцію з Serum/Jupiter. Кастомний мультисиг на Anchor пишемо коли Squads не покриває логіку.
На TON — кастомна реалізація на Tact або FunC. TON multisig wallet (офіційний контракт від TON Foundation) підходить для простих випадків зберігання TON. Для jetton-ів і складної логіки — кастомний контракт з обробкою bounce повідомлень.
Терміни
Інтеграція Safe + конфігурація owners/threshold + деплой TimeelockController — 2-3 дні. Кастомний мультисиг на Solidity з повним тест-покриттям — 3-5 днів. Weighted multisig з DAO-інтеграцією — 1-2 тижня.







