44 KiB
English | Русский
Мультичейн-управление переводом токенов DLE
Обзор системы
Система мультичейн-управления DLE позволяет холдерам токенов создавать предложения по переводу токенов со своего кошелька на другой адрес (или в казну) через процесс голосования во всех сетях, где развернут контракт DLE. Каждая сеть имеет независимый кворум, но предложения координируются и отображаются как единое целое.
Архитектура
Мультичейн-контракты DLE
- Один DLE может быть развернут в нескольких блокчейн-сетях (например, Sepolia, Arbitrum Sepolia, Base Sepolia)
- Каждый контракт DLE в каждой сети работает независимо
- Предложения создаются, голосуются и выполняются в каждой сети отдельно
- ID предложений уникальны для каждой сети (предложение с ID=1 в Sepolia и предложение с ID=1 в Arbitrum Sepolia - это разные предложения)
Группировка предложений
- Предложения с одинаковым описанием и инициатором группируются в одну карточку
- Карточка отображает статус предложения во всех сетях DLE
- Каждая сеть в карточке имеет свой собственный ID предложения, состояние и результаты голосования
Процесс перевода токенов
Этап 1: Создание предложения
Описание процесса
-
Пользователь заполняет форму перевода токенов:
- Описание предложения - текстовое описание цели перевода
- Адрес получателя - адрес кошелька или казны, на который будут переведены токены
- Количество токенов - количество DLE токенов для перевода
- Длительность голосования - период времени, в течение которого можно голосовать
- Ваш подключенный кошелек - автоматически заполняется адресом подключенного кошелька (токены будут отправлены с этого адреса)
-
Система определяет все сети, где развернут контракт DLE
-
Последовательное создание предложений в каждой сети:
- Для каждой сети DLE:
- Переключение MetaMask на соответствующую сеть
- Задержка 1 секунда после переключения
- Создание предложения в контракте DLE этой сети
- Получение уникального ID предложения для этой сети
- Задержка 3 секунды после подтверждения транзакции (5 секунд для Base Sepolia)
- При ошибках RPC выполняется автоматический retry с экспоненциальной задержкой (до 3 попыток)
- Для каждой сети DLE:
-
Подписи в MetaMask:
- Пользователь должен подписать транзакцию создания предложения в каждой сети DLE
- Количество подписей = количество сетей DLE
- Каждая подпись создает отдельное предложение в соответствующей сети
Технические детали
- Функция контракта:
createProposal(description, duration, operation, targetChains, timelockDelay)- Порядок параметров:
description,duration,operation,targetChains(массив),timelockDelay - targetChains: Массив ID сетей, где будет выполнена операция (обычно
[chainId]для текущей сети)
- Порядок параметров:
- Операция:
_transferTokens(sender, recipient, amount)- гдеsender= адрес инициатора предложения- Сигнатура:
_transferTokens(address,address,uint256)- все три параметра обязательны! senderполучается автоматически изsigner.getAddress()при создании предложения
- Сигнатура:
- ID предложения: Генерируется автоматически контрактом в каждой сети (начинается с 0, инкрементируется)
- Группировка: Предложения с одинаковым
descriptionиinitiatorгруппируются в одну карточку
Кодирование операции
Операция для выполнения должна быть закодирована в формате ABI (Application Binary Interface) перед передачей в createProposal.
Для операции перевода токенов _transferTokens(address,address,uint256):
- Сигнатура функции:
_transferTokens(address,address,uint256) - Селектор функции: Первые 4 байта от
keccak256(signature) - Параметры:
sender- адрес отправителя (инициатора предложения)recipient- адрес получателя токеновamount- количество токенов (в wei, т.е. количество * 10^18)
Пример кодирования (JavaScript/ethers.js):
// Способ 1: Использование Interface (рекомендуется)
const functionSignature = '_transferTokens(address,address,uint256)';
const iface = new ethers.Interface([`function ${functionSignature}`]);
const encodedOperation = iface.encodeFunctionData('_transferTokens', [
senderAddress, // адрес инициатора
recipientAddress, // адрес получателя
ethers.parseUnits(amount.toString(), 18) // количество в wei
]);
// Способ 2: Ручное кодирование
const functionSignature = '_transferTokens(address,address,uint256)';
const selectorBytes = ethers.keccak256(ethers.toUtf8Bytes(functionSignature));
const selector = '0x' + selectorBytes.slice(2, 10); // первые 4 байта
const abiCoder = ethers.AbiCoder.defaultAbiCoder();
const encodedParams = abiCoder.encode(
['address', 'address', 'uint256'],
[senderAddress, recipientAddress, ethers.parseUnits(amount.toString(), 18)]
);
const encodedOperation = ethers.concat([selector, encodedParams]);
Важные моменты:
senderдолжен совпадать с адресом инициатора предложения (проверяется в контракте при выполнении)amountОБЯЗАТЕЛЬНО передается в wei (1 токен = 10^18 wei) - используйтеethers.parseUnits(amount.toString(), 18)- КРИТИЧЕСКИ ВАЖНО:
senderдолжен определяться изsigner.getAddress()при создании предложения в каждой сети отдельно, а не один раз до цикла - Операция кодируется для каждой сети отдельно с актуальным адресом signer для этой сети
- Контракт декодирует операцию при выполнении и проверяет соответствие
senderиinitiator
КРИТИЧЕСКИ ВАЖНО - Правильная реализация:
// ✅ ПРАВИЛЬНО: Кодирование внутри цикла с актуальным адресом signer для каждой сети
async function createProposalsInAllChains(allChains, formData) {
const results = [];
for (let index = 0; index < allChains.length; index++) {
const chainId = allChains[index];
// 1. Переключаемся на нужную сеть
await switchToVotingNetwork(chainId.toString());
await new Promise(resolve => setTimeout(resolve, 1000)); // Задержка после переключения
// 2. КРИТИЧЕСКИ ВАЖНО: Получаем адрес signer для текущей сети
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const senderAddress = await signer.getAddress(); // Адрес инициатора из signer!
// 3. Кодируем операцию с актуальным адресом signer для этой сети
const transferCallData = encodeTransferTokensCall(
senderAddress, // адрес инициатора из signer (обязательно!)
formData.recipient, // адрес получателя
formData.amount // количество (будет сконвертировано в wei)
);
// 4. Создаем предложение
const proposalData = {
description: formData.description,
duration: formData.duration,
operation: transferCallData,
targetChains: [chainId],
timelockDelay: 0
};
await createProposal(contractAddress, proposalData);
}
}
function encodeTransferTokensCall(sender, recipient, amount) {
const functionSignature = '_transferTokens(address,address,uint256)';
const iface = new ethers.Interface([`function ${functionSignature}`]);
// КРИТИЧЕСКИ ВАЖНО: конвертируем amount в wei
const amountInWei = ethers.parseUnits(amount.toString(), 18);
const encodedCall = iface.encodeFunctionData('_transferTokens', [
sender, // адрес инициатора (обязательно! должен быть из signer.getAddress())
recipient, // адрес получателя
amountInWei // количество в wei (обязательно!)
]);
return encodedCall;
}
// ❌ НЕПРАВИЛЬНО: Кодирование один раз до цикла
// const transferCallData = encodeTransferTokensCall(formData.sender, ...); // НЕПРАВИЛЬНО!
// for (const chainId of allChains) {
// await createProposal(contractAddress, { operation: transferCallData, ... }); // sender может не совпасть!
// }
// ❌ НЕПРАВИЛЬНО: Отсутствует sender или amount не в wei
// const transferFunctionSelector = ethers.id("_transferTokens(address,uint256)"); // НЕПРАВИЛЬНО!
// const amount = transferData.amount; // НЕПРАВИЛЬНО! Нужна конвертация в wei
Другие поддерживаемые операции:
_addModule(bytes32,address)- добавление модуля_removeModule(bytes32)- удаление модуля_addSupportedChain(uint256)- добавление поддерживаемой сети_removeSupportedChain(uint256)- удаление поддерживаемой сети_updateVotingDurations(uint256,uint256)- обновление времени голосования_updateQuorumPercentage(uint256)- обновление процента кворума_updateDLEInfo(...)- обновление информации DLE_setLogoURI(string)- обновление URI логотипа
Результат
- Создано N предложений (по одному в каждой сети DLE)
- Каждое предложение имеет уникальный ID в своей сети
- Все предложения отображаются как одна карточка в интерфейсе
- Карточка показывает статус предложения в каждой сети
Этап 2: Голосование
Описание процесса
-
Пользователь видит карточку предложения с информацией о всех сетях DLE
-
Пользователь выбирает голос "За" или "Против"
-
Последовательное голосование во всех активных сетях:
- Система определяет все активные цепочки (state === 0 или 'active', не выполнены, не отменены)
- Для каждой активной сети:
- Переключение MetaMask на соответствующую сеть
- Задержка 1 секунда после переключения
- Проверка баланса токенов в этой сети (балансы могут отличаться в разных сетях)
- Если баланс отсутствует, сеть пропускается с предупреждением
- Голосование продолжается в других сетях
- Голосование с использованием уникального ID предложения для этой сети
- Задержка 3 секунды после подтверждения транзакции (5 секунд для Base Sepolia)
-
Подписи в MetaMask:
- Пользователь должен подписать транзакцию голосования в каждой активной сети DLE
- Количество подписей = количество активных сетей DLE
- Каждая подпись регистрирует голос в соответствующей сети
Технические детали
- Функция контракта:
vote(proposalId, support)- гдеproposalIdуникален для каждой сети - Проверка ID: Система использует
chain.id(ID предложения из конкретной сети), а не общий ID группы - Проверка баланса: Баланс токенов проверяется в каждой сети отдельно перед голосованием
- В мультичейн-системе балансы могут отличаться в разных сетях
- Если в сети нет токенов, голосование в этой сети пропускается
- Контракт также проверяет баланс через
getPastVotes()и вернет ошибку, если токенов нет
- Вес голоса: Зависит от баланса токенов голосующего в соответствующей сети
- Независимые кворумы: Каждая сеть имеет свой собственный кворум
Результат
- Голос зарегистрирован во всех активных сетях DLE
- Каждая сеть обновляет свои счетчики голосов (forVotes, againstVotes)
- Карточка предложения обновляется с новыми данными голосования
Этап 3: Выполнение предложения
Условия выполнения
КРИТИЧЕСКИ ВАЖНО: Предложение может быть выполнено только при условии, что кворум достигнут во всех сетях DLE, где предложение активно.
Условия для каждой сети:
- Состояние предложения:
ReadyForExecution(state === 5) - Кворум достигнут:
forVotes >= quorumRequired - Большинство голосов "За":
forVotes > againstVotes - Предложение не выполнено:
executed === false - Предложение не отменено:
canceled === false - Истек период голосования (если применимо)
- Истек период timelock (если применимо)
Описание процесса
-
Система проверяет, что предложение готово к выполнению во всех активных сетях DLE
-
Последовательное выполнение во всех готовых сетях:
- Для каждой сети, где предложение готово к выполнению:
- Переключение MetaMask на соответствующую сеть
- Задержка 1 секунда после переключения
- Выполнение предложения с использованием уникального ID предложения для этой сети
- Задержка 3 секунды после подтверждения транзакции (5 секунд для Base Sepolia)
- Для каждой сети, где предложение готово к выполнению:
-
Подписи в MetaMask:
- Пользователь должен подписать транзакцию выполнения в каждой готовой сети DLE
- Количество подписей = количество готовых сетей DLE
- Каждая подпись выполняет перевод токенов в соответствующей сети
Технические детали
- Функция контракта:
executeProposal(proposalId)- гдеproposalIdуникален для каждой сети - Операция перевода:
_transferTokens(sender, recipient, amount)sender= адрес инициатора предложения (проверяется в контракте)recipient= адрес получателя из предложенияamount= количество токенов из предложения
- Проверка безопасности: Контракт проверяет, что
senderсовпадает сinitiatorпредложения - Перевод токенов: Токены переводятся с кошелька инициатора, а не с баланса контракта
Результат
- Перевод токенов выполнен во всех готовых сетях DLE
- Каждая сеть независимо выполняет перевод с кошелька инициатора
- Карточка предложения обновляется, показывая статус "Выполнено" во всех сетях
Отмена предложения
Условия отмены
- Только инициатор предложения может отменить его
- Предложение должно быть активным (не выполнено, не отменено)
- Отмена возможна в любой момент до выполнения
Процесс отмены
-
Последовательная отмена во всех активных сетях:
- Для каждой активной сети:
- Переключение MetaMask на соответствующую сеть
- Задержка 1 секунда после переключения
- Отмена предложения с использованием уникального ID предложения для этой сети
- Задержка 3 секунды после подтверждения транзакции
- Для каждой активной сети:
-
Подписи в MetaMask:
- Пользователь должен подписать транзакцию отмены в каждой активной сети DLE
- Количество подписей = количество активных сетей DLE
Технические детали
- Функция контракта:
cancelProposal(proposalId, reason) - Проверка прав: Контракт проверяет, что вызывающий является инициатором предложения
Важные особенности системы
1. Уникальность ID предложений
- Каждая сеть имеет свой собственный счетчик предложений
- Предложение с ID=1 в Sepolia и предложение с ID=1 в Arbitrum Sepolia - это разные предложения
- При группировке система сохраняет ID из каждой сети отдельно
- При голосовании/выполнении/отмене используется правильный ID для каждой сети
2. Группировка предложений
- Предложения группируются по ключу:
${description}_${initiator} - Одна карточка = одно логическое предложение во всех сетях
- Карточка отображает:
- Общее описание
- Инициатора
- Список сетей с их статусами
- Результаты голосования по каждой сети
- Общий статус (активно/выполнено/отменено)
3. Независимые кворумы
- Каждая сеть имеет свой собственный кворум
- Кворум рассчитывается на основе общего предложения токенов в соответствующей сети
- Голосование в одной сети не влияет на кворум в другой сети
- Для выполнения предложения кворум должен быть достигнут во всех сетях
4. Последовательное выполнение операций
- Все операции (создание, голосование, выполнение, отмена) выполняются последовательно, а не параллельно
- Это необходимо, так как MetaMask может работать только с одной сетью одновременно
- Между операциями есть задержки для стабилизации MetaMask
- КРИТИЧЕСКИ ВАЖНО: Использование
Promise.allдля параллельного выполнения недопустимо и приведет к ошибкам
5. Обработка ошибок
- Retry для временных ошибок RPC:
- Автоматический retry до 3 попыток
- Экспоненциальная задержка (2s, 4s, 8s)
- Только для retryable ошибок (Internal JSON-RPC error, rate limiting)
- Ошибки в отдельных сетях:
- Если операция не удалась в одной сети, процесс продолжается для других сетей
- Пользователь получает сводку успешных и неудачных операций
- Обработка отсутствия баланса:
- Перед голосованием в каждой сети проверяется баланс токенов
- Если в сети нет токенов, голосование в этой сети пропускается с предупреждением
- Голосование продолжается в других сетях, где баланс есть
- Контракт также проверяет баланс и вернет ошибку
ErrNoPower, если токенов нет
6. Безопасность
- Проверка инициатора: При выполнении контракт проверяет, что
senderсовпадает сinitiator - Проверка баланса: Перед голосованием проверяется наличие токенов в каждой сети отдельно
- Балансы могут отличаться в разных сетях
- Если в сети нет токенов, голосование в этой сети пропускается
- Контракт также проверяет баланс через
getPastVotes()и вернет ошибкуErrNoPower, если токенов нет
- Проверка состояния: Перед каждой операцией проверяется актуальное состояние предложения
- Валидация данных: Все данные предложения валидируются перед отправкой в контракт
7. Перевод токенов
- Источник токенов: Токены переводятся с кошелька инициатора предложения, а не с баланса контракта
- Получатель: Может быть любой адрес (кошелек или казна)
- Количество: Указывается инициатором при создании предложения
- Проверка баланса: Контракт проверяет достаточность баланса инициатора перед выполнением
Пользовательский интерфейс
Карточка предложения
- Заголовок: Описание предложения
- Инициатор: Адрес создателя предложения
- Список сетей:
- Название сети
- Статус (Активно/Выполнено/Отменено/Истекло)
- ID предложения в этой сети
- Результаты голосования (За/Против)
- Кворум (достигнут/не достигнут)
- Действия:
- Голосовать "За" / "Против" (если активно)
- Выполнить (если готово к выполнению)
- Отменить (если инициатор)
Индикаторы статуса
- Активно: Предложение открыто для голосования
- Готово к выполнению: Кворум достигнут во всех сетях
- Выполнено: Перевод токенов выполнен во всех сетях
- Отменено: Предложение отменено инициатором
- Истекло: Истек период голосования
Технические детали реализации
Кодирование операций
Подробное описание процесса кодирования операций для создания предложений см. в разделе Кодирование операции (Этап 1: Создание предложения).
Структура данных предложения
{
id: number, // ID группы (из первой сети)
description: string, // Описание предложения
initiator: address, // Адрес инициатора
deadline: number, // Дедлайн голосования
chains: [ // Массив данных по каждой сети
{
id: number, // УНИКАЛЬНЫЙ ID для этой сети
chainId: number, // ID сети (11155111, 421614, 84532)
networkName: string, // Название сети
contractAddress: address, // Адрес контракта DLE в этой сети
state: number, // Состояние (0=Active, 3=Executed, 4=Canceled, 5=ReadyForExecution)
forVotes: bigint, // Голоса "За"
againstVotes: bigint, // Голоса "Против"
quorumRequired: bigint, // Требуемый кворум
executed: boolean, // Выполнено
canceled: boolean, // Отменено
transactionHash: string // Хеш транзакции создания
}
],
createdAt: number, // Время создания
uniqueId: string // Уникальный ключ группировки
}
Функции контракта DLE
createProposal
function createProposal(
string memory _description,
uint256 _duration,
bytes memory _operation,
uint256[] memory _targetChains,
uint256 _timelockDelay
) public returns (uint256 proposalId)
vote
function vote(uint256 _proposalId, bool _support) public
executeProposal
function executeProposal(uint256 _proposalId) public
cancelProposal
function cancelProposal(uint256 _proposalId, string memory _reason) public
_transferTokens (internal)
function _transferTokens(
address _sender,
address _recipient,
uint256 _amount
) internal
Примеры использования
Пример 0: Кодирование операции перевода токенов
Перед созданием предложения необходимо закодировать операцию. КРИТИЧЕСКИ ВАЖНО: Используйте правильную сигнатуру и конвертируйте amount в wei.
import { ethers } from 'ethers';
// Получаем адрес инициатора из signer
const signer = await provider.getSigner();
const sender = await signer.getAddress(); // Адрес инициатора (обязательно!)
// Параметры перевода
const recipient = '0x1234567890123456789012345678901234567890'; // Получатель
const amount = 100; // 100 токенов (в обычных единицах, не в wei)
// Кодирование операции
const functionSignature = '_transferTokens(address,address,uint256)';
const iface = new ethers.Interface([`function ${functionSignature}`]);
// КРИТИЧЕСКИ ВАЖНО: конвертируем amount в wei
const amountInWei = ethers.parseUnits(amount.toString(), 18); // 100 * 10^18 wei
const encodedOperation = iface.encodeFunctionData('_transferTokens', [
sender, // адрес инициатора (обязательно!)
recipient, // адрес получателя
amountInWei // количество в wei (обязательно!)
]);
// encodedOperation теперь можно использовать в createProposal
// Результат: 0x... (селектор функции + закодированные параметры)
// Создание предложения с правильным порядком параметров
const tx = await dle.createProposal(
"Перевод 100 токенов в казну", // description
86400, // duration (1 день в секундах)
encodedOperation, // operation
[chainId], // targetChains (массив!)
0 // timelockDelay
);
Результат: Закодированная операция в формате bytes, готовая для передачи в createProposal.
Частые ошибки:
- ❌ Использование
_transferTokens(address,uint256)- неправильная сигнатура - ❌ Отсутствие параметра
sender- контракт не сможет проверить инициатора - ❌ Передача
amountбез конвертации в wei - неправильное количество токенов - ❌ Неправильный порядок параметров в
createProposal- ошибка вызова контракта
Пример 1: Создание предложения в 3 сетях
- Пользователь создает предложение "Перевод 100 токенов в казну"
- Система определяет 3 сети: Sepolia, Arbitrum Sepolia, Base Sepolia
- Создаются 3 предложения:
- Sepolia: ID=5
- Arbitrum Sepolia: ID=3
- Base Sepolia: ID=7
- Все 3 предложения отображаются как одна карточка
Пример 2: Голосование
- Пользователь голосует "За" за предложение
- Система голосует в 3 сетях:
- Sepolia: vote(5, true)
- Arbitrum Sepolia: vote(3, true)
- Base Sepolia: vote(7, true)
- Каждое голосование требует отдельной подписи в MetaMask
Пример 3: Выполнение
- Кворум достигнут во всех 3 сетях
- Система выполняет предложение в 3 сетях:
- Sepolia: executeProposal(5)
- Arbitrum Sepolia: executeProposal(3)
- Base Sepolia: executeProposal(7)
- В каждой сети токены переводятся с кошелька инициатора на адрес получателя
Ограничения и особенности
Ограничения
- MetaMask может работать только с одной сетью одновременно
- Операции выполняются последовательно, что может занять время при большом количестве сетей
- Ошибки RPC могут потребовать ручного retry
Особенности
- Если предложение не создано в одной из сетей (из-за ошибки), оно все равно может быть создано в других сетях
- Голосование возможно только в тех сетях, где предложение активно
- Выполнение возможно только если кворум достигнут во всех активных сетях
Безопасность
Защита от атак
- Проверка инициатора при выполнении
- Проверка баланса перед переводом
- Независимые кворумы в каждой сети
- Валидация всех входных данных
Рекомендации
- Всегда проверяйте адрес получателя перед созданием предложения
- Убедитесь, что у вас достаточно токенов для перевода
- Проверяйте статус предложения перед голосованием/выполнением
- Следите за транзакциями в каждой сети
Известные проблемы и исправления
Исправленные критические ошибки
1. Отсутствие конвертации amount в wei
Проблема: Параметр amount передавался без конвертации в wei, что приводило к неправильному количеству токенов при выполнении.
Исправление: Добавлена обязательная конвертация через ethers.parseUnits(amount.toString(), 18) перед кодированием операции.
Файл: frontend/src/views/smartcontracts/TransferTokensFormView.vue
Статус: ✅ Исправлено
2. Неправильная сигнатура функции _transferTokens
Проблема: Использовалась неправильная сигнатура _transferTokens(address,uint256) вместо _transferTokens(address,address,uint256), что приводило к ошибкам декодирования в контракте.
Исправление: Исправлена сигнатура функции и добавлен обязательный параметр sender (адрес инициатора).
Файл: frontend/src/utils/dle-contract.js (функция createTransferTokensProposal)
Статус: ✅ Исправлено
3. Неправильный порядок параметров в createProposal
Проблема: В функцию createProposal передавался governanceChainId вместо targetChains в 4-м параметре, что нарушало сигнатуру контракта.
Исправление: Исправлен порядок параметров согласно сигнатуре контракта: description, duration, operation, targetChains, timelockDelay.
Файл: frontend/src/utils/dle-contract.js (функция createTransferTokensProposal)
Статус: ✅ Исправлено
4. Неправильное кодирование операции при создании предложений (КРИТИЧЕСКАЯ)
Проблема: Операция перевода токенов кодировалась один раз до цикла по сетям, используя адрес из formData.value.sender. По документации, sender должен определяться из signer.getAddress() при создании предложения в каждой сети, что гарантирует совпадение с инициатором предложения.
Исправление:
- Кодирование операции перемещено внутрь цикла по сетям
- Добавлено получение адреса signer для каждой сети через
await signer.getAddress() - Добавлена проверка соответствия адреса signer адресу из формы
- Операция теперь кодируется с актуальным адресом signer для каждой сети отдельно
Файл: frontend/src/views/smartcontracts/TransferTokensFormView.vue (функция submitForm)
Статус: ✅ Исправлено (2025-01-XX)
5. Параллельное выполнение в executeMultichainProposal (КРИТИЧЕСКАЯ)
Проблема: Функция executeMultichainProposal использовала Promise.all для параллельного выполнения операций во всех сетях одновременно. Это не работает с MetaMask, который может работать только с одной сетью одновременно.
Исправление:
- Заменен
Promise.allна последовательный циклfor - Добавлено переключение сетей через
switchToVotingNetworkдля каждой сети - Добавлены задержки после переключения сетей (1 секунда) и после подтверждения транзакций (3 секунды, 5 секунд для Base Sepolia)
- Добавлена фильтрация только готовых к выполнению цепочек
Файл: frontend/src/composables/useProposals.js (функция executeMultichainProposal)
Статус: ✅ Исправлено (2025-01-XX)
6. Отсутствие переключения сетей в voteOnMultichainProposal
Проблема: Функция voteOnMultichainProposal не использовала переключение сетей и проверку баланса токенов, что требовалось по документации для корректной работы в мультичейн-среде.
Исправление:
- Добавлено переключение сетей через
switchToVotingNetworkдля каждой сети - Добавлена проверка баланса токенов перед голосованием в каждой сети через
checkTokenBalance - Добавлены задержки после переключения сетей (1 секунда) и после подтверждения транзакций (3 секунды, 5 секунд для Base Sepolia)
- Добавлена фильтрация только активных цепочек
- Добавлена обработка ошибок с пропуском сетей при отсутствии баланса
Файл: frontend/src/composables/useProposals.js (функция voteOnMultichainProposal)
Статус: ✅ Исправлено (2025-01-XX)
Текущая корректная реализация
Все функции кодирования операций и мультичейн-операции теперь используют:
- ✅ Правильную сигнатуру:
_transferTokens(address,address,uint256) - ✅ Все три параметра:
sender,recipient,amount - ✅ Конвертацию amount в wei:
ethers.parseUnits(amount.toString(), 18) - ✅ Правильный порядок параметров в
createProposal - ✅ Определение
senderизsigner.getAddress()при создании предложения в каждой сети - ✅ Последовательное выполнение операций во всех мультичейн-функциях (не параллельное)
- ✅ Переключение сетей перед каждой операцией в мультичейн-функциях
- ✅ Проверку баланса токенов перед голосованием в каждой сети
Проверка корректности
Перед развертыванием убедитесь, что:
- Функция
_transferTokensкодируется с тремя параметрами (sender, recipient, amount) amountвсегда конвертируется в wei перед кодированиемsenderполучается изsigner.getAddress()при создании предложения в каждой сети и совпадает с инициатором предложения- Порядок параметров в
createProposalсоответствует сигнатуре контракта - Все мультичейн-операции (создание, голосование, выполнение) используют последовательное выполнение с переключением сетей
- При голосовании проверяется баланс токенов в каждой сети отдельно
Заключение
Система мультичейн-управления DLE обеспечивает децентрализованное управление переводом токенов через независимые кворумы в каждой сети, при этом предоставляя единый интерфейс для управления предложениями во всех сетях одновременно.
Важно: Все критические ошибки в кодировании операций исправлены. Код соответствует документации и контракту.