diff --git a/packages/bridge-ui-v2/src/libs/bridge/ERC1155Bridge.ts b/packages/bridge-ui-v2/src/libs/bridge/ERC1155Bridge.ts index 221aee4558..2f246de992 100644 --- a/packages/bridge-ui-v2/src/libs/bridge/ERC1155Bridge.ts +++ b/packages/bridge-ui-v2/src/libs/bridge/ERC1155Bridge.ts @@ -109,7 +109,7 @@ export class ERC1155Bridge extends Bridge { log('Claiming ERC721 token with message', message); log('Message status', messageStatus); if (messageStatus === MessageStatus.NEW) { - const proof = await this._prover.generateProofToProcessMessage(msgHash, srcChainId, destChainId); + const proof = await this._prover.encodedSignalProof(msgHash, srcChainId, destChainId); try { if (message.gasLimit > bridgeService.erc1155GasLimitThreshold) { diff --git a/packages/bridge-ui-v2/src/libs/bridge/ERC20Bridge.ts b/packages/bridge-ui-v2/src/libs/bridge/ERC20Bridge.ts index 10dec72f12..24cf6578ea 100644 --- a/packages/bridge-ui-v2/src/libs/bridge/ERC20Bridge.ts +++ b/packages/bridge-ui-v2/src/libs/bridge/ERC20Bridge.ts @@ -192,7 +192,7 @@ export class ERC20Bridge extends Bridge { const destChainId = Number(message.destChainId); if (messageStatus === MessageStatus.NEW) { - const proof = await this._prover.generateProofToProcessMessage(msgHash, srcChainId, destChainId); + const proof = await this._prover.encodedSignalProof(msgHash, srcChainId, destChainId); try { if (message.gasLimit > bridgeService.erc20GasLimitThreshold) { diff --git a/packages/bridge-ui-v2/src/libs/bridge/ERC721Bridge.ts b/packages/bridge-ui-v2/src/libs/bridge/ERC721Bridge.ts index 73f341fdf9..708ebceeff 100644 --- a/packages/bridge-ui-v2/src/libs/bridge/ERC721Bridge.ts +++ b/packages/bridge-ui-v2/src/libs/bridge/ERC721Bridge.ts @@ -116,7 +116,7 @@ export class ERC721Bridge extends Bridge { log('Claiming ERC721 token with message', message); log('Message status', messageStatus); if (messageStatus === MessageStatus.NEW) { - const proof = await this._prover.generateProofToProcessMessage(msgHash, srcChainId, destChainId); + const proof = await this._prover.encodedSignalProof(msgHash, srcChainId, destChainId); try { if (message.gasLimit > bridgeService.erc721GasLimitThreshold) { diff --git a/packages/bridge-ui-v2/src/libs/bridge/ETHBridge.ts b/packages/bridge-ui-v2/src/libs/bridge/ETHBridge.ts index eb1efe0a82..8895eaa31c 100644 --- a/packages/bridge-ui-v2/src/libs/bridge/ETHBridge.ts +++ b/packages/bridge-ui-v2/src/libs/bridge/ETHBridge.ts @@ -114,7 +114,7 @@ export class ETHBridge extends Bridge { const destChainId = Number(message.destChainId); if (messageStatus === MessageStatus.NEW) { - const proof = await this._prover.generateProofToProcessMessage(msgHash, srcChainId, destChainId); + const proof = await this._prover.encodedSignalProof(msgHash, srcChainId, destChainId); try { txHash = await destBridgeContract.write.processMessage([message, proof]); diff --git a/packages/bridge-ui-v2/src/libs/bridge/types.ts b/packages/bridge-ui-v2/src/libs/bridge/types.ts index 217e50141d..9c51bba704 100644 --- a/packages/bridge-ui-v2/src/libs/bridge/types.ts +++ b/packages/bridge-ui-v2/src/libs/bridge/types.ts @@ -48,7 +48,7 @@ export type RelayerMessage = { From: Address; SrcChainId: number | string | bigint; DestChainId: number | string | bigint; - User: Address; + Owner: Address; To: Address; RefundTo: Address; Value: bigint; diff --git a/packages/bridge-ui-v2/src/libs/proof/BridgeProver.ts b/packages/bridge-ui-v2/src/libs/proof/BridgeProver.ts index 3b7daef37d..6dede83453 100644 --- a/packages/bridge-ui-v2/src/libs/proof/BridgeProver.ts +++ b/packages/bridge-ui-v2/src/libs/proof/BridgeProver.ts @@ -1,48 +1,74 @@ -import { encodeAbiParameters, type Hash, toHex, toRlp } from 'viem'; +import { type Address, encodeAbiParameters, type Hash, type Hex, toHex } from 'viem'; import { routingContractsMap } from '$bridgeConfig'; import { MessageStatus } from '$libs/bridge'; import { InvalidProofError } from '$libs/error'; import { Prover } from './Prover'; -import type { EthGetProofResponse } from './types'; export class BridgeProver extends Prover { constructor() { super(); } - private _getSignalProof(proof: EthGetProofResponse, blockHeight: bigint) { - // RLP encode the proof together for LibTrieProof to decode - const encodedProof = toRlp(proof.storageProof[0].proof); + // Reference: EncodedSignalProof in relayer/proof/encoded_signal_proof.go + // protocol/contracts/signal/SignalService.sol + private _encodedSignalProof(crossChainSyncAddress: Address, rlpEncodedStorageProof: Hex, blockHeight: bigint) { + type Hop = { + signalRootRelay: Address; + signalRoot: Hex; + storageProof: Hex; + }; - // Encode the SignalProof struct: - // struct SignalProof { - // uint256 height; - // bytes proof; - // } + const hops: Hop[] = []; const signalProof = encodeAbiParameters( - // ['tuple(uint256 height, bytes proof)'], [ { type: 'tuple', components: [ - { name: 'height', type: 'uint256' }, - { name: 'proof', type: 'bytes' }, + { name: 'crossChainSync', type: 'address' }, + { name: 'height', type: 'uint64' }, + { name: 'storageProof', type: 'bytes' }, + { + type: 'tuple[]', + name: 'hops', + components: [ + { + type: 'address', + name: 'signalRootRelay', + }, + { + type: 'bytes32', + name: 'signalRoot', + }, + { + type: 'bytes', + name: 'storageProof', + }, + ], + }, ], }, ], - [{ height: blockHeight, proof: encodedProof }], + [ + { + crossChainSync: crossChainSyncAddress, + height: blockHeight, + storageProof: rlpEncodedStorageProof, + hops: hops, + }, + ], ); return signalProof; } - async generateProofToProcessMessage(msgHash: Hash, srcChainId: number, destChainId: number) { + async encodedSignalProof(msgHash: Hash, srcChainId: number, destChainId: number) { const srcBridgeAddress = routingContractsMap[srcChainId][destChainId].bridgeAddress; const srcSignalServiceAddress = routingContractsMap[srcChainId][destChainId].signalServiceAddress; + const destCrossChainSyncAddress = routingContractsMap[destChainId][srcChainId].crossChainSyncAddress; - const { proof, block } = await this.generateProof({ + const { rlpEncodedStorageProof, block } = await this.encodedStorageProof({ msgHash, clientChainId: srcChainId, contractAddress: srcBridgeAddress, @@ -50,19 +76,15 @@ export class BridgeProver extends Prover { proofForAccountAddress: srcSignalServiceAddress, }); - // Value must be 0x1 => isSignalSent - if (proof.storageProof[0].value !== toHex(true)) { - throw new InvalidProofError('storage proof value is not 1'); - } - - return this._getSignalProof(proof, block.number as bigint); + return this._encodedSignalProof(destCrossChainSyncAddress, rlpEncodedStorageProof, block.number as bigint); } async generateProofToRelease(msgHash: Hash, srcChainId: number, destChainId: number) { const srcBridgeAddress = routingContractsMap[srcChainId][destChainId].bridgeAddress; const destBridgeAddress = routingContractsMap[destChainId][srcChainId].bridgeAddress; + const srcCrossChainSyncAddress = routingContractsMap[srcChainId][destChainId].crossChainSyncAddress; - const { proof, block } = await this.generateProof({ + const { proof, rlpEncodedStorageProof, block } = await this.encodedStorageProof({ msgHash, clientChainId: destChainId, contractAddress: srcBridgeAddress, @@ -75,6 +97,6 @@ export class BridgeProver extends Prover { throw new InvalidProofError('storage proof value is not FAILED'); } - return this._getSignalProof(proof, block.number as bigint); + return this._encodedSignalProof(srcCrossChainSyncAddress, rlpEncodedStorageProof, block.number as bigint); } } diff --git a/packages/bridge-ui-v2/src/libs/proof/Prover.ts b/packages/bridge-ui-v2/src/libs/proof/Prover.ts index 6379d1b545..ac3c109b5e 100644 --- a/packages/bridge-ui-v2/src/libs/proof/Prover.ts +++ b/packages/bridge-ui-v2/src/libs/proof/Prover.ts @@ -1,9 +1,9 @@ import { getContract, type GetContractResult, type PublicClient } from '@wagmi/core'; -import { type Address, encodePacked, type Hex, keccak256 } from 'viem'; +import { type Address, encodePacked, type Hex, keccak256, toHex, toRlp } from 'viem'; import { crossChainSyncABI } from '$abi'; import { routingContractsMap } from '$bridgeConfig'; -import { PendingBlockError } from '$libs/error'; +import { InvalidProofError, PendingBlockError } from '$libs/error'; import { getLogger } from '$libs/util/logger'; import { publicClient } from '$libs/wagmi'; @@ -12,19 +12,22 @@ import type { ClientWithEthGetProofRequest, GenerateProofArgs } from './types'; const log = getLogger('proof:Prover'); export class Prover { - protected async _getKey(contractAddress: Address, msgHash: Hex) { - return keccak256(encodePacked(['address', 'bytes32'], [contractAddress, msgHash])); + async getSignalSlot(chainId: number, contractAddress: Address, msgHash: Hex) { + return keccak256( + encodePacked(['string', 'uint64', 'address', 'bytes32'], ['SIGNAL', BigInt(chainId), contractAddress, msgHash]), + ); } - protected async _getLatestBlock( + protected async getLatestBlockFromGetSyncedSnippet( client: PublicClient, crossChainSyncContract: GetContractResult, ) { - const { blockHash } = await crossChainSyncContract.read.getSyncedSnippet([BigInt(0)]); - return client.getBlock({ blockHash }); + const syncedSnippet = await crossChainSyncContract.read.getSyncedSnippet([BigInt(0)]); + const latestBlockHash = syncedSnippet['blockHash']; + return client.getBlock({ blockHash: latestBlockHash }); } - async generateProof(args: GenerateProofArgs) { + async encodedStorageProof(args: GenerateProofArgs) { const { msgHash, clientChainId, contractAddress, crossChainSyncChainId, proofForAccountAddress } = args; const crossChainSyncAddress = routingContractsMap[crossChainSyncChainId][clientChainId].crossChainSyncAddress; @@ -38,13 +41,13 @@ export class Prover { }); const client = publicClient({ chainId: clientChainId }); - const block = await this._getLatestBlock(client, crossChainSyncContract); + const block = await this.getLatestBlockFromGetSyncedSnippet(client, crossChainSyncContract); if (block.hash === null || block.number === null) { throw new PendingBlockError('block is pending'); } - const key = await this._getKey(contractAddress, msgHash); + const key = await this.getSignalSlot(clientChainId, contractAddress, msgHash); // Unfortunately, since this method is stagnant, it hasn't been included into Viem lib // as supported methods. Still stupported by Alchmey, Infura and others. @@ -68,6 +71,13 @@ export class Prover { log('Proof from eth_getProof', proof); - return { proof, block }; + if (proof.storageProof[0].value !== toHex(true)) { + throw new InvalidProofError('storage proof value is not 1'); + } + + // RLP encode the proof together for LibTrieProof to decode + const rlpEncodedStorageProof = toRlp(proof.storageProof[0].proof); + + return { proof, rlpEncodedStorageProof, block }; } } diff --git a/packages/bridge-ui-v2/src/libs/relayer/RelayerAPIService.ts b/packages/bridge-ui-v2/src/libs/relayer/RelayerAPIService.ts index 2d6d95491f..62a5fd74bd 100644 --- a/packages/bridge-ui-v2/src/libs/relayer/RelayerAPIService.ts +++ b/packages/bridge-ui-v2/src/libs/relayer/RelayerAPIService.ts @@ -189,7 +189,7 @@ export class RelayerAPIService { to: tx.data.Message.To, data, memo: tx.data.Message.Memo, - owner: tx.data.Message.User, + owner: tx.data.Message.Owner, from: tx.data.Message.From, gasLimit: BigInt(tx.data.Message.GasLimit), value: BigInt(tx.data.Message.Value),