Система синхронізації стану між чейнами
Синхронізація стану — ширша задача, ніж bridge токенів. Смарт-контракт на чейні B повинен "знати" про стан смарт-контракту на чейні A та реагувати на зміни. Приклади: governance vote на Ethereum застосовується до протоколу на Arbitrum; NFT куплений на Polygon розблокує контент на Solana; позиція в lending на Optimism використовується як collateral на Base.
Вимагає генерального мессенджингу не тільки токенів. І вирішення проблеми finality: коли чейн B може довіряти стану з чейна A?
Проблема Finality
| Chain | Тип Finality | Час |
|---|---|---|
| Ethereum | Absolute (Casper) | ~12 хв |
| Arbitrum | Soft sequencer | ~250ms; hard ~10 хв |
| Polygon PoS | Checkpoint на L1 | ~30 хв повна finality |
| Solana | ~1.5 сек (400ms slots) | ~1.5 сек |
| Bitcoin | Probabilistic 6 блоків | ~60 хв |
Optimistic: прийняти після soft finality, challenge window для rollback. Для non-financial стану.
Conservative: чекати hard finality (L1-anchored). 10-30 хв для L2. Для фінансових даних.
ZK-Верифікація: Trustless Рішення
Цільовий чейн верифікує ZK proof про стан вихідного чейна. Storage Proof через Herodotus: доказати значення конкретного storage slot на іншому чейні. Верифікується on-chain через Merkle-Patricia proof.
Практична Архітектура: General Message Passing
Для більшості проектів ZK light client надто складний/дорогий. Використовуємо GMP через Axelar/LayerZero/Wormhole з розумними параметрами безпеки.
// Source: відправляємо стан
contract StateSender {
struct GameState {
address player;
uint256 score;
uint256 level;
uint256 timestamp;
}
function syncPlayerState(
string calldata destChain,
string calldata destAddress,
address player
) external payable {
GameState memory state = GameState({
player: player,
score: playerScores[player],
level: playerLevels[player],
timestamp: block.timestamp
});
bytes memory payload = abi.encode(state);
gasService.payNativeGasForContractCall{value: msg.value}(
address(this), destChain, destAddress, payload, msg.sender
);
gateway.callContract(destChain, destAddress, payload);
}
}
// Destination: отримуємо та застосовуємо
contract StateReceiver is AxelarExecutable {
function _execute(
string calldata sourceChain,
string calldata sourceAddress,
bytes calldata payload
) internal override {
require(authorized[sourceChain][sourceAddress], "Unauthorized");
GameState memory state = abi.decode(payload, (GameState));
require(state.timestamp > syncedState[state.player].timestamp, "Stale");
syncedState[state.player] = state;
emit StateSynced(sourceChain, state.player, state.score);
}
}
Ідемпотентність та упорядкування
Повідомлення можуть прийти не в порядку або дубльовано.
mapping(string => mapping(address => uint256)) public lastSeq;
mapping(bytes32 => bool) public processed;
function _execute(string calldata source, string calldata addr, bytes calldata payload) internal override {
bytes32 msgId = keccak256(abi.encode(source, addr, payload));
require(!processed[msgId], "Already processed");
processed[msgId] = true;
(GameState state, uint256 seq) = abi.decode(payload, (GameState, uint256));
require(seq == lastSeq[source][state.player] + 1, "Out of order");
lastSeq[source][state.player] = seq;
_applyState(state);
}
Батчинг для високочастотних оновлень
Для ігор/trading синхронізувати кожну зміну on-chain неефективно. Батчуємо оновлення off-chain, відправляємо Merkle root.
class StateSyncWorker {
private pending = new Map();
private syncInterval = 30_000; // 30 сек
queueUpdate(playerId: string, state: PlayerState) {
this.pending.set(playerId, state);
}
async flushBatch() {
if (this.pending.size === 0) return;
const batch = Array.from(this.pending.entries());
this.pending.clear();
const leaves = batch.map(([id, state]) =>
keccak256(abi.encode(id, state))
);
const merkleRoot = new MerkleTree(leaves).getRoot();
await stateSyncContract.submitBatch(merkleRoot, batch.length);
await batchStore.save(merkleRoot, batch);
}
}
Таймлайн
Базова синхронізація (2 чейни, Axelar, упорядкована): 3-4 тижні. Merkle-batched з off-chain worker + ZK proof: 8-12 тижнів. ZK light client (Succinct/Herodotus): 12-20 тижнів.







