diff --git a/contracts/scripts/DeployBeefyClientWrapper.sol b/contracts/scripts/DeployBeefyClientWrapper.sol new file mode 100644 index 000000000..3fbaacee2 --- /dev/null +++ b/contracts/scripts/DeployBeefyClientWrapper.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +pragma solidity 0.8.33; + +import {Script} from "forge-std/Script.sol"; +import {console} from "forge-std/console.sol"; +import {BeefyClientWrapper} from "../src/BeefyClientWrapper.sol"; + +contract DeployBeefyClientWrapper is Script { + struct Config { + address beefyClient; + address owner; + uint256 maxGasPrice; + uint256 maxRefundAmount; + uint256 refundTarget; + } + + function readConfig() internal returns (Config memory config) { + config = Config({ + beefyClient: vm.envAddress("BEEFY_CLIENT_ADDRESS"), + owner: vm.envAddress("WRAPPER_OWNER"), + maxGasPrice: vm.envOr("MAX_GAS_PRICE", uint256(100 gwei)), + maxRefundAmount: vm.envOr("MAX_REFUND_AMOUNT", uint256(0.05 ether)), + refundTarget: vm.envOr("REFUND_TARGET", uint256(350)) // ~35 min for 100% refund + }); + } + + function run() public { + vm.startBroadcast(); + + Config memory config = readConfig(); + + BeefyClientWrapper wrapper = new BeefyClientWrapper( + config.beefyClient, + config.owner, + config.maxGasPrice, + config.maxRefundAmount, + config.refundTarget + ); + + console.log("BeefyClientWrapper:", address(wrapper)); + + vm.stopBroadcast(); + } +} diff --git a/contracts/scripts/DeployLocal.sol b/contracts/scripts/DeployLocal.sol index 2f39d2d28..d52c59f91 100644 --- a/contracts/scripts/DeployLocal.sol +++ b/contracts/scripts/DeployLocal.sol @@ -5,6 +5,7 @@ pragma solidity 0.8.33; import {WETH9} from "canonical-weth/WETH9.sol"; import {Script} from "forge-std/Script.sol"; import {BeefyClient} from "../src/BeefyClient.sol"; +import {BeefyClientWrapper} from "../src/BeefyClientWrapper.sol"; import {IGatewayV1} from "../src/v1/IGateway.sol"; import {GatewayProxy} from "../src/GatewayProxy.sol"; import {Gateway} from "../src/Gateway.sol"; @@ -61,6 +62,18 @@ contract DeployLocal is Script { next ); + // Deploy BeefyClientWrapper + BeefyClientWrapper beefyClientWrapper = new BeefyClientWrapper( + address(beefyClient), + deployer, + vm.envUint("BEEFY_WRAPPER_MAX_GAS_PRICE"), + vm.envUint("BEEFY_WRAPPER_MAX_REFUND_AMOUNT"), + vm.envUint("BEEFY_WRAPPER_REFUND_TARGET") + ); + + // Fund wrapper for refunds + payable(address(beefyClientWrapper)).call{value: vm.envUint("BEEFY_WRAPPER_INITIAL_DEPOSIT")}(""); + uint8 foreignTokenDecimals = uint8(vm.envUint("FOREIGN_TOKEN_DECIMALS")); uint128 maxDestinationFee = uint128(vm.envUint("RESERVE_TRANSFER_MAX_DESTINATION_FEE")); @@ -94,7 +107,7 @@ contract DeployLocal is Script { // For testing call contract new HelloWorld(); - // Deploy test token for registration testing + // Deploy test token for registration testing new Token("Test Token", "TEST", 18); // Fund the gateway proxy contract. Used to reward relayers diff --git a/contracts/src/BeefyClient.sol b/contracts/src/BeefyClient.sol index 4f28c85a0..1843c97fe 100644 --- a/contracts/src/BeefyClient.sol +++ b/contracts/src/BeefyClient.sol @@ -465,6 +465,14 @@ contract BeefyClient { return Bitfield.createBitfield(bitsToSet, length); } + /** + * @dev Compute the hash of a commitment + * @param commitment the commitment to hash + */ + function computeCommitmentHash(Commitment calldata commitment) external pure returns (bytes32) { + return keccak256(encodeCommitment(commitment)); + } + /** * @dev Helper to create a final bitfield, with subsampled validator selections * @param commitmentHash contains the commitmentHash signed by the validators diff --git a/contracts/src/BeefyClientWrapper.sol b/contracts/src/BeefyClientWrapper.sol new file mode 100644 index 000000000..bc1ae69b6 --- /dev/null +++ b/contracts/src/BeefyClientWrapper.sol @@ -0,0 +1,267 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +pragma solidity 0.8.33; + +import {IBeefyClient} from "./interfaces/IBeefyClient.sol"; + +/** + * @title BeefyClientWrapper + * @dev Forwards BeefyClient submissions and refunds gas costs to relayers. + * Anyone can relay. Refunds are only paid when the relayer advances the light + * client by at least `refundTarget` blocks, ensuring meaningful progress. + */ +contract BeefyClientWrapper { + event GasCredited(address indexed relayer, bytes32 indexed commitmentHash, uint256 gasUsed); + event SubmissionRefunded(address indexed relayer, uint256 progress, uint256 refundAmount, uint256 totalGasUsed); + event FundsDeposited(address indexed depositor, uint256 amount); + event FundsWithdrawn(address indexed recipient, uint256 amount); + + error Unauthorized(); + error InvalidAddress(); + error NotTicketOwner(); + error TransferFailed(); + + address public owner; + IBeefyClient public beefyClient; + + // Ticket tracking (for multi-step submission) + mapping(bytes32 => address) public ticketOwner; + mapping(bytes32 => uint256) public creditedGas; + + // Refund configuration + uint256 public maxGasPrice; + uint256 public maxRefundAmount; + + // Progress-based refund target + uint256 public refundTarget; // Blocks of progress for 100% gas refund (e.g., 350 = ~35 min) + + // Highest commitment block number currently in progress (helps relayers avoid duplicate work) + uint256 public highestPendingBlock; + uint256 public highestPendingBlockTimestamp; + + constructor( + address _beefyClient, + address _owner, + uint256 _maxGasPrice, + uint256 _maxRefundAmount, + uint256 _refundTarget + ) { + if (_beefyClient == address(0) || _owner == address(0)) { + revert InvalidAddress(); + } + + beefyClient = IBeefyClient(_beefyClient); + owner = _owner; + maxGasPrice = _maxGasPrice; + maxRefundAmount = _maxRefundAmount; + refundTarget = _refundTarget; + } + + /* Beefy Client Proxy Functions */ + + function submitInitial( + IBeefyClient.Commitment calldata commitment, + uint256[] calldata bitfield, + IBeefyClient.ValidatorProof calldata proof + ) external { + uint256 startGas = gasleft(); + + beefyClient.submitInitial(commitment, bitfield, proof); + + bytes32 commitmentHash = beefyClient.computeCommitmentHash(commitment); + ticketOwner[commitmentHash] = msg.sender; + + // Track highest pending block so other relayers can check before starting + if (commitment.blockNumber > highestPendingBlock) { + highestPendingBlock = commitment.blockNumber; + highestPendingBlockTimestamp = block.timestamp; + } + + _creditGas(startGas, commitmentHash); + } + + function commitPrevRandao(bytes32 commitmentHash) external { + uint256 startGas = gasleft(); + + if (ticketOwner[commitmentHash] != msg.sender) { + revert NotTicketOwner(); + } + + beefyClient.commitPrevRandao(commitmentHash); + + _creditGas(startGas, commitmentHash); + } + + function submitFinal( + IBeefyClient.Commitment calldata commitment, + uint256[] calldata bitfield, + IBeefyClient.ValidatorProof[] calldata proofs, + IBeefyClient.MMRLeaf calldata leaf, + bytes32[] calldata leafProof, + uint256 leafProofOrder + ) external { + uint256 startGas = gasleft(); + + // Capture previous state for progress calculation + uint64 previousBeefyBlock = beefyClient.latestBeefyBlock(); + + bytes32 commitmentHash = beefyClient.computeCommitmentHash(commitment); + if (ticketOwner[commitmentHash] != msg.sender) { + revert NotTicketOwner(); + } + + beefyClient.submitFinal(commitment, bitfield, proofs, leaf, leafProof, leafProofOrder); + + // Calculate progress + uint256 progress = commitment.blockNumber - previousBeefyBlock; + + // Clear highest pending block if light client has caught up + if (beefyClient.latestBeefyBlock() >= highestPendingBlock) { + highestPendingBlock = 0; + highestPendingBlockTimestamp = 0; + } + + uint256 previousGas = creditedGas[commitmentHash]; + delete creditedGas[commitmentHash]; + delete ticketOwner[commitmentHash]; + + _refundWithProgress(startGas, previousGas, progress); + } + + function submitFiatShamir( + IBeefyClient.Commitment calldata commitment, + uint256[] calldata bitfield, + IBeefyClient.ValidatorProof[] calldata proofs, + IBeefyClient.MMRLeaf calldata leaf, + bytes32[] calldata leafProof, + uint256 leafProofOrder + ) external { + beefyClient.submitFiatShamir(commitment, bitfield, proofs, leaf, leafProof, leafProofOrder); + + // Clear highest pending block if light client has caught up + if (beefyClient.latestBeefyBlock() >= highestPendingBlock) { + highestPendingBlock = 0; + highestPendingBlockTimestamp = 0; + } + } + + /** + * @dev Abandon a ticket. Useful if another relayer is competing for the same commitment. + * Credited gas is forfeited when clearing a ticket. + */ + function clearTicket(bytes32 commitmentHash) external { + if (ticketOwner[commitmentHash] != msg.sender) { + revert NotTicketOwner(); + } + + delete creditedGas[commitmentHash]; + delete ticketOwner[commitmentHash]; + } + + /* Internal Functions */ + + function _checkOwner() internal view { + if (msg.sender != owner) { + revert Unauthorized(); + } + } + + function _creditGas(uint256 startGas, bytes32 commitmentHash) internal { + uint256 gasUsed = startGas - gasleft() + 21000; + creditedGas[commitmentHash] += gasUsed; + emit GasCredited(msg.sender, commitmentHash, gasUsed); + } + + /** + * @dev Calculate and send refund if progress meets threshold. + * + * Refund if progress >= refundTarget. + */ + function _refundWithProgress(uint256 startGas, uint256 previousGas, uint256 progress) internal { + if (progress < refundTarget) { + return; + } + + uint256 currentGas = startGas - gasleft() + 21000; + uint256 totalGasUsed = currentGas + previousGas; + uint256 effectiveGasPrice = tx.gasprice < maxGasPrice ? tx.gasprice : maxGasPrice; + uint256 refundAmount = totalGasUsed * effectiveGasPrice; + + if (refundAmount > maxRefundAmount) { + refundAmount = maxRefundAmount; + } + + if (refundAmount > 0 && address(this).balance >= refundAmount) { + (bool success,) = payable(msg.sender).call{value: refundAmount}(""); + if (success) { + emit SubmissionRefunded(msg.sender, progress, refundAmount, totalGasUsed); + } + } + } + + /* Admin Functions */ + + function setMaxGasPrice(uint256 _maxGasPrice) external { + _checkOwner(); + maxGasPrice = _maxGasPrice; + } + + function setMaxRefundAmount(uint256 _maxRefundAmount) external { + _checkOwner(); + maxRefundAmount = _maxRefundAmount; + } + + function setRefundTarget(uint256 _refundTarget) external { + _checkOwner(); + refundTarget = _refundTarget; + } + + function withdrawFunds(address payable recipient, uint256 amount) external { + _checkOwner(); + if (recipient == address(0)) { + revert InvalidAddress(); + } + + (bool success,) = recipient.call{value: amount}(""); + if (!success) { + revert TransferFailed(); + } + + emit FundsWithdrawn(recipient, amount); + } + + function transferOwnership(address newOwner) external { + _checkOwner(); + if (newOwner == address(0)) { + revert InvalidAddress(); + } + owner = newOwner; + } + + /* View Functions */ + + /** + * @dev Calculate expected refund for a given progress. + * Useful for relayers to estimate payouts before submitting. + */ + function estimatePayout(uint256 gasUsed, uint256 gasPrice, uint256 progress) + external + view + returns (uint256 refundAmount) + { + if (progress < refundTarget) { + return 0; + } + + uint256 effectiveGasPrice = gasPrice < maxGasPrice ? gasPrice : maxGasPrice; + refundAmount = gasUsed * effectiveGasPrice; + + if (refundAmount > maxRefundAmount) { + refundAmount = maxRefundAmount; + } + } + + receive() external payable { + emit FundsDeposited(msg.sender, msg.value); + } +} diff --git a/contracts/src/interfaces/IBeefyClient.sol b/contracts/src/interfaces/IBeefyClient.sol new file mode 100644 index 000000000..76d99f0be --- /dev/null +++ b/contracts/src/interfaces/IBeefyClient.sol @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +pragma solidity 0.8.33; + +interface IBeefyClient { + /* Types */ + + struct PayloadItem { + bytes2 payloadID; + bytes data; + } + + struct Commitment { + uint32 blockNumber; + uint64 validatorSetID; + PayloadItem[] payload; + } + + struct ValidatorProof { + uint8 v; + bytes32 r; + bytes32 s; + uint256 index; + address account; + bytes32[] proof; + } + + struct MMRLeaf { + uint8 version; + uint32 parentNumber; + bytes32 parentHash; + uint64 nextAuthoritySetID; + uint32 nextAuthoritySetLen; + bytes32 nextAuthoritySetRoot; + bytes32 parachainHeadsRoot; + } + + function latestBeefyBlock() external view returns (uint64); + + function latestMMRRoot() external view returns (bytes32); + + function submitInitial( + Commitment calldata commitment, + uint256[] calldata bitfield, + ValidatorProof calldata proof + ) external; + + function commitPrevRandao(bytes32 commitmentHash) external; + + function submitFinal( + Commitment calldata commitment, + uint256[] calldata bitfield, + ValidatorProof[] calldata proofs, + MMRLeaf calldata leaf, + bytes32[] calldata leafProof, + uint256 leafProofOrder + ) external; + + function createFinalBitfield(bytes32 commitmentHash, uint256[] calldata bitfield) + external + view + returns (uint256[] memory); + + function createInitialBitfield(uint256[] calldata bitsToSet, uint256 length) + external + pure + returns (uint256[] memory); + + function computeCommitmentHash(Commitment calldata commitment) external pure returns (bytes32); + + function randaoCommitDelay() external view returns (uint256); + + function currentValidatorSet() + external + view + returns (uint128 id, uint128 length, bytes32 root); + + function nextValidatorSet() + external + view + returns (uint128 id, uint128 length, bytes32 root); + + function createFiatShamirFinalBitfield(Commitment calldata commitment, uint256[] calldata bitfield) + external + view + returns (uint256[] memory); + + function submitFiatShamir( + Commitment calldata commitment, + uint256[] calldata bitfield, + ValidatorProof[] calldata proofs, + MMRLeaf calldata leaf, + bytes32[] calldata leafProof, + uint256 leafProofOrder + ) external; +} diff --git a/contracts/test/BeefyClient.t.sol b/contracts/test/BeefyClient.t.sol index a02cdbda9..a154be9b8 100644 --- a/contracts/test/BeefyClient.t.sol +++ b/contracts/test/BeefyClient.t.sol @@ -879,4 +879,11 @@ contract BeefyClientTest is Test { commitment, bitfield, fiatShamirValidatorProofs, mmrLeaf, mmrLeafProofs, leafProofOrder ); } + + function testComputeCommitmentHash() public { + BeefyClient.Commitment memory commitment = initialize(setId); + bytes32 computedHash = beefyClient.computeCommitmentHash(commitment); + // commitHash is loaded from the test data file and should match + assertEq(computedHash, commitHash); + } } diff --git a/contracts/test/BeefyClientWrapper.t.sol b/contracts/test/BeefyClientWrapper.t.sol new file mode 100644 index 000000000..a740fb5e0 --- /dev/null +++ b/contracts/test/BeefyClientWrapper.t.sol @@ -0,0 +1,856 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity 0.8.33; + +import {Test} from "forge-std/Test.sol"; +import {console} from "forge-std/console.sol"; + +import {BeefyClientWrapper} from "../src/BeefyClientWrapper.sol"; +import {IBeefyClient} from "../src/interfaces/IBeefyClient.sol"; +import {ScaleCodec} from "../src/utils/ScaleCodec.sol"; + +/** + * @title MockBeefyClient + * @dev A simplified mock of BeefyClient for testing the wrapper + */ +contract MockBeefyClient { + uint64 public latestBeefyBlock; + bytes32 public latestMMRRoot; + + // Track submissions for verification + uint256 public submitInitialCount; + uint256 public commitPrevRandaoCount; + uint256 public submitFinalCount; + + mapping(bytes32 => bool) public ticketExists; + + constructor(uint64 _initialBeefyBlock) { + latestBeefyBlock = _initialBeefyBlock; + } + + function submitInitial( + IBeefyClient.Commitment calldata commitment, + uint256[] calldata, + IBeefyClient.ValidatorProof calldata + ) external { + // Just track that it was called and create a ticket + submitInitialCount++; + bytes32 commitmentHash = keccak256(_encodeCommitment(commitment)); + ticketExists[commitmentHash] = true; + } + + function commitPrevRandao(bytes32 commitmentHash) external { + require(ticketExists[commitmentHash], "No ticket"); + commitPrevRandaoCount++; + } + + function submitFinal( + IBeefyClient.Commitment calldata commitment, + uint256[] calldata, + IBeefyClient.ValidatorProof[] calldata, + IBeefyClient.MMRLeaf calldata, + bytes32[] calldata, + uint256 + ) external { + bytes32 commitmentHash = keccak256(_encodeCommitment(commitment)); + require(ticketExists[commitmentHash], "No ticket"); + submitFinalCount++; + delete ticketExists[commitmentHash]; + + // Update latest beefy block + latestBeefyBlock = commitment.blockNumber; + } + + function createFinalBitfield(bytes32, uint256[] calldata bitfield) + external + pure + returns (uint256[] memory) + { + return bitfield; + } + + function createInitialBitfield(uint256[] calldata, uint256) external pure returns (uint256[] memory) { + return new uint256[](1); + } + + function randaoCommitDelay() external pure returns (uint256) { + return 4; + } + + function currentValidatorSet() external pure returns (uint128 id, uint128 length, bytes32 root) { + return (1, 100, bytes32(0)); + } + + function nextValidatorSet() external pure returns (uint128 id, uint128 length, bytes32 root) { + return (2, 100, bytes32(0)); + } + + function computeCommitmentHash(IBeefyClient.Commitment calldata commitment) external pure returns (bytes32) { + return keccak256(_encodeCommitment(commitment)); + } + + function setLatestBeefyBlock(uint64 _block) external { + latestBeefyBlock = _block; + } + + uint256 public submitFiatShamirCount; + + function submitFiatShamir( + IBeefyClient.Commitment calldata commitment, + uint256[] calldata, + IBeefyClient.ValidatorProof[] calldata, + IBeefyClient.MMRLeaf calldata, + bytes32[] calldata, + uint256 + ) external { + submitFiatShamirCount++; + latestBeefyBlock = commitment.blockNumber; + } + + function _encodeCommitment(IBeefyClient.Commitment calldata commitment) + internal + pure + returns (bytes memory) + { + return bytes.concat( + _encodeCommitmentPayload(commitment.payload), + ScaleCodec.encodeU32(commitment.blockNumber), + ScaleCodec.encodeU64(commitment.validatorSetID) + ); + } + + function _encodeCommitmentPayload(IBeefyClient.PayloadItem[] calldata items) + internal + pure + returns (bytes memory) + { + bytes memory payload = ScaleCodec.checkedEncodeCompactU32(items.length); + for (uint256 i = 0; i < items.length; i++) { + payload = bytes.concat( + payload, + items[i].payloadID, + ScaleCodec.checkedEncodeCompactU32(items[i].data.length), + items[i].data + ); + } + return payload; + } +} + +contract BeefyClientWrapperTest is Test { + BeefyClientWrapper wrapper; + MockBeefyClient mockBeefyClient; + + address owner = address(0x1); + address relayer1 = address(0x2); + address relayer2 = address(0x3); + address anyone = address(0x5); + + uint256 constant MAX_GAS_PRICE = 100 gwei; + uint256 constant MAX_REFUND_AMOUNT = 0.05 ether; + uint256 constant REFUND_TARGET = 350; // ~35 min for 100% refund + uint256 constant INITIAL_BEEFY_BLOCK = 1000; + + function setUp() public { + // Deploy mock BeefyClient + mockBeefyClient = new MockBeefyClient(uint64(INITIAL_BEEFY_BLOCK)); + + // Deploy wrapper directly (no proxy) + wrapper = new BeefyClientWrapper( + address(mockBeefyClient), + owner, + MAX_GAS_PRICE, + MAX_REFUND_AMOUNT, + REFUND_TARGET + ); + + // Fund the wrapper with ETH for refunds + vm.deal(address(wrapper), 100 ether); + } + + /* Helper Functions */ + + function createCommitment(uint32 blockNumber) internal pure returns (IBeefyClient.Commitment memory) { + IBeefyClient.PayloadItem[] memory payload = new IBeefyClient.PayloadItem[](1); + payload[0] = IBeefyClient.PayloadItem(bytes2("mh"), abi.encodePacked(bytes32(0))); + return IBeefyClient.Commitment(blockNumber, 1, payload); + } + + function createValidatorProof() internal pure returns (IBeefyClient.ValidatorProof memory) { + bytes32[] memory proof = new bytes32[](0); + return IBeefyClient.ValidatorProof(27, bytes32(0), bytes32(0), 0, address(0), proof); + } + + function createValidatorProofs(uint256 count) internal pure returns (IBeefyClient.ValidatorProof[] memory) { + IBeefyClient.ValidatorProof[] memory proofs = new IBeefyClient.ValidatorProof[](count); + for (uint256 i = 0; i < count; i++) { + bytes32[] memory proof = new bytes32[](0); + proofs[i] = IBeefyClient.ValidatorProof(27, bytes32(0), bytes32(0), i, address(0), proof); + } + return proofs; + } + + function createMMRLeaf() internal pure returns (IBeefyClient.MMRLeaf memory) { + return IBeefyClient.MMRLeaf(1, 0, bytes32(0), 1, 100, bytes32(0), bytes32(0)); + } + + function computeCommitmentHash(IBeefyClient.Commitment memory commitment) internal pure returns (bytes32) { + bytes memory payload = ScaleCodec.checkedEncodeCompactU32(commitment.payload.length); + for (uint256 i = 0; i < commitment.payload.length; i++) { + payload = bytes.concat( + payload, + commitment.payload[i].payloadID, + ScaleCodec.checkedEncodeCompactU32(commitment.payload[i].data.length), + commitment.payload[i].data + ); + } + return keccak256( + bytes.concat( + payload, + ScaleCodec.encodeU32(commitment.blockNumber), + ScaleCodec.encodeU64(commitment.validatorSetID) + ) + ); + } + + /* Initialization Tests */ + + function test_initialization() public { + assertEq(wrapper.owner(), owner); + assertEq(address(wrapper.beefyClient()), address(mockBeefyClient)); + assertEq(wrapper.maxGasPrice(), MAX_GAS_PRICE); + assertEq(wrapper.maxRefundAmount(), MAX_REFUND_AMOUNT); + assertEq(wrapper.refundTarget(), REFUND_TARGET); + } + + function test_invalidBeefyClientAddress() public { + vm.expectRevert(BeefyClientWrapper.InvalidAddress.selector); + new BeefyClientWrapper( + address(0), + owner, + MAX_GAS_PRICE, + MAX_REFUND_AMOUNT, + REFUND_TARGET + ); + } + + function test_invalidOwnerAddress() public { + vm.expectRevert(BeefyClientWrapper.InvalidAddress.selector); + new BeefyClientWrapper( + address(mockBeefyClient), + address(0), + MAX_GAS_PRICE, + MAX_REFUND_AMOUNT, + REFUND_TARGET + ); + } + + /* Submission Flow Tests */ + + function test_fullSubmissionFlow() public { + uint32 newBlockNumber = uint32(INITIAL_BEEFY_BLOCK + 100); + IBeefyClient.Commitment memory commitment = createCommitment(newBlockNumber); + uint256[] memory bitfield = new uint256[](1); + IBeefyClient.ValidatorProof memory proof = createValidatorProof(); + IBeefyClient.ValidatorProof[] memory proofs = createValidatorProofs(1); + IBeefyClient.MMRLeaf memory leaf = createMMRLeaf(); + bytes32[] memory leafProof = new bytes32[](0); + + // Step 1: submitInitial + vm.prank(relayer1); + wrapper.submitInitial(commitment, bitfield, proof); + assertEq(mockBeefyClient.submitInitialCount(), 1); + + // Step 2: commitPrevRandao + bytes32 commitmentHash = computeCommitmentHash(commitment); + vm.prank(relayer1); + wrapper.commitPrevRandao(commitmentHash); + assertEq(mockBeefyClient.commitPrevRandaoCount(), 1); + + // Step 3: submitFinal + vm.prank(relayer1); + wrapper.submitFinal(commitment, bitfield, proofs, leaf, leafProof, 0); + assertEq(mockBeefyClient.submitFinalCount(), 1); + } + + function test_anyoneCanSubmit() public { + // Anyone can submit - no whitelist + uint32 newBlockNumber = uint32(INITIAL_BEEFY_BLOCK + 100); + IBeefyClient.Commitment memory commitment = createCommitment(newBlockNumber); + uint256[] memory bitfield = new uint256[](1); + IBeefyClient.ValidatorProof memory proof = createValidatorProof(); + + // Random address can submit + vm.prank(anyone); + wrapper.submitInitial(commitment, bitfield, proof); + + assertEq(mockBeefyClient.submitInitialCount(), 1); + } + + function test_onlyTicketOwnerCanCommitPrevRandao() public { + uint32 newBlockNumber = uint32(INITIAL_BEEFY_BLOCK + 100); + IBeefyClient.Commitment memory commitment = createCommitment(newBlockNumber); + uint256[] memory bitfield = new uint256[](1); + IBeefyClient.ValidatorProof memory proof = createValidatorProof(); + + // relayer1 submits initial + vm.prank(relayer1); + wrapper.submitInitial(commitment, bitfield, proof); + + // relayer2 tries to commit (should fail) + bytes32 commitmentHash = computeCommitmentHash(commitment); + vm.prank(relayer2); + vm.expectRevert(BeefyClientWrapper.NotTicketOwner.selector); + wrapper.commitPrevRandao(commitmentHash); + } + + function test_onlyTicketOwnerCanSubmitFinal() public { + uint32 newBlockNumber = uint32(INITIAL_BEEFY_BLOCK + 100); + IBeefyClient.Commitment memory commitment = createCommitment(newBlockNumber); + uint256[] memory bitfield = new uint256[](1); + IBeefyClient.ValidatorProof memory proof = createValidatorProof(); + IBeefyClient.ValidatorProof[] memory proofs = createValidatorProofs(1); + IBeefyClient.MMRLeaf memory leaf = createMMRLeaf(); + bytes32[] memory leafProof = new bytes32[](0); + + // relayer1 submits initial and commits + vm.startPrank(relayer1); + wrapper.submitInitial(commitment, bitfield, proof); + bytes32 commitmentHash = computeCommitmentHash(commitment); + wrapper.commitPrevRandao(commitmentHash); + vm.stopPrank(); + + // relayer2 tries to submit final (should fail) + vm.prank(relayer2); + vm.expectRevert(BeefyClientWrapper.NotTicketOwner.selector); + wrapper.submitFinal(commitment, bitfield, proofs, leaf, leafProof, 0); + } + + function test_clearTicket() public { + uint32 newBlockNumber = uint32(INITIAL_BEEFY_BLOCK + 100); + IBeefyClient.Commitment memory commitment = createCommitment(newBlockNumber); + uint256[] memory bitfield = new uint256[](1); + IBeefyClient.ValidatorProof memory proof = createValidatorProof(); + + vm.startPrank(relayer1); + wrapper.submitInitial(commitment, bitfield, proof); + + bytes32 commitmentHash = computeCommitmentHash(commitment); + assertEq(wrapper.ticketOwner(commitmentHash), relayer1); + assertGt(wrapper.creditedGas(commitmentHash), 0); + + // Clear ticket - gas should be forfeited + wrapper.clearTicket(commitmentHash); + assertEq(wrapper.ticketOwner(commitmentHash), address(0)); + assertEq(wrapper.creditedGas(commitmentHash), 0); + vm.stopPrank(); + } + + function test_clearTicket_notOwner() public { + uint32 newBlockNumber = uint32(INITIAL_BEEFY_BLOCK + 100); + IBeefyClient.Commitment memory commitment = createCommitment(newBlockNumber); + uint256[] memory bitfield = new uint256[](1); + IBeefyClient.ValidatorProof memory proof = createValidatorProof(); + + vm.prank(relayer1); + wrapper.submitInitial(commitment, bitfield, proof); + + bytes32 commitmentHash = computeCommitmentHash(commitment); + vm.prank(relayer2); + vm.expectRevert(BeefyClientWrapper.NotTicketOwner.selector); + wrapper.clearTicket(commitmentHash); + } + + /* Progress-Based Refund Tests */ + + function test_gasCreditedOnSubmitInitial() public { + uint32 newBlockNumber = uint32(INITIAL_BEEFY_BLOCK + 100); + IBeefyClient.Commitment memory commitment = createCommitment(newBlockNumber); + uint256[] memory bitfield = new uint256[](1); + IBeefyClient.ValidatorProof memory proof = createValidatorProof(); + + uint256 relayerBalanceBefore = relayer1.balance; + + vm.prank(relayer1); + vm.txGasPrice(50 gwei); + wrapper.submitInitial(commitment, bitfield, proof); + + // No immediate refund, gas is credited instead + assertEq(relayer1.balance, relayerBalanceBefore); + + // Gas should be credited + bytes32 commitmentHash = computeCommitmentHash(commitment); + assertGt(wrapper.creditedGas(commitmentHash), 0); + } + + function test_refundSentOnlyAfterSubmitFinal() public { + uint32 newBlockNumber = uint32(INITIAL_BEEFY_BLOCK + REFUND_TARGET); // 100% refund + IBeefyClient.Commitment memory commitment = createCommitment(newBlockNumber); + uint256[] memory bitfield = new uint256[](1); + IBeefyClient.ValidatorProof memory proof = createValidatorProof(); + IBeefyClient.ValidatorProof[] memory proofs = createValidatorProofs(1); + IBeefyClient.MMRLeaf memory leaf = createMMRLeaf(); + bytes32[] memory leafProof = new bytes32[](0); + + uint256 relayerBalanceBefore = relayer1.balance; + + vm.startPrank(relayer1); + vm.txGasPrice(50 gwei); + + wrapper.submitInitial(commitment, bitfield, proof); + assertEq(relayer1.balance, relayerBalanceBefore); + + bytes32 commitmentHash = computeCommitmentHash(commitment); + wrapper.commitPrevRandao(commitmentHash); + assertEq(relayer1.balance, relayerBalanceBefore); + + wrapper.submitFinal(commitment, bitfield, proofs, leaf, leafProof, 0); + assertGt(relayer1.balance, relayerBalanceBefore); + + vm.stopPrank(); + + assertEq(wrapper.creditedGas(commitmentHash), 0); + } + + function test_noRefundForLowProgress() public { + // Below refund target = no refund + uint32 progress = uint32(REFUND_TARGET / 2); // 150 blocks (below 300 threshold) + uint32 newBlockNumber = uint32(INITIAL_BEEFY_BLOCK + progress); + IBeefyClient.Commitment memory commitment = createCommitment(newBlockNumber); + uint256[] memory bitfield = new uint256[](1); + IBeefyClient.ValidatorProof memory proof = createValidatorProof(); + IBeefyClient.ValidatorProof[] memory proofs = createValidatorProofs(1); + IBeefyClient.MMRLeaf memory leaf = createMMRLeaf(); + bytes32[] memory leafProof = new bytes32[](0); + + uint256 relayerBalanceBefore = relayer1.balance; + + vm.startPrank(relayer1); + vm.txGasPrice(50 gwei); + wrapper.submitInitial(commitment, bitfield, proof); + bytes32 commitmentHash = computeCommitmentHash(commitment); + wrapper.commitPrevRandao(commitmentHash); + wrapper.submitFinal(commitment, bitfield, proofs, leaf, leafProof, 0); + vm.stopPrank(); + + // Verify no refund was paid (progress below threshold) + assertEq(relayer1.balance, relayerBalanceBefore); + } + + function test_fullRefundAt100PercentProgress() public { + // 100% of refund target = 100% refund + uint32 newBlockNumber = uint32(INITIAL_BEEFY_BLOCK + REFUND_TARGET); + IBeefyClient.Commitment memory commitment = createCommitment(newBlockNumber); + uint256[] memory bitfield = new uint256[](1); + IBeefyClient.ValidatorProof memory proof = createValidatorProof(); + IBeefyClient.ValidatorProof[] memory proofs = createValidatorProofs(1); + IBeefyClient.MMRLeaf memory leaf = createMMRLeaf(); + bytes32[] memory leafProof = new bytes32[](0); + + uint256 relayerBalanceBefore = relayer1.balance; + + vm.startPrank(relayer1); + vm.txGasPrice(50 gwei); + wrapper.submitInitial(commitment, bitfield, proof); + bytes32 commitmentHash = computeCommitmentHash(commitment); + wrapper.commitPrevRandao(commitmentHash); + wrapper.submitFinal(commitment, bitfield, proofs, leaf, leafProof, 0); + vm.stopPrank(); + + uint256 refundAmount = relayer1.balance - relayerBalanceBefore; + assertGt(refundAmount, 0); + } + + function test_refundCappedAtMaxGasPrice() public { + uint32 newBlockNumber = uint32(INITIAL_BEEFY_BLOCK + REFUND_TARGET); + IBeefyClient.Commitment memory commitment = createCommitment(newBlockNumber); + uint256[] memory bitfield = new uint256[](1); + IBeefyClient.ValidatorProof memory proof = createValidatorProof(); + IBeefyClient.ValidatorProof[] memory proofs = createValidatorProofs(1); + IBeefyClient.MMRLeaf memory leaf = createMMRLeaf(); + bytes32[] memory leafProof = new bytes32[](0); + + uint256 relayerBalanceBefore = relayer1.balance; + uint256 wrapperBalanceBefore = address(wrapper).balance; + + // Use gas price higher than max + vm.startPrank(relayer1); + vm.txGasPrice(200 gwei); // Higher than MAX_GAS_PRICE (100 gwei) + wrapper.submitInitial(commitment, bitfield, proof); + bytes32 commitmentHash = computeCommitmentHash(commitment); + wrapper.commitPrevRandao(commitmentHash); + wrapper.submitFinal(commitment, bitfield, proofs, leaf, leafProof, 0); + vm.stopPrank(); + + uint256 refundAmount = relayer1.balance - relayerBalanceBefore; + uint256 wrapperSpent = wrapperBalanceBefore - address(wrapper).balance; + + // Refund should be based on maxGasPrice, not actual tx.gasprice + assertEq(refundAmount, wrapperSpent); + } + + function test_refundCappedAtMaxRefundAmount() public { + // Set a very low maxRefundAmount to test capping + vm.prank(owner); + wrapper.setMaxRefundAmount(0.0001 ether); + + uint32 newBlockNumber = uint32(INITIAL_BEEFY_BLOCK + REFUND_TARGET); + IBeefyClient.Commitment memory commitment = createCommitment(newBlockNumber); + uint256[] memory bitfield = new uint256[](1); + IBeefyClient.ValidatorProof memory proof = createValidatorProof(); + IBeefyClient.ValidatorProof[] memory proofs = createValidatorProofs(1); + IBeefyClient.MMRLeaf memory leaf = createMMRLeaf(); + bytes32[] memory leafProof = new bytes32[](0); + + uint256 relayerBalanceBefore = relayer1.balance; + + vm.startPrank(relayer1); + vm.txGasPrice(100 gwei); + wrapper.submitInitial(commitment, bitfield, proof); + bytes32 commitmentHash = computeCommitmentHash(commitment); + wrapper.commitPrevRandao(commitmentHash); + wrapper.submitFinal(commitment, bitfield, proofs, leaf, leafProof, 0); + vm.stopPrank(); + + uint256 refundAmount = relayer1.balance - relayerBalanceBefore; + + // Refund should be capped at maxRefundAmount + assertEq(refundAmount, 0.0001 ether); + } + + function test_noRefundWhenInsufficientBalance() public { + // Drain wrapper balance + vm.prank(owner); + wrapper.withdrawFunds(payable(owner), address(wrapper).balance); + + uint32 newBlockNumber = uint32(INITIAL_BEEFY_BLOCK + REFUND_TARGET); + IBeefyClient.Commitment memory commitment = createCommitment(newBlockNumber); + uint256[] memory bitfield = new uint256[](1); + IBeefyClient.ValidatorProof memory proof = createValidatorProof(); + IBeefyClient.ValidatorProof[] memory proofs = createValidatorProofs(1); + IBeefyClient.MMRLeaf memory leaf = createMMRLeaf(); + bytes32[] memory leafProof = new bytes32[](0); + + uint256 relayerBalanceBefore = relayer1.balance; + + vm.startPrank(relayer1); + wrapper.submitInitial(commitment, bitfield, proof); + bytes32 commitmentHash = computeCommitmentHash(commitment); + wrapper.commitPrevRandao(commitmentHash); + wrapper.submitFinal(commitment, bitfield, proofs, leaf, leafProof, 0); + vm.stopPrank(); + + assertEq(relayer1.balance, relayerBalanceBefore); // No refund + assertEq(mockBeefyClient.submitFinalCount(), 1); // Submission succeeded + } + + function test_estimatePayout() public { + uint256 gasUsed = 500000; + uint256 gasPrice = 50 gwei; + + // Test below threshold (no refund) + uint256 refundBelow = wrapper.estimatePayout(gasUsed, gasPrice, REFUND_TARGET / 2); + assertEq(refundBelow, 0); + + // Test at threshold (full refund) + uint256 refundAt = wrapper.estimatePayout(gasUsed, gasPrice, REFUND_TARGET); + assertEq(refundAt, gasUsed * gasPrice); + + // Test above threshold (full refund) + uint256 refundAbove = wrapper.estimatePayout(gasUsed, gasPrice, REFUND_TARGET * 2); + assertEq(refundAbove, gasUsed * gasPrice); + } + + /* Admin Function Tests */ + + function test_setMaxGasPrice() public { + vm.prank(owner); + wrapper.setMaxGasPrice(200 gwei); + + assertEq(wrapper.maxGasPrice(), 200 gwei); + } + + function test_setMaxRefundAmount() public { + vm.prank(owner); + wrapper.setMaxRefundAmount(2 ether); + + assertEq(wrapper.maxRefundAmount(), 2 ether); + } + + function test_setRefundTarget() public { + vm.prank(owner); + wrapper.setRefundTarget(600); + + assertEq(wrapper.refundTarget(), 600); + } + + function test_withdrawFunds() public { + uint256 ownerBalanceBefore = owner.balance; + uint256 withdrawAmount = 10 ether; + + vm.prank(owner); + wrapper.withdrawFunds(payable(owner), withdrawAmount); + + assertEq(owner.balance, ownerBalanceBefore + withdrawAmount); + } + + function test_transferOwnership() public { + address newOwner = address(0x999); + + vm.prank(owner); + wrapper.transferOwnership(newOwner); + + assertEq(wrapper.owner(), newOwner); + } + + function test_adminFunctions_onlyOwner() public { + vm.startPrank(anyone); + + vm.expectRevert(BeefyClientWrapper.Unauthorized.selector); + wrapper.setMaxGasPrice(1); + + vm.expectRevert(BeefyClientWrapper.Unauthorized.selector); + wrapper.setMaxRefundAmount(1); + + vm.expectRevert(BeefyClientWrapper.Unauthorized.selector); + wrapper.setRefundTarget(1); + + vm.expectRevert(BeefyClientWrapper.Unauthorized.selector); + wrapper.withdrawFunds(payable(anyone), 1); + + vm.expectRevert(BeefyClientWrapper.Unauthorized.selector); + wrapper.transferOwnership(anyone); + + vm.stopPrank(); + } + + /* Deposit Tests */ + + function test_acceptsDeposits() public { + uint256 balanceBefore = address(wrapper).balance; + + vm.deal(address(this), 1 ether); + (bool success,) = address(wrapper).call{value: 1 ether}(""); + + assertTrue(success); + assertEq(address(wrapper).balance, balanceBefore + 1 ether); + } + + /* Fiat Shamir Tests */ + + function test_submitFiatShamir() public { + uint32 newBlockNumber = uint32(INITIAL_BEEFY_BLOCK + 500); + IBeefyClient.Commitment memory commitment = createCommitment(newBlockNumber); + uint256[] memory bitfield = new uint256[](1); + IBeefyClient.ValidatorProof[] memory proofs = createValidatorProofs(1); + IBeefyClient.MMRLeaf memory leaf = createMMRLeaf(); + bytes32[] memory leafProof = new bytes32[](0); + + vm.prank(relayer1); + wrapper.submitFiatShamir(commitment, bitfield, proofs, leaf, leafProof, 0); + + assertEq(mockBeefyClient.submitFiatShamirCount(), 1); + } + + function test_submitFiatShamir_clearsHighestPendingBlock() public { + // First create a pending session + uint32 pendingBlockNumber = uint32(INITIAL_BEEFY_BLOCK + 100); + IBeefyClient.Commitment memory pendingCommitment = createCommitment(pendingBlockNumber); + uint256[] memory bitfield = new uint256[](1); + IBeefyClient.ValidatorProof memory proof = createValidatorProof(); + + vm.prank(relayer1); + wrapper.submitInitial(pendingCommitment, bitfield, proof); + assertEq(wrapper.highestPendingBlock(), pendingBlockNumber); + + // Submit Fiat Shamir with higher block number + uint32 fiatShamirBlockNumber = uint32(INITIAL_BEEFY_BLOCK + 200); + IBeefyClient.Commitment memory fiatShamirCommitment = createCommitment(fiatShamirBlockNumber); + IBeefyClient.ValidatorProof[] memory proofs = createValidatorProofs(1); + IBeefyClient.MMRLeaf memory leaf = createMMRLeaf(); + bytes32[] memory leafProof = new bytes32[](0); + + vm.prank(relayer2); + wrapper.submitFiatShamir(fiatShamirCommitment, bitfield, proofs, leaf, leafProof, 0); + + // highestPendingBlock should be cleared since latestBeefyBlock >= highestPendingBlock + assertEq(wrapper.highestPendingBlock(), 0); + assertEq(wrapper.highestPendingBlockTimestamp(), 0); + } + + /* Additional Admin Function Tests */ + + function test_withdrawFunds_invalidRecipient() public { + vm.prank(owner); + vm.expectRevert(BeefyClientWrapper.InvalidAddress.selector); + wrapper.withdrawFunds(payable(address(0)), 1 ether); + } + + function test_transferOwnership_invalidAddress() public { + vm.prank(owner); + vm.expectRevert(BeefyClientWrapper.InvalidAddress.selector); + wrapper.transferOwnership(address(0)); + } + + /* Highest Pending Block Tests */ + + function test_submitInitial_doesNotUpdateHighestPendingBlock_whenLower() public { + // First submission sets highestPendingBlock + uint32 higherBlockNumber = uint32(INITIAL_BEEFY_BLOCK + 500); + IBeefyClient.Commitment memory commitment1 = createCommitment(higherBlockNumber); + uint256[] memory bitfield = new uint256[](1); + IBeefyClient.ValidatorProof memory proof = createValidatorProof(); + + vm.prank(relayer1); + wrapper.submitInitial(commitment1, bitfield, proof); + assertEq(wrapper.highestPendingBlock(), higherBlockNumber); + uint256 timestamp1 = wrapper.highestPendingBlockTimestamp(); + + // Second submission with lower block number should NOT update + uint32 lowerBlockNumber = uint32(INITIAL_BEEFY_BLOCK + 200); + IBeefyClient.Commitment memory commitment2 = createCommitment(lowerBlockNumber); + + vm.prank(relayer2); + wrapper.submitInitial(commitment2, bitfield, proof); + + // Should still be the higher block number + assertEq(wrapper.highestPendingBlock(), higherBlockNumber); + assertEq(wrapper.highestPendingBlockTimestamp(), timestamp1); + } + + /* Additional EstimatePayout Tests */ + + function test_estimatePayout_capsGasPrice() public { + uint256 gasUsed = 500000; + uint256 highGasPrice = 200 gwei; // Higher than MAX_GAS_PRICE (100 gwei) + + uint256 refund = wrapper.estimatePayout(gasUsed, highGasPrice, REFUND_TARGET); + + // Should use maxGasPrice (100 gwei), not the provided highGasPrice + assertEq(refund, gasUsed * MAX_GAS_PRICE); + } + + function test_estimatePayout_capsRefundAmount() public { + uint256 gasUsed = 1000000000; // Very high gas to exceed max refund + uint256 gasPrice = 100 gwei; + + uint256 refund = wrapper.estimatePayout(gasUsed, gasPrice, REFUND_TARGET); + + // Should be capped at maxRefundAmount + assertEq(refund, MAX_REFUND_AMOUNT); + } +} + +/** + * @dev Contract that rejects ETH transfers for testing TransferFailed + */ +contract RejectingRecipient { + receive() external payable { + revert("No ETH accepted"); + } +} + +/** + * @dev Contract relayer that rejects ETH refunds for testing failed refund transfers + */ +contract RejectingRelayer { + BeefyClientWrapper public wrapper; + + constructor(BeefyClientWrapper _wrapper) { + wrapper = _wrapper; + } + + function submitInitial( + IBeefyClient.Commitment calldata commitment, + uint256[] calldata bitfield, + IBeefyClient.ValidatorProof calldata proof + ) external { + wrapper.submitInitial(commitment, bitfield, proof); + } + + function commitPrevRandao(bytes32 commitmentHash) external { + wrapper.commitPrevRandao(commitmentHash); + } + + function submitFinal( + IBeefyClient.Commitment calldata commitment, + uint256[] calldata bitfield, + IBeefyClient.ValidatorProof[] calldata proofs, + IBeefyClient.MMRLeaf calldata leaf, + bytes32[] calldata leafProof, + uint256 leafProofOrder + ) external { + wrapper.submitFinal(commitment, bitfield, proofs, leaf, leafProof, leafProofOrder); + } + + // Reject ETH transfers + receive() external payable { + revert("No refunds accepted"); + } +} + +contract BeefyClientWrapperTransferFailedTest is Test { + BeefyClientWrapper wrapper; + MockBeefyClient mockBeefyClient; + RejectingRecipient rejectingRecipient; + + address owner = address(0x1); + uint256 constant INITIAL_BEEFY_BLOCK = 1000; + uint256 constant REFUND_TARGET = 350; + + function setUp() public { + mockBeefyClient = new MockBeefyClient(uint64(INITIAL_BEEFY_BLOCK)); + wrapper = new BeefyClientWrapper( + address(mockBeefyClient), + owner, + 100 gwei, + 0.05 ether, + REFUND_TARGET + ); + vm.deal(address(wrapper), 100 ether); + rejectingRecipient = new RejectingRecipient(); + } + + function test_withdrawFunds_transferFailed() public { + vm.prank(owner); + vm.expectRevert(BeefyClientWrapper.TransferFailed.selector); + wrapper.withdrawFunds(payable(address(rejectingRecipient)), 1 ether); + } + + function test_refundFailsWhenRelayerRejectsETH() public { + // Create a relayer contract that rejects ETH + RejectingRelayer rejectingRelayer = new RejectingRelayer(wrapper); + + // Create commitment with enough progress for refund + uint32 newBlockNumber = uint32(INITIAL_BEEFY_BLOCK + REFUND_TARGET); + IBeefyClient.PayloadItem[] memory payload = new IBeefyClient.PayloadItem[](1); + payload[0] = IBeefyClient.PayloadItem(bytes2("mh"), abi.encodePacked(bytes32(0))); + IBeefyClient.Commitment memory commitment = IBeefyClient.Commitment(newBlockNumber, 1, payload); + + uint256[] memory bitfield = new uint256[](1); + bytes32[] memory proof = new bytes32[](0); + IBeefyClient.ValidatorProof memory validatorProof = IBeefyClient.ValidatorProof(27, bytes32(0), bytes32(0), 0, address(0), proof); + IBeefyClient.ValidatorProof[] memory proofs = new IBeefyClient.ValidatorProof[](1); + proofs[0] = validatorProof; + IBeefyClient.MMRLeaf memory leaf = IBeefyClient.MMRLeaf(1, 0, bytes32(0), 1, 100, bytes32(0), bytes32(0)); + bytes32[] memory leafProof = new bytes32[](0); + + uint256 wrapperBalanceBefore = address(wrapper).balance; + + // Submit through the rejecting relayer + vm.txGasPrice(50 gwei); + rejectingRelayer.submitInitial(commitment, bitfield, validatorProof); + + bytes32 commitmentHash = mockBeefyClient.computeCommitmentHash(commitment); + rejectingRelayer.commitPrevRandao(commitmentHash); + + // submitFinal should succeed even though refund transfer fails + rejectingRelayer.submitFinal(commitment, bitfield, proofs, leaf, leafProof, 0); + + // Verify submission succeeded + assertEq(mockBeefyClient.submitFinalCount(), 1); + assertEq(mockBeefyClient.latestBeefyBlock(), newBlockNumber); + + // Verify no refund was sent (wrapper balance unchanged) + assertEq(address(wrapper).balance, wrapperBalanceBefore); + } +} diff --git a/relayer/contracts/beefy_client.go b/relayer/contracts/beefy_client.go index 759b93a79..94f8b909a 100644 --- a/relayer/contracts/beefy_client.go +++ b/relayer/contracts/beefy_client.go @@ -78,7 +78,7 @@ type Uint16Array struct { // BeefyClientMetaData contains all meta data concerning the BeefyClient contract. var BeefyClientMetaData = &bind.MetaData{ - ABI: "[{\"type\":\"constructor\",\"inputs\":[{\"name\":\"_randaoCommitDelay\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_randaoCommitExpiration\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_minNumRequiredSignatures\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_fiatShamirRequiredSignatures\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_initialBeefyBlock\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"_initialValidatorSet\",\"type\":\"tuple\",\"internalType\":\"structBeefyClient.ValidatorSet\",\"components\":[{\"name\":\"id\",\"type\":\"uint128\",\"internalType\":\"uint128\"},{\"name\":\"length\",\"type\":\"uint128\",\"internalType\":\"uint128\"},{\"name\":\"root\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]},{\"name\":\"_nextValidatorSet\",\"type\":\"tuple\",\"internalType\":\"structBeefyClient.ValidatorSet\",\"components\":[{\"name\":\"id\",\"type\":\"uint128\",\"internalType\":\"uint128\"},{\"name\":\"length\",\"type\":\"uint128\",\"internalType\":\"uint128\"},{\"name\":\"root\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]}],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"FIAT_SHAMIR_DOMAIN_ID\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"MMR_ROOT_ID\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes2\",\"internalType\":\"bytes2\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"commitPrevRandao\",\"inputs\":[{\"name\":\"commitmentHash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"createFiatShamirFinalBitfield\",\"inputs\":[{\"name\":\"commitment\",\"type\":\"tuple\",\"internalType\":\"structBeefyClient.Commitment\",\"components\":[{\"name\":\"blockNumber\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"validatorSetID\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"payload\",\"type\":\"tuple[]\",\"internalType\":\"structBeefyClient.PayloadItem[]\",\"components\":[{\"name\":\"payloadID\",\"type\":\"bytes2\",\"internalType\":\"bytes2\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]}]},{\"name\":\"bitfield\",\"type\":\"uint256[]\",\"internalType\":\"uint256[]\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256[]\",\"internalType\":\"uint256[]\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"createFinalBitfield\",\"inputs\":[{\"name\":\"commitmentHash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"bitfield\",\"type\":\"uint256[]\",\"internalType\":\"uint256[]\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256[]\",\"internalType\":\"uint256[]\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"createInitialBitfield\",\"inputs\":[{\"name\":\"bitsToSet\",\"type\":\"uint256[]\",\"internalType\":\"uint256[]\"},{\"name\":\"length\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256[]\",\"internalType\":\"uint256[]\"}],\"stateMutability\":\"pure\"},{\"type\":\"function\",\"name\":\"currentValidatorSet\",\"inputs\":[],\"outputs\":[{\"name\":\"id\",\"type\":\"uint128\",\"internalType\":\"uint128\"},{\"name\":\"length\",\"type\":\"uint128\",\"internalType\":\"uint128\"},{\"name\":\"root\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"usageCounters\",\"type\":\"tuple\",\"internalType\":\"structUint16Array\",\"components\":[{\"name\":\"data\",\"type\":\"uint256[]\",\"internalType\":\"uint256[]\"},{\"name\":\"length\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"fiatShamirRequiredSignatures\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"latestBeefyBlock\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint64\",\"internalType\":\"uint64\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"latestMMRRoot\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"minNumRequiredSignatures\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"nextValidatorSet\",\"inputs\":[],\"outputs\":[{\"name\":\"id\",\"type\":\"uint128\",\"internalType\":\"uint128\"},{\"name\":\"length\",\"type\":\"uint128\",\"internalType\":\"uint128\"},{\"name\":\"root\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"usageCounters\",\"type\":\"tuple\",\"internalType\":\"structUint16Array\",\"components\":[{\"name\":\"data\",\"type\":\"uint256[]\",\"internalType\":\"uint256[]\"},{\"name\":\"length\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"randaoCommitDelay\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"randaoCommitExpiration\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"submitFiatShamir\",\"inputs\":[{\"name\":\"commitment\",\"type\":\"tuple\",\"internalType\":\"structBeefyClient.Commitment\",\"components\":[{\"name\":\"blockNumber\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"validatorSetID\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"payload\",\"type\":\"tuple[]\",\"internalType\":\"structBeefyClient.PayloadItem[]\",\"components\":[{\"name\":\"payloadID\",\"type\":\"bytes2\",\"internalType\":\"bytes2\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]}]},{\"name\":\"bitfield\",\"type\":\"uint256[]\",\"internalType\":\"uint256[]\"},{\"name\":\"proofs\",\"type\":\"tuple[]\",\"internalType\":\"structBeefyClient.ValidatorProof[]\",\"components\":[{\"name\":\"v\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"r\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"s\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"index\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"account\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"proof\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"}]},{\"name\":\"leaf\",\"type\":\"tuple\",\"internalType\":\"structBeefyClient.MMRLeaf\",\"components\":[{\"name\":\"version\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"parentNumber\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"parentHash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"nextAuthoritySetID\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"nextAuthoritySetLen\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"nextAuthoritySetRoot\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"parachainHeadsRoot\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]},{\"name\":\"leafProof\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"},{\"name\":\"leafProofOrder\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"submitFinal\",\"inputs\":[{\"name\":\"commitment\",\"type\":\"tuple\",\"internalType\":\"structBeefyClient.Commitment\",\"components\":[{\"name\":\"blockNumber\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"validatorSetID\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"payload\",\"type\":\"tuple[]\",\"internalType\":\"structBeefyClient.PayloadItem[]\",\"components\":[{\"name\":\"payloadID\",\"type\":\"bytes2\",\"internalType\":\"bytes2\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]}]},{\"name\":\"bitfield\",\"type\":\"uint256[]\",\"internalType\":\"uint256[]\"},{\"name\":\"proofs\",\"type\":\"tuple[]\",\"internalType\":\"structBeefyClient.ValidatorProof[]\",\"components\":[{\"name\":\"v\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"r\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"s\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"index\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"account\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"proof\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"}]},{\"name\":\"leaf\",\"type\":\"tuple\",\"internalType\":\"structBeefyClient.MMRLeaf\",\"components\":[{\"name\":\"version\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"parentNumber\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"parentHash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"nextAuthoritySetID\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"nextAuthoritySetLen\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"nextAuthoritySetRoot\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"parachainHeadsRoot\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]},{\"name\":\"leafProof\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"},{\"name\":\"leafProofOrder\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"submitInitial\",\"inputs\":[{\"name\":\"commitment\",\"type\":\"tuple\",\"internalType\":\"structBeefyClient.Commitment\",\"components\":[{\"name\":\"blockNumber\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"validatorSetID\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"payload\",\"type\":\"tuple[]\",\"internalType\":\"structBeefyClient.PayloadItem[]\",\"components\":[{\"name\":\"payloadID\",\"type\":\"bytes2\",\"internalType\":\"bytes2\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]}]},{\"name\":\"bitfield\",\"type\":\"uint256[]\",\"internalType\":\"uint256[]\"},{\"name\":\"proof\",\"type\":\"tuple\",\"internalType\":\"structBeefyClient.ValidatorProof\",\"components\":[{\"name\":\"v\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"r\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"s\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"index\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"account\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"proof\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"}]}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"tickets\",\"inputs\":[{\"name\":\"ticketID\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[{\"name\":\"blockNumber\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"validatorSetLen\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"numRequiredSignatures\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"prevRandao\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"bitfieldHash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"verifyMMRLeafProof\",\"inputs\":[{\"name\":\"leafHash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"proof\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"},{\"name\":\"proofOrder\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"event\",\"name\":\"NewMMRRoot\",\"inputs\":[{\"name\":\"mmrRoot\",\"type\":\"bytes32\",\"indexed\":false,\"internalType\":\"bytes32\"},{\"name\":\"blockNumber\",\"type\":\"uint64\",\"indexed\":false,\"internalType\":\"uint64\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"NewTicket\",\"inputs\":[{\"name\":\"relayer\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"},{\"name\":\"blockNumber\",\"type\":\"uint64\",\"indexed\":false,\"internalType\":\"uint64\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"TicketExpired\",\"inputs\":[],\"anonymous\":false},{\"type\":\"error\",\"name\":\"CommitmentNotRelevant\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ECDSAInvalidSignature\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ECDSAInvalidSignatureLength\",\"inputs\":[{\"name\":\"length\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"ECDSAInvalidSignatureS\",\"inputs\":[{\"name\":\"s\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]},{\"type\":\"error\",\"name\":\"IndexOutOfBounds\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidBitfield\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidBitfieldLength\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidBitfieldPadding\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidCommitment\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidMMRLeaf\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidMMRLeafProof\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidMMRRootLength\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidSamplingParams\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidSignature\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidTicket\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidValidatorProof\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidValidatorProofLength\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"PrevRandaoAlreadyCaptured\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"PrevRandaoNotCaptured\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ProofSizeExceeded\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"StaleCommitment\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"UnsupportedCompactEncoding\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"WaitPeriodNotOver\",\"inputs\":[]}]", + ABI: "[{\"type\":\"constructor\",\"inputs\":[{\"name\":\"_randaoCommitDelay\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_randaoCommitExpiration\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_minNumRequiredSignatures\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_fiatShamirRequiredSignatures\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_initialBeefyBlock\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"_initialValidatorSet\",\"type\":\"tuple\",\"internalType\":\"structBeefyClient.ValidatorSet\",\"components\":[{\"name\":\"id\",\"type\":\"uint128\",\"internalType\":\"uint128\"},{\"name\":\"length\",\"type\":\"uint128\",\"internalType\":\"uint128\"},{\"name\":\"root\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]},{\"name\":\"_nextValidatorSet\",\"type\":\"tuple\",\"internalType\":\"structBeefyClient.ValidatorSet\",\"components\":[{\"name\":\"id\",\"type\":\"uint128\",\"internalType\":\"uint128\"},{\"name\":\"length\",\"type\":\"uint128\",\"internalType\":\"uint128\"},{\"name\":\"root\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]}],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"FIAT_SHAMIR_DOMAIN_ID\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"MMR_ROOT_ID\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes2\",\"internalType\":\"bytes2\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"commitPrevRandao\",\"inputs\":[{\"name\":\"commitmentHash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"computeCommitmentHash\",\"inputs\":[{\"name\":\"commitment\",\"type\":\"tuple\",\"internalType\":\"structBeefyClient.Commitment\",\"components\":[{\"name\":\"blockNumber\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"validatorSetID\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"payload\",\"type\":\"tuple[]\",\"internalType\":\"structBeefyClient.PayloadItem[]\",\"components\":[{\"name\":\"payloadID\",\"type\":\"bytes2\",\"internalType\":\"bytes2\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]}]}],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"pure\"},{\"type\":\"function\",\"name\":\"createFiatShamirFinalBitfield\",\"inputs\":[{\"name\":\"commitment\",\"type\":\"tuple\",\"internalType\":\"structBeefyClient.Commitment\",\"components\":[{\"name\":\"blockNumber\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"validatorSetID\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"payload\",\"type\":\"tuple[]\",\"internalType\":\"structBeefyClient.PayloadItem[]\",\"components\":[{\"name\":\"payloadID\",\"type\":\"bytes2\",\"internalType\":\"bytes2\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]}]},{\"name\":\"bitfield\",\"type\":\"uint256[]\",\"internalType\":\"uint256[]\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256[]\",\"internalType\":\"uint256[]\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"createFinalBitfield\",\"inputs\":[{\"name\":\"commitmentHash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"bitfield\",\"type\":\"uint256[]\",\"internalType\":\"uint256[]\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256[]\",\"internalType\":\"uint256[]\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"createInitialBitfield\",\"inputs\":[{\"name\":\"bitsToSet\",\"type\":\"uint256[]\",\"internalType\":\"uint256[]\"},{\"name\":\"length\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256[]\",\"internalType\":\"uint256[]\"}],\"stateMutability\":\"pure\"},{\"type\":\"function\",\"name\":\"currentValidatorSet\",\"inputs\":[],\"outputs\":[{\"name\":\"id\",\"type\":\"uint128\",\"internalType\":\"uint128\"},{\"name\":\"length\",\"type\":\"uint128\",\"internalType\":\"uint128\"},{\"name\":\"root\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"usageCounters\",\"type\":\"tuple\",\"internalType\":\"structUint16Array\",\"components\":[{\"name\":\"data\",\"type\":\"uint256[]\",\"internalType\":\"uint256[]\"},{\"name\":\"length\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"fiatShamirRequiredSignatures\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"latestBeefyBlock\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint64\",\"internalType\":\"uint64\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"latestMMRRoot\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"minNumRequiredSignatures\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"nextValidatorSet\",\"inputs\":[],\"outputs\":[{\"name\":\"id\",\"type\":\"uint128\",\"internalType\":\"uint128\"},{\"name\":\"length\",\"type\":\"uint128\",\"internalType\":\"uint128\"},{\"name\":\"root\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"usageCounters\",\"type\":\"tuple\",\"internalType\":\"structUint16Array\",\"components\":[{\"name\":\"data\",\"type\":\"uint256[]\",\"internalType\":\"uint256[]\"},{\"name\":\"length\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"randaoCommitDelay\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"randaoCommitExpiration\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"submitFiatShamir\",\"inputs\":[{\"name\":\"commitment\",\"type\":\"tuple\",\"internalType\":\"structBeefyClient.Commitment\",\"components\":[{\"name\":\"blockNumber\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"validatorSetID\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"payload\",\"type\":\"tuple[]\",\"internalType\":\"structBeefyClient.PayloadItem[]\",\"components\":[{\"name\":\"payloadID\",\"type\":\"bytes2\",\"internalType\":\"bytes2\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]}]},{\"name\":\"bitfield\",\"type\":\"uint256[]\",\"internalType\":\"uint256[]\"},{\"name\":\"proofs\",\"type\":\"tuple[]\",\"internalType\":\"structBeefyClient.ValidatorProof[]\",\"components\":[{\"name\":\"v\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"r\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"s\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"index\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"account\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"proof\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"}]},{\"name\":\"leaf\",\"type\":\"tuple\",\"internalType\":\"structBeefyClient.MMRLeaf\",\"components\":[{\"name\":\"version\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"parentNumber\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"parentHash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"nextAuthoritySetID\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"nextAuthoritySetLen\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"nextAuthoritySetRoot\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"parachainHeadsRoot\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]},{\"name\":\"leafProof\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"},{\"name\":\"leafProofOrder\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"submitFinal\",\"inputs\":[{\"name\":\"commitment\",\"type\":\"tuple\",\"internalType\":\"structBeefyClient.Commitment\",\"components\":[{\"name\":\"blockNumber\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"validatorSetID\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"payload\",\"type\":\"tuple[]\",\"internalType\":\"structBeefyClient.PayloadItem[]\",\"components\":[{\"name\":\"payloadID\",\"type\":\"bytes2\",\"internalType\":\"bytes2\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]}]},{\"name\":\"bitfield\",\"type\":\"uint256[]\",\"internalType\":\"uint256[]\"},{\"name\":\"proofs\",\"type\":\"tuple[]\",\"internalType\":\"structBeefyClient.ValidatorProof[]\",\"components\":[{\"name\":\"v\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"r\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"s\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"index\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"account\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"proof\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"}]},{\"name\":\"leaf\",\"type\":\"tuple\",\"internalType\":\"structBeefyClient.MMRLeaf\",\"components\":[{\"name\":\"version\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"parentNumber\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"parentHash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"nextAuthoritySetID\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"nextAuthoritySetLen\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"nextAuthoritySetRoot\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"parachainHeadsRoot\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]},{\"name\":\"leafProof\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"},{\"name\":\"leafProofOrder\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"submitInitial\",\"inputs\":[{\"name\":\"commitment\",\"type\":\"tuple\",\"internalType\":\"structBeefyClient.Commitment\",\"components\":[{\"name\":\"blockNumber\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"validatorSetID\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"payload\",\"type\":\"tuple[]\",\"internalType\":\"structBeefyClient.PayloadItem[]\",\"components\":[{\"name\":\"payloadID\",\"type\":\"bytes2\",\"internalType\":\"bytes2\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]}]},{\"name\":\"bitfield\",\"type\":\"uint256[]\",\"internalType\":\"uint256[]\"},{\"name\":\"proof\",\"type\":\"tuple\",\"internalType\":\"structBeefyClient.ValidatorProof\",\"components\":[{\"name\":\"v\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"r\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"s\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"index\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"account\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"proof\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"}]}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"tickets\",\"inputs\":[{\"name\":\"ticketID\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[{\"name\":\"blockNumber\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"validatorSetLen\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"numRequiredSignatures\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"prevRandao\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"bitfieldHash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"verifyMMRLeafProof\",\"inputs\":[{\"name\":\"leafHash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"proof\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"},{\"name\":\"proofOrder\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"event\",\"name\":\"NewMMRRoot\",\"inputs\":[{\"name\":\"mmrRoot\",\"type\":\"bytes32\",\"indexed\":false,\"internalType\":\"bytes32\"},{\"name\":\"blockNumber\",\"type\":\"uint64\",\"indexed\":false,\"internalType\":\"uint64\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"NewTicket\",\"inputs\":[{\"name\":\"relayer\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"},{\"name\":\"blockNumber\",\"type\":\"uint64\",\"indexed\":false,\"internalType\":\"uint64\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"TicketExpired\",\"inputs\":[],\"anonymous\":false},{\"type\":\"error\",\"name\":\"CommitmentNotRelevant\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ECDSAInvalidSignature\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ECDSAInvalidSignatureLength\",\"inputs\":[{\"name\":\"length\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"ECDSAInvalidSignatureS\",\"inputs\":[{\"name\":\"s\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]},{\"type\":\"error\",\"name\":\"IndexOutOfBounds\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidBitfield\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidBitfieldLength\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidBitfieldPadding\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidCommitment\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidMMRLeaf\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidMMRLeafProof\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidMMRRootLength\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidSamplingParams\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidSignature\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidTicket\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidValidatorProof\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidValidatorProofLength\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"PrevRandaoAlreadyCaptured\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"PrevRandaoNotCaptured\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ProofSizeExceeded\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"StaleCommitment\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"UnsupportedCompactEncoding\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"WaitPeriodNotOver\",\"inputs\":[]}]", } // BeefyClientABI is the input ABI used to generate the binding from. @@ -289,6 +289,37 @@ func (_BeefyClient *BeefyClientCallerSession) MMRROOTID() ([2]byte, error) { return _BeefyClient.Contract.MMRROOTID(&_BeefyClient.CallOpts) } +// ComputeCommitmentHash is a free data retrieval call binding the contract method 0xc6405f31. +// +// Solidity: function computeCommitmentHash((uint32,uint64,(bytes2,bytes)[]) commitment) pure returns(bytes32) +func (_BeefyClient *BeefyClientCaller) ComputeCommitmentHash(opts *bind.CallOpts, commitment BeefyClientCommitment) ([32]byte, error) { + var out []interface{} + err := _BeefyClient.contract.Call(opts, &out, "computeCommitmentHash", commitment) + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +// ComputeCommitmentHash is a free data retrieval call binding the contract method 0xc6405f31. +// +// Solidity: function computeCommitmentHash((uint32,uint64,(bytes2,bytes)[]) commitment) pure returns(bytes32) +func (_BeefyClient *BeefyClientSession) ComputeCommitmentHash(commitment BeefyClientCommitment) ([32]byte, error) { + return _BeefyClient.Contract.ComputeCommitmentHash(&_BeefyClient.CallOpts, commitment) +} + +// ComputeCommitmentHash is a free data retrieval call binding the contract method 0xc6405f31. +// +// Solidity: function computeCommitmentHash((uint32,uint64,(bytes2,bytes)[]) commitment) pure returns(bytes32) +func (_BeefyClient *BeefyClientCallerSession) ComputeCommitmentHash(commitment BeefyClientCommitment) ([32]byte, error) { + return _BeefyClient.Contract.ComputeCommitmentHash(&_BeefyClient.CallOpts, commitment) +} + // CreateFiatShamirFinalBitfield is a free data retrieval call binding the contract method 0x4b4cfb28. // // Solidity: function createFiatShamirFinalBitfield((uint32,uint64,(bytes2,bytes)[]) commitment, uint256[] bitfield) view returns(uint256[]) diff --git a/relayer/contracts/beefy_client_wrapper.go b/relayer/contracts/beefy_client_wrapper.go new file mode 100644 index 000000000..8b86478ba --- /dev/null +++ b/relayer/contracts/beefy_client_wrapper.go @@ -0,0 +1,1347 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package contracts + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// IBeefyClientCommitment is an auto generated low-level Go binding around an user-defined struct. +type IBeefyClientCommitment struct { + BlockNumber uint32 + ValidatorSetID uint64 + Payload []IBeefyClientPayloadItem +} + +// IBeefyClientMMRLeaf is an auto generated low-level Go binding around an user-defined struct. +type IBeefyClientMMRLeaf struct { + Version uint8 + ParentNumber uint32 + ParentHash [32]byte + NextAuthoritySetID uint64 + NextAuthoritySetLen uint32 + NextAuthoritySetRoot [32]byte + ParachainHeadsRoot [32]byte +} + +// IBeefyClientPayloadItem is an auto generated low-level Go binding around an user-defined struct. +type IBeefyClientPayloadItem struct { + PayloadID [2]byte + Data []byte +} + +// IBeefyClientValidatorProof is an auto generated low-level Go binding around an user-defined struct. +type IBeefyClientValidatorProof struct { + V uint8 + R [32]byte + S [32]byte + Index *big.Int + Account common.Address + Proof [][32]byte +} + +// BeefyClientWrapperMetaData contains all meta data concerning the BeefyClientWrapper contract. +var BeefyClientWrapperMetaData = &bind.MetaData{ + ABI: "[{\"type\":\"constructor\",\"inputs\":[{\"name\":\"_beefyClient\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_owner\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_maxGasPrice\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_maxRefundAmount\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_refundTarget\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"nonpayable\"},{\"type\":\"receive\",\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"beefyClient\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"contractIBeefyClient\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"clearTicket\",\"inputs\":[{\"name\":\"commitmentHash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"commitPrevRandao\",\"inputs\":[{\"name\":\"commitmentHash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"creditedGas\",\"inputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"estimatePayout\",\"inputs\":[{\"name\":\"gasUsed\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"gasPrice\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"progress\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"refundAmount\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"highestPendingBlock\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"highestPendingBlockTimestamp\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"maxGasPrice\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"maxRefundAmount\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"owner\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"refundTarget\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"setMaxGasPrice\",\"inputs\":[{\"name\":\"_maxGasPrice\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setMaxRefundAmount\",\"inputs\":[{\"name\":\"_maxRefundAmount\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setRefundTarget\",\"inputs\":[{\"name\":\"_refundTarget\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"submitFiatShamir\",\"inputs\":[{\"name\":\"commitment\",\"type\":\"tuple\",\"internalType\":\"structIBeefyClient.Commitment\",\"components\":[{\"name\":\"blockNumber\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"validatorSetID\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"payload\",\"type\":\"tuple[]\",\"internalType\":\"structIBeefyClient.PayloadItem[]\",\"components\":[{\"name\":\"payloadID\",\"type\":\"bytes2\",\"internalType\":\"bytes2\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]}]},{\"name\":\"bitfield\",\"type\":\"uint256[]\",\"internalType\":\"uint256[]\"},{\"name\":\"proofs\",\"type\":\"tuple[]\",\"internalType\":\"structIBeefyClient.ValidatorProof[]\",\"components\":[{\"name\":\"v\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"r\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"s\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"index\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"account\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"proof\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"}]},{\"name\":\"leaf\",\"type\":\"tuple\",\"internalType\":\"structIBeefyClient.MMRLeaf\",\"components\":[{\"name\":\"version\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"parentNumber\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"parentHash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"nextAuthoritySetID\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"nextAuthoritySetLen\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"nextAuthoritySetRoot\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"parachainHeadsRoot\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]},{\"name\":\"leafProof\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"},{\"name\":\"leafProofOrder\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"submitFinal\",\"inputs\":[{\"name\":\"commitment\",\"type\":\"tuple\",\"internalType\":\"structIBeefyClient.Commitment\",\"components\":[{\"name\":\"blockNumber\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"validatorSetID\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"payload\",\"type\":\"tuple[]\",\"internalType\":\"structIBeefyClient.PayloadItem[]\",\"components\":[{\"name\":\"payloadID\",\"type\":\"bytes2\",\"internalType\":\"bytes2\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]}]},{\"name\":\"bitfield\",\"type\":\"uint256[]\",\"internalType\":\"uint256[]\"},{\"name\":\"proofs\",\"type\":\"tuple[]\",\"internalType\":\"structIBeefyClient.ValidatorProof[]\",\"components\":[{\"name\":\"v\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"r\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"s\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"index\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"account\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"proof\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"}]},{\"name\":\"leaf\",\"type\":\"tuple\",\"internalType\":\"structIBeefyClient.MMRLeaf\",\"components\":[{\"name\":\"version\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"parentNumber\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"parentHash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"nextAuthoritySetID\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"nextAuthoritySetLen\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"nextAuthoritySetRoot\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"parachainHeadsRoot\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]},{\"name\":\"leafProof\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"},{\"name\":\"leafProofOrder\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"submitInitial\",\"inputs\":[{\"name\":\"commitment\",\"type\":\"tuple\",\"internalType\":\"structIBeefyClient.Commitment\",\"components\":[{\"name\":\"blockNumber\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"validatorSetID\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"payload\",\"type\":\"tuple[]\",\"internalType\":\"structIBeefyClient.PayloadItem[]\",\"components\":[{\"name\":\"payloadID\",\"type\":\"bytes2\",\"internalType\":\"bytes2\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]}]},{\"name\":\"bitfield\",\"type\":\"uint256[]\",\"internalType\":\"uint256[]\"},{\"name\":\"proof\",\"type\":\"tuple\",\"internalType\":\"structIBeefyClient.ValidatorProof\",\"components\":[{\"name\":\"v\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"r\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"s\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"index\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"account\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"proof\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"}]}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"ticketOwner\",\"inputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"transferOwnership\",\"inputs\":[{\"name\":\"newOwner\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"withdrawFunds\",\"inputs\":[{\"name\":\"recipient\",\"type\":\"address\",\"internalType\":\"addresspayable\"},{\"name\":\"amount\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"event\",\"name\":\"FundsDeposited\",\"inputs\":[{\"name\":\"depositor\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"FundsWithdrawn\",\"inputs\":[{\"name\":\"recipient\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"GasCredited\",\"inputs\":[{\"name\":\"relayer\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"commitmentHash\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"gasUsed\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"SubmissionRefunded\",\"inputs\":[{\"name\":\"relayer\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"progress\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"},{\"name\":\"refundAmount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"},{\"name\":\"totalGasUsed\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"error\",\"name\":\"InvalidAddress\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"NotTicketOwner\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"TransferFailed\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"Unauthorized\",\"inputs\":[]}]", +} + +// BeefyClientWrapperABI is the input ABI used to generate the binding from. +// Deprecated: Use BeefyClientWrapperMetaData.ABI instead. +var BeefyClientWrapperABI = BeefyClientWrapperMetaData.ABI + +// BeefyClientWrapper is an auto generated Go binding around an Ethereum contract. +type BeefyClientWrapper struct { + BeefyClientWrapperCaller // Read-only binding to the contract + BeefyClientWrapperTransactor // Write-only binding to the contract + BeefyClientWrapperFilterer // Log filterer for contract events +} + +// BeefyClientWrapperCaller is an auto generated read-only Go binding around an Ethereum contract. +type BeefyClientWrapperCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// BeefyClientWrapperTransactor is an auto generated write-only Go binding around an Ethereum contract. +type BeefyClientWrapperTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// BeefyClientWrapperFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type BeefyClientWrapperFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// BeefyClientWrapperSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type BeefyClientWrapperSession struct { + Contract *BeefyClientWrapper // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// BeefyClientWrapperCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type BeefyClientWrapperCallerSession struct { + Contract *BeefyClientWrapperCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// BeefyClientWrapperTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type BeefyClientWrapperTransactorSession struct { + Contract *BeefyClientWrapperTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// BeefyClientWrapperRaw is an auto generated low-level Go binding around an Ethereum contract. +type BeefyClientWrapperRaw struct { + Contract *BeefyClientWrapper // Generic contract binding to access the raw methods on +} + +// BeefyClientWrapperCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type BeefyClientWrapperCallerRaw struct { + Contract *BeefyClientWrapperCaller // Generic read-only contract binding to access the raw methods on +} + +// BeefyClientWrapperTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type BeefyClientWrapperTransactorRaw struct { + Contract *BeefyClientWrapperTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewBeefyClientWrapper creates a new instance of BeefyClientWrapper, bound to a specific deployed contract. +func NewBeefyClientWrapper(address common.Address, backend bind.ContractBackend) (*BeefyClientWrapper, error) { + contract, err := bindBeefyClientWrapper(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &BeefyClientWrapper{BeefyClientWrapperCaller: BeefyClientWrapperCaller{contract: contract}, BeefyClientWrapperTransactor: BeefyClientWrapperTransactor{contract: contract}, BeefyClientWrapperFilterer: BeefyClientWrapperFilterer{contract: contract}}, nil +} + +// NewBeefyClientWrapperCaller creates a new read-only instance of BeefyClientWrapper, bound to a specific deployed contract. +func NewBeefyClientWrapperCaller(address common.Address, caller bind.ContractCaller) (*BeefyClientWrapperCaller, error) { + contract, err := bindBeefyClientWrapper(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &BeefyClientWrapperCaller{contract: contract}, nil +} + +// NewBeefyClientWrapperTransactor creates a new write-only instance of BeefyClientWrapper, bound to a specific deployed contract. +func NewBeefyClientWrapperTransactor(address common.Address, transactor bind.ContractTransactor) (*BeefyClientWrapperTransactor, error) { + contract, err := bindBeefyClientWrapper(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &BeefyClientWrapperTransactor{contract: contract}, nil +} + +// NewBeefyClientWrapperFilterer creates a new log filterer instance of BeefyClientWrapper, bound to a specific deployed contract. +func NewBeefyClientWrapperFilterer(address common.Address, filterer bind.ContractFilterer) (*BeefyClientWrapperFilterer, error) { + contract, err := bindBeefyClientWrapper(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &BeefyClientWrapperFilterer{contract: contract}, nil +} + +// bindBeefyClientWrapper binds a generic wrapper to an already deployed contract. +func bindBeefyClientWrapper(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := BeefyClientWrapperMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_BeefyClientWrapper *BeefyClientWrapperRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _BeefyClientWrapper.Contract.BeefyClientWrapperCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_BeefyClientWrapper *BeefyClientWrapperRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _BeefyClientWrapper.Contract.BeefyClientWrapperTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_BeefyClientWrapper *BeefyClientWrapperRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _BeefyClientWrapper.Contract.BeefyClientWrapperTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_BeefyClientWrapper *BeefyClientWrapperCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _BeefyClientWrapper.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_BeefyClientWrapper *BeefyClientWrapperTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _BeefyClientWrapper.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_BeefyClientWrapper *BeefyClientWrapperTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _BeefyClientWrapper.Contract.contract.Transact(opts, method, params...) +} + +// BeefyClient is a free data retrieval call binding the contract method 0x776c81c3. +// +// Solidity: function beefyClient() view returns(address) +func (_BeefyClientWrapper *BeefyClientWrapperCaller) BeefyClient(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _BeefyClientWrapper.contract.Call(opts, &out, "beefyClient") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// BeefyClient is a free data retrieval call binding the contract method 0x776c81c3. +// +// Solidity: function beefyClient() view returns(address) +func (_BeefyClientWrapper *BeefyClientWrapperSession) BeefyClient() (common.Address, error) { + return _BeefyClientWrapper.Contract.BeefyClient(&_BeefyClientWrapper.CallOpts) +} + +// BeefyClient is a free data retrieval call binding the contract method 0x776c81c3. +// +// Solidity: function beefyClient() view returns(address) +func (_BeefyClientWrapper *BeefyClientWrapperCallerSession) BeefyClient() (common.Address, error) { + return _BeefyClientWrapper.Contract.BeefyClient(&_BeefyClientWrapper.CallOpts) +} + +// CreditedGas is a free data retrieval call binding the contract method 0x660b2928. +// +// Solidity: function creditedGas(bytes32 ) view returns(uint256) +func (_BeefyClientWrapper *BeefyClientWrapperCaller) CreditedGas(opts *bind.CallOpts, arg0 [32]byte) (*big.Int, error) { + var out []interface{} + err := _BeefyClientWrapper.contract.Call(opts, &out, "creditedGas", arg0) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// CreditedGas is a free data retrieval call binding the contract method 0x660b2928. +// +// Solidity: function creditedGas(bytes32 ) view returns(uint256) +func (_BeefyClientWrapper *BeefyClientWrapperSession) CreditedGas(arg0 [32]byte) (*big.Int, error) { + return _BeefyClientWrapper.Contract.CreditedGas(&_BeefyClientWrapper.CallOpts, arg0) +} + +// CreditedGas is a free data retrieval call binding the contract method 0x660b2928. +// +// Solidity: function creditedGas(bytes32 ) view returns(uint256) +func (_BeefyClientWrapper *BeefyClientWrapperCallerSession) CreditedGas(arg0 [32]byte) (*big.Int, error) { + return _BeefyClientWrapper.Contract.CreditedGas(&_BeefyClientWrapper.CallOpts, arg0) +} + +// EstimatePayout is a free data retrieval call binding the contract method 0x83d29b40. +// +// Solidity: function estimatePayout(uint256 gasUsed, uint256 gasPrice, uint256 progress) view returns(uint256 refundAmount) +func (_BeefyClientWrapper *BeefyClientWrapperCaller) EstimatePayout(opts *bind.CallOpts, gasUsed *big.Int, gasPrice *big.Int, progress *big.Int) (*big.Int, error) { + var out []interface{} + err := _BeefyClientWrapper.contract.Call(opts, &out, "estimatePayout", gasUsed, gasPrice, progress) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// EstimatePayout is a free data retrieval call binding the contract method 0x83d29b40. +// +// Solidity: function estimatePayout(uint256 gasUsed, uint256 gasPrice, uint256 progress) view returns(uint256 refundAmount) +func (_BeefyClientWrapper *BeefyClientWrapperSession) EstimatePayout(gasUsed *big.Int, gasPrice *big.Int, progress *big.Int) (*big.Int, error) { + return _BeefyClientWrapper.Contract.EstimatePayout(&_BeefyClientWrapper.CallOpts, gasUsed, gasPrice, progress) +} + +// EstimatePayout is a free data retrieval call binding the contract method 0x83d29b40. +// +// Solidity: function estimatePayout(uint256 gasUsed, uint256 gasPrice, uint256 progress) view returns(uint256 refundAmount) +func (_BeefyClientWrapper *BeefyClientWrapperCallerSession) EstimatePayout(gasUsed *big.Int, gasPrice *big.Int, progress *big.Int) (*big.Int, error) { + return _BeefyClientWrapper.Contract.EstimatePayout(&_BeefyClientWrapper.CallOpts, gasUsed, gasPrice, progress) +} + +// HighestPendingBlock is a free data retrieval call binding the contract method 0x0dc2be13. +// +// Solidity: function highestPendingBlock() view returns(uint256) +func (_BeefyClientWrapper *BeefyClientWrapperCaller) HighestPendingBlock(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _BeefyClientWrapper.contract.Call(opts, &out, "highestPendingBlock") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// HighestPendingBlock is a free data retrieval call binding the contract method 0x0dc2be13. +// +// Solidity: function highestPendingBlock() view returns(uint256) +func (_BeefyClientWrapper *BeefyClientWrapperSession) HighestPendingBlock() (*big.Int, error) { + return _BeefyClientWrapper.Contract.HighestPendingBlock(&_BeefyClientWrapper.CallOpts) +} + +// HighestPendingBlock is a free data retrieval call binding the contract method 0x0dc2be13. +// +// Solidity: function highestPendingBlock() view returns(uint256) +func (_BeefyClientWrapper *BeefyClientWrapperCallerSession) HighestPendingBlock() (*big.Int, error) { + return _BeefyClientWrapper.Contract.HighestPendingBlock(&_BeefyClientWrapper.CallOpts) +} + +// HighestPendingBlockTimestamp is a free data retrieval call binding the contract method 0x33e2c682. +// +// Solidity: function highestPendingBlockTimestamp() view returns(uint256) +func (_BeefyClientWrapper *BeefyClientWrapperCaller) HighestPendingBlockTimestamp(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _BeefyClientWrapper.contract.Call(opts, &out, "highestPendingBlockTimestamp") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// HighestPendingBlockTimestamp is a free data retrieval call binding the contract method 0x33e2c682. +// +// Solidity: function highestPendingBlockTimestamp() view returns(uint256) +func (_BeefyClientWrapper *BeefyClientWrapperSession) HighestPendingBlockTimestamp() (*big.Int, error) { + return _BeefyClientWrapper.Contract.HighestPendingBlockTimestamp(&_BeefyClientWrapper.CallOpts) +} + +// HighestPendingBlockTimestamp is a free data retrieval call binding the contract method 0x33e2c682. +// +// Solidity: function highestPendingBlockTimestamp() view returns(uint256) +func (_BeefyClientWrapper *BeefyClientWrapperCallerSession) HighestPendingBlockTimestamp() (*big.Int, error) { + return _BeefyClientWrapper.Contract.HighestPendingBlockTimestamp(&_BeefyClientWrapper.CallOpts) +} + +// MaxGasPrice is a free data retrieval call binding the contract method 0x3de39c11. +// +// Solidity: function maxGasPrice() view returns(uint256) +func (_BeefyClientWrapper *BeefyClientWrapperCaller) MaxGasPrice(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _BeefyClientWrapper.contract.Call(opts, &out, "maxGasPrice") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// MaxGasPrice is a free data retrieval call binding the contract method 0x3de39c11. +// +// Solidity: function maxGasPrice() view returns(uint256) +func (_BeefyClientWrapper *BeefyClientWrapperSession) MaxGasPrice() (*big.Int, error) { + return _BeefyClientWrapper.Contract.MaxGasPrice(&_BeefyClientWrapper.CallOpts) +} + +// MaxGasPrice is a free data retrieval call binding the contract method 0x3de39c11. +// +// Solidity: function maxGasPrice() view returns(uint256) +func (_BeefyClientWrapper *BeefyClientWrapperCallerSession) MaxGasPrice() (*big.Int, error) { + return _BeefyClientWrapper.Contract.MaxGasPrice(&_BeefyClientWrapper.CallOpts) +} + +// MaxRefundAmount is a free data retrieval call binding the contract method 0x28bbc5c1. +// +// Solidity: function maxRefundAmount() view returns(uint256) +func (_BeefyClientWrapper *BeefyClientWrapperCaller) MaxRefundAmount(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _BeefyClientWrapper.contract.Call(opts, &out, "maxRefundAmount") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// MaxRefundAmount is a free data retrieval call binding the contract method 0x28bbc5c1. +// +// Solidity: function maxRefundAmount() view returns(uint256) +func (_BeefyClientWrapper *BeefyClientWrapperSession) MaxRefundAmount() (*big.Int, error) { + return _BeefyClientWrapper.Contract.MaxRefundAmount(&_BeefyClientWrapper.CallOpts) +} + +// MaxRefundAmount is a free data retrieval call binding the contract method 0x28bbc5c1. +// +// Solidity: function maxRefundAmount() view returns(uint256) +func (_BeefyClientWrapper *BeefyClientWrapperCallerSession) MaxRefundAmount() (*big.Int, error) { + return _BeefyClientWrapper.Contract.MaxRefundAmount(&_BeefyClientWrapper.CallOpts) +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_BeefyClientWrapper *BeefyClientWrapperCaller) Owner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _BeefyClientWrapper.contract.Call(opts, &out, "owner") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_BeefyClientWrapper *BeefyClientWrapperSession) Owner() (common.Address, error) { + return _BeefyClientWrapper.Contract.Owner(&_BeefyClientWrapper.CallOpts) +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_BeefyClientWrapper *BeefyClientWrapperCallerSession) Owner() (common.Address, error) { + return _BeefyClientWrapper.Contract.Owner(&_BeefyClientWrapper.CallOpts) +} + +// RefundTarget is a free data retrieval call binding the contract method 0xd679e02a. +// +// Solidity: function refundTarget() view returns(uint256) +func (_BeefyClientWrapper *BeefyClientWrapperCaller) RefundTarget(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _BeefyClientWrapper.contract.Call(opts, &out, "refundTarget") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// RefundTarget is a free data retrieval call binding the contract method 0xd679e02a. +// +// Solidity: function refundTarget() view returns(uint256) +func (_BeefyClientWrapper *BeefyClientWrapperSession) RefundTarget() (*big.Int, error) { + return _BeefyClientWrapper.Contract.RefundTarget(&_BeefyClientWrapper.CallOpts) +} + +// RefundTarget is a free data retrieval call binding the contract method 0xd679e02a. +// +// Solidity: function refundTarget() view returns(uint256) +func (_BeefyClientWrapper *BeefyClientWrapperCallerSession) RefundTarget() (*big.Int, error) { + return _BeefyClientWrapper.Contract.RefundTarget(&_BeefyClientWrapper.CallOpts) +} + +// TicketOwner is a free data retrieval call binding the contract method 0xd2e82bfe. +// +// Solidity: function ticketOwner(bytes32 ) view returns(address) +func (_BeefyClientWrapper *BeefyClientWrapperCaller) TicketOwner(opts *bind.CallOpts, arg0 [32]byte) (common.Address, error) { + var out []interface{} + err := _BeefyClientWrapper.contract.Call(opts, &out, "ticketOwner", arg0) + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// TicketOwner is a free data retrieval call binding the contract method 0xd2e82bfe. +// +// Solidity: function ticketOwner(bytes32 ) view returns(address) +func (_BeefyClientWrapper *BeefyClientWrapperSession) TicketOwner(arg0 [32]byte) (common.Address, error) { + return _BeefyClientWrapper.Contract.TicketOwner(&_BeefyClientWrapper.CallOpts, arg0) +} + +// TicketOwner is a free data retrieval call binding the contract method 0xd2e82bfe. +// +// Solidity: function ticketOwner(bytes32 ) view returns(address) +func (_BeefyClientWrapper *BeefyClientWrapperCallerSession) TicketOwner(arg0 [32]byte) (common.Address, error) { + return _BeefyClientWrapper.Contract.TicketOwner(&_BeefyClientWrapper.CallOpts, arg0) +} + +// ClearTicket is a paid mutator transaction binding the contract method 0xa82218ed. +// +// Solidity: function clearTicket(bytes32 commitmentHash) returns() +func (_BeefyClientWrapper *BeefyClientWrapperTransactor) ClearTicket(opts *bind.TransactOpts, commitmentHash [32]byte) (*types.Transaction, error) { + return _BeefyClientWrapper.contract.Transact(opts, "clearTicket", commitmentHash) +} + +// ClearTicket is a paid mutator transaction binding the contract method 0xa82218ed. +// +// Solidity: function clearTicket(bytes32 commitmentHash) returns() +func (_BeefyClientWrapper *BeefyClientWrapperSession) ClearTicket(commitmentHash [32]byte) (*types.Transaction, error) { + return _BeefyClientWrapper.Contract.ClearTicket(&_BeefyClientWrapper.TransactOpts, commitmentHash) +} + +// ClearTicket is a paid mutator transaction binding the contract method 0xa82218ed. +// +// Solidity: function clearTicket(bytes32 commitmentHash) returns() +func (_BeefyClientWrapper *BeefyClientWrapperTransactorSession) ClearTicket(commitmentHash [32]byte) (*types.Transaction, error) { + return _BeefyClientWrapper.Contract.ClearTicket(&_BeefyClientWrapper.TransactOpts, commitmentHash) +} + +// CommitPrevRandao is a paid mutator transaction binding the contract method 0xa77cf3d2. +// +// Solidity: function commitPrevRandao(bytes32 commitmentHash) returns() +func (_BeefyClientWrapper *BeefyClientWrapperTransactor) CommitPrevRandao(opts *bind.TransactOpts, commitmentHash [32]byte) (*types.Transaction, error) { + return _BeefyClientWrapper.contract.Transact(opts, "commitPrevRandao", commitmentHash) +} + +// CommitPrevRandao is a paid mutator transaction binding the contract method 0xa77cf3d2. +// +// Solidity: function commitPrevRandao(bytes32 commitmentHash) returns() +func (_BeefyClientWrapper *BeefyClientWrapperSession) CommitPrevRandao(commitmentHash [32]byte) (*types.Transaction, error) { + return _BeefyClientWrapper.Contract.CommitPrevRandao(&_BeefyClientWrapper.TransactOpts, commitmentHash) +} + +// CommitPrevRandao is a paid mutator transaction binding the contract method 0xa77cf3d2. +// +// Solidity: function commitPrevRandao(bytes32 commitmentHash) returns() +func (_BeefyClientWrapper *BeefyClientWrapperTransactorSession) CommitPrevRandao(commitmentHash [32]byte) (*types.Transaction, error) { + return _BeefyClientWrapper.Contract.CommitPrevRandao(&_BeefyClientWrapper.TransactOpts, commitmentHash) +} + +// SetMaxGasPrice is a paid mutator transaction binding the contract method 0xd2fa635e. +// +// Solidity: function setMaxGasPrice(uint256 _maxGasPrice) returns() +func (_BeefyClientWrapper *BeefyClientWrapperTransactor) SetMaxGasPrice(opts *bind.TransactOpts, _maxGasPrice *big.Int) (*types.Transaction, error) { + return _BeefyClientWrapper.contract.Transact(opts, "setMaxGasPrice", _maxGasPrice) +} + +// SetMaxGasPrice is a paid mutator transaction binding the contract method 0xd2fa635e. +// +// Solidity: function setMaxGasPrice(uint256 _maxGasPrice) returns() +func (_BeefyClientWrapper *BeefyClientWrapperSession) SetMaxGasPrice(_maxGasPrice *big.Int) (*types.Transaction, error) { + return _BeefyClientWrapper.Contract.SetMaxGasPrice(&_BeefyClientWrapper.TransactOpts, _maxGasPrice) +} + +// SetMaxGasPrice is a paid mutator transaction binding the contract method 0xd2fa635e. +// +// Solidity: function setMaxGasPrice(uint256 _maxGasPrice) returns() +func (_BeefyClientWrapper *BeefyClientWrapperTransactorSession) SetMaxGasPrice(_maxGasPrice *big.Int) (*types.Transaction, error) { + return _BeefyClientWrapper.Contract.SetMaxGasPrice(&_BeefyClientWrapper.TransactOpts, _maxGasPrice) +} + +// SetMaxRefundAmount is a paid mutator transaction binding the contract method 0x2efbeccd. +// +// Solidity: function setMaxRefundAmount(uint256 _maxRefundAmount) returns() +func (_BeefyClientWrapper *BeefyClientWrapperTransactor) SetMaxRefundAmount(opts *bind.TransactOpts, _maxRefundAmount *big.Int) (*types.Transaction, error) { + return _BeefyClientWrapper.contract.Transact(opts, "setMaxRefundAmount", _maxRefundAmount) +} + +// SetMaxRefundAmount is a paid mutator transaction binding the contract method 0x2efbeccd. +// +// Solidity: function setMaxRefundAmount(uint256 _maxRefundAmount) returns() +func (_BeefyClientWrapper *BeefyClientWrapperSession) SetMaxRefundAmount(_maxRefundAmount *big.Int) (*types.Transaction, error) { + return _BeefyClientWrapper.Contract.SetMaxRefundAmount(&_BeefyClientWrapper.TransactOpts, _maxRefundAmount) +} + +// SetMaxRefundAmount is a paid mutator transaction binding the contract method 0x2efbeccd. +// +// Solidity: function setMaxRefundAmount(uint256 _maxRefundAmount) returns() +func (_BeefyClientWrapper *BeefyClientWrapperTransactorSession) SetMaxRefundAmount(_maxRefundAmount *big.Int) (*types.Transaction, error) { + return _BeefyClientWrapper.Contract.SetMaxRefundAmount(&_BeefyClientWrapper.TransactOpts, _maxRefundAmount) +} + +// SetRefundTarget is a paid mutator transaction binding the contract method 0xc15ea4b5. +// +// Solidity: function setRefundTarget(uint256 _refundTarget) returns() +func (_BeefyClientWrapper *BeefyClientWrapperTransactor) SetRefundTarget(opts *bind.TransactOpts, _refundTarget *big.Int) (*types.Transaction, error) { + return _BeefyClientWrapper.contract.Transact(opts, "setRefundTarget", _refundTarget) +} + +// SetRefundTarget is a paid mutator transaction binding the contract method 0xc15ea4b5. +// +// Solidity: function setRefundTarget(uint256 _refundTarget) returns() +func (_BeefyClientWrapper *BeefyClientWrapperSession) SetRefundTarget(_refundTarget *big.Int) (*types.Transaction, error) { + return _BeefyClientWrapper.Contract.SetRefundTarget(&_BeefyClientWrapper.TransactOpts, _refundTarget) +} + +// SetRefundTarget is a paid mutator transaction binding the contract method 0xc15ea4b5. +// +// Solidity: function setRefundTarget(uint256 _refundTarget) returns() +func (_BeefyClientWrapper *BeefyClientWrapperTransactorSession) SetRefundTarget(_refundTarget *big.Int) (*types.Transaction, error) { + return _BeefyClientWrapper.Contract.SetRefundTarget(&_BeefyClientWrapper.TransactOpts, _refundTarget) +} + +// SubmitFiatShamir is a paid mutator transaction binding the contract method 0xc7d6e93d. +// +// Solidity: function submitFiatShamir((uint32,uint64,(bytes2,bytes)[]) commitment, uint256[] bitfield, (uint8,bytes32,bytes32,uint256,address,bytes32[])[] proofs, (uint8,uint32,bytes32,uint64,uint32,bytes32,bytes32) leaf, bytes32[] leafProof, uint256 leafProofOrder) returns() +func (_BeefyClientWrapper *BeefyClientWrapperTransactor) SubmitFiatShamir(opts *bind.TransactOpts, commitment IBeefyClientCommitment, bitfield []*big.Int, proofs []IBeefyClientValidatorProof, leaf IBeefyClientMMRLeaf, leafProof [][32]byte, leafProofOrder *big.Int) (*types.Transaction, error) { + return _BeefyClientWrapper.contract.Transact(opts, "submitFiatShamir", commitment, bitfield, proofs, leaf, leafProof, leafProofOrder) +} + +// SubmitFiatShamir is a paid mutator transaction binding the contract method 0xc7d6e93d. +// +// Solidity: function submitFiatShamir((uint32,uint64,(bytes2,bytes)[]) commitment, uint256[] bitfield, (uint8,bytes32,bytes32,uint256,address,bytes32[])[] proofs, (uint8,uint32,bytes32,uint64,uint32,bytes32,bytes32) leaf, bytes32[] leafProof, uint256 leafProofOrder) returns() +func (_BeefyClientWrapper *BeefyClientWrapperSession) SubmitFiatShamir(commitment IBeefyClientCommitment, bitfield []*big.Int, proofs []IBeefyClientValidatorProof, leaf IBeefyClientMMRLeaf, leafProof [][32]byte, leafProofOrder *big.Int) (*types.Transaction, error) { + return _BeefyClientWrapper.Contract.SubmitFiatShamir(&_BeefyClientWrapper.TransactOpts, commitment, bitfield, proofs, leaf, leafProof, leafProofOrder) +} + +// SubmitFiatShamir is a paid mutator transaction binding the contract method 0xc7d6e93d. +// +// Solidity: function submitFiatShamir((uint32,uint64,(bytes2,bytes)[]) commitment, uint256[] bitfield, (uint8,bytes32,bytes32,uint256,address,bytes32[])[] proofs, (uint8,uint32,bytes32,uint64,uint32,bytes32,bytes32) leaf, bytes32[] leafProof, uint256 leafProofOrder) returns() +func (_BeefyClientWrapper *BeefyClientWrapperTransactorSession) SubmitFiatShamir(commitment IBeefyClientCommitment, bitfield []*big.Int, proofs []IBeefyClientValidatorProof, leaf IBeefyClientMMRLeaf, leafProof [][32]byte, leafProofOrder *big.Int) (*types.Transaction, error) { + return _BeefyClientWrapper.Contract.SubmitFiatShamir(&_BeefyClientWrapper.TransactOpts, commitment, bitfield, proofs, leaf, leafProof, leafProofOrder) +} + +// SubmitFinal is a paid mutator transaction binding the contract method 0x623b223d. +// +// Solidity: function submitFinal((uint32,uint64,(bytes2,bytes)[]) commitment, uint256[] bitfield, (uint8,bytes32,bytes32,uint256,address,bytes32[])[] proofs, (uint8,uint32,bytes32,uint64,uint32,bytes32,bytes32) leaf, bytes32[] leafProof, uint256 leafProofOrder) returns() +func (_BeefyClientWrapper *BeefyClientWrapperTransactor) SubmitFinal(opts *bind.TransactOpts, commitment IBeefyClientCommitment, bitfield []*big.Int, proofs []IBeefyClientValidatorProof, leaf IBeefyClientMMRLeaf, leafProof [][32]byte, leafProofOrder *big.Int) (*types.Transaction, error) { + return _BeefyClientWrapper.contract.Transact(opts, "submitFinal", commitment, bitfield, proofs, leaf, leafProof, leafProofOrder) +} + +// SubmitFinal is a paid mutator transaction binding the contract method 0x623b223d. +// +// Solidity: function submitFinal((uint32,uint64,(bytes2,bytes)[]) commitment, uint256[] bitfield, (uint8,bytes32,bytes32,uint256,address,bytes32[])[] proofs, (uint8,uint32,bytes32,uint64,uint32,bytes32,bytes32) leaf, bytes32[] leafProof, uint256 leafProofOrder) returns() +func (_BeefyClientWrapper *BeefyClientWrapperSession) SubmitFinal(commitment IBeefyClientCommitment, bitfield []*big.Int, proofs []IBeefyClientValidatorProof, leaf IBeefyClientMMRLeaf, leafProof [][32]byte, leafProofOrder *big.Int) (*types.Transaction, error) { + return _BeefyClientWrapper.Contract.SubmitFinal(&_BeefyClientWrapper.TransactOpts, commitment, bitfield, proofs, leaf, leafProof, leafProofOrder) +} + +// SubmitFinal is a paid mutator transaction binding the contract method 0x623b223d. +// +// Solidity: function submitFinal((uint32,uint64,(bytes2,bytes)[]) commitment, uint256[] bitfield, (uint8,bytes32,bytes32,uint256,address,bytes32[])[] proofs, (uint8,uint32,bytes32,uint64,uint32,bytes32,bytes32) leaf, bytes32[] leafProof, uint256 leafProofOrder) returns() +func (_BeefyClientWrapper *BeefyClientWrapperTransactorSession) SubmitFinal(commitment IBeefyClientCommitment, bitfield []*big.Int, proofs []IBeefyClientValidatorProof, leaf IBeefyClientMMRLeaf, leafProof [][32]byte, leafProofOrder *big.Int) (*types.Transaction, error) { + return _BeefyClientWrapper.Contract.SubmitFinal(&_BeefyClientWrapper.TransactOpts, commitment, bitfield, proofs, leaf, leafProof, leafProofOrder) +} + +// SubmitInitial is a paid mutator transaction binding the contract method 0xbb51f1eb. +// +// Solidity: function submitInitial((uint32,uint64,(bytes2,bytes)[]) commitment, uint256[] bitfield, (uint8,bytes32,bytes32,uint256,address,bytes32[]) proof) returns() +func (_BeefyClientWrapper *BeefyClientWrapperTransactor) SubmitInitial(opts *bind.TransactOpts, commitment IBeefyClientCommitment, bitfield []*big.Int, proof IBeefyClientValidatorProof) (*types.Transaction, error) { + return _BeefyClientWrapper.contract.Transact(opts, "submitInitial", commitment, bitfield, proof) +} + +// SubmitInitial is a paid mutator transaction binding the contract method 0xbb51f1eb. +// +// Solidity: function submitInitial((uint32,uint64,(bytes2,bytes)[]) commitment, uint256[] bitfield, (uint8,bytes32,bytes32,uint256,address,bytes32[]) proof) returns() +func (_BeefyClientWrapper *BeefyClientWrapperSession) SubmitInitial(commitment IBeefyClientCommitment, bitfield []*big.Int, proof IBeefyClientValidatorProof) (*types.Transaction, error) { + return _BeefyClientWrapper.Contract.SubmitInitial(&_BeefyClientWrapper.TransactOpts, commitment, bitfield, proof) +} + +// SubmitInitial is a paid mutator transaction binding the contract method 0xbb51f1eb. +// +// Solidity: function submitInitial((uint32,uint64,(bytes2,bytes)[]) commitment, uint256[] bitfield, (uint8,bytes32,bytes32,uint256,address,bytes32[]) proof) returns() +func (_BeefyClientWrapper *BeefyClientWrapperTransactorSession) SubmitInitial(commitment IBeefyClientCommitment, bitfield []*big.Int, proof IBeefyClientValidatorProof) (*types.Transaction, error) { + return _BeefyClientWrapper.Contract.SubmitInitial(&_BeefyClientWrapper.TransactOpts, commitment, bitfield, proof) +} + +// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. +// +// Solidity: function transferOwnership(address newOwner) returns() +func (_BeefyClientWrapper *BeefyClientWrapperTransactor) TransferOwnership(opts *bind.TransactOpts, newOwner common.Address) (*types.Transaction, error) { + return _BeefyClientWrapper.contract.Transact(opts, "transferOwnership", newOwner) +} + +// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. +// +// Solidity: function transferOwnership(address newOwner) returns() +func (_BeefyClientWrapper *BeefyClientWrapperSession) TransferOwnership(newOwner common.Address) (*types.Transaction, error) { + return _BeefyClientWrapper.Contract.TransferOwnership(&_BeefyClientWrapper.TransactOpts, newOwner) +} + +// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. +// +// Solidity: function transferOwnership(address newOwner) returns() +func (_BeefyClientWrapper *BeefyClientWrapperTransactorSession) TransferOwnership(newOwner common.Address) (*types.Transaction, error) { + return _BeefyClientWrapper.Contract.TransferOwnership(&_BeefyClientWrapper.TransactOpts, newOwner) +} + +// WithdrawFunds is a paid mutator transaction binding the contract method 0xc1075329. +// +// Solidity: function withdrawFunds(address recipient, uint256 amount) returns() +func (_BeefyClientWrapper *BeefyClientWrapperTransactor) WithdrawFunds(opts *bind.TransactOpts, recipient common.Address, amount *big.Int) (*types.Transaction, error) { + return _BeefyClientWrapper.contract.Transact(opts, "withdrawFunds", recipient, amount) +} + +// WithdrawFunds is a paid mutator transaction binding the contract method 0xc1075329. +// +// Solidity: function withdrawFunds(address recipient, uint256 amount) returns() +func (_BeefyClientWrapper *BeefyClientWrapperSession) WithdrawFunds(recipient common.Address, amount *big.Int) (*types.Transaction, error) { + return _BeefyClientWrapper.Contract.WithdrawFunds(&_BeefyClientWrapper.TransactOpts, recipient, amount) +} + +// WithdrawFunds is a paid mutator transaction binding the contract method 0xc1075329. +// +// Solidity: function withdrawFunds(address recipient, uint256 amount) returns() +func (_BeefyClientWrapper *BeefyClientWrapperTransactorSession) WithdrawFunds(recipient common.Address, amount *big.Int) (*types.Transaction, error) { + return _BeefyClientWrapper.Contract.WithdrawFunds(&_BeefyClientWrapper.TransactOpts, recipient, amount) +} + +// Receive is a paid mutator transaction binding the contract receive function. +// +// Solidity: receive() payable returns() +func (_BeefyClientWrapper *BeefyClientWrapperTransactor) Receive(opts *bind.TransactOpts) (*types.Transaction, error) { + return _BeefyClientWrapper.contract.RawTransact(opts, nil) // calldata is disallowed for receive function +} + +// Receive is a paid mutator transaction binding the contract receive function. +// +// Solidity: receive() payable returns() +func (_BeefyClientWrapper *BeefyClientWrapperSession) Receive() (*types.Transaction, error) { + return _BeefyClientWrapper.Contract.Receive(&_BeefyClientWrapper.TransactOpts) +} + +// Receive is a paid mutator transaction binding the contract receive function. +// +// Solidity: receive() payable returns() +func (_BeefyClientWrapper *BeefyClientWrapperTransactorSession) Receive() (*types.Transaction, error) { + return _BeefyClientWrapper.Contract.Receive(&_BeefyClientWrapper.TransactOpts) +} + +// BeefyClientWrapperFundsDepositedIterator is returned from FilterFundsDeposited and is used to iterate over the raw logs and unpacked data for FundsDeposited events raised by the BeefyClientWrapper contract. +type BeefyClientWrapperFundsDepositedIterator struct { + Event *BeefyClientWrapperFundsDeposited // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *BeefyClientWrapperFundsDepositedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(BeefyClientWrapperFundsDeposited) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(BeefyClientWrapperFundsDeposited) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *BeefyClientWrapperFundsDepositedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BeefyClientWrapperFundsDepositedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BeefyClientWrapperFundsDeposited represents a FundsDeposited event raised by the BeefyClientWrapper contract. +type BeefyClientWrapperFundsDeposited struct { + Depositor common.Address + Amount *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterFundsDeposited is a free log retrieval operation binding the contract event 0x543ba50a5eec5e6178218e364b1d0f396157b3c8fa278522c2cb7fd99407d474. +// +// Solidity: event FundsDeposited(address indexed depositor, uint256 amount) +func (_BeefyClientWrapper *BeefyClientWrapperFilterer) FilterFundsDeposited(opts *bind.FilterOpts, depositor []common.Address) (*BeefyClientWrapperFundsDepositedIterator, error) { + + var depositorRule []interface{} + for _, depositorItem := range depositor { + depositorRule = append(depositorRule, depositorItem) + } + + logs, sub, err := _BeefyClientWrapper.contract.FilterLogs(opts, "FundsDeposited", depositorRule) + if err != nil { + return nil, err + } + return &BeefyClientWrapperFundsDepositedIterator{contract: _BeefyClientWrapper.contract, event: "FundsDeposited", logs: logs, sub: sub}, nil +} + +// WatchFundsDeposited is a free log subscription operation binding the contract event 0x543ba50a5eec5e6178218e364b1d0f396157b3c8fa278522c2cb7fd99407d474. +// +// Solidity: event FundsDeposited(address indexed depositor, uint256 amount) +func (_BeefyClientWrapper *BeefyClientWrapperFilterer) WatchFundsDeposited(opts *bind.WatchOpts, sink chan<- *BeefyClientWrapperFundsDeposited, depositor []common.Address) (event.Subscription, error) { + + var depositorRule []interface{} + for _, depositorItem := range depositor { + depositorRule = append(depositorRule, depositorItem) + } + + logs, sub, err := _BeefyClientWrapper.contract.WatchLogs(opts, "FundsDeposited", depositorRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(BeefyClientWrapperFundsDeposited) + if err := _BeefyClientWrapper.contract.UnpackLog(event, "FundsDeposited", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseFundsDeposited is a log parse operation binding the contract event 0x543ba50a5eec5e6178218e364b1d0f396157b3c8fa278522c2cb7fd99407d474. +// +// Solidity: event FundsDeposited(address indexed depositor, uint256 amount) +func (_BeefyClientWrapper *BeefyClientWrapperFilterer) ParseFundsDeposited(log types.Log) (*BeefyClientWrapperFundsDeposited, error) { + event := new(BeefyClientWrapperFundsDeposited) + if err := _BeefyClientWrapper.contract.UnpackLog(event, "FundsDeposited", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// BeefyClientWrapperFundsWithdrawnIterator is returned from FilterFundsWithdrawn and is used to iterate over the raw logs and unpacked data for FundsWithdrawn events raised by the BeefyClientWrapper contract. +type BeefyClientWrapperFundsWithdrawnIterator struct { + Event *BeefyClientWrapperFundsWithdrawn // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *BeefyClientWrapperFundsWithdrawnIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(BeefyClientWrapperFundsWithdrawn) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(BeefyClientWrapperFundsWithdrawn) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *BeefyClientWrapperFundsWithdrawnIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BeefyClientWrapperFundsWithdrawnIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BeefyClientWrapperFundsWithdrawn represents a FundsWithdrawn event raised by the BeefyClientWrapper contract. +type BeefyClientWrapperFundsWithdrawn struct { + Recipient common.Address + Amount *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterFundsWithdrawn is a free log retrieval operation binding the contract event 0xeaff4b37086828766ad3268786972c0cd24259d4c87a80f9d3963a3c3d999b0d. +// +// Solidity: event FundsWithdrawn(address indexed recipient, uint256 amount) +func (_BeefyClientWrapper *BeefyClientWrapperFilterer) FilterFundsWithdrawn(opts *bind.FilterOpts, recipient []common.Address) (*BeefyClientWrapperFundsWithdrawnIterator, error) { + + var recipientRule []interface{} + for _, recipientItem := range recipient { + recipientRule = append(recipientRule, recipientItem) + } + + logs, sub, err := _BeefyClientWrapper.contract.FilterLogs(opts, "FundsWithdrawn", recipientRule) + if err != nil { + return nil, err + } + return &BeefyClientWrapperFundsWithdrawnIterator{contract: _BeefyClientWrapper.contract, event: "FundsWithdrawn", logs: logs, sub: sub}, nil +} + +// WatchFundsWithdrawn is a free log subscription operation binding the contract event 0xeaff4b37086828766ad3268786972c0cd24259d4c87a80f9d3963a3c3d999b0d. +// +// Solidity: event FundsWithdrawn(address indexed recipient, uint256 amount) +func (_BeefyClientWrapper *BeefyClientWrapperFilterer) WatchFundsWithdrawn(opts *bind.WatchOpts, sink chan<- *BeefyClientWrapperFundsWithdrawn, recipient []common.Address) (event.Subscription, error) { + + var recipientRule []interface{} + for _, recipientItem := range recipient { + recipientRule = append(recipientRule, recipientItem) + } + + logs, sub, err := _BeefyClientWrapper.contract.WatchLogs(opts, "FundsWithdrawn", recipientRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(BeefyClientWrapperFundsWithdrawn) + if err := _BeefyClientWrapper.contract.UnpackLog(event, "FundsWithdrawn", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseFundsWithdrawn is a log parse operation binding the contract event 0xeaff4b37086828766ad3268786972c0cd24259d4c87a80f9d3963a3c3d999b0d. +// +// Solidity: event FundsWithdrawn(address indexed recipient, uint256 amount) +func (_BeefyClientWrapper *BeefyClientWrapperFilterer) ParseFundsWithdrawn(log types.Log) (*BeefyClientWrapperFundsWithdrawn, error) { + event := new(BeefyClientWrapperFundsWithdrawn) + if err := _BeefyClientWrapper.contract.UnpackLog(event, "FundsWithdrawn", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// BeefyClientWrapperGasCreditedIterator is returned from FilterGasCredited and is used to iterate over the raw logs and unpacked data for GasCredited events raised by the BeefyClientWrapper contract. +type BeefyClientWrapperGasCreditedIterator struct { + Event *BeefyClientWrapperGasCredited // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *BeefyClientWrapperGasCreditedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(BeefyClientWrapperGasCredited) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(BeefyClientWrapperGasCredited) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *BeefyClientWrapperGasCreditedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BeefyClientWrapperGasCreditedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BeefyClientWrapperGasCredited represents a GasCredited event raised by the BeefyClientWrapper contract. +type BeefyClientWrapperGasCredited struct { + Relayer common.Address + CommitmentHash [32]byte + GasUsed *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterGasCredited is a free log retrieval operation binding the contract event 0xa96627e523aafcce96e4a95478ed8181042d6fcd32fd0892e1743408da8948e2. +// +// Solidity: event GasCredited(address indexed relayer, bytes32 indexed commitmentHash, uint256 gasUsed) +func (_BeefyClientWrapper *BeefyClientWrapperFilterer) FilterGasCredited(opts *bind.FilterOpts, relayer []common.Address, commitmentHash [][32]byte) (*BeefyClientWrapperGasCreditedIterator, error) { + + var relayerRule []interface{} + for _, relayerItem := range relayer { + relayerRule = append(relayerRule, relayerItem) + } + var commitmentHashRule []interface{} + for _, commitmentHashItem := range commitmentHash { + commitmentHashRule = append(commitmentHashRule, commitmentHashItem) + } + + logs, sub, err := _BeefyClientWrapper.contract.FilterLogs(opts, "GasCredited", relayerRule, commitmentHashRule) + if err != nil { + return nil, err + } + return &BeefyClientWrapperGasCreditedIterator{contract: _BeefyClientWrapper.contract, event: "GasCredited", logs: logs, sub: sub}, nil +} + +// WatchGasCredited is a free log subscription operation binding the contract event 0xa96627e523aafcce96e4a95478ed8181042d6fcd32fd0892e1743408da8948e2. +// +// Solidity: event GasCredited(address indexed relayer, bytes32 indexed commitmentHash, uint256 gasUsed) +func (_BeefyClientWrapper *BeefyClientWrapperFilterer) WatchGasCredited(opts *bind.WatchOpts, sink chan<- *BeefyClientWrapperGasCredited, relayer []common.Address, commitmentHash [][32]byte) (event.Subscription, error) { + + var relayerRule []interface{} + for _, relayerItem := range relayer { + relayerRule = append(relayerRule, relayerItem) + } + var commitmentHashRule []interface{} + for _, commitmentHashItem := range commitmentHash { + commitmentHashRule = append(commitmentHashRule, commitmentHashItem) + } + + logs, sub, err := _BeefyClientWrapper.contract.WatchLogs(opts, "GasCredited", relayerRule, commitmentHashRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(BeefyClientWrapperGasCredited) + if err := _BeefyClientWrapper.contract.UnpackLog(event, "GasCredited", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseGasCredited is a log parse operation binding the contract event 0xa96627e523aafcce96e4a95478ed8181042d6fcd32fd0892e1743408da8948e2. +// +// Solidity: event GasCredited(address indexed relayer, bytes32 indexed commitmentHash, uint256 gasUsed) +func (_BeefyClientWrapper *BeefyClientWrapperFilterer) ParseGasCredited(log types.Log) (*BeefyClientWrapperGasCredited, error) { + event := new(BeefyClientWrapperGasCredited) + if err := _BeefyClientWrapper.contract.UnpackLog(event, "GasCredited", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// BeefyClientWrapperSubmissionRefundedIterator is returned from FilterSubmissionRefunded and is used to iterate over the raw logs and unpacked data for SubmissionRefunded events raised by the BeefyClientWrapper contract. +type BeefyClientWrapperSubmissionRefundedIterator struct { + Event *BeefyClientWrapperSubmissionRefunded // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *BeefyClientWrapperSubmissionRefundedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(BeefyClientWrapperSubmissionRefunded) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(BeefyClientWrapperSubmissionRefunded) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *BeefyClientWrapperSubmissionRefundedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BeefyClientWrapperSubmissionRefundedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BeefyClientWrapperSubmissionRefunded represents a SubmissionRefunded event raised by the BeefyClientWrapper contract. +type BeefyClientWrapperSubmissionRefunded struct { + Relayer common.Address + Progress *big.Int + RefundAmount *big.Int + TotalGasUsed *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterSubmissionRefunded is a free log retrieval operation binding the contract event 0x9e15c83bdb5f5cd34d28652d4d4f05f485bcbc3f54404fd5b830fc9aad6dfd1c. +// +// Solidity: event SubmissionRefunded(address indexed relayer, uint256 progress, uint256 refundAmount, uint256 totalGasUsed) +func (_BeefyClientWrapper *BeefyClientWrapperFilterer) FilterSubmissionRefunded(opts *bind.FilterOpts, relayer []common.Address) (*BeefyClientWrapperSubmissionRefundedIterator, error) { + + var relayerRule []interface{} + for _, relayerItem := range relayer { + relayerRule = append(relayerRule, relayerItem) + } + + logs, sub, err := _BeefyClientWrapper.contract.FilterLogs(opts, "SubmissionRefunded", relayerRule) + if err != nil { + return nil, err + } + return &BeefyClientWrapperSubmissionRefundedIterator{contract: _BeefyClientWrapper.contract, event: "SubmissionRefunded", logs: logs, sub: sub}, nil +} + +// WatchSubmissionRefunded is a free log subscription operation binding the contract event 0x9e15c83bdb5f5cd34d28652d4d4f05f485bcbc3f54404fd5b830fc9aad6dfd1c. +// +// Solidity: event SubmissionRefunded(address indexed relayer, uint256 progress, uint256 refundAmount, uint256 totalGasUsed) +func (_BeefyClientWrapper *BeefyClientWrapperFilterer) WatchSubmissionRefunded(opts *bind.WatchOpts, sink chan<- *BeefyClientWrapperSubmissionRefunded, relayer []common.Address) (event.Subscription, error) { + + var relayerRule []interface{} + for _, relayerItem := range relayer { + relayerRule = append(relayerRule, relayerItem) + } + + logs, sub, err := _BeefyClientWrapper.contract.WatchLogs(opts, "SubmissionRefunded", relayerRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(BeefyClientWrapperSubmissionRefunded) + if err := _BeefyClientWrapper.contract.UnpackLog(event, "SubmissionRefunded", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseSubmissionRefunded is a log parse operation binding the contract event 0x9e15c83bdb5f5cd34d28652d4d4f05f485bcbc3f54404fd5b830fc9aad6dfd1c. +// +// Solidity: event SubmissionRefunded(address indexed relayer, uint256 progress, uint256 refundAmount, uint256 totalGasUsed) +func (_BeefyClientWrapper *BeefyClientWrapperFilterer) ParseSubmissionRefunded(log types.Log) (*BeefyClientWrapperSubmissionRefunded, error) { + event := new(BeefyClientWrapperSubmissionRefunded) + if err := _BeefyClientWrapper.contract.UnpackLog(event, "SubmissionRefunded", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/relayer/generate.go b/relayer/generate.go index 308e8dba7..48543762f 100644 --- a/relayer/generate.go +++ b/relayer/generate.go @@ -1,4 +1,5 @@ //go:generate bash -c "jq .abi ../contracts/out/BeefyClient.sol/BeefyClient.json | abigen --abi - --type BeefyClient --pkg contracts --out contracts/beefy_client.go" +//go:generate bash -c "jq .abi ../contracts/out/BeefyClientWrapper.sol/BeefyClientWrapper.json | abigen --abi - --type BeefyClientWrapper --pkg contracts --out contracts/beefy_client_wrapper.go" //go:generate bash -c "jq .abi ../contracts/out/IGateway.sol/IGatewayV2.json | abigen --abi - --type Gateway --pkg contracts --out contracts/gateway.go" //go:generate bash -c "jq .abi ../contracts/out/IGateway.sol/IGatewayV1.json | abigen --abi - --type Gateway --pkg contractsv1 --out contracts/v1/gateway.go" diff --git a/relayer/relays/beefy/config.go b/relayer/relays/beefy/config.go index 1018826a8..d0b9b0099 100644 --- a/relayer/relays/beefy/config.go +++ b/relayer/relays/beefy/config.go @@ -24,8 +24,9 @@ type SinkConfig struct { } type ContractsConfig struct { - BeefyClient string `mapstructure:"BeefyClient"` - Gateway string `mapstructure:"Gateway"` + BeefyClient string `mapstructure:"BeefyClient"` + BeefyClientWrapper string `mapstructure:"BeefyClientWrapper"` // Optional: if set, use wrapper for gas refunds + Gateway string `mapstructure:"Gateway"` } type OnDemandSyncConfig struct { diff --git a/relayer/relays/beefy/ethereum-writer.go b/relayer/relays/beefy/ethereum-writer.go index a871abc92..bb864b1d5 100644 --- a/relayer/relays/beefy/ethereum-writer.go +++ b/relayer/relays/beefy/ethereum-writer.go @@ -6,6 +6,7 @@ import ( "fmt" "math/big" "math/rand" + "time" "golang.org/x/sync/errgroup" @@ -22,10 +23,15 @@ import ( log "github.com/sirupsen/logrus" ) +// SessionTimeout is the duration after which a pending session is considered expired +const SessionTimeout = 40 * time.Minute + type EthereumWriter struct { config *SinkConfig conn *ethereum.Connection - contract *contracts.BeefyClient + useWrapper bool + wrapperContract *contracts.BeefyClientWrapper + beefyClient *contracts.BeefyClient blockWaitPeriod uint64 } @@ -96,16 +102,17 @@ func (wr *EthereumWriter) queryBeefyClientState(ctx context.Context) (*BeefyClie Context: ctx, } - latestBeefyBlock, err := wr.contract.LatestBeefyBlock(&callOpts) + latestBeefyBlock, err := wr.getLatestBeefyBlock(&callOpts) if err != nil { return nil, err } - currentValidatorSet, err := wr.contract.CurrentValidatorSet(&callOpts) + currentValidatorSet, err := wr.getCurrentValidatorSet(&callOpts) if err != nil { return nil, err } - nextValidatorSet, err := wr.contract.NextValidatorSet(&callOpts) + + nextValidatorSet, err := wr.getNextValidatorSet(&callOpts) if err != nil { return nil, err } @@ -120,6 +127,20 @@ func (wr *EthereumWriter) queryBeefyClientState(ctx context.Context) (*BeefyClie } func (wr *EthereumWriter) submit(ctx context.Context, task *Request) error { + // Check if another relayer already has a session in progress (wrapper only) + if wr.useWrapper { + shouldSkip, err := wr.shouldSkipDueToPendingSession(ctx, task) + if err != nil { + return fmt.Errorf("check pending session: %w", err) + } + if shouldSkip { + log.WithFields(logrus.Fields{ + "beefyBlock": task.SignedCommitment.Commitment.BlockNumber, + }).Info("Skipping submission: another session already in progress with sufficient progress") + return nil + } + } + // Initial submission tx, initialBitfield, err := wr.doSubmitInitial(ctx, task) if err != nil { @@ -165,12 +186,10 @@ func (wr *EthereumWriter) submit(ctx context.Context, task *Request) error { }).Info("Commitment already synced") return nil } + // Commit PrevRandao which will be used as seed to randomly select subset of validators // https://github.com/Snowfork/snowbridge/blob/75a475cbf8fc8e13577ad6b773ac452b2bf82fbb/contracts/contracts/BeefyClient.sol#L446-L447 - tx, err = wr.contract.CommitPrevRandao( - wr.conn.MakeTxOpts(ctx), - *commitmentHash, - ) + tx, err = wr.doCommitPrevRandao(ctx, *commitmentHash) if err != nil { return fmt.Errorf("Failed to call CommitPrevRandao: %w", err) } @@ -196,6 +215,7 @@ func (wr *EthereumWriter) submit(ctx context.Context, task *Request) error { }).Info("Commitment already synced") return nil } + // Final submission tx, err = wr.doSubmitFinal(ctx, *commitmentHash, initialBitfield, task) if err != nil { @@ -213,7 +233,6 @@ func (wr *EthereumWriter) submit(ctx context.Context, task *Request) error { }).Debug("Transaction SubmitFinal succeeded") return nil - } func (wr *EthereumWriter) doSubmitInitial(ctx context.Context, task *Request) (*types.Transaction, []*big.Int, error) { @@ -235,13 +254,7 @@ func (wr *EthereumWriter) doSubmitInitial(ctx context.Context, task *Request) (* "chosenValidator": chosenValidator, }).Info("Creating initial bitfield") - initialBitfield, err := wr.contract.CreateInitialBitfield( - &bind.CallOpts{ - Pending: true, - From: wr.conn.Keypair().CommonAddress(), - }, - signedValidators, validatorCount, - ) + initialBitfield, err := wr.createInitialBitfield(signedValidators, validatorCount) if err != nil { return nil, nil, fmt.Errorf("create initial bitfield: %w", err) } @@ -251,13 +264,7 @@ func (wr *EthereumWriter) doSubmitInitial(ctx context.Context, task *Request) (* return nil, nil, err } - var tx *types.Transaction - tx, err = wr.contract.SubmitInitial( - wr.conn.MakeTxOpts(ctx), - msg.Commitment, - msg.Bitfield, - msg.Proof, - ) + tx, err := wr.submitInitial(ctx, msg) if err != nil { return nil, nil, fmt.Errorf("initial submit: %w", err) } @@ -279,15 +286,7 @@ func (wr *EthereumWriter) doSubmitInitial(ctx context.Context, task *Request) (* // doSubmitFinal sends a SubmitFinal tx to the BeefyClient contract func (wr *EthereumWriter) doSubmitFinal(ctx context.Context, commitmentHash [32]byte, initialBitfield []*big.Int, task *Request) (*types.Transaction, error) { - finalBitfield, err := wr.contract.CreateFinalBitfield( - &bind.CallOpts{ - Pending: true, - From: wr.conn.Keypair().CommonAddress(), - }, - commitmentHash, - initialBitfield, - ) - + finalBitfield, err := wr.createFinalBitfield(commitmentHash, initialBitfield) if err != nil { return nil, fmt.Errorf("create validator bitfield: %w", err) } @@ -304,15 +303,7 @@ func (wr *EthereumWriter) doSubmitFinal(ctx context.Context, commitmentHash [32] return nil, fmt.Errorf("logging params: %w", err) } - tx, err := wr.contract.SubmitFinal( - wr.conn.MakeTxOpts(ctx), - params.Commitment, - params.Bitfield, - params.Proofs, - params.Leaf, - params.LeafProof, - params.LeafProofOrder, - ) + tx, err := wr.submitFinal(ctx, params) if err != nil { return nil, fmt.Errorf("final submission: %w", err) } @@ -325,26 +316,62 @@ func (wr *EthereumWriter) doSubmitFinal(ctx context.Context, commitmentHash [32] } func (wr *EthereumWriter) initialize(ctx context.Context) error { - address := common.HexToAddress(wr.config.Contracts.BeefyClient) - contract, err := contracts.NewBeefyClient(address, wr.conn.Client()) + callOpts := bind.CallOpts{ + Context: ctx, + } + + beefyClientAddress := common.HexToAddress(wr.config.Contracts.BeefyClient) + beefyClient, err := contracts.NewBeefyClient(beefyClientAddress, wr.conn.Client()) if err != nil { return fmt.Errorf("create beefy client: %w", err) } - wr.contract = contract + wr.beefyClient = beefyClient - callOpts := bind.CallOpts{ - Context: ctx, - } - blockWaitPeriod, err := wr.contract.RandaoCommitDelay(&callOpts) + blockWaitPeriod, err := wr.beefyClient.RandaoCommitDelay(&callOpts) if err != nil { - return fmt.Errorf("create randao commit delay: %w", err) + return fmt.Errorf("get randao commit delay: %w", err) } wr.blockWaitPeriod = blockWaitPeriod.Uint64() - log.WithField("randaoCommitDelay", wr.blockWaitPeriod).Trace("Fetched randaoCommitDelay") + + // Optionally initialize wrapper for state-changing functions with gas refunds + if wr.config.Contracts.BeefyClientWrapper != "" { + wr.useWrapper = true + wrapperAddress := common.HexToAddress(wr.config.Contracts.BeefyClientWrapper) + + wrapperContract, err := contracts.NewBeefyClientWrapper(wrapperAddress, wr.conn.Client()) + if err != nil { + return fmt.Errorf("create beefy client wrapper: %w", err) + } + wr.wrapperContract = wrapperContract + + log.WithFields(logrus.Fields{ + "beefyClient": beefyClientAddress.Hex(), + "wrapper": wrapperAddress.Hex(), + "randaoCommitDelay": wr.blockWaitPeriod, + }).Info("Using BeefyClientWrapper for gas refunds") + } else { + wr.useWrapper = false + log.WithFields(logrus.Fields{ + "beefyClient": beefyClientAddress.Hex(), + "randaoCommitDelay": wr.blockWaitPeriod, + }).Info("Using BeefyClient directly (no gas refunds)") + } return nil } +func (wr *EthereumWriter) isTaskOutdated(ctx context.Context, task *Request) (bool, error) { + state, err := wr.queryBeefyClientState(ctx) + if err != nil { + return false, fmt.Errorf("query beefy client state: %w", err) + } + + if task.SignedCommitment.Commitment.BlockNumber <= uint32(state.LatestBeefyBlock) { + return true, nil + } + return false, nil +} + func (wr *EthereumWriter) submitFiatShamir(ctx context.Context, task *Request) error { signedValidators := []*big.Int{} for i, signature := range task.SignedCommitment.Signatures { @@ -364,28 +391,14 @@ func (wr *EthereumWriter) submitFiatShamir(ctx context.Context, task *Request) e "chosenValidator": chosenValidator, }).Info("Creating initial bitfield") - initialBitfield, err := wr.contract.CreateInitialBitfield( - &bind.CallOpts{ - Pending: true, - From: wr.conn.Keypair().CommonAddress(), - }, - signedValidators, validatorCount, - ) + initialBitfield, err := wr.createInitialBitfield(signedValidators, validatorCount) if err != nil { return fmt.Errorf("create initial bitfield: %w", err) } commitment := toBeefyClientCommitment(&task.SignedCommitment.Commitment) - finalBitfield, err := wr.contract.CreateFiatShamirFinalBitfield( - &bind.CallOpts{ - Pending: true, - From: wr.conn.Keypair().CommonAddress(), - }, - *commitment, - initialBitfield, - ) - + finalBitfield, err := wr.createFiatShamirFinalBitfield(commitment, initialBitfield) if err != nil { return fmt.Errorf("create validator final bitfield: %w", err) } @@ -402,15 +415,7 @@ func (wr *EthereumWriter) submitFiatShamir(ctx context.Context, task *Request) e return fmt.Errorf("logging params: %w", err) } - tx, err := wr.contract.SubmitFiatShamir( - wr.conn.MakeTxOpts(ctx), - params.Commitment, - params.Bitfield, - params.Proofs, - params.Leaf, - params.LeafProof, - params.LeafProofOrder, - ) + tx, err := wr.doSubmitFiatShamir(ctx, params) if err != nil { return fmt.Errorf("SubmitFiatShamir: %w", err) } @@ -432,14 +437,185 @@ func (wr *EthereumWriter) submitFiatShamir(ctx context.Context, task *Request) e return nil } -func (wr *EthereumWriter) isTaskOutdated(ctx context.Context, task *Request) (bool, error) { - state, err := wr.queryBeefyClientState(ctx) +// shouldSkipDueToPendingSession checks if another relayer already has a session in progress +// that would advance the light client sufficiently. Returns true if we should skip. +// Note: This is only available when using the wrapper contract. +func (wr *EthereumWriter) shouldSkipDueToPendingSession(ctx context.Context, task *Request) (bool, error) { + callOpts := bind.CallOpts{ + Context: ctx, + } + + highestPendingBlock, err := wr.wrapperContract.HighestPendingBlock(&callOpts) if err != nil { - return false, fmt.Errorf("query beefy client state: %w", err) + return false, fmt.Errorf("get highest pending block: %w", err) } - if task.SignedCommitment.Commitment.BlockNumber <= uint32(state.LatestBeefyBlock) { - return true, nil + // No pending session + if highestPendingBlock.Uint64() == 0 { + return false, nil } - return false, nil + + latestBeefyBlock, err := wr.beefyClient.LatestBeefyBlock(&callOpts) + if err != nil { + return false, fmt.Errorf("get latest beefy block: %w", err) + } + + refundTarget, err := wr.wrapperContract.RefundTarget(&callOpts) + if err != nil { + return false, fmt.Errorf("get refund target: %w", err) + } + + // Check if the pending session would give sufficient progress + pendingProgress := highestPendingBlock.Uint64() - latestBeefyBlock + if pendingProgress < refundTarget.Uint64() { + // Pending session wouldn't give good progress, ok to proceed + return false, nil + } + + // Check if the session has expired (> 40 minutes old) + pendingTimestamp, err := wr.wrapperContract.HighestPendingBlockTimestamp(&callOpts) + if err != nil { + return false, fmt.Errorf("get highest pending block timestamp: %w", err) + } + + sessionAge := time.Now().Unix() - pendingTimestamp.Int64() + if sessionAge > int64(SessionTimeout.Seconds()) { + log.WithFields(logrus.Fields{ + "highestPendingBlock": highestPendingBlock.Uint64(), + "sessionAgeMinutes": sessionAge / 60, + }).Info("Pending session has expired, proceeding with new submission") + return false, nil + } + + log.WithFields(logrus.Fields{ + "highestPendingBlock": highestPendingBlock.Uint64(), + "latestBeefyBlock": latestBeefyBlock, + "pendingProgress": pendingProgress, + "sessionAgeMinutes": sessionAge / 60, + }).Debug("Active session in progress with sufficient progress") + + return true, nil +} + +// Contract abstraction helpers +// View functions always use beefyClient directly +// State-changing functions use wrapper (if configured) or beefyClient + +type validatorSetResult struct { + Id *big.Int + Root [32]byte +} + +func (wr *EthereumWriter) getLatestBeefyBlock(callOpts *bind.CallOpts) (uint64, error) { + return wr.beefyClient.LatestBeefyBlock(callOpts) +} + +func (wr *EthereumWriter) getCurrentValidatorSet(callOpts *bind.CallOpts) (*validatorSetResult, error) { + result, err := wr.beefyClient.CurrentValidatorSet(callOpts) + if err != nil { + return nil, err + } + return &validatorSetResult{Id: result.Id, Root: result.Root}, nil +} + +func (wr *EthereumWriter) getNextValidatorSet(callOpts *bind.CallOpts) (*validatorSetResult, error) { + result, err := wr.beefyClient.NextValidatorSet(callOpts) + if err != nil { + return nil, err + } + return &validatorSetResult{Id: result.Id, Root: result.Root}, nil +} + +func (wr *EthereumWriter) createInitialBitfield(signedValidators []*big.Int, validatorCount *big.Int) ([]*big.Int, error) { + callOpts := &bind.CallOpts{ + Pending: true, + From: wr.conn.Keypair().CommonAddress(), + } + return wr.beefyClient.CreateInitialBitfield(callOpts, signedValidators, validatorCount) +} + +func (wr *EthereumWriter) createFinalBitfield(commitmentHash [32]byte, initialBitfield []*big.Int) ([]*big.Int, error) { + callOpts := &bind.CallOpts{ + Pending: true, + From: wr.conn.Keypair().CommonAddress(), + } + return wr.beefyClient.CreateFinalBitfield(callOpts, commitmentHash, initialBitfield) +} + +func (wr *EthereumWriter) createFiatShamirFinalBitfield(commitment *contracts.IBeefyClientCommitment, initialBitfield []*big.Int) ([]*big.Int, error) { + callOpts := &bind.CallOpts{ + Pending: true, + From: wr.conn.Keypair().CommonAddress(), + } + return wr.beefyClient.CreateFiatShamirFinalBitfield(callOpts, ToBeefyClientCommitment(commitment), initialBitfield) +} + +func (wr *EthereumWriter) submitInitial(ctx context.Context, msg *InitialRequestParams) (*types.Transaction, error) { + if wr.useWrapper { + return wr.wrapperContract.SubmitInitial( + wr.conn.MakeTxOpts(ctx), + msg.Commitment, + msg.Bitfield, + msg.Proof, + ) + } + return wr.beefyClient.SubmitInitial( + wr.conn.MakeTxOpts(ctx), + ToBeefyClientCommitment(&msg.Commitment), + msg.Bitfield, + ToBeefyClientValidatorProof(&msg.Proof), + ) +} + +func (wr *EthereumWriter) doCommitPrevRandao(ctx context.Context, commitmentHash [32]byte) (*types.Transaction, error) { + if wr.useWrapper { + return wr.wrapperContract.CommitPrevRandao(wr.conn.MakeTxOpts(ctx), commitmentHash) + } + return wr.beefyClient.CommitPrevRandao(wr.conn.MakeTxOpts(ctx), commitmentHash) +} + +func (wr *EthereumWriter) submitFinal(ctx context.Context, params *FinalRequestParams) (*types.Transaction, error) { + if wr.useWrapper { + return wr.wrapperContract.SubmitFinal( + wr.conn.MakeTxOpts(ctx), + params.Commitment, + params.Bitfield, + params.Proofs, + params.Leaf, + params.LeafProof, + params.LeafProofOrder, + ) + } + return wr.beefyClient.SubmitFinal( + wr.conn.MakeTxOpts(ctx), + ToBeefyClientCommitment(¶ms.Commitment), + params.Bitfield, + ToBeefyClientValidatorProofs(params.Proofs), + ToBeefyClientMMRLeaf(¶ms.Leaf), + params.LeafProof, + params.LeafProofOrder, + ) +} + +func (wr *EthereumWriter) doSubmitFiatShamir(ctx context.Context, params *FinalRequestParams) (*types.Transaction, error) { + if wr.useWrapper { + return wr.wrapperContract.SubmitFiatShamir( + wr.conn.MakeTxOpts(ctx), + params.Commitment, + params.Bitfield, + params.Proofs, + params.Leaf, + params.LeafProof, + params.LeafProofOrder, + ) + } + return wr.beefyClient.SubmitFiatShamir( + wr.conn.MakeTxOpts(ctx), + ToBeefyClientCommitment(¶ms.Commitment), + params.Bitfield, + ToBeefyClientValidatorProofs(params.Proofs), + ToBeefyClientMMRLeaf(¶ms.Leaf), + params.LeafProof, + params.LeafProofOrder, + ) } diff --git a/relayer/relays/beefy/parameters.go b/relayer/relays/beefy/parameters.go index 4023e8984..8badf9804 100644 --- a/relayer/relays/beefy/parameters.go +++ b/relayer/relays/beefy/parameters.go @@ -16,16 +16,16 @@ import ( ) type InitialRequestParams struct { - Commitment contracts.BeefyClientCommitment + Commitment contracts.IBeefyClientCommitment Bitfield []*big.Int - Proof contracts.BeefyClientValidatorProof + Proof contracts.IBeefyClientValidatorProof } type FinalRequestParams struct { - Commitment contracts.BeefyClientCommitment + Commitment contracts.IBeefyClientCommitment Bitfield []*big.Int - Proofs []contracts.BeefyClientValidatorProof - Leaf contracts.BeefyClientMMRLeaf + Proofs []contracts.IBeefyClientValidatorProof + Leaf contracts.IBeefyClientMMRLeaf LeafProof [][32]byte LeafProofOrder *big.Int } @@ -72,7 +72,7 @@ func (r *Request) MakeSubmitInitialParams(valAddrIndex int64, initialBitfield [] msg := InitialRequestParams{ Commitment: *commitment, Bitfield: initialBitfield, - Proof: contracts.BeefyClientValidatorProof{ + Proof: contracts.IBeefyClientValidatorProof{ V: v, R: _r, S: s, @@ -85,8 +85,8 @@ func (r *Request) MakeSubmitInitialParams(valAddrIndex int64, initialBitfield [] return &msg, nil } -func toBeefyClientCommitment(c *types.Commitment) *contracts.BeefyClientCommitment { - return &contracts.BeefyClientCommitment{ +func toBeefyClientCommitment(c *types.Commitment) *contracts.IBeefyClientCommitment { + return &contracts.IBeefyClientCommitment{ BlockNumber: c.BlockNumber, ValidatorSetID: c.ValidatorSetID, Payload: toBeefyPayload(c.Payload), @@ -158,7 +158,7 @@ func (r *Request) generateValidatorAddressProof(validatorIndex int64) ([][32]byt } func (r *Request) MakeSubmitFinalParams(validatorIndices []uint64, initialBitfield []*big.Int) (*FinalRequestParams, error) { - validatorProofs := []contracts.BeefyClientValidatorProof{} + validatorProofs := []contracts.IBeefyClientValidatorProof{} for _, validatorIndex := range validatorIndices { ok, beefySig := r.SignedCommitment.Signatures[validatorIndex].Unwrap() @@ -180,7 +180,7 @@ func (r *Request) MakeSubmitFinalParams(validatorIndices []uint64, initialBitfie return nil, err } - validatorProofs = append(validatorProofs, contracts.BeefyClientValidatorProof{ + validatorProofs = append(validatorProofs, contracts.IBeefyClientValidatorProof{ V: v, R: _r, S: s, @@ -190,18 +190,18 @@ func (r *Request) MakeSubmitFinalParams(validatorIndices []uint64, initialBitfie }) } - commitment := contracts.BeefyClientCommitment{ + commitment := contracts.IBeefyClientCommitment{ Payload: toBeefyPayload(r.SignedCommitment.Commitment.Payload), BlockNumber: r.SignedCommitment.Commitment.BlockNumber, ValidatorSetID: r.SignedCommitment.Commitment.ValidatorSetID, } - inputLeaf := contracts.BeefyClientMMRLeaf{} + inputLeaf := contracts.IBeefyClientMMRLeaf{} var merkleProofItems [][32]byte proofOrder := new(big.Int) - inputLeaf = contracts.BeefyClientMMRLeaf{ + inputLeaf = contracts.IBeefyClientMMRLeaf{ Version: uint8(r.Proof.Leaf.Version), ParentNumber: uint32(r.Proof.Leaf.ParentNumberAndHash.ParentNumber), ParentHash: r.Proof.Leaf.ParentNumberAndHash.Hash, @@ -227,10 +227,10 @@ func (r *Request) MakeSubmitFinalParams(validatorIndices []uint64, initialBitfie return &msg, nil } -func toBeefyPayload(items []types.PayloadItem) []contracts.BeefyClientPayloadItem { - beefyItems := make([]contracts.BeefyClientPayloadItem, len(items)) +func toBeefyPayload(items []types.PayloadItem) []contracts.IBeefyClientPayloadItem { + beefyItems := make([]contracts.IBeefyClientPayloadItem, len(items)) for i, item := range items { - beefyItems[i] = contracts.BeefyClientPayloadItem{ + beefyItems[i] = contracts.IBeefyClientPayloadItem{ PayloadID: item.ID, Data: item.Data, } @@ -239,7 +239,7 @@ func toBeefyPayload(items []types.PayloadItem) []contracts.BeefyClientPayloadIte return beefyItems } -func commitmentToLog(commitment contracts.BeefyClientCommitment) logrus.Fields { +func commitmentToLog(commitment contracts.IBeefyClientCommitment) logrus.Fields { payloadFields := make([]logrus.Fields, len(commitment.Payload)) for i, payloadItem := range commitment.Payload { payloadFields[i] = logrus.Fields{ @@ -264,7 +264,7 @@ func bitfieldToStrings(bitfield []*big.Int) []string { return strings } -func proofToLog(proof contracts.BeefyClientValidatorProof) logrus.Fields { +func proofToLog(proof contracts.IBeefyClientValidatorProof) logrus.Fields { hexProof := make([]string, len(proof.Proof)) for i, proof := range proof.Proof { hexProof[i] = Hex(proof[:]) @@ -279,3 +279,50 @@ func proofToLog(proof contracts.BeefyClientValidatorProof) logrus.Fields { "Proof": hexProof, } } + +// Converter functions for direct BeefyClient contract (without wrapper) +func ToBeefyClientCommitment(c *contracts.IBeefyClientCommitment) contracts.BeefyClientCommitment { + payload := make([]contracts.BeefyClientPayloadItem, len(c.Payload)) + for i, item := range c.Payload { + payload[i] = contracts.BeefyClientPayloadItem{ + PayloadID: item.PayloadID, + Data: item.Data, + } + } + return contracts.BeefyClientCommitment{ + BlockNumber: c.BlockNumber, + ValidatorSetID: c.ValidatorSetID, + Payload: payload, + } +} + +func ToBeefyClientValidatorProof(p *contracts.IBeefyClientValidatorProof) contracts.BeefyClientValidatorProof { + return contracts.BeefyClientValidatorProof{ + V: p.V, + R: p.R, + S: p.S, + Index: p.Index, + Account: p.Account, + Proof: p.Proof, + } +} + +func ToBeefyClientValidatorProofs(proofs []contracts.IBeefyClientValidatorProof) []contracts.BeefyClientValidatorProof { + result := make([]contracts.BeefyClientValidatorProof, len(proofs)) + for i, p := range proofs { + result[i] = ToBeefyClientValidatorProof(&p) + } + return result +} + +func ToBeefyClientMMRLeaf(leaf *contracts.IBeefyClientMMRLeaf) contracts.BeefyClientMMRLeaf { + return contracts.BeefyClientMMRLeaf{ + Version: leaf.Version, + ParentNumber: leaf.ParentNumber, + ParentHash: leaf.ParentHash, + ParachainHeadsRoot: leaf.ParachainHeadsRoot, + NextAuthoritySetID: leaf.NextAuthoritySetID, + NextAuthoritySetLen: leaf.NextAuthoritySetLen, + NextAuthoritySetRoot: leaf.NextAuthoritySetRoot, + } +} diff --git a/web/packages/test/scripts/set-env.sh b/web/packages/test/scripts/set-env.sh index 1cea629b7..37f57c980 100755 --- a/web/packages/test/scripts/set-env.sh +++ b/web/packages/test/scripts/set-env.sh @@ -31,6 +31,7 @@ parachain_relay_primary_gov_eth_key="${PARACHAIN_RELAY_PRIMARY_GOV_ETH_KEY:-0x80 parachain_relay_secondary_gov_eth_key="${PARACHAIN_RELAY_SECONDARY_GOV_ETH_KEY:-0xe699de86629f0e795b27e26b33c343876f9282c821a62086b21aef0baa7d7ca7}" parachain_relay_assethub_eth_key="${PARACHAIN_RELAY_ASSETHUB_ETH_KEY:-0x3646505e08a0f3a61417d91db18f2911b2d17c01563044f3e2f106bf36679a6a}" beefy_relay_eth_key="${BEEFY_RELAY_ETH_KEY:-0x935b65c833ced92c43ef9de6bff30703d941bd92a2637cb00cfad389f5862109}" +beefy_relay_eth_key_2="${BEEFY_RELAY_ETH_KEY_2:-0x27ae0c7b8d7c698e41b173265490a4c64b4e39ae78599166e003f868f12140fd}" beacon_endpoint_http="${BEACON_HTTP_ENDPOINT:-http://127.0.0.1:9596}" @@ -112,6 +113,16 @@ export REMOTE_REWARD="${REMOTE_REWARD:-1000000000000000}" ## Vault export GATEWAY_PROXY_INITIAL_DEPOSIT="${GATEWAY_PROXY_INITIAL_DEPOSIT:-10000000000000000000}" +## BeefyClientWrapper +export BEEFY_WRAPPER_MAX_GAS_PRICE="${BEEFY_WRAPPER_MAX_GAS_PRICE:-100000000000}" +export BEEFY_WRAPPER_MAX_REFUND_AMOUNT="${BEEFY_WRAPPER_MAX_REFUND_AMOUNT:-1000000000000000000}" +export BEEFY_WRAPPER_REFUND_TARGET="${BEEFY_WRAPPER_REFUND_TARGET:-300}" +export BEEFY_WRAPPER_REWARD_TARGET="${BEEFY_WRAPPER_REWARD_TARGET:-2400}" +export BEEFY_WRAPPER_INITIAL_DEPOSIT="${BEEFY_WRAPPER_INITIAL_DEPOSIT:-10000000000000000000}" +# Relayer addresses (derived from private keys) +export BEEFY_RELAY_ETH_ADDRESS_1="${BEEFY_RELAY_ETH_ADDRESS_1:-0x87D987206180B8f3807Dd90455606eEa85cdB87a}" +export BEEFY_RELAY_ETH_ADDRESS_2="${BEEFY_RELAY_ETH_ADDRESS_2:-0xACbd24742b87c34dED607FB87b22401B2Ede167E}" + export GATEWAY_STORAGE_KEY="${GATEWAY_STORAGE_KEY:-0xaed97c7854d601808b98ae43079dafb3}" export GATEWAY_PROXY_CONTRACT="${GATEWAY_PROXY_CONTRACT:-0xb1185ede04202fe62d38f5db72f71e38ff3e8305}" diff --git a/web/packages/test/scripts/start-beefy-on-demand-relayer.sh b/web/packages/test/scripts/start-beefy-on-demand-relayer.sh index f36b99367..94dd66c67 100755 --- a/web/packages/test/scripts/start-beefy-on-demand-relayer.sh +++ b/web/packages/test/scripts/start-beefy-on-demand-relayer.sh @@ -4,9 +4,9 @@ set -eu source scripts/set-env.sh config_relayer() { - # Configure beefy relay + # Configure beefy relay (uses wrapper address) jq \ - --arg k1 "$(address_for BeefyClient)" \ + --arg k1 "$(address_for BeefyClientWrapperProxy)" \ --arg k2 "$(address_for GatewayProxy)" \ --arg eth_endpoint_ws $eth_endpoint_ws \ --arg eth_gas_limit $eth_gas_limit \ diff --git a/web/packages/test/scripts/start-relayer.sh b/web/packages/test/scripts/start-relayer.sh index 87bbb5001..976f2122f 100755 --- a/web/packages/test/scripts/start-relayer.sh +++ b/web/packages/test/scripts/start-relayer.sh @@ -6,9 +6,9 @@ source scripts/set-env.sh config_relayer() { local electra_forked_epoch=0 local fulu_forked_epoch=50000000 - # Configure beefy relay + # Configure beefy relay 1 (uses wrapper address) jq \ - --arg k1 "$(address_for BeefyClient)" \ + --arg k1 "$(address_for BeefyClientWrapperProxy)" \ --arg k2 "$(address_for GatewayProxy)" \ --arg eth_endpoint_ws $eth_endpoint_ws \ --arg eth_gas_limit $eth_gas_limit \ @@ -20,6 +20,20 @@ config_relayer() { ' \ config/beefy-relay.json >$output_dir/beefy-relay.json + # Configure beefy relay 2 (uses wrapper address) + jq \ + --arg k1 "$(address_for BeefyClientWrapperProxy)" \ + --arg k2 "$(address_for GatewayProxy)" \ + --arg eth_endpoint_ws $eth_endpoint_ws \ + --arg eth_gas_limit $eth_gas_limit \ + ' + .sink.contracts.BeefyClient = $k1 + | .sink.contracts.Gateway = $k2 + | .sink.ethereum.endpoint = $eth_endpoint_ws + | .sink.ethereum."gas-limit" = $eth_gas_limit + ' \ + config/beefy-relay.json >$output_dir/beefy-relay-2.json + # Configure parachain relay v1 jq \ --arg k1 "$(address_for GatewayProxy)" \ @@ -170,7 +184,7 @@ config_relayer() { start_relayer() { echo "Starting relay services" - # Launch beefy relay + # Launch beefy relay 1 ( : >"$output_dir"/beefy-relay.log while :; do @@ -183,6 +197,19 @@ start_relayer() { done ) & + # Launch beefy relay 2 + ( + : >"$output_dir"/beefy-relay-2.log + while :; do + echo "Starting beefy relay 2 at $(date)" + "${relayer}" run beefy \ + --config "$output_dir/beefy-relay-2.json" \ + --ethereum.private-key $beefy_relay_eth_key_2 \ + >>"$output_dir"/beefy-relay-2.log 2>&1 || true + sleep 20 + done + ) & + # Launch parachain relay v1 ( : >"$output_dir"/parachain-relay-v1.log