Продолжаем разбирать контракт простого мультисиг кошелька, и сегодня поговорим о функциях в нем.
Хочу еще раз уточнить, что это исключительно показательный пример такого контракта, и, если вы хотите реализовать что-то с его использованием, то лучше всего искать схожие проекты, изучать их код и уже после копировать структуру к себе. Кроме того, аудит проходить в этом случае будет крайне необходимым решением.
Итак, какие же функции нам потребуются?
1. Во-первых, нам нужно как-то создавать транзакцию, которую будет необходимо подписать определенным количествам владельцев мультисига.
2. Также нам потребуется собирать подписи участников для одобрения транзакции;
3. Кроме того, возможно, некоторые захотят отозвать свою подпись после каких-то раздумий.
4. Ну, и понятное дело, исполнять ее.
В итоге у нас получается:
function submitTransaction(...) public {...}
function confirmTransaction(...) public {...}
function revokeConfirmation(...) public {...}
function executeTransaction(...) public {...}Начнем с первой.
У нас уже была структура подписи и массив для сохранения. Поэтому тут все просто:
function submitTransaction(address _to, uint256 _value, bytes memory _data) public onlyOwner {
uint256 txIndex = transactions.length;
transactions.push(
Transaction({
to: _to,
value: _value,
data: _data,
executed: false,
numConfirmations: 0
})
);
emit SubmitTransaction(msg.sender, txIndex, _to, _value, _data);
}Просто определяем id транзакции по длине текущего массива, создаем новые записи в структуре и помещаем в массив. При необходимости можно порождать событие.
Далее функции ободрения и отзыва для владельцев мультисига.
function confirmTransaction(uint256 _txIndex)
public
onlyOwner
txExists(_txIndex)
notExecuted(_txIndex)
notConfirmed(_txIndex)
{
Transaction storage transaction = transactions[_txIndex];
transaction.numConfirmations += 1;
isConfirmed[_txIndex][msg.sender] = true;
emit ConfirmTransaction(msg.sender, _txIndex);
}
function revokeConfirmation(uint256 _txIndex)
public
onlyOwner
txExists(_txIndex)
notExecuted(_txIndex)
{
Transaction storage transaction = transactions[_txIndex];
require(isConfirmed[_txIndex][msg.sender], "tx not confirmed");
transaction.numConfirmations -= 1;
isConfirmed[_txIndex][msg.sender] = false;
emit RevokeConfirmation(msg.sender, _txIndex);
}
Тут будет обязательным использование модификаторов для проверки, что такая транзакция вообще была создана ранее, что она еще не исполнена, и что данный участник еще не голосовал за нее.
Берем id транзакции, делаем проверки и изменяем numConfirmations в нужную сторону.
Далее исполнение транзакции.
function executeTransaction(uint256 _txIndex)
public
onlyOwner
txExists(_txIndex)
notExecuted(_txIndex)
{
Transaction storage transaction = transactions[_txIndex];
require(
transaction.numConfirmations >= numConfirmationsRequired,
"cannot execute tx"
);
transaction.executed = true;
(bool success,) =
transaction.to.call{value: transaction.value}(transaction.data);
require(success, "tx failed");
emit ExecuteTransaction(msg.sender, _txIndex);
}
В общем, проверяем также на существование транзакции, ее статус, количество собранных подтверждений, и в завершении используем call для ее исполнения.
Вы также можете добавить функцию получения Эфира и несколько геттеров, которые сделают использование контракта более удобным.