diff --git a/core/packages/contracts/.envrc-example b/core/packages/contracts/.envrc-example index a20809074f..71b29aa61f 100644 --- a/core/packages/contracts/.envrc-example +++ b/core/packages/contracts/.envrc-example @@ -5,6 +5,8 @@ source_up_if_exists ## BeefyClient export RANDAO_COMMIT_DELAY=3 export RANDAO_COMMIT_EXP=8 +export PREV_RANDAO=377 +export ValidatorSetSize=300 ## ParachainClient export PARAID=1000 diff --git a/core/packages/contracts/.gas-snapshot b/core/packages/contracts/.gas-snapshot index 5054626955..9a7eb4ef60 100644 --- a/core/packages/contracts/.gas-snapshot +++ b/core/packages/contracts/.gas-snapshot @@ -1,44 +1,70 @@ -BeefyClientTest:testScaleEncodeCommit() (gas: 32228) -BeefyClientTest:testSubmit() (gas: 640426) -BeefyClientTest:testSubmitFailForPrevRandaoCapturedMoreThanOnce() (gas: 269498) -BeefyClientTest:testSubmitFailForPrevRandaoTooEarlyOrTooLate() (gas: 210184) -BeefyClientTest:testSubmitFailInvalidSignature() (gas: 581588) -BeefyClientTest:testSubmitFailValidatorNotInBitfield() (gas: 568657) -BeefyClientTest:testSubmitFailWithInvalidBitfield() (gas: 559304) -BeefyClientTest:testSubmitFailWithStaleCommitment() (gas: 757824) -BeefyClientTest:testSubmitFailWithoutPrevRandao() (gas: 535343) -BeefyClientTest:testSubmitWithHandover() (gas: 668392) -BeefyClientTest:testSubmitWithHandoverFailStaleCommitment() (gas: 781735) -BeefyClientTest:testSubmitWithHandoverFailWithoutPrevRandao() (gas: 559560) -InboundQueueTest:testSubmit() (gas: 117979) -InboundQueueTest:testSubmitShouldFailInsufficientBalance() (gas: 92436) -InboundQueueTest:testSubmitShouldFailInvalidNonce() (gas: 66955) -InboundQueueTest:testSubmitShouldFailInvalidProof() (gas: 62637) -InboundQueueTest:testSubmitShouldNotFailOnHandlerFailure() (gas: 142351) -InboundQueueTest:testSubmitShouldNotFailOnHandlerOOG() (gas: 641523) -NativeTokensTest:testCreateFailOnBadFeePayment() (gas: 11593) -NativeTokensTest:testCreateFailOnBadToken() (gas: 19174) -NativeTokensTest:testCreateSuccessful() (gas: 79603) -NativeTokensTest:testHandleRevertsUnknownOrigin() (gas: 12280) -NativeTokensTest:testHandleRevertsUnknownSender() (gas: 44241) -NativeTokensTest:testHandleUnlockMessageSuccessful() (gas: 154612) -NativeTokensTest:testLockRevertsZeroAmount() (gas: 11361) -NativeTokensTest:testLockSuccessful() (gas: 161012) -OutboundQueueTest:testSubmit() (gas: 82052) -OutboundQueueTest:testSubmitFailFeePaymentTooLow() (gas: 30057) -ParachainClientTest:testCreateParachainHeaderMerkleFailInvalidHeader() (gas: 12690) -ParachainClientTest:testCreateParachainHeaderMerkleLeaf() (gas: 23474) -ParachainClientTest:testIsCommitmentInHeaderDigest() (gas: 23454) -ScaleCodecTest:testEncodeU128() (gas: 193) -ScaleCodecTest:testEncodeU16() (gas: 414) -ScaleCodecTest:testEncodeU256() (gas: 453) -ScaleCodecTest:testEncodeU32() (gas: 171) -ScaleCodecTest:testEncodeU64() (gas: 281) -TokenVaultTest:testDepositSuccessful() (gas: 106091) -TokenVaultTest:testInsufficientBalance() (gas: 17425) -TokenVaultTest:testNonOwnerCannotWithdraw() (gas: 44613) -TokenVaultTest:testTokenTransferFailedInsufficientAllowance() (gas: 69957) -TokenVaultTest:testWithdrawSuccessful() (gas: 121381) -UpgradeProxyTest:testUpgrade() (gas: 35658) -UpgradeProxyTest:testUpgradeFail() (gas: 20802) -UpgradeProxyTest:testUpgradeFailBadOrigin() (gas: 11326) +AgentTest:testInvoke() (gas: 20810) +AgentTest:testInvokeFail() (gas: 13429) +AgentTest:testInvokeUnauthorized() (gas: 13974) +BeefyClientTest:testCreateFinalBitfield() (gas: 278225) +BeefyClientTest:testCreateFinalBitfieldInvalid() (gas: 265008) +BeefyClientTest:testCreateInitialBitfield() (gas: 650507) +BeefyClientTest:testCreateInitialBitfieldInvalid() (gas: 578841) +BeefyClientTest:testScaleEncodeCommit() (gas: 32353) +BeefyClientTest:testSubmit():((uint32,uint64,(bytes2,bytes)[])) (gas: 708627) +BeefyClientTest:testSubmitFailForPrevRandaoCapturedMoreThanOnce() (gas: 266184) +BeefyClientTest:testSubmitFailForPrevRandaoTooEarlyOrTooLate() (gas: 207388) +BeefyClientTest:testSubmitFailInvalidSignature() (gas: 657123) +BeefyClientTest:testSubmitFailValidatorNotInBitfield() (gas: 645077) +BeefyClientTest:testSubmitFailWithInvalidBitfield() (gas: 634383) +BeefyClientTest:testSubmitFailWithInvalidMMRLeaf() (gas: 748779) +BeefyClientTest:testSubmitFailWithInvalidMMRLeafProof() (gas: 752764) +BeefyClientTest:testSubmitFailWithInvalidTicket() (gas: 638107) +BeefyClientTest:testSubmitFailWithInvalidValidatorSet() (gas: 642432) +BeefyClientTest:testSubmitFailWithNotEnoughClaims() (gas: 197586) +BeefyClientTest:testSubmitFailWithStaleCommitment() (gas: 835952) +BeefyClientTest:testSubmitFailWithoutPrevRandao() (gas: 532261) +BeefyClientTest:testSubmitWithHandover() (gas: 736139) +BeefyClientTest:testSubmitWithHandoverFailStaleCommitment() (gas: 859922) +BeefyClientTest:testSubmitWithHandoverFailWithInvalidValidatorSet() (gas: 666242) +BeefyClientTest:testSubmitWithHandoverFailWithoutPrevRandao() (gas: 556301) +GatewayTest:testAgentExecution() (gas: 52791) +GatewayTest:testAgentExecutionBadOrigin() (gas: 23354) +GatewayTest:testAgentExecutionBadPayload() (gas: 22077) +GatewayTest:testCreateAgent() (gas: 254909) +GatewayTest:testCreateAgentAlreadyCreated() (gas: 256489) +GatewayTest:testCreateChannel() (gas: 332117) +GatewayTest:testCreateChannelFailsAgentDoesNotExist() (gas: 19185) +GatewayTest:testCreateChannelFailsChannelAlreadyExists() (gas: 335995) +GatewayTest:testDisableOutboundMessaging() (gas: 67040) +GatewayTest:testDisableOutboundMessagingForChannel() (gas: 161374) +GatewayTest:testGetters() (gas: 37883) +GatewayTest:testHandlersNotExternallyCallable() (gas: 52292) +GatewayTest:testInitializeNotExternallyCallable() (gas: 16762) +GatewayTest:testRegisterToken() (gas: 85845) +GatewayTest:testRelayerNotRewarded() (gas: 75289) +GatewayTest:testRelayerRewardedFromAgent() (gas: 310728) +GatewayTest:testSendTokenAddress20() (gas: 128049) +GatewayTest:testSendTokenAddress20FailsInvalidDestination() (gas: 59703) +GatewayTest:testSendTokenAddress32() (gas: 129387) +GatewayTest:testSendTokenAddress32ToAssetHub() (gas: 128776) +GatewayTest:testSetOperatingMode() (gas: 39208) +GatewayTest:testSubmitFailInvalidChannel() (gas: 27926) +GatewayTest:testSubmitFailInvalidNonce() (gas: 318948) +GatewayTest:testSubmitHappyPath() (gas: 312342) +GatewayTest:testUpdateChannel() (gas: 58121) +GatewayTest:testUpdateChannelFailDoesNotExist() (gas: 20107) +GatewayTest:testUpdateChannelSanityChecksForBridgeHubChannel() (gas: 22147) +GatewayTest:testUpgrade() (gas: 208455) +GatewayTest:testUpgradeFailCodeHashMismatch() (gas: 178843) +GatewayTest:testUpgradeFailOnInitializationFailure() (gas: 182568) +GatewayTest:testUserDoesNotProvideEnoughFees() (gas: 218463) +GatewayTest:testUserPaysFees() (gas: 244678) +GatewayTest:testWithdrawAgentFunds() (gas: 64315) +MMRProofTest:testVerifyLeafProof() (gas: 370749) +MMRProofTest:testVerifyLeafProofFailsExceededProofSize() (gas: 358741) +ScaleCodecTest:testCheckedEncodeCompactU32() (gas: 5120) +ScaleCodecTest:testEncodeCompactU32() (gas: 9722) +ScaleCodecTest:testEncodeU128() (gas: 192) +ScaleCodecTest:testEncodeU16() (gas: 434) +ScaleCodecTest:testEncodeU256() (gas: 478) +ScaleCodecTest:testEncodeU32() (gas: 170) +ScaleCodecTest:testEncodeU64() (gas: 280) +VerificationTest:testCreateParachainHeaderMerkleFailInvalidHeader() (gas: 11341) +VerificationTest:testCreateParachainHeaderMerkleLeaf() (gas: 20830) +VerificationTest:testIsCommitmentInHeaderDigest() (gas: 21863) \ No newline at end of file diff --git a/core/packages/contracts/foundry.toml b/core/packages/contracts/foundry.toml index 3679f37062..4b22198d96 100644 --- a/core/packages/contracts/foundry.toml +++ b/core/packages/contracts/foundry.toml @@ -4,10 +4,11 @@ optimizer = true optimizer_runs = 20_000 via_ir = true test = 'test' -ffi = true -fs_permissions = [{ access = "read", path = "test/data"}, { access = "read", path = "./"}] +fs_permissions = [{ access = "read-write", path = "test/data"}, { access = "read", path = "./"}] ignored_error_codes = [ # DeployScript.sol is never deployed 5574 ] + +# no_match_test = "testRegenerateBitField" diff --git a/core/packages/contracts/scripts/ffiWrapper.ts b/core/packages/contracts/scripts/ffiWrapper.ts deleted file mode 100755 index f51c667cea..0000000000 --- a/core/packages/contracts/scripts/ffiWrapper.ts +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env ts-node -import { ValidatorSet, createRandomSubset, readSetBits } from "./helpers" -import { BigNumber, ethers } from "ethers" -import fs from "fs" -import type { BeefyClient } from "../types/BeefyClient" -import { accounts } from "./wallets" - -const encoder = new ethers.utils.AbiCoder() -const command = process.argv[2] -const fixtureData = JSON.parse(fs.readFileSync("test/data/beefy-commitment.json", "utf8")) -const validatorSetID = fixtureData.params.id -const validatorSetSize = process.env["FixedSet"] == "true" ? accounts.length : 300 -const commitHash = fixtureData.commitmentHash -const blockNumber = fixtureData.params.commitment.blockNumber -const mmrLeafProofs = fixtureData.params.leafProof -const mmrRoot = fixtureData.params.commitment.payload[0].data -const mmrLeaf: BeefyClient.MMRLeafStruct = fixtureData.params.leaf -const leafProofOrder = fixtureData.params.leafProofOrder - -const absentSubsetSize = Math.floor((validatorSetSize - 1) / 3) -const subsetSize = validatorSetSize - absentSubsetSize -const subset = createRandomSubset(validatorSetSize, subsetSize) -const absentSubset = createRandomSubset(validatorSetSize, absentSubsetSize) -let validatorSet: ValidatorSet - -if (command == "GenerateInitialSet") { - process.stdout.write( - `${encoder.encode( - ["uint32", "uint32", "uint32", "uint256[]", "uint256[]", "bytes32", "bytes32"], - [blockNumber, validatorSetID, validatorSetSize, subset, absentSubset, commitHash, mmrRoot] - )}` - ) -} else if (command == "GenerateProofs") { - if (process.env["FixedSet"] == "true") { - validatorSet = new ValidatorSet( - validatorSetID, - validatorSetSize, - accounts.map((account) => account.privateKey) - ) - } else { - validatorSet = new ValidatorSet(validatorSetID, validatorSetSize) - } - const finalBitfieldLength = parseInt(process.argv[3]) - let finalBitfield: BigNumber[] = [] - for (let i = 0; i < finalBitfieldLength; i++) { - finalBitfield.push(ethers.BigNumber.from(process.argv[4 + i])) - } - const validatorFinalProofs: BeefyClient.ValidatorProofStruct[] = readSetBits(finalBitfield).map( - (i) => validatorSet.createSignatureProof(i, commitHash) - ) - process.stdout.write( - `${encoder.encode( - [ - "bytes32", - "tuple(uint8 v, bytes32 r, bytes32 s, uint256 index,address account,bytes32[] proof)[]", - "bytes32[]", - "tuple(uint8 version,uint32 parentNumber,bytes32 parentHash,uint64 nextAuthoritySetID,uint32 nextAuthoritySetLen,bytes32 nextAuthoritySetRoot,bytes32 parachainHeadsRoot)", - "uint256", - ], - [validatorSet.root, validatorFinalProofs, mmrLeafProofs, mmrLeaf, leafProofOrder] - )}` - ) -} diff --git a/core/packages/contracts/scripts/generateBeefyTestFixture.ts b/core/packages/contracts/scripts/generateBeefyTestFixture.ts new file mode 100755 index 0000000000..77768c6cea --- /dev/null +++ b/core/packages/contracts/scripts/generateBeefyTestFixture.ts @@ -0,0 +1,98 @@ +#!/usr/bin/env ts-node +import { ValidatorSet, createRandomSubset, readSetBits } from "./helpers" +import { BigNumber, ethers } from "ethers" +import fs from "fs" +import type { BeefyClient } from "../types/BeefyClient" +import { accounts } from "./wallets" +import path from "path" +const encoder = new ethers.utils.AbiCoder() + +const run = async () => { + const fixtureData = JSON.parse( + fs.readFileSync( + path.join(process.env.contract_dir || ".", "test/data/beefy-commitment.json"), + "utf8" + ) + ) + const BeefyValidatorSetFile = path.join( + process.env.contract_dir || ".", + "test/data/beefy-validator-set.json" + ) + const BeefyBitFieldFile = path.join( + process.env.contract_dir || ".", + "test/data/beefy-final-bitfield.json" + ) + const BeefyValidatorProofFile = path.join( + process.env.contract_dir || ".", + "test/data/beefy-final-proof.json" + ) + const command = process.argv[2] + const validatorSetID = fixtureData.params.id + const validatorSetSize = + process.env["FixedSet"] == "true" + ? accounts.length + : process.env["ValidatorSetSize"] + ? parseInt(process.env["ValidatorSetSize"]) + : 300 + const commitHash = fixtureData.commitmentHash + let validatorSet: ValidatorSet + if (process.env["FixedSet"] == "true") { + validatorSet = new ValidatorSet( + validatorSetID, + validatorSetSize, + accounts.map((account) => account.privateKey) + ) + } else { + validatorSet = new ValidatorSet(validatorSetID, validatorSetSize) + } + + if (command == "GenerateInitialSet") { + const mmrLeaf: BeefyClient.MMRLeafStruct = fixtureData.params.leaf + + const absentSubsetSize = Math.floor((validatorSetSize - 1) / 3) + const subsetSize = validatorSetSize - absentSubsetSize + const randomSet = createRandomSubset(validatorSetSize, subsetSize) + const participants = randomSet.participants + const absentees = randomSet.absentees + + const testFixture = { + validatorSetSize, + participants, + absentees, + validatorRoot: validatorSet.root, + } + fs.writeFileSync(BeefyValidatorSetFile, JSON.stringify(testFixture, null, 2), "utf8") + console.log("Beefy fixture writing to dest file: " + BeefyValidatorSetFile) + } else if (command == "GenerateProofs") { + const testFixture = JSON.parse(fs.readFileSync(BeefyBitFieldFile, "utf8")) + const bitField = encoder.decode(["uint256[]"], testFixture.final.finalBitFieldRaw)[0] + console.log(bitField) + let finalBitfield: BigNumber[] = [] + for (let i = 0; i < bitField.length; i++) { + finalBitfield.push(bitField[i]) + } + const finalValidatorsProof: BeefyClient.ValidatorProofStruct[] = readSetBits( + finalBitfield + ).map((i) => validatorSet.createSignatureProof(i, commitHash)) + console.log("Final Validator proofs:", finalValidatorsProof) + const finalValidatorsProofRaw = encoder.encode( + [ + "tuple(uint8 v, bytes32 r, bytes32 s, uint256 index,address account,bytes32[] proof)[]", + ], + [finalValidatorsProof] + ) + fs.writeFileSync( + BeefyValidatorProofFile, + JSON.stringify({ finalValidatorsProof, finalValidatorsProofRaw }, null, 2), + "utf8" + ) + console.log("Beefy fixture writing to dest file: " + BeefyValidatorProofFile) + } +} + +run() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error) + process.exit(1) + }) diff --git a/core/packages/contracts/scripts/helpers.ts b/core/packages/contracts/scripts/helpers.ts index 69355595b5..c4238bb885 100644 --- a/core/packages/contracts/scripts/helpers.ts +++ b/core/packages/contracts/scripts/helpers.ts @@ -101,9 +101,12 @@ class ValidatorSet { function createRandomSubset(population: number, size: number) { seedrandom("test", { global: true }) - return _.runInContext() - .sampleSize(Array.from(Array(population).keys()), size) + const all = Array.from(Array(population).keys()) + const participants = _.runInContext() + .sampleSize(all, size) .sort((a, b) => a - b) + const absentees = all.filter((o) => !participants.includes(o)) + return { participants, absentees } } export { encodeLog, createRandomSubset, ValidatorSet, readSetBits } diff --git a/core/packages/contracts/src/AgentExecutor.sol b/core/packages/contracts/src/AgentExecutor.sol index bd7005f9f2..488eb4bb9e 100644 --- a/core/packages/contracts/src/AgentExecutor.sol +++ b/core/packages/contracts/src/AgentExecutor.sol @@ -2,7 +2,7 @@ // SPDX-FileCopyrightText: 2023 Snowfork pragma solidity 0.8.20; -import {ParaID} from "./Types.sol"; +import {AgentExecuteCommand, ParaID} from "./Types.sol"; import {SubstrateTypes} from "./SubstrateTypes.sol"; import {IERC20} from "./interfaces/IERC20.sol"; @@ -14,15 +14,13 @@ contract AgentExecutor { using SafeTokenTransfer for IERC20; using SafeNativeTransfer for address payable; - bytes32 internal constant COMMAND_TRANSFER_TOKEN = keccak256("transferToken"); - /// @dev Execute a message which originated from the Polkadot side of the bridge. In other terms, /// the `data` parameter is constructed by the BridgeHub parachain. /// /// NOTE: There are no authorization checks here. Since this contract is only used for its code. function execute(address, bytes memory data) external { - (bytes32 command, bytes memory params) = abi.decode(data, (bytes32, bytes)); - if (command == COMMAND_TRANSFER_TOKEN) { + (AgentExecuteCommand command, bytes memory params) = abi.decode(data, (AgentExecuteCommand, bytes)); + if (command == AgentExecuteCommand.TransferToken) { (address token, address recipient, uint128 amount) = abi.decode(params, (address, address, uint128)); _transferToken(token, recipient, amount); } diff --git a/core/packages/contracts/src/Assets.sol b/core/packages/contracts/src/Assets.sol index 6b2bf1c838..5fe9402e83 100644 --- a/core/packages/contracts/src/Assets.sol +++ b/core/packages/contracts/src/Assets.sol @@ -49,9 +49,9 @@ library Assets { _transferToAgent(assetHubAgent, token, sender, amount); if (destinationChain == assetHubParaID) { - payload = SubstrateTypes.SendToken(assetHubAgent, token, destinationAddress, amount); + payload = SubstrateTypes.SendToken(address(this), token, destinationAddress, amount); } else { - payload = SubstrateTypes.SendToken(assetHubAgent, token, destinationChain, destinationAddress, amount); + payload = SubstrateTypes.SendToken(address(this), token, destinationChain, destinationAddress, amount); } extraFee = $.sendTokenFee; @@ -75,7 +75,7 @@ library Assets { _transferToAgent(assetHubAgent, token, sender, amount); - payload = SubstrateTypes.SendToken(assetHubAgent, token, destinationChain, destinationAddress, amount); + payload = SubstrateTypes.SendToken(address(this), token, destinationChain, destinationAddress, amount); extraFee = $.sendTokenFee; emit TokenSent(sender, token, destinationChain, abi.encodePacked(destinationAddress), amount); } @@ -95,7 +95,7 @@ library Assets { /// @dev Enqueues a create native token message to substrate. /// @param token The ERC20 token address. - function registerToken(address gateway, address token, bytes2 createTokenCallID) + function registerToken(address token, bytes2 createTokenCallID) external returns (bytes memory payload, uint256 extraFee) { @@ -105,7 +105,7 @@ library Assets { revert InvalidToken(); } - payload = SubstrateTypes.RegisterToken(gateway, token, createTokenCallID); + payload = SubstrateTypes.RegisterToken(address(this), token, createTokenCallID); extraFee = $.registerTokenFee; emit TokenRegistrationSent(token); diff --git a/core/packages/contracts/src/BeefyClient.sol b/core/packages/contracts/src/BeefyClient.sol index 68be482d3a..71cc78d7c1 100644 --- a/core/packages/contracts/src/BeefyClient.sol +++ b/core/packages/contracts/src/BeefyClient.sol @@ -169,22 +169,22 @@ contract BeefyClient { uint256 public immutable randaoCommitExpiration; /* Errors */ + error InvalidBitfield(); + error InvalidBitfieldLength(); error InvalidCommitment(); - error StaleCommitment(); - error InvalidValidatorProof(); - error InvalidSignature(); - error NotEnoughClaims(); error InvalidMMRLeaf(); error InvalidMMRLeafProof(); - error InvalidBitfield(); - error WaitPeriodNotOver(); - error TicketExpired(); - error PrevRandaoAlreadyCaptured(); - error PrevRandaoNotCaptured(); - error InvalidBitfieldLength(); - error InvalidTicket(); error InvalidMMRRootLength(); + error InvalidSignature(); + error InvalidTicket(); + error InvalidValidatorProof(); error NoMMRRootInCommitment(); + error NotEnoughClaims(); + error PrevRandaoAlreadyCaptured(); + error PrevRandaoNotCaptured(); + error StaleCommitment(); + error TicketExpired(); + error WaitPeriodNotOver(); constructor( uint256 _randaoCommitDelay, @@ -240,6 +240,10 @@ contract BeefyClient { revert InvalidValidatorProof(); } + if (commitment.validatorSetID != vset.id) { + revert InvalidCommitment(); + } + // Check if validatorSignature is correct, ie. check if it matches // the signature of senderPublicKey on the commitmentHash bytes32 commitmentHash = keccak256(encodeCommitment(commitment)); @@ -591,6 +595,7 @@ contract BeefyClient { Ticket storage ticket = tickets[ticketID]; if (ticket.blockNumber == 0) { + // Zero value ticket: submitInitial hasn't run for this commitment revert InvalidTicket(); } diff --git a/core/packages/contracts/src/Gateway.sol b/core/packages/contracts/src/Gateway.sol index 6d471b9a47..a9905c1bf3 100644 --- a/core/packages/contracts/src/Gateway.sol +++ b/core/packages/contracts/src/Gateway.sol @@ -416,10 +416,7 @@ contract Gateway is IGateway { // Register a token on AssetHub function registerToken(address token) external payable { - CoreStorage.Layout storage $ = CoreStorage.layout(); - address assetHubAgent = $.agents[ASSET_HUB_AGENT_ID]; - - (bytes memory payload, uint256 extraFee) = Assets.registerToken(assetHubAgent, token, CREATE_TOKEN_CALL_ID); + (bytes memory payload, uint256 extraFee) = Assets.registerToken(token, CREATE_TOKEN_CALL_ID); _submitOutbound(ASSET_HUB_PARA_ID, payload, extraFee); } diff --git a/core/packages/contracts/src/Types.sol b/core/packages/contracts/src/Types.sol index f50a9ce7af..432f6782f3 100644 --- a/core/packages/contracts/src/Types.sol +++ b/core/packages/contracts/src/Types.sol @@ -70,3 +70,5 @@ enum Command { SetOperatingMode, TransferNativeFromAgent } + +enum AgentExecuteCommand {TransferToken} diff --git a/core/packages/contracts/test/BeefyClient.t.sol b/core/packages/contracts/test/BeefyClient.t.sol index abd4c4338b..8a24f7e1cb 100644 --- a/core/packages/contracts/test/BeefyClient.t.sol +++ b/core/packages/contracts/test/BeefyClient.t.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.20; import {Strings} from "openzeppelin/utils/Strings.sol"; import {Test} from "forge-std/Test.sol"; import {console} from "forge-std/console.sol"; +import {stdJson} from "forge-std/StdJson.sol"; import {BeefyClient} from "../src/BeefyClient.sol"; import {BeefyClientMock} from "./mocks/BeefyClientMock.sol"; @@ -11,11 +12,13 @@ import {ScaleCodec} from "../src/utils/ScaleCodec.sol"; import {Bitfield} from "../src/utils/Bitfield.sol"; contract BeefyClientTest is Test { + using stdJson for string; + BeefyClientMock beefyClient; uint8 randaoCommitDelay; uint8 randaoCommitExpiration; uint32 blockNumber; - uint32 difficulty; + uint32 prevRandao; uint32 setSize; uint32 setId; uint128 currentSetId; @@ -34,45 +37,52 @@ contract BeefyClientTest is Test { BeefyClient.MMRLeaf mmrLeaf; uint256 leafProofOrder; bytes2 mmrRootID = bytes2("mh"); + string beefyCommitmentFile; + string beefyCommitmentRaw; + string beefyValidatorSetFile; + string beefyValidatorSetRaw; + string beefyValidatorProofFile; + string beefyValidatorProofRaw; + string beefyFinalBitFieldFile; function setUp() public { - randaoCommitDelay = 3; - randaoCommitExpiration = 8; - difficulty = 377; + randaoCommitDelay = uint8(vm.envOr("RANDAO_COMMIT_DELAY", uint256(3))); + randaoCommitExpiration = uint8(vm.envOr("RANDAO_COMMIT_EXP", uint256(8))); + prevRandao = uint32(vm.envOr("PREV_RANDAO", uint256(377))); beefyClient = new BeefyClientMock(randaoCommitDelay, randaoCommitExpiration); - // Allocate for input variables - string[] memory inputs = new string[](10); - inputs[0] = "node_modules/.bin/ts-node"; - inputs[1] = "scripts/ffiWrapper.ts"; - inputs[2] = "GenerateInitialSet"; + beefyCommitmentFile = string.concat(vm.projectRoot(), "/test/data/beefy-commitment.json"); + beefyCommitmentRaw = vm.readFile(beefyCommitmentFile); - // generate initial fixture data with ffi - (blockNumber, setId, setSize, bitSetArray, absentBitSetArray, commitHash, mmrRoot) = - abi.decode(vm.ffi(inputs), (uint32, uint32, uint32, uint256[], uint256[], bytes32, bytes32)); - bitfield = beefyClient.createInitialBitfield(bitSetArray, setSize); - absentBitfield = beefyClient.createInitialBitfield(absentBitSetArray, setSize); + beefyValidatorSetFile = string.concat(vm.projectRoot(), "/test/data/beefy-validator-set.json"); + beefyValidatorSetRaw = vm.readFile(beefyValidatorSetFile); - // To avoid another round of ffi in multiple tests - // except for the initial merkle root and proof for validators - // we also precalculate finalValidatorProofs and cached here - finalBitfield = - Bitfield.subsample(difficulty, bitfield, beefyClient.minimumSignatureThreshold_public(setSize), setSize); + beefyValidatorProofFile = string.concat(vm.projectRoot(), "/test/data/beefy-final-proof.json"); + beefyValidatorProofRaw = vm.readFile(beefyValidatorProofFile); + + beefyFinalBitFieldFile = string.concat(vm.projectRoot(), "/test/data/beefy-final-bitfield.json"); + + blockNumber = uint32(beefyCommitmentRaw.readUint(".params.commitment.blockNumber")); + setId = uint32(beefyCommitmentRaw.readUint(".params.commitment.validatorSetID")); + commitHash = beefyCommitmentRaw.readBytes32(".commitmentHash"); + + setSize = uint32(beefyValidatorSetRaw.readUint(".validatorSetSize")); + root = beefyValidatorSetRaw.readBytes32(".validatorRoot"); + bitSetArray = beefyValidatorSetRaw.readUintArray(".participants"); + absentBitSetArray = beefyValidatorSetRaw.readUintArray(".absentees"); - inputs[2] = "GenerateProofs"; - inputs[3] = Strings.toString(finalBitfield.length); - for (uint256 i = 0; i < finalBitfield.length; i++) { - inputs[i + 4] = Strings.toString(finalBitfield[i]); - } - BeefyClient.ValidatorProof[] memory proofs; - (root, proofs, mmrLeafProofs, mmrLeaf, leafProofOrder) = - abi.decode(vm.ffi(inputs), (bytes32, BeefyClient.ValidatorProof[], bytes32[], BeefyClient.MMRLeaf, uint256)); - // Cache finalValidatorProofs to storage in order to reuse in submitFinal later - for (uint256 i = 0; i < proofs.length; i++) { - finalValidatorProofs.push(proofs[i]); - } console.log("current validator's merkle root is: %s", Strings.toHexString(uint256(root), 32)); + + mmrRoot = beefyCommitmentRaw.readBytes32(".params.commitment.payload[0].data"); + mmrLeafProofs = beefyCommitmentRaw.readBytes32Array(".params.leafProof"); + leafProofOrder = beefyCommitmentRaw.readUint(".params.leafProofOrder"); + decodeMMRLeaf(); + + bitfield = beefyClient.createInitialBitfield(bitSetArray, setSize); + absentBitfield = beefyClient.createInitialBitfield(absentBitSetArray, setSize); + + loadFinalProofs(); } function initialize(uint32 _setId) public returns (BeefyClient.Commitment memory) { @@ -88,10 +98,70 @@ contract BeefyClientTest is Test { function printBitArray(uint256[] memory bits) private view { for (uint256 i = 0; i < bits.length; i++) { - console.log("bits index at %d is %x", i, bits[i]); + console.log("bits index at %d is %d", i, bits[i]); + } + } + + function loadFinalProofs() internal { + bytes memory proofRaw = beefyValidatorProofRaw.readBytes(".finalValidatorsProofRaw"); + BeefyClient.ValidatorProof[] memory proofs = abi.decode(proofRaw, (BeefyClient.ValidatorProof[])); + for (uint256 i = 0; i < proofs.length; i++) { + finalValidatorProofs.push(proofs[i]); } } + // Ideally should also update `finalValidatorProofs` with another round of ffi based on the `finalBitfield` here + // For simplicity we just use the proof previously cached + // still update `finalBitfield` here is to simulate more close to the real workflow and make gas estimation more accurate + function createFinalProofs() internal { + finalBitfield = beefyClient.createFinalBitfield(commitHash, bitfield); + } + + function commitPrevRandao() internal { + vm.prevrandao(bytes32(uint256(prevRandao))); + beefyClient.commitPrevRandao(commitHash); + } + + // Regenerate bitField file + function regenerateBitField() internal { + console.log("print initialBitField"); + printBitArray(bitfield); + prevRandao = uint32(vm.envOr("PREV_RANDAO", prevRandao)); + finalBitfield = + Bitfield.subsample(prevRandao, bitfield, beefyClient.minimumSignatureThreshold_public(setSize), setSize); + console.log("print finalBitField"); + printBitArray(finalBitfield); + + string memory finalBitFieldRaw = ""; + finalBitFieldRaw = finalBitFieldRaw.serialize("finalBitFieldRaw", abi.encode(finalBitfield)); + + string memory finaliBitFieldStr = ""; + finaliBitFieldStr = finaliBitFieldStr.serialize("finalBitField", finalBitfield); + + string memory output = finalBitFieldRaw.serialize("final", finaliBitFieldStr); + + vm.writeJson(output, beefyFinalBitFieldFile); + } + + function decodeMMRLeaf() internal { + uint8 version = uint8(beefyCommitmentRaw.readUint(".params.leaf.version")); + uint32 parentNumber = uint32(beefyCommitmentRaw.readUint(".params.leaf.parentNumber")); + bytes32 parentHash = beefyCommitmentRaw.readBytes32(".params.leaf.parentHash"); + uint64 nextAuthoritySetID = uint64(beefyCommitmentRaw.readUint(".params.leaf.nextAuthoritySetID")); + uint32 nextAuthoritySetLen = uint32(beefyCommitmentRaw.readUint(".params.leaf.nextAuthoritySetLen")); + bytes32 nextAuthoritySetRoot = beefyCommitmentRaw.readBytes32(".params.leaf.nextAuthoritySetRoot"); + bytes32 parachainHeadsRoot = beefyCommitmentRaw.readBytes32(".params.leaf.parachainHeadsRoot"); + mmrLeaf = BeefyClient.MMRLeaf( + version, + parentNumber, + parentHash, + nextAuthoritySetID, + nextAuthoritySetLen, + nextAuthoritySetRoot, + parachainHeadsRoot + ); + } + function testSubmit() public returns (BeefyClient.Commitment memory) { BeefyClient.Commitment memory commitment = initialize(setId); @@ -100,10 +170,9 @@ contract BeefyClientTest is Test { // mine random delay blocks vm.roll(block.number + randaoCommitDelay); - // set difficulty as PrevRandao - vm.prevrandao(bytes32(uint256(difficulty))); + commitPrevRandao(); - beefyClient.commitPrevRandao(commitHash); + createFinalProofs(); beefyClient.submitFinal(commitment, bitfield, finalValidatorProofs); @@ -119,10 +188,9 @@ contract BeefyClientTest is Test { // mine random delay blocks vm.roll(block.number + randaoCommitDelay); - // set difficulty as PrevRandao - vm.prevrandao(bytes32(uint256(difficulty))); + commitPrevRandao(); - beefyClient.commitPrevRandao(commitHash); + createFinalProofs(); // make an invalid signature finalValidatorProofs[0].r = 0xb5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c; @@ -138,10 +206,9 @@ contract BeefyClientTest is Test { // mine random delay blocks vm.roll(block.number + randaoCommitDelay); - // set difficulty as PrevRandao - vm.prevrandao(bytes32(uint256(difficulty))); + commitPrevRandao(); - beefyClient.commitPrevRandao(commitHash); + createFinalProofs(); // make an invalid validator index finalValidatorProofs[0].index = 0; @@ -155,8 +222,11 @@ contract BeefyClientTest is Test { beefyClient.submitInitial(commitment, bitfield, finalValidatorProofs[0]); vm.roll(block.number + randaoCommitDelay); - vm.prevrandao(bytes32(uint256(difficulty))); - beefyClient.commitPrevRandao(commitHash); + + commitPrevRandao(); + + createFinalProofs(); + //submit again will be reverted with StaleCommitment vm.expectRevert(BeefyClient.StaleCommitment.selector); beefyClient.submitFinal(commitment, bitfield, finalValidatorProofs); @@ -169,9 +239,9 @@ contract BeefyClientTest is Test { vm.roll(block.number + randaoCommitDelay); - vm.prevrandao(bytes32(uint256(difficulty))); + commitPrevRandao(); - beefyClient.commitPrevRandao(commitHash); + createFinalProofs(); // invalid bitfield here bitfield[0] = 0; @@ -183,6 +253,7 @@ contract BeefyClientTest is Test { BeefyClient.Commitment memory commitment = initialize(setId); beefyClient.submitInitial(commitment, bitfield, finalValidatorProofs[0]); + // reverted without commit PrevRandao vm.expectRevert(BeefyClient.PrevRandaoNotCaptured.selector); beefyClient.submitFinal(commitment, bitfield, finalValidatorProofs); @@ -194,12 +265,12 @@ contract BeefyClientTest is Test { beefyClient.submitInitial(commitment, bitfield, finalValidatorProofs[0]); // reverted for commit PrevRandao too early vm.expectRevert(BeefyClient.WaitPeriodNotOver.selector); - beefyClient.commitPrevRandao(commitHash); + commitPrevRandao(); // reverted for commit PrevRandao too late vm.roll(block.number + randaoCommitDelay + randaoCommitExpiration + 1); vm.expectRevert(BeefyClient.TicketExpired.selector); - beefyClient.commitPrevRandao(commitHash); + commitPrevRandao(); } function testSubmitFailForPrevRandaoCapturedMoreThanOnce() public { @@ -207,11 +278,10 @@ contract BeefyClientTest is Test { beefyClient.submitInitial(commitment, bitfield, finalValidatorProofs[0]); vm.roll(block.number + randaoCommitDelay); - vm.prevrandao(bytes32(uint256(difficulty))); - beefyClient.commitPrevRandao(commitHash); + commitPrevRandao(); vm.expectRevert(BeefyClient.PrevRandaoAlreadyCaptured.selector); - beefyClient.commitPrevRandao(commitHash); + commitPrevRandao(); } function testSubmitWithHandover() public { @@ -222,9 +292,9 @@ contract BeefyClientTest is Test { vm.roll(block.number + randaoCommitDelay); - vm.prevrandao(bytes32(uint256(difficulty))); + commitPrevRandao(); - beefyClient.commitPrevRandao(commitHash); + createFinalProofs(); beefyClient.submitFinalWithHandover( commitment, bitfield, finalValidatorProofs, mmrLeaf, mmrLeafProofs, leafProofOrder @@ -245,15 +315,16 @@ contract BeefyClientTest is Test { } function testSubmitWithHandoverFailStaleCommitment() public { - BeefyClient.Commitment memory commitment = testSubmit(); + BeefyClient.Commitment memory commitment = initialize(setId - 1); + beefyClient.setLatestBeefyBlock(blockNumber); beefyClient.submitInitialWithHandover(commitment, bitfield, finalValidatorProofs[0]); vm.roll(block.number + randaoCommitDelay); - vm.prevrandao(bytes32(uint256(difficulty))); + commitPrevRandao(); - beefyClient.commitPrevRandao(commitHash); + createFinalProofs(); vm.expectRevert(BeefyClient.StaleCommitment.selector); beefyClient.submitFinalWithHandover( @@ -280,10 +351,8 @@ contract BeefyClientTest is Test { function testCreateInitialBitfield() public { initialize(setId); uint256[] memory initialBitfield = beefyClient.createInitialBitfield(bitSetArray, setSize); - assertTrue(initialBitfield.length == 2); + assertTrue(initialBitfield.length == (setSize + 255) / 256); printBitArray(initialBitfield); - assertEq(initialBitfield[0], 0xd9fbb69bb8dfe46bffd2fd7feefffb185aef39fafcec0beba6db619efad1f6db); - assertEq(initialBitfield[1], 0x7f76cee2a3f); } function testCreateInitialBitfieldInvalid() public { @@ -296,8 +365,7 @@ contract BeefyClientTest is Test { BeefyClient.Commitment memory commitment = initialize(setId); beefyClient.submitInitial(commitment, bitfield, finalValidatorProofs[0]); vm.roll(block.number + randaoCommitDelay); - vm.prevrandao(bytes32(uint256(difficulty))); - beefyClient.commitPrevRandao(commitHash); + commitPrevRandao(); uint256[] memory finalBits = beefyClient.createFinalBitfield(commitHash, bitfield); assertTrue(Bitfield.countSetBits(finalBits) < Bitfield.countSetBits(bitfield)); @@ -307,8 +375,7 @@ contract BeefyClientTest is Test { BeefyClient.Commitment memory commitment = initialize(setId); beefyClient.submitInitial(commitment, bitfield, finalValidatorProofs[0]); vm.roll(block.number + randaoCommitDelay); - vm.prevrandao(bytes32(uint256(difficulty))); - beefyClient.commitPrevRandao(commitHash); + commitPrevRandao(); // make invalid bitfield not same as initialized bitfield[0] = 0; @@ -319,9 +386,11 @@ contract BeefyClientTest is Test { function testSubmitFailWithInvalidValidatorSet() public { BeefyClient.Commitment memory commitment = initialize(setId); beefyClient.submitInitial(commitment, bitfield, finalValidatorProofs[0]); + vm.roll(block.number + randaoCommitDelay); - vm.prevrandao(bytes32(uint256(difficulty))); - beefyClient.commitPrevRandao(commitHash); + commitPrevRandao(); + + createFinalProofs(); //reinitialize with next validator set initialize(setId + 1); @@ -337,10 +406,9 @@ contract BeefyClientTest is Test { beefyClient.submitInitialWithHandover(commitment, bitfield, finalValidatorProofs[0]); vm.roll(block.number + randaoCommitDelay); + commitPrevRandao(); - vm.prevrandao(bytes32(uint256(difficulty))); - - beefyClient.commitPrevRandao(commitHash); + createFinalProofs(); //reinitialize with next validator set initialize(setId); @@ -354,10 +422,14 @@ contract BeefyClientTest is Test { function testSubmitFailWithInvalidTicket() public { BeefyClient.Commitment memory commitment = initialize(setId); beefyClient.submitInitial(commitment, bitfield, finalValidatorProofs[0]); + vm.roll(block.number + randaoCommitDelay); - vm.prevrandao(bytes32(uint256(difficulty))); - beefyClient.commitPrevRandao(commitHash); + commitPrevRandao(); + + createFinalProofs(); + // Changing the commitment changes its hash, so the ticket can't be found. + // A zero value ticket is returned in this case, because submitInitial hasn't run for this commitment. BeefyClient.Commitment memory _commitment = BeefyClient.Commitment(blockNumber, setId + 1, commitment.payload); //submit will be reverted with InvalidTicket vm.expectRevert(BeefyClient.InvalidTicket.selector); @@ -372,10 +444,12 @@ contract BeefyClientTest is Test { vm.roll(block.number + randaoCommitDelay); - vm.prevrandao(bytes32(uint256(difficulty))); + vm.prevrandao(bytes32(uint256(prevRandao))); beefyClient.commitPrevRandao(commitHash); + createFinalProofs(); + //construct nextAuthoritySetID with a wrong value mmrLeaf.nextAuthoritySetID = setId; //submit will be reverted with InvalidCommitment @@ -393,9 +467,9 @@ contract BeefyClientTest is Test { vm.roll(block.number + randaoCommitDelay); - vm.prevrandao(bytes32(uint256(difficulty))); + commitPrevRandao(); - beefyClient.commitPrevRandao(commitHash); + createFinalProofs(); //construct parentNumber with a wrong value mmrLeaf.parentNumber = 1; @@ -414,4 +488,22 @@ contract BeefyClientTest is Test { vm.expectRevert(BeefyClient.NotEnoughClaims.selector); beefyClient.submitInitial(commitment, initialBits, finalValidatorProofs[0]); } + + function testSubmitInitialFailWithIncorrectValidatorSet() public { + BeefyClient.Commitment memory commitment = initialize(setId); + + vm.expectRevert(BeefyClient.InvalidCommitment.selector); + beefyClient.submitInitialWithHandover(commitment, bitfield, finalValidatorProofs[0]); + } + + function testSubmitInitialHandoverFailWithIncorrectValidatorSet() public { + BeefyClient.Commitment memory commitment = initialize(setId - 1); + + vm.expectRevert(BeefyClient.InvalidCommitment.selector); + beefyClient.submitInitial(commitment, bitfield, finalValidatorProofs[0]); + } + + function testRegenerateBitField() public { + regenerateBitField(); + } } diff --git a/core/packages/contracts/test/Gateway.t.sol b/core/packages/contracts/test/Gateway.t.sol index 912a940f14..45b1130c0b 100644 --- a/core/packages/contracts/test/Gateway.t.sol +++ b/core/packages/contracts/test/Gateway.t.sol @@ -21,7 +21,7 @@ import {SubstrateTypes} from "./../src/SubstrateTypes.sol"; import {NativeTransferFailed} from "../src/utils/SafeTransfer.sol"; -import {InboundMessage, OperatingMode, ParaID, Config, Command} from "../src/Types.sol"; +import {AgentExecuteCommand, InboundMessage, OperatingMode, ParaID, Config, Command} from "../src/Types.sol"; import {WETH9} from "canonical-weth/WETH9.sol"; @@ -259,7 +259,7 @@ contract GatewayTest is Test { Gateway.AgentExecuteParams memory params = Gateway.AgentExecuteParams({ agentID: assetHubAgentID, - payload: abi.encode(keccak256("transferToken"), abi.encode(address(token), address(this), 1)) + payload: abi.encode(AgentExecuteCommand.TransferToken, abi.encode(address(token), address(this), 1)) }); GatewayMock(address(gateway)).agentExecutePublic(abi.encode(params)); diff --git a/core/packages/contracts/test/data/beefy-final-bitfield.json b/core/packages/contracts/test/data/beefy-final-bitfield.json new file mode 100644 index 0000000000..c254164281 --- /dev/null +++ b/core/packages/contracts/test/data/beefy-final-bitfield.json @@ -0,0 +1,9 @@ +{ + "final": { + "finalBitField": [ + 3618502792036124440576582696777723666402714743437477508623650775118001471488, + 17179869200 + ], + "finalBitFieldRaw": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000208000000200000000800000800002000004000000008000000000000800000000000000000000000000000000000000000000000000000000000000400000010" + } +} \ No newline at end of file diff --git a/core/packages/contracts/test/data/beefy-final-proof.json b/core/packages/contracts/test/data/beefy-final-proof.json new file mode 100644 index 0000000000..f613d386f7 --- /dev/null +++ b/core/packages/contracts/test/data/beefy-final-proof.json @@ -0,0 +1,840 @@ +{ + "finalValidatorsProof": [ + { + "v": 27, + "r": { + "0": 56, + "1": 87, + "2": 185, + "3": 73, + "4": 67, + "5": 171, + "6": 245, + "7": 94, + "8": 216, + "9": 127, + "10": 175, + "11": 120, + "12": 201, + "13": 220, + "14": 0, + "15": 229, + "16": 248, + "17": 72, + "18": 10, + "19": 218, + "20": 67, + "21": 207, + "22": 40, + "23": 90, + "24": 154, + "25": 131, + "26": 132, + "27": 3, + "28": 69, + "29": 219, + "30": 47, + "31": 222 + }, + "s": { + "0": 79, + "1": 142, + "2": 79, + "3": 87, + "4": 204, + "5": 67, + "6": 94, + "7": 173, + "8": 56, + "9": 222, + "10": 4, + "11": 96, + "12": 32, + "13": 188, + "14": 17, + "15": 114, + "16": 127, + "17": 139, + "18": 236, + "19": 35, + "20": 246, + "21": 173, + "22": 67, + "23": 109, + "24": 188, + "25": 182, + "26": 97, + "27": 128, + "28": 139, + "29": 9, + "30": 201, + "31": 100 + }, + "index": 31, + "account": "0x5473090d3a9D7B984B31f2d36f2d004a9188796d", + "proof": [ + "0x2d3fe5fcf7e9b1559b73b0055d813911a135fd0f7311e5a69899cf420c418c9f", + "0x8df6a7abbefe9b2fe4e384876b71ec1eb90d23950c68b78df4e22909590d1930", + "0x7126a8044f997db28aaf469f8f66a8a4c09c25ab03c0b902bf02618d54d5e86e", + "0x325666c5a681388f68098fa3270d9f8b05de0c3a929cf61ce2fff465db97ffa4", + "0xdf9973cc4b389edb18d204fb23baa094715a7f530e120a19fd643bd338f242ed", + "0x8c83332339cab7a6008352495919f7102dbe1cc4d6d243682565de39b33d4899", + "0xb8aefe65af72a69011a5539ab2d19e55a34db316d3b0235a2117d5b6fc42ffde", + "0xf804afe61ff1c61cdcb536df0f29b62f6ca8ae121863b66c826a2ee9823f3365", + "0x73c8c40d242d326ab7e07cd18bd406f4110d0df04f7168adf68e655359b8a3d0" + ] + }, + { + "v": 28, + "r": { + "0": 134, + "1": 9, + "2": 91, + "3": 10, + "4": 201, + "5": 18, + "6": 162, + "7": 237, + "8": 46, + "9": 123, + "10": 22, + "11": 151, + "12": 10, + "13": 238, + "14": 159, + "15": 92, + "16": 231, + "17": 239, + "18": 177, + "19": 133, + "20": 18, + "21": 232, + "22": 13, + "23": 64, + "24": 118, + "25": 196, + "26": 117, + "27": 15, + "28": 62, + "29": 114, + "30": 189, + "31": 234 + }, + "s": { + "0": 43, + "1": 50, + "2": 161, + "3": 46, + "4": 114, + "5": 133, + "6": 190, + "7": 78, + "8": 192, + "9": 73, + "10": 223, + "11": 154, + "12": 45, + "13": 43, + "14": 218, + "15": 183, + "16": 80, + "17": 240, + "18": 0, + "19": 224, + "20": 248, + "21": 12, + "22": 201, + "23": 15, + "24": 140, + "25": 156, + "26": 176, + "27": 115, + "28": 21, + "29": 239, + "30": 90, + "31": 143 + }, + "index": 83, + "account": "0xe11cEabDd29528aFFcC2f7936e0E80877CcF1E1A", + "proof": [ + "0x95f466dc7f9837f333cf284f84b3ef47d968203b93571d6e67379a55239bac68", + "0x1f22928e92219f9cbe815f7a2baa4b71238a975a4017f36fdbe7cc736cc77870", + "0x70e953d23047cbc07dc21fa318e1dd6851ccd19a5fed5976976a78236a66d647", + "0x7bc44cfe90425291d8c8808375892cb895a9dd67ffd994a74bb0985b7072d98b", + "0xe1f4f439878975a3e8cfc749957c8b204653fd135da9b547d302697138859613", + "0x03abdd191345b346cc826490fd23eb43349fab167ad091f4c48d9d2f6d70a540", + "0x2586b1acf8187086c42dd317785c79d85fb281987845a982517a5925ff9c7b2b", + "0xf804afe61ff1c61cdcb536df0f29b62f6ca8ae121863b66c826a2ee9823f3365", + "0x73c8c40d242d326ab7e07cd18bd406f4110d0df04f7168adf68e655359b8a3d0" + ] + }, + { + "v": 28, + "r": { + "0": 64, + "1": 6, + "2": 83, + "3": 215, + "4": 60, + "5": 41, + "6": 219, + "7": 174, + "8": 102, + "9": 105, + "10": 11, + "11": 249, + "12": 26, + "13": 87, + "14": 231, + "15": 130, + "16": 77, + "17": 156, + "18": 147, + "19": 87, + "20": 30, + "21": 24, + "22": 102, + "23": 36, + "24": 242, + "25": 231, + "26": 53, + "27": 186, + "28": 95, + "29": 250, + "30": 185, + "31": 13 + }, + "s": { + "0": 91, + "1": 174, + "2": 224, + "3": 47, + "4": 225, + "5": 90, + "6": 2, + "7": 244, + "8": 10, + "9": 31, + "10": 93, + "11": 248, + "12": 120, + "13": 190, + "14": 221, + "15": 70, + "16": 250, + "17": 85, + "18": 134, + "19": 27, + "20": 94, + "21": 7, + "22": 44, + "23": 146, + "24": 249, + "25": 76, + "26": 236, + "27": 105, + "28": 50, + "29": 142, + "30": 186, + "31": 228 + }, + "index": 118, + "account": "0xb79df9BBD3Cc5a316374d40a35898657e2D8e18E", + "proof": [ + "0x7df725da049349c532528abe804b608d47fd20993d68053fc1b3d6042ca0002b", + "0x2f3e8719554d71589ae679692ef3951fdaee3445126323eddf5edfbc78be6aa5", + "0xa44761bb661fd68431ba5962280d515d2e286060fac0a5893bf5c7a7bc4309d1", + "0x3669134efbdc64823fbca48af53f72ffb0dccdef81c3b0d35f804893d535e886", + "0x6eafa8fc9b0e67ab52141344ed25288a2bb3d158af038ea9ecc3c2ddf30f32e7", + "0x9c88a3c47b71bf6a0902cb29f0acc01a5e6b16f50d9030a2c6b6cc46aa3abfc8", + "0x2586b1acf8187086c42dd317785c79d85fb281987845a982517a5925ff9c7b2b", + "0xf804afe61ff1c61cdcb536df0f29b62f6ca8ae121863b66c826a2ee9823f3365", + "0x73c8c40d242d326ab7e07cd18bd406f4110d0df04f7168adf68e655359b8a3d0" + ] + }, + { + "v": 27, + "r": { + "0": 224, + "1": 135, + "2": 154, + "3": 30, + "4": 139, + "5": 155, + "6": 130, + "7": 154, + "8": 212, + "9": 156, + "10": 49, + "11": 92, + "12": 42, + "13": 95, + "14": 214, + "15": 238, + "16": 38, + "17": 118, + "18": 173, + "19": 164, + "20": 215, + "21": 173, + "22": 184, + "23": 227, + "24": 70, + "25": 46, + "26": 24, + "27": 67, + "28": 149, + "29": 27, + "30": 105, + "31": 53 + }, + "s": { + "0": 116, + "1": 62, + "2": 232, + "3": 154, + "4": 164, + "5": 74, + "6": 59, + "7": 107, + "8": 37, + "9": 5, + "10": 26, + "11": 123, + "12": 130, + "13": 173, + "14": 193, + "15": 237, + "16": 25, + "17": 228, + "18": 210, + "19": 146, + "20": 38, + "21": 158, + "22": 25, + "23": 61, + "24": 124, + "25": 175, + "26": 112, + "27": 125, + "28": 154, + "29": 83, + "30": 51, + "31": 184 + }, + "index": 141, + "account": "0xC66888DDEbd37D715B5278A86a35d58F0D9B90F4", + "proof": [ + "0x08b8935325ae43169b960e19bb8d97eadf46e3e881978aff6decf2cad2edd8d4", + "0x518211971ae9be734ef9576dc3a94f2e7a24cee2b67d158e6af5e6ede48b8e2b", + "0x1137b81220981703e7f8fb9c13d3052513b17f8fe0dc801500ea64b25eee49fe", + "0x42a0104a13de7b8090ba2326a97024c1157d88281804ce5fb85ea82abaf1eef4", + "0xe05e69cbeca48f72fd3d81601b5419215fb234ba3a76467e8cd973d335c9e83a", + "0xc72bd3c5ef89cc841cebd46ee834312bbb107ee0b90e729f93eb7ce924ea2d80", + "0x64aaad909501e44338a8a9191abc63f8def273fb9f03108864016a17cad259dc", + "0xf2683e1db31d79f14ec5f379fa90e6044c55ee73ee1b5280e6dc22235cbb941d", + "0x73c8c40d242d326ab7e07cd18bd406f4110d0df04f7168adf68e655359b8a3d0" + ] + }, + { + "v": 28, + "r": { + "0": 179, + "1": 36, + "2": 218, + "3": 96, + "4": 164, + "5": 120, + "6": 156, + "7": 156, + "8": 99, + "9": 187, + "10": 40, + "11": 126, + "12": 223, + "13": 152, + "14": 155, + "15": 65, + "16": 8, + "17": 19, + "18": 29, + "19": 41, + "20": 91, + "21": 200, + "22": 89, + "23": 208, + "24": 231, + "25": 49, + "26": 107, + "27": 101, + "28": 50, + "29": 85, + "30": 210, + "31": 147 + }, + "s": { + "0": 10, + "1": 0, + "2": 233, + "3": 251, + "4": 123, + "5": 27, + "6": 238, + "7": 177, + "8": 220, + "9": 145, + "10": 125, + "11": 6, + "12": 218, + "13": 69, + "14": 30, + "15": 43, + "16": 231, + "17": 225, + "18": 237, + "19": 255, + "20": 193, + "21": 56, + "22": 148, + "23": 175, + "24": 30, + "25": 97, + "26": 193, + "27": 172, + "28": 204, + "29": 18, + "30": 172, + "31": 129 + }, + "index": 163, + "account": "0x66a04195aCacaf002D88928B582f729E52655815", + "proof": [ + "0x0cd1ee823e290df8b9dff0067abab1eaed0749a7453852fffedbf62429be9608", + "0x4a77cc45d751eb4a541863939cdc472bd7179db4e9bce47aff72e11d53d824ca", + "0x136d1789bce723dc057eea270cb480ec6e3308e1fdffe9d1e9fdc26b673b37f1", + "0x68a649c5d1d5b44417b82100e8f3a87587a7affaadd64d88e6d5af06f3878c72", + "0xa907b494272792d0106ddc58923600fa00a366d0eb6bd7264a9fcea91d5585fb", + "0x137973435ae824f670344e596684069be579cc265fa6f74b78a23a04ef85d88d", + "0x64aaad909501e44338a8a9191abc63f8def273fb9f03108864016a17cad259dc", + "0xf2683e1db31d79f14ec5f379fa90e6044c55ee73ee1b5280e6dc22235cbb941d", + "0x73c8c40d242d326ab7e07cd18bd406f4110d0df04f7168adf68e655359b8a3d0" + ] + }, + { + "v": 27, + "r": { + "0": 217, + "1": 40, + "2": 246, + "3": 19, + "4": 235, + "5": 200, + "6": 153, + "7": 35, + "8": 151, + "9": 223, + "10": 220, + "11": 51, + "12": 133, + "13": 232, + "14": 230, + "15": 180, + "16": 222, + "17": 148, + "18": 220, + "19": 95, + "20": 75, + "21": 146, + "22": 222, + "23": 87, + "24": 153, + "25": 170, + "26": 4, + "27": 165, + "28": 156, + "29": 176, + "30": 154, + "31": 25 + }, + "s": { + "0": 34, + "1": 5, + "2": 53, + "3": 51, + "4": 59, + "5": 229, + "6": 167, + "7": 143, + "8": 165, + "9": 227, + "10": 165, + "11": 29, + "12": 103, + "13": 244, + "14": 135, + "15": 14, + "16": 156, + "17": 72, + "18": 101, + "19": 87, + "20": 252, + "21": 32, + "22": 26, + "23": 118, + "24": 193, + "25": 101, + "26": 6, + "27": 206, + "28": 221, + "29": 108, + "30": 71, + "31": 134 + }, + "index": 187, + "account": "0x85098Dfb29a8A71208fFE981886A33a2c3E3f2B2", + "proof": [ + "0xc90699ab2080cfb90faf611369b3270e75c46722954fb05ddc48ff4231d6bee3", + "0x6ba8a511b931a886b8547f75c94c89188aa817901edbcf4076d2e28c29cda4aa", + "0x0268cdd4761f48b12c987142c2f89dace26b4cfea9edef0e71a934a805d15794", + "0x2ba6746783167c393bea28563490ded0680108da88f6fd0245e8b8e18b1004fc", + "0x599a8674d32d090e93042776f9b7c43790c455091892e35deb595b5954a7471e", + "0x137973435ae824f670344e596684069be579cc265fa6f74b78a23a04ef85d88d", + "0x64aaad909501e44338a8a9191abc63f8def273fb9f03108864016a17cad259dc", + "0xf2683e1db31d79f14ec5f379fa90e6044c55ee73ee1b5280e6dc22235cbb941d", + "0x73c8c40d242d326ab7e07cd18bd406f4110d0df04f7168adf68e655359b8a3d0" + ] + }, + { + "v": 28, + "r": { + "0": 182, + "1": 143, + "2": 236, + "3": 153, + "4": 83, + "5": 159, + "6": 124, + "7": 131, + "8": 55, + "9": 248, + "10": 149, + "11": 146, + "12": 192, + "13": 249, + "14": 220, + "15": 178, + "16": 188, + "17": 55, + "18": 252, + "19": 191, + "20": 237, + "21": 211, + "22": 68, + "23": 227, + "24": 217, + "25": 130, + "26": 206, + "27": 37, + "28": 74, + "29": 117, + "30": 251, + "31": 84 + }, + "s": { + "0": 26, + "1": 86, + "2": 142, + "3": 125, + "4": 88, + "5": 205, + "6": 193, + "7": 220, + "8": 219, + "9": 54, + "10": 188, + "11": 152, + "12": 223, + "13": 132, + "14": 183, + "15": 68, + "16": 83, + "17": 176, + "18": 56, + "19": 251, + "20": 213, + "21": 39, + "22": 203, + "23": 185, + "24": 71, + "25": 88, + "26": 196, + "27": 8, + "28": 67, + "29": 30, + "30": 75, + "31": 73 + }, + "index": 221, + "account": "0x3b5F3d19020fb26367a6a0eA611CB0E7dE28DE33", + "proof": [ + "0x1b1b9ace3e2c1a1785ee845eed31a37e3d9dffdd2702868b18d8ff2a5b309ea5", + "0xe37762d4ee9ceac6ff6676f1cc9e062a81aa4dbcb60946f32f4d8b72c2b8cadd", + "0x6861b76364d1c3170f84724667f2dab3c206b1a2b3db9a5b1ca64ba233997c5f", + "0x85067ce849428405cf1b407341b9376d504e3ff593b00e4aae4f65e30a36db3c", + "0xa3e540d32650f8682146d5649323edcf497d1451d505914616d409a89efff9da", + "0x1bcc228cc309f83f90031fb5c788d2487eed6b8a12c98774d7a9c23c0cee17c6", + "0xdbf5e78cdca685e455240593bc327f715dbb1552bdcd7e5f821dc6cc41e2cfb0", + "0xf2683e1db31d79f14ec5f379fa90e6044c55ee73ee1b5280e6dc22235cbb941d", + "0x73c8c40d242d326ab7e07cd18bd406f4110d0df04f7168adf68e655359b8a3d0" + ] + }, + { + "v": 28, + "r": { + "0": 212, + "1": 212, + "2": 229, + "3": 193, + "4": 74, + "5": 51, + "6": 5, + "7": 76, + "8": 91, + "9": 138, + "10": 190, + "11": 156, + "12": 136, + "13": 149, + "14": 58, + "15": 239, + "16": 44, + "17": 134, + "18": 86, + "19": 122, + "20": 135, + "21": 254, + "22": 66, + "23": 82, + "24": 100, + "25": 4, + "26": 164, + "27": 136, + "28": 122, + "29": 77, + "30": 93, + "31": 177 + }, + "s": { + "0": 70, + "1": 4, + "2": 156, + "3": 172, + "4": 246, + "5": 28, + "6": 95, + "7": 220, + "8": 66, + "9": 221, + "10": 42, + "11": 224, + "12": 205, + "13": 242, + "14": 100, + "15": 86, + "16": 190, + "17": 149, + "18": 178, + "19": 218, + "20": 169, + "21": 130, + "22": 182, + "23": 95, + "24": 234, + "25": 54, + "26": 153, + "27": 29, + "28": 197, + "29": 235, + "30": 158, + "31": 247 + }, + "index": 251, + "account": "0x83694089d1e2FBD01c090a3198C64A9153D2F8Fa", + "proof": [ + "0x9fe76ba617982f9986fc59e8521171ebe81e0c7491d91521a0fe9a0b635af8a5", + "0x462f5f4793463267cac15dacb74be47bddc2f19985682e9a8dae06fbdcd60293", + "0x942ca9c53961d698e1d0cd11e72c60329679204ae7a8357a4b557f88f767aacd", + "0x638ed41e5e35db1049fe4794634692551b91f937358d0253016627fc3dbbf8a0", + "0xff0e576ff6a911efa30fd9002adcfa5d69f7e14818215b7fe0f22a1c22686161", + "0x8527ec55aceec37ad9c2b70e7e14ebe2c51994e496fcbedae2db5aedf4bd4a5e", + "0xdbf5e78cdca685e455240593bc327f715dbb1552bdcd7e5f821dc6cc41e2cfb0", + "0xf2683e1db31d79f14ec5f379fa90e6044c55ee73ee1b5280e6dc22235cbb941d", + "0x73c8c40d242d326ab7e07cd18bd406f4110d0df04f7168adf68e655359b8a3d0" + ] + }, + { + "v": 27, + "r": { + "0": 168, + "1": 227, + "2": 31, + "3": 135, + "4": 190, + "5": 211, + "6": 178, + "7": 184, + "8": 221, + "9": 77, + "10": 114, + "11": 7, + "12": 20, + "13": 115, + "14": 201, + "15": 102, + "16": 11, + "17": 140, + "18": 179, + "19": 253, + "20": 96, + "21": 41, + "22": 34, + "23": 121, + "24": 197, + "25": 161, + "26": 69, + "27": 165, + "28": 22, + "29": 200, + "30": 227, + "31": 189 + }, + "s": { + "0": 115, + "1": 206, + "2": 181, + "3": 240, + "4": 94, + "5": 219, + "6": 114, + "7": 2, + "8": 107, + "9": 207, + "10": 152, + "11": 28, + "12": 148, + "13": 241, + "14": 2, + "15": 148, + "16": 200, + "17": 212, + "18": 95, + "19": 77, + "20": 1, + "21": 186, + "22": 179, + "23": 162, + "24": 32, + "25": 216, + "26": 223, + "27": 22, + "28": 76, + "29": 61, + "30": 94, + "31": 203 + }, + "index": 260, + "account": "0xE94dc0038fAD3Bee81cD5eb54E02407CdDAD9a60", + "proof": [ + "0xf66a64c6b93269cbe6648a25111eab894edecc8bfc7146be1b80462982dc76b9", + "0x01840fef395955c1ffab0190e185a1e4e00c39d5bad2d46fc0f0f6ed0c7bf367", + "0x7a64198bc497a4465cfe60609f06b28b93220df36c48a504ef75feb198868689", + "0x415afb9adc1b79fc758a10c06b82fbdb020b4c15034aa142031dd8d167383ff7", + "0x4ced2037a4343c973b4f31226b11a0c4ad66f8d3ba93a58abe1ad3f52bee37f2", + "0x199289f8be72bebb34866c29cbe95e9f374c18f61de30b9671f334125971bd87", + "0xaae970f3226e259a58f4a0dda903a5f743d15d606867b7394ccdd139004c2433" + ] + }, + { + "v": 28, + "r": { + "0": 248, + "1": 202, + "2": 84, + "3": 144, + "4": 95, + "5": 244, + "6": 10, + "7": 48, + "8": 250, + "9": 40, + "10": 190, + "11": 112, + "12": 108, + "13": 140, + "14": 10, + "15": 212, + "16": 252, + "17": 127, + "18": 173, + "19": 209, + "20": 190, + "21": 3, + "22": 188, + "23": 199, + "24": 124, + "25": 35, + "26": 108, + "27": 189, + "28": 41, + "29": 141, + "30": 108, + "31": 123 + }, + "s": { + "0": 48, + "1": 173, + "2": 86, + "3": 115, + "4": 203, + "5": 18, + "6": 12, + "7": 146, + "8": 150, + "9": 252, + "10": 74, + "11": 44, + "12": 230, + "13": 237, + "14": 110, + "15": 179, + "16": 145, + "17": 190, + "18": 204, + "19": 52, + "20": 124, + "21": 81, + "22": 215, + "23": 7, + "24": 40, + "25": 76, + "26": 75, + "27": 91, + "28": 23, + "29": 26, + "30": 197, + "31": 18 + }, + "index": 290, + "account": "0x01BEA320fe842E39dFc559d2316Ac4AcB2E3c17B", + "proof": [ + "0xb06a220df5a620e5723c3c7d5dd4252da1ac29abd2b1730e1d18c12713297ce1", + "0x5782b7b385b33e5ab799e824f080cd03a092f32e89144b6022570986710f2572", + "0xc5f613829c6f188e934a1064f2d4643a50ad69d3877a30313257814ef3413dc3", + "0x67b4644bea2b3ed13a16e64bf6e0bf9cada409232106972c00d0b7070ae5acbe", + "0x75fda4b696227a082ae644377814cb58e5b22955eaba511048eb6794fbc071f3", + "0xaae970f3226e259a58f4a0dda903a5f743d15d606867b7394ccdd139004c2433" + ] + } + ], + "finalValidatorsProofRaw": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000003400000000000000000000000000000000000000000000000000000000000000540000000000000000000000000000000000000000000000000000000000000074000000000000000000000000000000000000000000000000000000000000009400000000000000000000000000000000000000000000000000000000000000b400000000000000000000000000000000000000000000000000000000000000d400000000000000000000000000000000000000000000000000000000000000f4000000000000000000000000000000000000000000000000000000000000011400000000000000000000000000000000000000000000000000000000000001300000000000000000000000000000000000000000000000000000000000000001b3857b94943abf55ed87faf78c9dc00e5f8480ada43cf285a9a83840345db2fde4f8e4f57cc435ead38de046020bc11727f8bec23f6ad436dbcb661808b09c964000000000000000000000000000000000000000000000000000000000000001f0000000000000000000000005473090d3a9d7b984b31f2d36f2d004a9188796d00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000092d3fe5fcf7e9b1559b73b0055d813911a135fd0f7311e5a69899cf420c418c9f8df6a7abbefe9b2fe4e384876b71ec1eb90d23950c68b78df4e22909590d19307126a8044f997db28aaf469f8f66a8a4c09c25ab03c0b902bf02618d54d5e86e325666c5a681388f68098fa3270d9f8b05de0c3a929cf61ce2fff465db97ffa4df9973cc4b389edb18d204fb23baa094715a7f530e120a19fd643bd338f242ed8c83332339cab7a6008352495919f7102dbe1cc4d6d243682565de39b33d4899b8aefe65af72a69011a5539ab2d19e55a34db316d3b0235a2117d5b6fc42ffdef804afe61ff1c61cdcb536df0f29b62f6ca8ae121863b66c826a2ee9823f336573c8c40d242d326ab7e07cd18bd406f4110d0df04f7168adf68e655359b8a3d0000000000000000000000000000000000000000000000000000000000000001c86095b0ac912a2ed2e7b16970aee9f5ce7efb18512e80d4076c4750f3e72bdea2b32a12e7285be4ec049df9a2d2bdab750f000e0f80cc90f8c9cb07315ef5a8f0000000000000000000000000000000000000000000000000000000000000053000000000000000000000000e11ceabdd29528affcc2f7936e0e80877ccf1e1a00000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000995f466dc7f9837f333cf284f84b3ef47d968203b93571d6e67379a55239bac681f22928e92219f9cbe815f7a2baa4b71238a975a4017f36fdbe7cc736cc7787070e953d23047cbc07dc21fa318e1dd6851ccd19a5fed5976976a78236a66d6477bc44cfe90425291d8c8808375892cb895a9dd67ffd994a74bb0985b7072d98be1f4f439878975a3e8cfc749957c8b204653fd135da9b547d30269713885961303abdd191345b346cc826490fd23eb43349fab167ad091f4c48d9d2f6d70a5402586b1acf8187086c42dd317785c79d85fb281987845a982517a5925ff9c7b2bf804afe61ff1c61cdcb536df0f29b62f6ca8ae121863b66c826a2ee9823f336573c8c40d242d326ab7e07cd18bd406f4110d0df04f7168adf68e655359b8a3d0000000000000000000000000000000000000000000000000000000000000001c400653d73c29dbae66690bf91a57e7824d9c93571e186624f2e735ba5ffab90d5baee02fe15a02f40a1f5df878bedd46fa55861b5e072c92f94cec69328ebae40000000000000000000000000000000000000000000000000000000000000076000000000000000000000000b79df9bbd3cc5a316374d40a35898657e2d8e18e00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000097df725da049349c532528abe804b608d47fd20993d68053fc1b3d6042ca0002b2f3e8719554d71589ae679692ef3951fdaee3445126323eddf5edfbc78be6aa5a44761bb661fd68431ba5962280d515d2e286060fac0a5893bf5c7a7bc4309d13669134efbdc64823fbca48af53f72ffb0dccdef81c3b0d35f804893d535e8866eafa8fc9b0e67ab52141344ed25288a2bb3d158af038ea9ecc3c2ddf30f32e79c88a3c47b71bf6a0902cb29f0acc01a5e6b16f50d9030a2c6b6cc46aa3abfc82586b1acf8187086c42dd317785c79d85fb281987845a982517a5925ff9c7b2bf804afe61ff1c61cdcb536df0f29b62f6ca8ae121863b66c826a2ee9823f336573c8c40d242d326ab7e07cd18bd406f4110d0df04f7168adf68e655359b8a3d0000000000000000000000000000000000000000000000000000000000000001be0879a1e8b9b829ad49c315c2a5fd6ee2676ada4d7adb8e3462e1843951b6935743ee89aa44a3b6b25051a7b82adc1ed19e4d292269e193d7caf707d9a5333b8000000000000000000000000000000000000000000000000000000000000008d000000000000000000000000c66888ddebd37d715b5278a86a35d58f0d9b90f400000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000908b8935325ae43169b960e19bb8d97eadf46e3e881978aff6decf2cad2edd8d4518211971ae9be734ef9576dc3a94f2e7a24cee2b67d158e6af5e6ede48b8e2b1137b81220981703e7f8fb9c13d3052513b17f8fe0dc801500ea64b25eee49fe42a0104a13de7b8090ba2326a97024c1157d88281804ce5fb85ea82abaf1eef4e05e69cbeca48f72fd3d81601b5419215fb234ba3a76467e8cd973d335c9e83ac72bd3c5ef89cc841cebd46ee834312bbb107ee0b90e729f93eb7ce924ea2d8064aaad909501e44338a8a9191abc63f8def273fb9f03108864016a17cad259dcf2683e1db31d79f14ec5f379fa90e6044c55ee73ee1b5280e6dc22235cbb941d73c8c40d242d326ab7e07cd18bd406f4110d0df04f7168adf68e655359b8a3d0000000000000000000000000000000000000000000000000000000000000001cb324da60a4789c9c63bb287edf989b4108131d295bc859d0e7316b653255d2930a00e9fb7b1beeb1dc917d06da451e2be7e1edffc13894af1e61c1accc12ac8100000000000000000000000000000000000000000000000000000000000000a300000000000000000000000066a04195acacaf002d88928b582f729e5265581500000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000090cd1ee823e290df8b9dff0067abab1eaed0749a7453852fffedbf62429be96084a77cc45d751eb4a541863939cdc472bd7179db4e9bce47aff72e11d53d824ca136d1789bce723dc057eea270cb480ec6e3308e1fdffe9d1e9fdc26b673b37f168a649c5d1d5b44417b82100e8f3a87587a7affaadd64d88e6d5af06f3878c72a907b494272792d0106ddc58923600fa00a366d0eb6bd7264a9fcea91d5585fb137973435ae824f670344e596684069be579cc265fa6f74b78a23a04ef85d88d64aaad909501e44338a8a9191abc63f8def273fb9f03108864016a17cad259dcf2683e1db31d79f14ec5f379fa90e6044c55ee73ee1b5280e6dc22235cbb941d73c8c40d242d326ab7e07cd18bd406f4110d0df04f7168adf68e655359b8a3d0000000000000000000000000000000000000000000000000000000000000001bd928f613ebc8992397dfdc3385e8e6b4de94dc5f4b92de5799aa04a59cb09a19220535333be5a78fa5e3a51d67f4870e9c486557fc201a76c16506cedd6c478600000000000000000000000000000000000000000000000000000000000000bb00000000000000000000000085098dfb29a8a71208ffe981886a33a2c3e3f2b200000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000009c90699ab2080cfb90faf611369b3270e75c46722954fb05ddc48ff4231d6bee36ba8a511b931a886b8547f75c94c89188aa817901edbcf4076d2e28c29cda4aa0268cdd4761f48b12c987142c2f89dace26b4cfea9edef0e71a934a805d157942ba6746783167c393bea28563490ded0680108da88f6fd0245e8b8e18b1004fc599a8674d32d090e93042776f9b7c43790c455091892e35deb595b5954a7471e137973435ae824f670344e596684069be579cc265fa6f74b78a23a04ef85d88d64aaad909501e44338a8a9191abc63f8def273fb9f03108864016a17cad259dcf2683e1db31d79f14ec5f379fa90e6044c55ee73ee1b5280e6dc22235cbb941d73c8c40d242d326ab7e07cd18bd406f4110d0df04f7168adf68e655359b8a3d0000000000000000000000000000000000000000000000000000000000000001cb68fec99539f7c8337f89592c0f9dcb2bc37fcbfedd344e3d982ce254a75fb541a568e7d58cdc1dcdb36bc98df84b74453b038fbd527cbb94758c408431e4b4900000000000000000000000000000000000000000000000000000000000000dd0000000000000000000000003b5f3d19020fb26367a6a0ea611cb0e7de28de3300000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000091b1b9ace3e2c1a1785ee845eed31a37e3d9dffdd2702868b18d8ff2a5b309ea5e37762d4ee9ceac6ff6676f1cc9e062a81aa4dbcb60946f32f4d8b72c2b8cadd6861b76364d1c3170f84724667f2dab3c206b1a2b3db9a5b1ca64ba233997c5f85067ce849428405cf1b407341b9376d504e3ff593b00e4aae4f65e30a36db3ca3e540d32650f8682146d5649323edcf497d1451d505914616d409a89efff9da1bcc228cc309f83f90031fb5c788d2487eed6b8a12c98774d7a9c23c0cee17c6dbf5e78cdca685e455240593bc327f715dbb1552bdcd7e5f821dc6cc41e2cfb0f2683e1db31d79f14ec5f379fa90e6044c55ee73ee1b5280e6dc22235cbb941d73c8c40d242d326ab7e07cd18bd406f4110d0df04f7168adf68e655359b8a3d0000000000000000000000000000000000000000000000000000000000000001cd4d4e5c14a33054c5b8abe9c88953aef2c86567a87fe42526404a4887a4d5db146049cacf61c5fdc42dd2ae0cdf26456be95b2daa982b65fea36991dc5eb9ef700000000000000000000000000000000000000000000000000000000000000fb00000000000000000000000083694089d1e2fbd01c090a3198c64a9153d2f8fa00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000099fe76ba617982f9986fc59e8521171ebe81e0c7491d91521a0fe9a0b635af8a5462f5f4793463267cac15dacb74be47bddc2f19985682e9a8dae06fbdcd60293942ca9c53961d698e1d0cd11e72c60329679204ae7a8357a4b557f88f767aacd638ed41e5e35db1049fe4794634692551b91f937358d0253016627fc3dbbf8a0ff0e576ff6a911efa30fd9002adcfa5d69f7e14818215b7fe0f22a1c226861618527ec55aceec37ad9c2b70e7e14ebe2c51994e496fcbedae2db5aedf4bd4a5edbf5e78cdca685e455240593bc327f715dbb1552bdcd7e5f821dc6cc41e2cfb0f2683e1db31d79f14ec5f379fa90e6044c55ee73ee1b5280e6dc22235cbb941d73c8c40d242d326ab7e07cd18bd406f4110d0df04f7168adf68e655359b8a3d0000000000000000000000000000000000000000000000000000000000000001ba8e31f87bed3b2b8dd4d72071473c9660b8cb3fd60292279c5a145a516c8e3bd73ceb5f05edb72026bcf981c94f10294c8d45f4d01bab3a220d8df164c3d5ecb0000000000000000000000000000000000000000000000000000000000000104000000000000000000000000e94dc0038fad3bee81cd5eb54e02407cddad9a6000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000007f66a64c6b93269cbe6648a25111eab894edecc8bfc7146be1b80462982dc76b901840fef395955c1ffab0190e185a1e4e00c39d5bad2d46fc0f0f6ed0c7bf3677a64198bc497a4465cfe60609f06b28b93220df36c48a504ef75feb198868689415afb9adc1b79fc758a10c06b82fbdb020b4c15034aa142031dd8d167383ff74ced2037a4343c973b4f31226b11a0c4ad66f8d3ba93a58abe1ad3f52bee37f2199289f8be72bebb34866c29cbe95e9f374c18f61de30b9671f334125971bd87aae970f3226e259a58f4a0dda903a5f743d15d606867b7394ccdd139004c2433000000000000000000000000000000000000000000000000000000000000001cf8ca54905ff40a30fa28be706c8c0ad4fc7fadd1be03bcc77c236cbd298d6c7b30ad5673cb120c9296fc4a2ce6ed6eb391becc347c51d707284c4b5b171ac512000000000000000000000000000000000000000000000000000000000000012200000000000000000000000001bea320fe842e39dfc559d2316ac4acb2e3c17b00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000006b06a220df5a620e5723c3c7d5dd4252da1ac29abd2b1730e1d18c12713297ce15782b7b385b33e5ab799e824f080cd03a092f32e89144b6022570986710f2572c5f613829c6f188e934a1064f2d4643a50ad69d3877a30313257814ef3413dc367b4644bea2b3ed13a16e64bf6e0bf9cada409232106972c00d0b7070ae5acbe75fda4b696227a082ae644377814cb58e5b22955eaba511048eb6794fbc071f3aae970f3226e259a58f4a0dda903a5f743d15d606867b7394ccdd139004c2433" +} \ No newline at end of file diff --git a/core/packages/contracts/test/data/beefy-validator-set.json b/core/packages/contracts/test/data/beefy-validator-set.json new file mode 100644 index 0000000000..ee6e82d120 --- /dev/null +++ b/core/packages/contracts/test/data/beefy-validator-set.json @@ -0,0 +1,308 @@ +{ + "validatorSetSize": 300, + "participants": [ + 0, + 1, + 3, + 4, + 6, + 7, + 9, + 10, + 12, + 13, + 14, + 15, + 16, + 20, + 22, + 23, + 25, + 27, + 28, + 29, + 30, + 31, + 33, + 34, + 35, + 36, + 39, + 40, + 45, + 46, + 48, + 49, + 51, + 52, + 54, + 55, + 57, + 58, + 61, + 63, + 64, + 65, + 67, + 69, + 70, + 71, + 72, + 73, + 75, + 82, + 83, + 85, + 86, + 87, + 90, + 91, + 92, + 93, + 94, + 95, + 97, + 99, + 100, + 101, + 102, + 103, + 104, + 107, + 108, + 109, + 112, + 113, + 114, + 115, + 117, + 118, + 119, + 121, + 123, + 124, + 126, + 131, + 132, + 136, + 137, + 139, + 140, + 141, + 142, + 143, + 144, + 145, + 146, + 147, + 148, + 149, + 150, + 151, + 153, + 154, + 155, + 157, + 158, + 159, + 160, + 161, + 162, + 163, + 164, + 165, + 166, + 168, + 170, + 171, + 172, + 173, + 174, + 175, + 177, + 180, + 182, + 183, + 184, + 185, + 186, + 187, + 188, + 189, + 190, + 191, + 192, + 193, + 195, + 197, + 198, + 202, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 214, + 215, + 219, + 220, + 221, + 223, + 224, + 225, + 227, + 228, + 231, + 233, + 234, + 236, + 237, + 239, + 240, + 241, + 243, + 244, + 245, + 246, + 247, + 248, + 251, + 252, + 254, + 255, + 256, + 257, + 258, + 259, + 260, + 261, + 265, + 267, + 269, + 273, + 274, + 275, + 277, + 278, + 279, + 282, + 283, + 285, + 286, + 288, + 289, + 290, + 292, + 293, + 294, + 295, + 296, + 297, + 298 + ], + "absentees": [ + 2, + 5, + 8, + 11, + 17, + 18, + 19, + 21, + 24, + 26, + 32, + 37, + 38, + 41, + 42, + 43, + 44, + 47, + 50, + 53, + 56, + 59, + 60, + 62, + 66, + 68, + 74, + 76, + 77, + 78, + 79, + 80, + 81, + 84, + 88, + 89, + 96, + 98, + 105, + 106, + 110, + 111, + 116, + 120, + 122, + 125, + 127, + 128, + 129, + 130, + 133, + 134, + 135, + 138, + 152, + 156, + 167, + 169, + 176, + 178, + 179, + 181, + 194, + 196, + 199, + 200, + 201, + 203, + 204, + 213, + 216, + 217, + 218, + 222, + 226, + 229, + 230, + 232, + 235, + 238, + 242, + 249, + 250, + 253, + 262, + 263, + 264, + 266, + 268, + 270, + 271, + 272, + 276, + 280, + 281, + 284, + 287, + 291, + 299 + ], + "validatorRoot": "0x9e1c83813ce2c712d2753f9e3b4864e586bc34104fe2d9c40b87778977dba3b6" +} \ No newline at end of file diff --git a/core/packages/contracts/test/mocks/BeefyClientMock.sol b/core/packages/contracts/test/mocks/BeefyClientMock.sol index f66b391a8b..392e2696c3 100644 --- a/core/packages/contracts/test/mocks/BeefyClientMock.sol +++ b/core/packages/contracts/test/mocks/BeefyClientMock.sol @@ -22,6 +22,14 @@ contract BeefyClientMock is BeefyClient { return minimumSignatureThreshold(validatorSetLen); } + function setTicketValidatorSetLen(bytes32 commitmentHash, uint32 validatorSetLen) external { + tickets[createTicketID(msg.sender, commitmentHash)].validatorSetLen = validatorSetLen; + } + + function setLatestBeefyBlock(uint32 _latestBeefyBlock) external { + latestBeefyBlock = _latestBeefyBlock; + } + function initialize_public( uint64 _initialBeefyBlock, ValidatorSet calldata _initialValidatorSet, diff --git a/core/packages/test/config/genesis.json b/core/packages/test/config/genesis.json index 9d7431a098..c672a786a7 100644 --- a/core/packages/test/config/genesis.json +++ b/core/packages/test/config/genesis.json @@ -21,7 +21,7 @@ "gasLimit": "80000000", "alloc": { "90A987B944Cb1dCcE5564e5FDeCD7a54D3de27Fe": { - "balance": "100000000000000000000" + "balance": "10000000000000000000000" }, "Be68fC2d8249eb60bfCf0e71D5A0d2F2e292c4eD": { "balance": "100000000000000000000" diff --git a/core/packages/test/scripts/configure-bridgehub.sh b/core/packages/test/scripts/configure-bridgehub.sh index ba2412f6e7..6c7e0e2206 100755 --- a/core/packages/test/scripts/configure-bridgehub.sh +++ b/core/packages/test/scripts/configure-bridgehub.sh @@ -63,7 +63,7 @@ fund_accounts() transfer_balance $relaychain_ws_url "//Charlie" 1013 1000000000000000 $statemine_sovereign_account transfer_balance $relaychain_ws_url "//Charlie" 1013 1000000000000000 $beacon_relayer_pub_key transfer_balance $relaychain_ws_url "//Charlie" 1013 1000000000000000 $execution_relayer_pub_key - transfer_balance $relaychain_ws_url "//Charlie" 1000 1000000000000000 $registry_contract_sovereign_account + transfer_balance $relaychain_ws_url "//Charlie" 1000 1000000000000000 $gateway_contract_sovereign_account } configure_bridgehub() diff --git a/core/packages/test/scripts/set-env.sh b/core/packages/test/scripts/set-env.sh index 22c13850fb..56fc96a770 100755 --- a/core/packages/test/scripts/set-env.sh +++ b/core/packages/test/scripts/set-env.sh @@ -62,8 +62,8 @@ statemine_sovereign_account="${STATEMINE_SOVEREIGN_ACCOUNT:-0x70617261e803000000 beacon_relayer_pub_key="${BEACON_RELAYER_PUB_KEY:-0xc46e141b5083721ad5f5056ba1cded69dce4a65f027ed3362357605b1687986a}" # Execution relay account (//ExecutionRelay 5CFNWKMFPsw5Cs2Teo6Pvg7rWyjKiFfqPZs8U4MZXzMYFwXL in testnet) execution_relayer_pub_key="${EXECUTION_RELAYER_PUB_KEY:-0x08228efd065c58a043da95c8bf177659fc587643e71e7ed1534666177730196f}" -# Registry contract account (5EBBfBLm4uV4JMXXcKvZrPVmP9VyER9YSCgGdMUw5wBXnqag in testnet) -registry_contract_sovereign_account="${REGISTRY_CONTRACT_SOVEREIGN_ACCOUNT:-0x5d6987649e0dac78ddf852eb0f1b1d1bf2be9623d81cb16c17cfa145948bb6dc}" +# Gateway contract account (H8VBFC4LG91ByxMG6GwsCcAacjitnzGmGbqnvSEQFBywJEL in testnet) +gateway_contract_sovereign_account="${GATEWAY_CONTRACT_SOVEREIGN_ACCOUNT:-0xc9794dd8013efb2ad83f668845c62b373c16ad33971745731408058e4d0c6ff5}" # Config for deploying contracts @@ -80,11 +80,11 @@ export RANDAO_COMMIT_EXP="${ETH_RANDAO_EXP:-3}" export BRIDGE_HUB_PARAID=$bridgehub_para_id # TODO: update placeholder value -export BRIDGE_HUB_AGENT_ID="00000000000000000000000000000000000000000000000000000000000003f5" +export BRIDGE_HUB_AGENT_ID="0x05f0ced792884ed09997292bd95f8d0d1094bb3bded91ec3f2f08531624037d6" export ASSET_HUB_PARAID=$statemine_para_id # TODO: update placeholder value -export ASSET_HUB_AGENT_ID="00000000000000000000000000000000000000000000000000000000000003e8" +export ASSET_HUB_AGENT_ID="0x72456f48efed08af20e5b317abf8648ac66e86bb90a411d9b0b713f7364b75b4" export DEFAULT_FEE=1 export DEFAULT_REWARD=1 diff --git a/cumulus b/cumulus index c0d90e08c0..c6cf3c37f9 160000 --- a/cumulus +++ b/cumulus @@ -1 +1 @@ -Subproject commit c0d90e08c0b4e89cabefe983c09eaec0ad3a0e81 +Subproject commit c6cf3c37f94f5433ca3bfa2751083b9d1add1f35 diff --git a/parachain/Cargo.lock b/parachain/Cargo.lock index 4c12853d98..b5afdccb5b 100644 --- a/parachain/Cargo.lock +++ b/parachain/Cargo.lock @@ -2646,6 +2646,7 @@ dependencies = [ "sp-std", "xcm", "xcm-builder", + "xcm-executor", ] [[package]] @@ -2820,6 +2821,7 @@ dependencies = [ "sp-runtime", "sp-std", "xcm", + "xcm-builder", "xcm-executor", ] diff --git a/parachain/pallets/control/Cargo.toml b/parachain/pallets/control/Cargo.toml index 1bbe4d7589..f3fa0511d2 100644 --- a/parachain/pallets/control/Cargo.toml +++ b/parachain/pallets/control/Cargo.toml @@ -29,6 +29,7 @@ sp-runtime = { git = "https://github.com/paritytech/substrate.git", branch = "ma xcm = { git = "https://github.com/paritytech/polkadot.git", branch = "master", default-features = false } xcm-builder = { git = "https://github.com/paritytech/polkadot.git", branch = "master", default-features = false } +xcm-executor = { git = "https://github.com/paritytech/polkadot.git", branch = "master", default-features = false } ethabi = { git = "https://github.com/Snowfork/ethabi-decode.git", package = "ethabi-decode", branch = "master", default-features = false } @@ -49,10 +50,12 @@ std = [ "sp-runtime/std", "xcm/std", "xcm-builder/std", + "xcm-executor/std", "ethabi/std" ] runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", - "xcm-builder/runtime-benchmarks" + "xcm-builder/runtime-benchmarks", + "xcm-executor/runtime-benchmarks" ] try-runtime = ["frame-support/try-runtime"] diff --git a/parachain/pallets/control/src/lib.rs b/parachain/pallets/control/src/lib.rs index 45f0e77c61..fe0dcb9ab0 100644 --- a/parachain/pallets/control/src/lib.rs +++ b/parachain/pallets/control/src/lib.rs @@ -21,11 +21,10 @@ pub use weights::*; use snowbridge_core::{Command, OutboundMessage, OutboundQueue as OutboundQueueTrait, ParaId}; use sp_core::{H160, H256}; -use sp_io::hashing::blake2_256; use sp_runtime::traits::Hash; use sp_std::prelude::*; use xcm::prelude::*; -use xcm_builder::DescribeLocation; +use xcm_executor::traits::ConvertLocation; pub use pallet::*; @@ -47,7 +46,9 @@ pub mod pallet { type WeightInfo: WeightInfo; type MaxUpgradeDataSize: Get; type CreateAgentOrigin: EnsureOrigin; - type DescribeAgentLocation: DescribeLocation; + type AgentHashedDescription: ConvertLocation; + type UniversalLocation: Get; + type RelayLocation: Get; } #[pallet::event] @@ -107,13 +108,19 @@ pub mod pallet { #[pallet::call_index(1)] #[pallet::weight(T::WeightInfo::create_agent())] pub fn create_agent(origin: OriginFor) -> DispatchResult { - let agent_location: MultiLocation = T::CreateAgentOrigin::ensure_origin(origin)?; + let mut agent_location: MultiLocation = T::CreateAgentOrigin::ensure_origin(origin)?; + + // Normalize all locations relative to the relay unless its the relay itself. + let relay_location = T::RelayLocation::get(); + if agent_location != relay_location { + agent_location + .reanchor(&relay_location, T::UniversalLocation::get()) + .or(Err(Error::::LocationConversionFailed))?; + } - let agent_description = T::DescribeAgentLocation::describe_location(&agent_location) + let agent_id = T::AgentHashedDescription::convert_location(&agent_location) .ok_or(Error::::LocationConversionFailed)?; - let agent_id: H256 = blake2_256(&agent_description).into(); - if Agents::::contains_key(agent_id) { return Ok(()); } diff --git a/parachain/pallets/control/src/mock.rs b/parachain/pallets/control/src/mock.rs index 78af4a4d6a..f0026b9b85 100644 --- a/parachain/pallets/control/src/mock.rs +++ b/parachain/pallets/control/src/mock.rs @@ -8,7 +8,7 @@ use frame_support::{ }; #[cfg(feature = "runtime-benchmarks")] -use frame_benchmarking::whitelisted_caller; +use frame_benchmarking::v2::whitelisted_caller; use snowbridge_core::{OutboundMessage, OutboundMessageHash, ParaId, SubmitError}; use sp_core::H256; @@ -18,10 +18,7 @@ use sp_runtime::{ AccountId32, }; use xcm::prelude::*; -use xcm_builder::{ - DescribeAccountId32Terminal, DescribeAccountKey20Terminal, DescribeAllTerminal, DescribeFamily, - DescribePalletTerminal, -}; +use xcm_builder::{DescribeAllTerminal, DescribeFamily, HashedDescription}; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; @@ -71,6 +68,10 @@ parameter_types! { pub const MaxUpgradeDataSize: u32 = 1024; pub const SS58Prefix: u8 = 42; pub const AnyNetwork: Option = None; + pub const RelayNetwork: Option = Some(NetworkId::Kusama); + pub const RelayLocation: MultiLocation = MultiLocation::parent(); + pub UniversalLocation: InteriorMultiLocation = + X2(GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(1013)); } static ORIGIN_TABLE: &[([u8; 32], MultiLocation)] = &[ @@ -184,13 +185,6 @@ impl snowbridge_control::OutboundQueueTrait for MockOutboundQueue { } } -pub type DescribeAgentLocation = ( - DescribePalletTerminal, - DescribeAccountId32Terminal, - DescribeAccountKey20Terminal, - DescribeFamily, -); - impl snowbridge_control::Config for Test { type RuntimeEvent = RuntimeEvent; type OwnParaId = OwnParaId; @@ -198,7 +192,9 @@ impl snowbridge_control::Config for Test { type MessageHasher = BlakeTwo256; type MaxUpgradeDataSize = MaxUpgradeDataSize; type CreateAgentOrigin = EnsureOriginFromTable; - type DescribeAgentLocation = DescribeAgentLocation; + type UniversalLocation = UniversalLocation; + type RelayLocation = RelayLocation; + type AgentHashedDescription = HashedDescription>; type WeightInfo = (); } diff --git a/parachain/pallets/control/src/tests.rs b/parachain/pallets/control/src/tests.rs index 33deb990fd..fe8d8f6f4d 100644 --- a/parachain/pallets/control/src/tests.rs +++ b/parachain/pallets/control/src/tests.rs @@ -67,10 +67,10 @@ fn create_agent_with_local_account32_yields_success() { new_test_ext().execute_with(|| { let origin = RuntimeOrigin::signed(AccountId32::new([2; 32])); let expected_agent_id = - H256(hex!("9e85ef53611dcb973a337977a79217890f6c0d605de20ae4a828b1b9a95162c4")); + H256(hex!("57fc5659083f0cc883125ccb2c380a1397a3b08434586b8647cc44bcb3647d29")); let expected_multi_location = MultiLocation { parents: 0, - interior: X1(Junction::AccountId32 { network: None, id: [0; 32] }), + interior: X2(Parachain(1013), Junction::AccountId32 { network: None, id: [0; 32] }), }; assert!(!Agents::::contains_key(expected_agent_id)); @@ -89,10 +89,10 @@ fn create_agent_with_local_account20_yields_success() { new_test_ext().execute_with(|| { let origin = RuntimeOrigin::signed(AccountId32::new([3; 32])); let expected_agent_id = - H256(hex!("927a4def0d0bdd151dfa247a07e4036e12335ee71977426847be6e6e36e3c460")); + H256(hex!("fc29ec0899cf25874937d04b9b011760fa5dc5cf59af1448abefd389bba7bea2")); let expected_multi_location = MultiLocation { parents: 0, - interior: X1(AccountKey20 { network: None, key: [0; 20] }), + interior: X2(Parachain(1013), AccountKey20 { network: None, key: [0; 20] }), }; assert!(!Agents::::contains_key(expected_agent_id)); @@ -111,8 +111,9 @@ fn create_agent_with_local_pallet_yields_success() { new_test_ext().execute_with(|| { let origin = RuntimeOrigin::signed(AccountId32::new([4; 32])); let expected_agent_id = - H256(hex!("964c3b3f978db1febb282d675dcf2196eae3c28fd7c0885b738cee828262fcc2")); - let expected_multi_location = MultiLocation { parents: 0, interior: X1(PalletInstance(1)) }; + H256(hex!("ed40c69763094b73c0e3585eeb576fbcee6999123ff1f1beac1f05f5f4c9d945")); + let expected_multi_location = + MultiLocation { parents: 0, interior: X2(Parachain(1013), PalletInstance(1)) }; assert!(!Agents::::contains_key(expected_agent_id)); assert_eq!(EthereumControl::create_agent(origin), Ok(())); @@ -130,8 +131,8 @@ fn create_agent_with_sibling_chain_origin_yields_success() { new_test_ext().execute_with(|| { let origin = RuntimeOrigin::signed(AccountId32::new([5; 32])); let expected_agent_id = - H256(hex!("81c5ab2571199e3188135178f3c2c8e2d268be1313d029b30f534fa579b69b79")); - let expected_multi_location = MultiLocation { parents: 1, interior: X1(Parachain(1000)) }; + H256(hex!("72456f48efed08af20e5b317abf8648ac66e86bb90a411d9b0b713f7364b75b4")); + let expected_multi_location = MultiLocation { parents: 0, interior: X1(Parachain(1000)) }; assert!(!Agents::::contains_key(expected_agent_id)); assert_eq!(EthereumControl::create_agent(origin), Ok(())); @@ -149,9 +150,9 @@ fn create_agent_with_sibling_chain_account32_origin_yields_success() { new_test_ext().execute_with(|| { let origin = RuntimeOrigin::signed(AccountId32::new([7; 32])); let expected_agent_id = - H256(hex!("75ad585e231db5daf900819e8fb62af432610619d0d7a1156e5d78531b2c6493")); + H256(hex!("fb804b0b77f9c9d69a16d7a45de81225ab8da112e0eb8d2e0229c78086b8927a")); let expected_multi_location = MultiLocation { - parents: 1, + parents: 0, interior: X2(Parachain(1000), Junction::AccountId32 { network: None, id: [0; 32] }), }; @@ -171,9 +172,9 @@ fn create_agent_with_sibling_chain_account20_origin_yields_success() { new_test_ext().execute_with(|| { let origin = RuntimeOrigin::signed(AccountId32::new([8; 32])); let expected_agent_id = - H256(hex!("3a737a558137d674c5e9c49bd0e6389bf69e1825c8fd531af5534081016501ef")); + H256(hex!("74867486f141b159ba1e295bf616d740429269879d4291a12a65eaedbb4b502a")); let expected_multi_location = MultiLocation { - parents: 1, + parents: 0, interior: X2(Parachain(1000), AccountKey20 { network: None, key: [0; 20] }), }; diff --git a/parachain/pallets/inbound-queue/src/benchmarking/mod.rs b/parachain/pallets/inbound-queue/src/benchmarking/mod.rs index 669cca4cc8..5c6396948b 100644 --- a/parachain/pallets/inbound-queue/src/benchmarking/mod.rs +++ b/parachain/pallets/inbound-queue/src/benchmarking/mod.rs @@ -13,7 +13,6 @@ mod benchmarks { use super::*; use crate::benchmarking::fixtures::make_create_message; use hex_literal::hex; - use sp_runtime::print; const GATEWAY_ADDRESS: [u8; 20] = hex!["eda338e4dc46038493b885327842fd3e301cab39"]; @@ -34,7 +33,8 @@ mod benchmarks { let sovereign_account = dest_para.into_account_truncating(); let minimum_balance = T::Token::minimum_balance(); - let minimum_balance_u32: u32 = minimum_balance.try_into() + let minimum_balance_u32: u32 = minimum_balance + .try_into() .unwrap_or_else(|_| panic!("unable to cast minimum balance to u32")); // Make sure the sovereign balance is enough. This is a funny number, because diff --git a/parachain/primitives/core/src/lib.rs b/parachain/primitives/core/src/lib.rs index b0fb5420db..bc9cebbc4d 100644 --- a/parachain/primitives/core/src/lib.rs +++ b/parachain/primitives/core/src/lib.rs @@ -143,13 +143,21 @@ pub enum AgentExecuteCommand { } impl AgentExecuteCommand { + fn index(&self) -> u8 { + match self { + AgentExecuteCommand::TransferToken { .. } => 0, + } + } pub fn abi_encode(&self) -> Vec { match self { AgentExecuteCommand::TransferToken { token, recipient, amount } => { ethabi::encode(&vec![ - Token::Address(*token), - Token::Address(*token), - Token::Uint(U256::from(*amount)), + Token::Uint(self.index().into()), + Token::Bytes(ethabi::encode(&vec![ + Token::Address(*token), + Token::Address(*recipient), + Token::Uint(U256::from(*amount)), + ])), ]) }, } diff --git a/parachain/primitives/router/Cargo.toml b/parachain/primitives/router/Cargo.toml index 6d54a747df..2fe1bbf22e 100644 --- a/parachain/primitives/router/Cargo.toml +++ b/parachain/primitives/router/Cargo.toml @@ -17,6 +17,7 @@ sp-runtime = { git = "https://github.com/paritytech/substrate.git", branch = "ma sp-std = { git = "https://github.com/paritytech/substrate.git", branch = "master", default-features = false } xcm = { git = "https://github.com/paritytech/polkadot.git", branch = "master", default-features = false } +xcm-builder = { git = "https://github.com/paritytech/polkadot.git", branch = "master", default-features = false } xcm-executor = { git = "https://github.com/paritytech/polkadot.git", branch = "master", default-features = false } snowbridge-core = { path = "../../primitives/core", default-features = false } @@ -41,6 +42,7 @@ std = [ "sp-runtime/std", "sp-std/std", "xcm/std", + "xcm-builder/std", "xcm-executor/std", "snowbridge-core/std", "ethabi/std", diff --git a/parachain/primitives/router/src/inbound/mod.rs b/parachain/primitives/router/src/inbound/mod.rs index b68ead14e6..f610d31a75 100644 --- a/parachain/primitives/router/src/inbound/mod.rs +++ b/parachain/primitives/router/src/inbound/mod.rs @@ -243,13 +243,13 @@ mod tests { use xcm::v3::prelude::*; use xcm_executor::traits::ConvertLocation; - const CONTRACT_ADDRESS: [u8; 20] = hex!("D184c103F7acc340847eEE82a0B909E3358bc28d"); + const CONTRACT_ADDRESS: [u8; 20] = hex!("EDa338E4dC46038493b885327842fD3E301CaB39"); const NETWORK: NetworkId = Ethereum { chain_id: 15 }; const SS58_FORMAT: u16 = 2; const EXPECTED_SOVEREIGN_KEY: [u8; 32] = - hex!("5d6987649e0dac78ddf852eb0f1b1d1bf2be9623d81cb16c17cfa145948bb6dc"); + hex!("c9794dd8013efb2ad83f668845c62b373c16ad33971745731408058e4d0c6ff5"); const EXPECTED_SOVEREIGN_ADDRESS: &'static str = - "EgoKVgdhGVz41LyP2jckLrmXjnD35xitaX221ktZjQ2Xsxw"; + "H8VBFC4LG91ByxMG6GwsCcAacjitnzGmGbqnvSEQFBywJEL"; parameter_types! { pub EthereumNetwork: NetworkId = NETWORK; @@ -272,10 +272,11 @@ mod tests { .unwrap(); let address = frame_support::sp_runtime::AccountId32::new(account) .to_ss58check_with_version(SS58_FORMAT.into()); - assert_eq!(account, EXPECTED_SOVEREIGN_KEY); - assert_eq!(address, EXPECTED_SOVEREIGN_ADDRESS); println!("SS58: {}\nBytes: {:?}", address, account); + + assert_eq!(account, EXPECTED_SOVEREIGN_KEY); + assert_eq!(address, EXPECTED_SOVEREIGN_ADDRESS); } #[test] diff --git a/parachain/primitives/router/src/outbound/mod.rs b/parachain/primitives/router/src/outbound/mod.rs index fe46649302..1d4c0a6ea0 100644 --- a/parachain/primitives/router/src/outbound/mod.rs +++ b/parachain/primitives/router/src/outbound/mod.rs @@ -11,19 +11,26 @@ use snowbridge_core::{ use sp_core::{H160, H256}; use sp_std::{marker::PhantomData, prelude::*}; use xcm::v3::prelude::*; -use xcm_executor::traits::ExportXcm; +use xcm_builder::{DescribeAllTerminal, DescribeFamily, HashedDescription}; +use xcm_executor::traits::{ConvertLocation, ExportXcm}; -pub struct EthereumBlobExporter( - PhantomData<(UniversalLocation, BridgeLocation, OutboundQueue)>, -); +pub type AgentHashedDescription = HashedDescription>; -impl ExportXcm - for EthereumBlobExporter +pub struct EthereumBlobExporter< + UniversalLocation, + BridgeLocation, + OutboundQueue, + AgentHashedDescription, +>(PhantomData<(UniversalLocation, BridgeLocation, OutboundQueue, AgentHashedDescription)>); + +impl ExportXcm + for EthereumBlobExporter where UniversalLocation: Get, BridgeLocation: Get, OutboundQueue: OutboundQueueTrait, OutboundQueue::Ticket: Encode + Decode, + AgentHashedDescription: ConvertLocation, { type Ticket = Vec; @@ -107,20 +114,27 @@ where return Err(SendError::Unroutable); } + // local_sub is relative to the relaychain. No conversion needed. + let local_sub_location: MultiLocation = local_sub.into(); + let agent_id = match AgentHashedDescription::convert_location(&local_sub_location) { + Some(id) => id, + None => { + log::error!(target: "xcm::ethereum_blob_exporter", "unroutable due to not being able to create agent id. '{local_sub_location:?}'"); + return Err(SendError::Unroutable); + }, + }; + let outbound_message = OutboundMessage { origin: para_id.into(), - command: Command::AgentExecute { - agent_id: H256::zero(), - command: agent_execute_command, - }, + command: Command::AgentExecute { agent_id, command: agent_execute_command }, }; - let ticket = OutboundQueue::validate(&outbound_message).map_err(|_| { - log::error!(target: "xcm::ethereum_blob_exporter", "OutboundQueue validation of message failed"); + let ticket = OutboundQueue::validate(&outbound_message).map_err(|err| { + log::error!(target: "xcm::ethereum_blob_exporter", "OutboundQueue validation of message failed. {err:?}"); SendError::Unroutable })?; - log::info!(target: "xcm::ethereum_blob_exporter", "message validated"); + log::info!(target: "xcm::ethereum_blob_exporter", "message validated: location = {local_sub_location:?}, agent_id = '{agent_id:?}'"); // TODO: Fees if any currently returning empty multi assets as cost Ok((ticket.encode(), MultiAssets::default())) @@ -351,13 +365,12 @@ mod tests { let mut message: Option> = None; let result = - EthereumBlobExporter::::validate( - network, - channel, - &mut universal_source, - &mut destination, - &mut message, - ); + EthereumBlobExporter::< + UniversalLocation, + BridgedLocation, + MockOkOutboundQueue, + AgentHashedDescription, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(SendError::NotApplicable)); } @@ -370,13 +383,12 @@ mod tests { let mut message: Option> = None; let result = - EthereumBlobExporter::::validate( - network, - channel, - &mut universal_source, - &mut destination, - &mut message, - ); + EthereumBlobExporter::< + UniversalLocation, + BridgedLocation, + MockOkOutboundQueue, + AgentHashedDescription, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(SendError::MissingArgument)); } @@ -391,13 +403,12 @@ mod tests { let mut message: Option> = None; let result = - EthereumBlobExporter::::validate( - network, - channel, - &mut universal_source, - &mut destination, - &mut message, - ); + EthereumBlobExporter::< + UniversalLocation, + BridgedLocation, + MockOkOutboundQueue, + AgentHashedDescription, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(SendError::NotApplicable)); } @@ -410,13 +421,12 @@ mod tests { let mut message: Option> = None; let result = - EthereumBlobExporter::::validate( - network, - channel, - &mut universal_source, - &mut destination, - &mut message, - ); + EthereumBlobExporter::< + UniversalLocation, + BridgedLocation, + MockOkOutboundQueue, + AgentHashedDescription, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(SendError::MissingArgument)); } @@ -429,13 +439,12 @@ mod tests { let mut message: Option> = None; let result = - EthereumBlobExporter::::validate( - network, - channel, - &mut universal_source, - &mut destination, - &mut message, - ); + EthereumBlobExporter::< + UniversalLocation, + BridgedLocation, + MockOkOutboundQueue, + AgentHashedDescription, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(SendError::Unroutable)); } @@ -452,6 +461,7 @@ mod tests { UniversalLocation, BridgedLocationWithoutGlobalConsensus, MockOkOutboundQueue, + AgentHashedDescription, >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(SendError::NotApplicable)); } @@ -469,6 +479,7 @@ mod tests { UniversalLocation, BridgedLocationWithoutRegistry, MockOkOutboundQueue, + AgentHashedDescription, >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(SendError::NotApplicable)); } @@ -483,13 +494,12 @@ mod tests { let mut message: Option> = None; let result = - EthereumBlobExporter::::validate( - network, - channel, - &mut universal_source, - &mut destination, - &mut message, - ); + EthereumBlobExporter::< + UniversalLocation, + BridgedLocation, + MockOkOutboundQueue, + AgentHashedDescription, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(SendError::NotApplicable)); } @@ -503,13 +513,12 @@ mod tests { let mut message: Option> = None; let result = - EthereumBlobExporter::::validate( - network, - channel, - &mut universal_source, - &mut destination, - &mut message, - ); + EthereumBlobExporter::< + UniversalLocation, + BridgedLocation, + MockOkOutboundQueue, + AgentHashedDescription, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(SendError::MissingArgument)); } @@ -523,13 +532,12 @@ mod tests { let mut message: Option> = None; let result = - EthereumBlobExporter::::validate( - network, - channel, - &mut universal_source, - &mut destination, - &mut message, - ); + EthereumBlobExporter::< + UniversalLocation, + BridgedLocation, + MockOkOutboundQueue, + AgentHashedDescription, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(SendError::MissingArgument)); } @@ -543,13 +551,12 @@ mod tests { let mut message: Option> = None; let result = - EthereumBlobExporter::::validate( - network, - channel, - &mut universal_source, - &mut destination, - &mut message, - ); + EthereumBlobExporter::< + UniversalLocation, + BridgedLocation, + MockOkOutboundQueue, + AgentHashedDescription, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(SendError::MissingArgument)); } @@ -599,13 +606,12 @@ mod tests { ); let result = - EthereumBlobExporter::::validate( - network, - channel, - &mut universal_source, - &mut destination, - &mut message, - ); + EthereumBlobExporter::< + UniversalLocation, + BridgedLocation, + MockOkOutboundQueue, + AgentHashedDescription, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(SendError::Unroutable)); } @@ -627,13 +633,12 @@ mod tests { ); let result = - EthereumBlobExporter::::validate( - network, - channel, - &mut universal_source, - &mut destination, - &mut message, - ); + EthereumBlobExporter::< + UniversalLocation, + BridgedLocation, + MockOkOutboundQueue, + AgentHashedDescription, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(SendError::Unroutable)); } @@ -681,23 +686,24 @@ mod tests { ); let result = - EthereumBlobExporter::::validate( - network, - channel, - &mut universal_source, - &mut destination, - &mut message, - ); + EthereumBlobExporter::< + UniversalLocation, + BridgedLocation, + MockOkOutboundQueue, + AgentHashedDescription, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert!(result.is_ok()); } #[test] fn exporter_deliver_with_submit_failure_yields_unroutable() { - let result = - EthereumBlobExporter::::deliver( - hex!("deadbeef").to_vec(), - ); + let result = EthereumBlobExporter::< + UniversalLocation, + BridgedLocation, + MockErrOutboundQueue, + AgentHashedDescription, + >::deliver(hex!("deadbeef").to_vec()); assert_eq!(result, Err(SendError::Transport("other transport error"))) } diff --git a/relayer/chain/ethereum/message.go b/relayer/chain/ethereum/message.go index 1bd36c5e8a..9659ec1867 100644 --- a/relayer/chain/ethereum/message.go +++ b/relayer/chain/ethereum/message.go @@ -50,6 +50,7 @@ func MakeMessageFromEvent(event *etypes.Log, receiptsTrie *etrie.Trie) (*paracha "payload": value, "blockHash": m.Proof.BlockHash.Hex(), "eventIndex": m.Proof.TxIndex, + "txHash": event.TxHash.Hex(), }).Debug("Generated message from Ethereum log") return &m, nil diff --git a/relayer/chain/parachain/writer.go b/relayer/chain/parachain/writer.go index 3c82e74c18..8e35ff98ac 100644 --- a/relayer/chain/parachain/writer.go +++ b/relayer/chain/parachain/writer.go @@ -6,6 +6,7 @@ import ( "sync" "github.com/ethereum/go-ethereum/common" + log "github.com/sirupsen/logrus" "github.com/snowfork/go-substrate-rpc-client/v4/rpc/author" "github.com/snowfork/go-substrate-rpc-client/v4/types" "github.com/snowfork/snowbridge/relayer/relays/beacon/header/syncer/scale" @@ -120,6 +121,8 @@ func (wr *ParachainWriter) WriteToParachainAndWatch(ctx context.Context, extrins return fmt.Errorf("parachain write status was dropped, invalid, usurped or finality timed out") } if status.IsFinalized { + log.WithFields(log.Fields{ + "extrinsic": extrinsicName, "block": status.AsFinalized}).Debug("extrinsic finalized") return nil } case err = <-sub.Err(): diff --git a/relayer/contracts/beefy_client.go b/relayer/contracts/beefy_client.go index 64fc8a1170..17a791c812 100644 --- a/relayer/contracts/beefy_client.go +++ b/relayer/contracts/beefy_client.go @@ -72,7 +72,7 @@ type BeefyClientValidatorSet struct { // BeefyClientMetaData contains all meta data concerning the BeefyClient contract. var BeefyClientMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_randaoCommitDelay\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_randaoCommitExpiration\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"InvalidBitfield\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidBitfieldLength\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidCommitment\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidMMRLeaf\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidMMRLeafProof\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidMMRRootLength\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSignature\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTicket\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidValidatorProof\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NoMMRRootInCommitment\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotEnoughClaims\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PrevRandaoAlreadyCaptured\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PrevRandaoNotCaptured\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ProofSizeExceeded\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"StaleCommitment\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TicketExpired\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnsupportedCompactEncoding\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"WaitPeriodNotOver\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"mmrRoot\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"blockNumber\",\"type\":\"uint64\"}],\"name\":\"NewMMRRoot\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"MMR_ROOT_ID\",\"outputs\":[{\"internalType\":\"bytes2\",\"name\":\"\",\"type\":\"bytes2\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"commitmentHash\",\"type\":\"bytes32\"}],\"name\":\"commitPrevRandao\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"commitmentHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256[]\",\"name\":\"bitfield\",\"type\":\"uint256[]\"}],\"name\":\"createFinalBitfield\",\"outputs\":[{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"bitsToSet\",\"type\":\"uint256[]\"},{\"internalType\":\"uint256\",\"name\":\"length\",\"type\":\"uint256\"}],\"name\":\"createInitialBitfield\",\"outputs\":[{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"currentValidatorSet\",\"outputs\":[{\"internalType\":\"uint128\",\"name\":\"id\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"length\",\"type\":\"uint128\"},{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"_initialBeefyBlock\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"uint128\",\"name\":\"id\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"length\",\"type\":\"uint128\"},{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"internalType\":\"structBeefyClient.ValidatorSet\",\"name\":\"_initialValidatorSet\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint128\",\"name\":\"id\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"length\",\"type\":\"uint128\"},{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"internalType\":\"structBeefyClient.ValidatorSet\",\"name\":\"_nextValidatorSet\",\"type\":\"tuple\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestBeefyBlock\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestMMRRoot\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"nextValidatorSet\",\"outputs\":[{\"internalType\":\"uint128\",\"name\":\"id\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"length\",\"type\":\"uint128\"},{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"randaoCommitDelay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"randaoCommitExpiration\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"blockNumber\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"validatorSetID\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes2\",\"name\":\"payloadID\",\"type\":\"bytes2\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"structBeefyClient.PayloadItem[]\",\"name\":\"payload\",\"type\":\"tuple[]\"}],\"internalType\":\"structBeefyClient.Commitment\",\"name\":\"commitment\",\"type\":\"tuple\"},{\"internalType\":\"uint256[]\",\"name\":\"bitfield\",\"type\":\"uint256[]\"},{\"components\":[{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"bytes32[]\",\"name\":\"proof\",\"type\":\"bytes32[]\"}],\"internalType\":\"structBeefyClient.ValidatorProof[]\",\"name\":\"proofs\",\"type\":\"tuple[]\"}],\"name\":\"submitFinal\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"blockNumber\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"validatorSetID\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes2\",\"name\":\"payloadID\",\"type\":\"bytes2\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"structBeefyClient.PayloadItem[]\",\"name\":\"payload\",\"type\":\"tuple[]\"}],\"internalType\":\"structBeefyClient.Commitment\",\"name\":\"commitment\",\"type\":\"tuple\"},{\"internalType\":\"uint256[]\",\"name\":\"bitfield\",\"type\":\"uint256[]\"},{\"components\":[{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"bytes32[]\",\"name\":\"proof\",\"type\":\"bytes32[]\"}],\"internalType\":\"structBeefyClient.ValidatorProof[]\",\"name\":\"proofs\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"},{\"internalType\":\"uint32\",\"name\":\"parentNumber\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"parentHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"nextAuthoritySetID\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"nextAuthoritySetLen\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"nextAuthoritySetRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"parachainHeadsRoot\",\"type\":\"bytes32\"}],\"internalType\":\"structBeefyClient.MMRLeaf\",\"name\":\"leaf\",\"type\":\"tuple\"},{\"internalType\":\"bytes32[]\",\"name\":\"leafProof\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint256\",\"name\":\"leafProofOrder\",\"type\":\"uint256\"}],\"name\":\"submitFinalWithHandover\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"blockNumber\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"validatorSetID\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes2\",\"name\":\"payloadID\",\"type\":\"bytes2\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"structBeefyClient.PayloadItem[]\",\"name\":\"payload\",\"type\":\"tuple[]\"}],\"internalType\":\"structBeefyClient.Commitment\",\"name\":\"commitment\",\"type\":\"tuple\"},{\"internalType\":\"uint256[]\",\"name\":\"bitfield\",\"type\":\"uint256[]\"},{\"components\":[{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"bytes32[]\",\"name\":\"proof\",\"type\":\"bytes32[]\"}],\"internalType\":\"structBeefyClient.ValidatorProof\",\"name\":\"proof\",\"type\":\"tuple\"}],\"name\":\"submitInitial\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"blockNumber\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"validatorSetID\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes2\",\"name\":\"payloadID\",\"type\":\"bytes2\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"structBeefyClient.PayloadItem[]\",\"name\":\"payload\",\"type\":\"tuple[]\"}],\"internalType\":\"structBeefyClient.Commitment\",\"name\":\"commitment\",\"type\":\"tuple\"},{\"internalType\":\"uint256[]\",\"name\":\"bitfield\",\"type\":\"uint256[]\"},{\"components\":[{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"bytes32[]\",\"name\":\"proof\",\"type\":\"bytes32[]\"}],\"internalType\":\"structBeefyClient.ValidatorProof\",\"name\":\"proof\",\"type\":\"tuple\"}],\"name\":\"submitInitialWithHandover\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"tickets\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"blockNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"validatorSetLen\",\"type\":\"uint32\"},{\"internalType\":\"uint256\",\"name\":\"prevRandao\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"bitfieldHash\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"leafHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"proof\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint256\",\"name\":\"proofOrder\",\"type\":\"uint256\"}],\"name\":\"verifyMMRLeafProof\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + ABI: "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_randaoCommitDelay\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_randaoCommitExpiration\",\"type\":\"uint256\"},{\"internalType\":\"uint64\",\"name\":\"_initialBeefyBlock\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"uint128\",\"name\":\"id\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"length\",\"type\":\"uint128\"},{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"internalType\":\"structBeefyClient.ValidatorSet\",\"name\":\"_initialValidatorSet\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint128\",\"name\":\"id\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"length\",\"type\":\"uint128\"},{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"internalType\":\"structBeefyClient.ValidatorSet\",\"name\":\"_nextValidatorSet\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"InvalidBitfield\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidBitfieldLength\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidCommitment\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidMMRLeaf\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidMMRLeafProof\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidMMRRootLength\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSignature\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTicket\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidValidatorProof\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NoMMRRootInCommitment\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotEnoughClaims\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PrevRandaoAlreadyCaptured\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PrevRandaoNotCaptured\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ProofSizeExceeded\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"StaleCommitment\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TicketExpired\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnsupportedCompactEncoding\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"WaitPeriodNotOver\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"mmrRoot\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"blockNumber\",\"type\":\"uint64\"}],\"name\":\"NewMMRRoot\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"MMR_ROOT_ID\",\"outputs\":[{\"internalType\":\"bytes2\",\"name\":\"\",\"type\":\"bytes2\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"commitmentHash\",\"type\":\"bytes32\"}],\"name\":\"commitPrevRandao\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"commitmentHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256[]\",\"name\":\"bitfield\",\"type\":\"uint256[]\"}],\"name\":\"createFinalBitfield\",\"outputs\":[{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"bitsToSet\",\"type\":\"uint256[]\"},{\"internalType\":\"uint256\",\"name\":\"length\",\"type\":\"uint256\"}],\"name\":\"createInitialBitfield\",\"outputs\":[{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"currentValidatorSet\",\"outputs\":[{\"internalType\":\"uint128\",\"name\":\"id\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"length\",\"type\":\"uint128\"},{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestBeefyBlock\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestMMRRoot\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"nextValidatorSet\",\"outputs\":[{\"internalType\":\"uint128\",\"name\":\"id\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"length\",\"type\":\"uint128\"},{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"randaoCommitDelay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"randaoCommitExpiration\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"blockNumber\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"validatorSetID\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes2\",\"name\":\"payloadID\",\"type\":\"bytes2\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"structBeefyClient.PayloadItem[]\",\"name\":\"payload\",\"type\":\"tuple[]\"}],\"internalType\":\"structBeefyClient.Commitment\",\"name\":\"commitment\",\"type\":\"tuple\"},{\"internalType\":\"uint256[]\",\"name\":\"bitfield\",\"type\":\"uint256[]\"},{\"components\":[{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"bytes32[]\",\"name\":\"proof\",\"type\":\"bytes32[]\"}],\"internalType\":\"structBeefyClient.ValidatorProof[]\",\"name\":\"proofs\",\"type\":\"tuple[]\"}],\"name\":\"submitFinal\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"blockNumber\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"validatorSetID\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes2\",\"name\":\"payloadID\",\"type\":\"bytes2\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"structBeefyClient.PayloadItem[]\",\"name\":\"payload\",\"type\":\"tuple[]\"}],\"internalType\":\"structBeefyClient.Commitment\",\"name\":\"commitment\",\"type\":\"tuple\"},{\"internalType\":\"uint256[]\",\"name\":\"bitfield\",\"type\":\"uint256[]\"},{\"components\":[{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"bytes32[]\",\"name\":\"proof\",\"type\":\"bytes32[]\"}],\"internalType\":\"structBeefyClient.ValidatorProof[]\",\"name\":\"proofs\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"},{\"internalType\":\"uint32\",\"name\":\"parentNumber\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"parentHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"nextAuthoritySetID\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"nextAuthoritySetLen\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"nextAuthoritySetRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"parachainHeadsRoot\",\"type\":\"bytes32\"}],\"internalType\":\"structBeefyClient.MMRLeaf\",\"name\":\"leaf\",\"type\":\"tuple\"},{\"internalType\":\"bytes32[]\",\"name\":\"leafProof\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint256\",\"name\":\"leafProofOrder\",\"type\":\"uint256\"}],\"name\":\"submitFinalWithHandover\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"blockNumber\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"validatorSetID\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes2\",\"name\":\"payloadID\",\"type\":\"bytes2\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"structBeefyClient.PayloadItem[]\",\"name\":\"payload\",\"type\":\"tuple[]\"}],\"internalType\":\"structBeefyClient.Commitment\",\"name\":\"commitment\",\"type\":\"tuple\"},{\"internalType\":\"uint256[]\",\"name\":\"bitfield\",\"type\":\"uint256[]\"},{\"components\":[{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"bytes32[]\",\"name\":\"proof\",\"type\":\"bytes32[]\"}],\"internalType\":\"structBeefyClient.ValidatorProof\",\"name\":\"proof\",\"type\":\"tuple\"}],\"name\":\"submitInitial\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"blockNumber\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"validatorSetID\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes2\",\"name\":\"payloadID\",\"type\":\"bytes2\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"structBeefyClient.PayloadItem[]\",\"name\":\"payload\",\"type\":\"tuple[]\"}],\"internalType\":\"structBeefyClient.Commitment\",\"name\":\"commitment\",\"type\":\"tuple\"},{\"internalType\":\"uint256[]\",\"name\":\"bitfield\",\"type\":\"uint256[]\"},{\"components\":[{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"bytes32[]\",\"name\":\"proof\",\"type\":\"bytes32[]\"}],\"internalType\":\"structBeefyClient.ValidatorProof\",\"name\":\"proof\",\"type\":\"tuple\"}],\"name\":\"submitInitialWithHandover\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"tickets\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"blockNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"validatorSetLen\",\"type\":\"uint32\"},{\"internalType\":\"uint256\",\"name\":\"prevRandao\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"bitfieldHash\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"leafHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"proof\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint256\",\"name\":\"proofOrder\",\"type\":\"uint256\"}],\"name\":\"verifyMMRLeafProof\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", } // BeefyClientABI is the input ABI used to generate the binding from. @@ -650,27 +650,6 @@ func (_BeefyClient *BeefyClientTransactorSession) CommitPrevRandao(commitmentHas return _BeefyClient.Contract.CommitPrevRandao(&_BeefyClient.TransactOpts, commitmentHash) } -// Initialize is a paid mutator transaction binding the contract method 0xe104815d. -// -// Solidity: function initialize(uint64 _initialBeefyBlock, (uint128,uint128,bytes32) _initialValidatorSet, (uint128,uint128,bytes32) _nextValidatorSet) returns() -func (_BeefyClient *BeefyClientTransactor) Initialize(opts *bind.TransactOpts, _initialBeefyBlock uint64, _initialValidatorSet BeefyClientValidatorSet, _nextValidatorSet BeefyClientValidatorSet) (*types.Transaction, error) { - return _BeefyClient.contract.Transact(opts, "initialize", _initialBeefyBlock, _initialValidatorSet, _nextValidatorSet) -} - -// Initialize is a paid mutator transaction binding the contract method 0xe104815d. -// -// Solidity: function initialize(uint64 _initialBeefyBlock, (uint128,uint128,bytes32) _initialValidatorSet, (uint128,uint128,bytes32) _nextValidatorSet) returns() -func (_BeefyClient *BeefyClientSession) Initialize(_initialBeefyBlock uint64, _initialValidatorSet BeefyClientValidatorSet, _nextValidatorSet BeefyClientValidatorSet) (*types.Transaction, error) { - return _BeefyClient.Contract.Initialize(&_BeefyClient.TransactOpts, _initialBeefyBlock, _initialValidatorSet, _nextValidatorSet) -} - -// Initialize is a paid mutator transaction binding the contract method 0xe104815d. -// -// Solidity: function initialize(uint64 _initialBeefyBlock, (uint128,uint128,bytes32) _initialValidatorSet, (uint128,uint128,bytes32) _nextValidatorSet) returns() -func (_BeefyClient *BeefyClientTransactorSession) Initialize(_initialBeefyBlock uint64, _initialValidatorSet BeefyClientValidatorSet, _nextValidatorSet BeefyClientValidatorSet) (*types.Transaction, error) { - return _BeefyClient.Contract.Initialize(&_BeefyClient.TransactOpts, _initialBeefyBlock, _initialValidatorSet, _nextValidatorSet) -} - // SubmitFinal is a paid mutator transaction binding the contract method 0x719321f0. // // Solidity: function submitFinal((uint32,uint64,(bytes2,bytes)[]) commitment, uint256[] bitfield, (uint8,bytes32,bytes32,uint256,address,bytes32[])[] proofs) returns() diff --git a/relayer/contracts/gateway.go b/relayer/contracts/gateway.go index d5629e8751..f9e382bea2 100644 --- a/relayer/contracts/gateway.go +++ b/relayer/contracts/gateway.go @@ -81,7 +81,7 @@ type VerificationProof struct { // GatewayMetaData contains all meta data concerning the Gateway contract. var GatewayMetaData = &bind.MetaData{ - ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"agentID\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"agent\",\"type\":\"address\"}],\"name\":\"AgentCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"agentID\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"AgentFundsWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"ParaID\",\"name\":\"paraID\",\"type\":\"uint256\"}],\"name\":\"ChannelCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"ParaID\",\"name\":\"paraID\",\"type\":\"uint256\"}],\"name\":\"ChannelUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"ParaID\",\"name\":\"origin\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"name\":\"InboundMessageDispatched\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"enumOperatingMode\",\"name\":\"mode\",\"type\":\"uint8\"}],\"name\":\"OperatingModeChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"ParaID\",\"name\":\"dest\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"payload\",\"type\":\"bytes\"}],\"name\":\"OutboundMessageAccepted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"implementation\",\"type\":\"address\"}],\"name\":\"Upgraded\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"agentID\",\"type\":\"bytes32\"}],\"name\":\"agentOf\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"ParaID\",\"name\":\"paraID\",\"type\":\"uint256\"}],\"name\":\"channelFeeRewardOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"ParaID\",\"name\":\"paraID\",\"type\":\"uint256\"}],\"name\":\"channelNoncesOf\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"ParaID\",\"name\":\"paraID\",\"type\":\"uint256\"}],\"name\":\"channelOperatingModeOf\",\"outputs\":[{\"internalType\":\"enumOperatingMode\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"implementation\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"operatingMode\",\"outputs\":[{\"internalType\":\"enumOperatingMode\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"registerNativeToken\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"recipient\",\"type\":\"bytes\"},{\"internalType\":\"uint128\",\"name\":\"amount\",\"type\":\"uint128\"}],\"name\":\"sendNativeToken\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"ParaID\",\"name\":\"origin\",\"type\":\"uint256\"},{\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"},{\"internalType\":\"enumCommand\",\"name\":\"command\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"params\",\"type\":\"bytes\"}],\"internalType\":\"structInboundMessage\",\"name\":\"message\",\"type\":\"tuple\"},{\"internalType\":\"bytes32[]\",\"name\":\"leafProof\",\"type\":\"bytes32[]\"},{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"parentHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"number\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"stateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"extrinsicsRoot\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"kind\",\"type\":\"uint256\"},{\"internalType\":\"bytes4\",\"name\":\"consensusEngineID\",\"type\":\"bytes4\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"structVerification.DigestItem[]\",\"name\":\"digestItems\",\"type\":\"tuple[]\"}],\"internalType\":\"structVerification.ParachainHeader\",\"name\":\"header\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"pos\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"width\",\"type\":\"uint256\"},{\"internalType\":\"bytes32[]\",\"name\":\"proof\",\"type\":\"bytes32[]\"}],\"internalType\":\"structVerification.HeadProof\",\"name\":\"headProof\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"},{\"internalType\":\"uint32\",\"name\":\"parentNumber\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"parentHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"nextAuthoritySetID\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"nextAuthoritySetLen\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"nextAuthoritySetRoot\",\"type\":\"bytes32\"}],\"internalType\":\"structVerification.MMRLeafPartial\",\"name\":\"leafPartial\",\"type\":\"tuple\"},{\"internalType\":\"bytes32[]\",\"name\":\"leafProof\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint256\",\"name\":\"leafProofOrder\",\"type\":\"uint256\"}],\"internalType\":\"structVerification.Proof\",\"name\":\"headerProof\",\"type\":\"tuple\"}],\"name\":\"submitInbound\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"agentID\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"agent\",\"type\":\"address\"}],\"name\":\"AgentCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"agentID\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"AgentFundsWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"ParaID\",\"name\":\"paraID\",\"type\":\"uint256\"}],\"name\":\"ChannelCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"ParaID\",\"name\":\"paraID\",\"type\":\"uint256\"}],\"name\":\"ChannelUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"ParaID\",\"name\":\"origin\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"name\":\"InboundMessageDispatched\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"enumOperatingMode\",\"name\":\"mode\",\"type\":\"uint8\"}],\"name\":\"OperatingModeChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"ParaID\",\"name\":\"destination\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"payload\",\"type\":\"bytes\"}],\"name\":\"OutboundMessageAccepted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"TokenRegistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"ParaID\",\"name\":\"destinationChain\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"destinationAddress\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint128\",\"name\":\"amount\",\"type\":\"uint128\"}],\"name\":\"TokenSent\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"implementation\",\"type\":\"address\"}],\"name\":\"Upgraded\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"agentID\",\"type\":\"bytes32\"}],\"name\":\"agentOf\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"ParaID\",\"name\":\"paraID\",\"type\":\"uint256\"}],\"name\":\"channelFeeRewardOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"ParaID\",\"name\":\"paraID\",\"type\":\"uint256\"}],\"name\":\"channelNoncesOf\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"ParaID\",\"name\":\"paraID\",\"type\":\"uint256\"}],\"name\":\"channelOperatingModeOf\",\"outputs\":[{\"internalType\":\"enumOperatingMode\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"implementation\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"operatingMode\",\"outputs\":[{\"internalType\":\"enumOperatingMode\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"registerToken\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"ParaID\",\"name\":\"destinationChain\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"destinationAddress\",\"type\":\"bytes32\"},{\"internalType\":\"uint128\",\"name\":\"amount\",\"type\":\"uint128\"}],\"name\":\"sendToken\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"ParaID\",\"name\":\"destinationChain\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"destinationAddress\",\"type\":\"address\"},{\"internalType\":\"uint128\",\"name\":\"amount\",\"type\":\"uint128\"}],\"name\":\"sendToken\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"ParaID\",\"name\":\"origin\",\"type\":\"uint256\"},{\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"},{\"internalType\":\"enumCommand\",\"name\":\"command\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"params\",\"type\":\"bytes\"}],\"internalType\":\"structInboundMessage\",\"name\":\"message\",\"type\":\"tuple\"},{\"internalType\":\"bytes32[]\",\"name\":\"leafProof\",\"type\":\"bytes32[]\"},{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"parentHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"number\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"stateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"extrinsicsRoot\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"kind\",\"type\":\"uint256\"},{\"internalType\":\"bytes4\",\"name\":\"consensusEngineID\",\"type\":\"bytes4\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"structVerification.DigestItem[]\",\"name\":\"digestItems\",\"type\":\"tuple[]\"}],\"internalType\":\"structVerification.ParachainHeader\",\"name\":\"header\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"pos\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"width\",\"type\":\"uint256\"},{\"internalType\":\"bytes32[]\",\"name\":\"proof\",\"type\":\"bytes32[]\"}],\"internalType\":\"structVerification.HeadProof\",\"name\":\"headProof\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"},{\"internalType\":\"uint32\",\"name\":\"parentNumber\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"parentHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"nextAuthoritySetID\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"nextAuthoritySetLen\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"nextAuthoritySetRoot\",\"type\":\"bytes32\"}],\"internalType\":\"structVerification.MMRLeafPartial\",\"name\":\"leafPartial\",\"type\":\"tuple\"},{\"internalType\":\"bytes32[]\",\"name\":\"leafProof\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint256\",\"name\":\"leafProofOrder\",\"type\":\"uint256\"}],\"internalType\":\"structVerification.Proof\",\"name\":\"headerProof\",\"type\":\"tuple\"}],\"name\":\"submitInbound\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", } // GatewayABI is the input ABI used to generate the binding from. @@ -418,46 +418,67 @@ func (_Gateway *GatewayCallerSession) OperatingMode() (uint8, error) { return _Gateway.Contract.OperatingMode(&_Gateway.CallOpts) } -// RegisterNativeToken is a paid mutator transaction binding the contract method 0x0830ce14. +// RegisterToken is a paid mutator transaction binding the contract method 0x09824a80. // -// Solidity: function registerNativeToken(address token) payable returns() -func (_Gateway *GatewayTransactor) RegisterNativeToken(opts *bind.TransactOpts, token common.Address) (*types.Transaction, error) { - return _Gateway.contract.Transact(opts, "registerNativeToken", token) +// Solidity: function registerToken(address token) payable returns() +func (_Gateway *GatewayTransactor) RegisterToken(opts *bind.TransactOpts, token common.Address) (*types.Transaction, error) { + return _Gateway.contract.Transact(opts, "registerToken", token) } -// RegisterNativeToken is a paid mutator transaction binding the contract method 0x0830ce14. +// RegisterToken is a paid mutator transaction binding the contract method 0x09824a80. // -// Solidity: function registerNativeToken(address token) payable returns() -func (_Gateway *GatewaySession) RegisterNativeToken(token common.Address) (*types.Transaction, error) { - return _Gateway.Contract.RegisterNativeToken(&_Gateway.TransactOpts, token) +// Solidity: function registerToken(address token) payable returns() +func (_Gateway *GatewaySession) RegisterToken(token common.Address) (*types.Transaction, error) { + return _Gateway.Contract.RegisterToken(&_Gateway.TransactOpts, token) } -// RegisterNativeToken is a paid mutator transaction binding the contract method 0x0830ce14. +// RegisterToken is a paid mutator transaction binding the contract method 0x09824a80. // -// Solidity: function registerNativeToken(address token) payable returns() -func (_Gateway *GatewayTransactorSession) RegisterNativeToken(token common.Address) (*types.Transaction, error) { - return _Gateway.Contract.RegisterNativeToken(&_Gateway.TransactOpts, token) +// Solidity: function registerToken(address token) payable returns() +func (_Gateway *GatewayTransactorSession) RegisterToken(token common.Address) (*types.Transaction, error) { + return _Gateway.Contract.RegisterToken(&_Gateway.TransactOpts, token) } -// SendNativeToken is a paid mutator transaction binding the contract method 0x1125e84b. +// SendToken is a paid mutator transaction binding the contract method 0x506acef5. // -// Solidity: function sendNativeToken(address token, bytes recipient, uint128 amount) payable returns() -func (_Gateway *GatewayTransactor) SendNativeToken(opts *bind.TransactOpts, token common.Address, recipient []byte, amount *big.Int) (*types.Transaction, error) { - return _Gateway.contract.Transact(opts, "sendNativeToken", token, recipient, amount) +// Solidity: function sendToken(address token, uint256 destinationChain, bytes32 destinationAddress, uint128 amount) payable returns() +func (_Gateway *GatewayTransactor) SendToken(opts *bind.TransactOpts, token common.Address, destinationChain *big.Int, destinationAddress [32]byte, amount *big.Int) (*types.Transaction, error) { + return _Gateway.contract.Transact(opts, "sendToken", token, destinationChain, destinationAddress, amount) } -// SendNativeToken is a paid mutator transaction binding the contract method 0x1125e84b. +// SendToken is a paid mutator transaction binding the contract method 0x506acef5. // -// Solidity: function sendNativeToken(address token, bytes recipient, uint128 amount) payable returns() -func (_Gateway *GatewaySession) SendNativeToken(token common.Address, recipient []byte, amount *big.Int) (*types.Transaction, error) { - return _Gateway.Contract.SendNativeToken(&_Gateway.TransactOpts, token, recipient, amount) +// Solidity: function sendToken(address token, uint256 destinationChain, bytes32 destinationAddress, uint128 amount) payable returns() +func (_Gateway *GatewaySession) SendToken(token common.Address, destinationChain *big.Int, destinationAddress [32]byte, amount *big.Int) (*types.Transaction, error) { + return _Gateway.Contract.SendToken(&_Gateway.TransactOpts, token, destinationChain, destinationAddress, amount) } -// SendNativeToken is a paid mutator transaction binding the contract method 0x1125e84b. +// SendToken is a paid mutator transaction binding the contract method 0x506acef5. // -// Solidity: function sendNativeToken(address token, bytes recipient, uint128 amount) payable returns() -func (_Gateway *GatewayTransactorSession) SendNativeToken(token common.Address, recipient []byte, amount *big.Int) (*types.Transaction, error) { - return _Gateway.Contract.SendNativeToken(&_Gateway.TransactOpts, token, recipient, amount) +// Solidity: function sendToken(address token, uint256 destinationChain, bytes32 destinationAddress, uint128 amount) payable returns() +func (_Gateway *GatewayTransactorSession) SendToken(token common.Address, destinationChain *big.Int, destinationAddress [32]byte, amount *big.Int) (*types.Transaction, error) { + return _Gateway.Contract.SendToken(&_Gateway.TransactOpts, token, destinationChain, destinationAddress, amount) +} + +// SendToken0 is a paid mutator transaction binding the contract method 0x960897bf. +// +// Solidity: function sendToken(address token, uint256 destinationChain, address destinationAddress, uint128 amount) payable returns() +func (_Gateway *GatewayTransactor) SendToken0(opts *bind.TransactOpts, token common.Address, destinationChain *big.Int, destinationAddress common.Address, amount *big.Int) (*types.Transaction, error) { + return _Gateway.contract.Transact(opts, "sendToken0", token, destinationChain, destinationAddress, amount) +} + +// SendToken0 is a paid mutator transaction binding the contract method 0x960897bf. +// +// Solidity: function sendToken(address token, uint256 destinationChain, address destinationAddress, uint128 amount) payable returns() +func (_Gateway *GatewaySession) SendToken0(token common.Address, destinationChain *big.Int, destinationAddress common.Address, amount *big.Int) (*types.Transaction, error) { + return _Gateway.Contract.SendToken0(&_Gateway.TransactOpts, token, destinationChain, destinationAddress, amount) +} + +// SendToken0 is a paid mutator transaction binding the contract method 0x960897bf. +// +// Solidity: function sendToken(address token, uint256 destinationChain, address destinationAddress, uint128 amount) payable returns() +func (_Gateway *GatewayTransactorSession) SendToken0(token common.Address, destinationChain *big.Int, destinationAddress common.Address, amount *big.Int) (*types.Transaction, error) { + return _Gateway.Contract.SendToken0(&_Gateway.TransactOpts, token, destinationChain, destinationAddress, amount) } // SubmitInbound is a paid mutator transaction binding the contract method 0x920ee69f. @@ -1407,23 +1428,23 @@ func (it *GatewayOutboundMessageAcceptedIterator) Close() error { // GatewayOutboundMessageAccepted represents a OutboundMessageAccepted event raised by the Gateway contract. type GatewayOutboundMessageAccepted struct { - Dest *big.Int - Nonce uint64 - Payload []byte - Raw types.Log // Blockchain specific contextual infos + Destination *big.Int + Nonce uint64 + Payload []byte + Raw types.Log // Blockchain specific contextual infos } // FilterOutboundMessageAccepted is a free log retrieval operation binding the contract event 0xd56f1b8dfd3ba41f19c499ceec5f9546f61befa5f10398a75d7dba53a219fece. // -// Solidity: event OutboundMessageAccepted(uint256 indexed dest, uint64 nonce, bytes payload) -func (_Gateway *GatewayFilterer) FilterOutboundMessageAccepted(opts *bind.FilterOpts, dest []*big.Int) (*GatewayOutboundMessageAcceptedIterator, error) { +// Solidity: event OutboundMessageAccepted(uint256 indexed destination, uint64 nonce, bytes payload) +func (_Gateway *GatewayFilterer) FilterOutboundMessageAccepted(opts *bind.FilterOpts, destination []*big.Int) (*GatewayOutboundMessageAcceptedIterator, error) { - var destRule []interface{} - for _, destItem := range dest { - destRule = append(destRule, destItem) + var destinationRule []interface{} + for _, destinationItem := range destination { + destinationRule = append(destinationRule, destinationItem) } - logs, sub, err := _Gateway.contract.FilterLogs(opts, "OutboundMessageAccepted", destRule) + logs, sub, err := _Gateway.contract.FilterLogs(opts, "OutboundMessageAccepted", destinationRule) if err != nil { return nil, err } @@ -1432,15 +1453,15 @@ func (_Gateway *GatewayFilterer) FilterOutboundMessageAccepted(opts *bind.Filter // WatchOutboundMessageAccepted is a free log subscription operation binding the contract event 0xd56f1b8dfd3ba41f19c499ceec5f9546f61befa5f10398a75d7dba53a219fece. // -// Solidity: event OutboundMessageAccepted(uint256 indexed dest, uint64 nonce, bytes payload) -func (_Gateway *GatewayFilterer) WatchOutboundMessageAccepted(opts *bind.WatchOpts, sink chan<- *GatewayOutboundMessageAccepted, dest []*big.Int) (event.Subscription, error) { +// Solidity: event OutboundMessageAccepted(uint256 indexed destination, uint64 nonce, bytes payload) +func (_Gateway *GatewayFilterer) WatchOutboundMessageAccepted(opts *bind.WatchOpts, sink chan<- *GatewayOutboundMessageAccepted, destination []*big.Int) (event.Subscription, error) { - var destRule []interface{} - for _, destItem := range dest { - destRule = append(destRule, destItem) + var destinationRule []interface{} + for _, destinationItem := range destination { + destinationRule = append(destinationRule, destinationItem) } - logs, sub, err := _Gateway.contract.WatchLogs(opts, "OutboundMessageAccepted", destRule) + logs, sub, err := _Gateway.contract.WatchLogs(opts, "OutboundMessageAccepted", destinationRule) if err != nil { return nil, err } @@ -1474,7 +1495,7 @@ func (_Gateway *GatewayFilterer) WatchOutboundMessageAccepted(opts *bind.WatchOp // ParseOutboundMessageAccepted is a log parse operation binding the contract event 0xd56f1b8dfd3ba41f19c499ceec5f9546f61befa5f10398a75d7dba53a219fece. // -// Solidity: event OutboundMessageAccepted(uint256 indexed dest, uint64 nonce, bytes payload) +// Solidity: event OutboundMessageAccepted(uint256 indexed destination, uint64 nonce, bytes payload) func (_Gateway *GatewayFilterer) ParseOutboundMessageAccepted(log types.Log) (*GatewayOutboundMessageAccepted, error) { event := new(GatewayOutboundMessageAccepted) if err := _Gateway.contract.UnpackLog(event, "OutboundMessageAccepted", log); err != nil { @@ -1484,6 +1505,277 @@ func (_Gateway *GatewayFilterer) ParseOutboundMessageAccepted(log types.Log) (*G return event, nil } +// GatewayTokenRegisteredIterator is returned from FilterTokenRegistered and is used to iterate over the raw logs and unpacked data for TokenRegistered events raised by the Gateway contract. +type GatewayTokenRegisteredIterator struct { + Event *GatewayTokenRegistered // 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 *GatewayTokenRegisteredIterator) 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(GatewayTokenRegistered) + 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(GatewayTokenRegistered) + 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 *GatewayTokenRegisteredIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *GatewayTokenRegisteredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// GatewayTokenRegistered represents a TokenRegistered event raised by the Gateway contract. +type GatewayTokenRegistered struct { + Token common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterTokenRegistered is a free log retrieval operation binding the contract event 0x158412daecdc1456d01568828bcdb18464cc7f1ce0215ddbc3f3cfede9d1e63d. +// +// Solidity: event TokenRegistered(address token) +func (_Gateway *GatewayFilterer) FilterTokenRegistered(opts *bind.FilterOpts) (*GatewayTokenRegisteredIterator, error) { + + logs, sub, err := _Gateway.contract.FilterLogs(opts, "TokenRegistered") + if err != nil { + return nil, err + } + return &GatewayTokenRegisteredIterator{contract: _Gateway.contract, event: "TokenRegistered", logs: logs, sub: sub}, nil +} + +// WatchTokenRegistered is a free log subscription operation binding the contract event 0x158412daecdc1456d01568828bcdb18464cc7f1ce0215ddbc3f3cfede9d1e63d. +// +// Solidity: event TokenRegistered(address token) +func (_Gateway *GatewayFilterer) WatchTokenRegistered(opts *bind.WatchOpts, sink chan<- *GatewayTokenRegistered) (event.Subscription, error) { + + logs, sub, err := _Gateway.contract.WatchLogs(opts, "TokenRegistered") + 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(GatewayTokenRegistered) + if err := _Gateway.contract.UnpackLog(event, "TokenRegistered", 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 +} + +// ParseTokenRegistered is a log parse operation binding the contract event 0x158412daecdc1456d01568828bcdb18464cc7f1ce0215ddbc3f3cfede9d1e63d. +// +// Solidity: event TokenRegistered(address token) +func (_Gateway *GatewayFilterer) ParseTokenRegistered(log types.Log) (*GatewayTokenRegistered, error) { + event := new(GatewayTokenRegistered) + if err := _Gateway.contract.UnpackLog(event, "TokenRegistered", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// GatewayTokenSentIterator is returned from FilterTokenSent and is used to iterate over the raw logs and unpacked data for TokenSent events raised by the Gateway contract. +type GatewayTokenSentIterator struct { + Event *GatewayTokenSent // 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 *GatewayTokenSentIterator) 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(GatewayTokenSent) + 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(GatewayTokenSent) + 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 *GatewayTokenSentIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *GatewayTokenSentIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// GatewayTokenSent represents a TokenSent event raised by the Gateway contract. +type GatewayTokenSent struct { + Token common.Address + DestinationChain *big.Int + DestinationAddress []byte + Amount *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterTokenSent is a free log retrieval operation binding the contract event 0xe1ae29e81ebd3cc6a74fb91c2d9f33e3d80313f1d106f2b24e62cac4bfc86d98. +// +// Solidity: event TokenSent(address token, uint256 destinationChain, bytes destinationAddress, uint128 amount) +func (_Gateway *GatewayFilterer) FilterTokenSent(opts *bind.FilterOpts) (*GatewayTokenSentIterator, error) { + + logs, sub, err := _Gateway.contract.FilterLogs(opts, "TokenSent") + if err != nil { + return nil, err + } + return &GatewayTokenSentIterator{contract: _Gateway.contract, event: "TokenSent", logs: logs, sub: sub}, nil +} + +// WatchTokenSent is a free log subscription operation binding the contract event 0xe1ae29e81ebd3cc6a74fb91c2d9f33e3d80313f1d106f2b24e62cac4bfc86d98. +// +// Solidity: event TokenSent(address token, uint256 destinationChain, bytes destinationAddress, uint128 amount) +func (_Gateway *GatewayFilterer) WatchTokenSent(opts *bind.WatchOpts, sink chan<- *GatewayTokenSent) (event.Subscription, error) { + + logs, sub, err := _Gateway.contract.WatchLogs(opts, "TokenSent") + 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(GatewayTokenSent) + if err := _Gateway.contract.UnpackLog(event, "TokenSent", 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 +} + +// ParseTokenSent is a log parse operation binding the contract event 0xe1ae29e81ebd3cc6a74fb91c2d9f33e3d80313f1d106f2b24e62cac4bfc86d98. +// +// Solidity: event TokenSent(address token, uint256 destinationChain, bytes destinationAddress, uint128 amount) +func (_Gateway *GatewayFilterer) ParseTokenSent(log types.Log) (*GatewayTokenSent, error) { + event := new(GatewayTokenSent) + if err := _Gateway.contract.UnpackLog(event, "TokenSent", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + // GatewayUpgradedIterator is returned from FilterUpgraded and is used to iterate over the raw logs and unpacked data for Upgraded events raised by the Gateway contract. type GatewayUpgradedIterator struct { Event *GatewayUpgraded // Event containing the contract specifics and raw log diff --git a/relayer/relays/execution/main.go b/relayer/relays/execution/main.go index d27e41fedb..2519b88837 100644 --- a/relayer/relays/execution/main.go +++ b/relayer/relays/execution/main.go @@ -97,6 +97,13 @@ func (r *Relay) Start(ctx context.Context, eg *errgroup.Group) error { return err } + if executionHeaderState.BlockNumber == 0 { + log.WithFields(log.Fields{ + "channelId": r.config.Source.ChannelID, + }).Info("Beacon execution state syncing not started, waiting...") + continue + } + ethNonce, err := r.fetchEthereumNonce(ctx, executionHeaderState.BlockNumber) if err != nil { return err @@ -114,17 +121,42 @@ func (r *Relay) Start(ctx context.Context, eg *errgroup.Group) error { } events, err := r.findEvents(ctx, executionHeaderState.BlockNumber, paraNonce+1) + if err != nil { + return fmt.Errorf("find events: %w", err) + } for _, ev := range events { inboundMsg, err := r.makeInboundMessage(ctx, headerCache, ev) if err != nil { return fmt.Errorf("make outgoing message: %w", err) } + logger := log.WithFields(log.Fields{ + "paraNonce": paraNonce, + "ethNonce": ethNonce, + "msgNonce": ev.Nonce, + "address": ev.Raw.Address.Hex(), + "blockHash": ev.Raw.BlockHash.Hex(), + "blockNumber": ev.Raw.BlockNumber, + "txHash": ev.Raw.TxHash.Hex(), + "dest": ev.Destination, + }) + + if ev.Nonce <= paraNonce { + logger.Warn("inbound message outdated, just skipped") + continue + } err = writer.WriteToParachainAndWatch(ctx, "EthereumInboundQueue.submit", inboundMsg) if err != nil { + logger.Error("inbound message fail to sent") return fmt.Errorf("write to parachain: %w", err) } + paraNonce, _ = r.fetchLatestParachainNonce() + if paraNonce != ev.Nonce { + logger.Error("inbound message sent but fail to execute") + return fmt.Errorf("inbound message fail to execute") + } + logger.Info("inbound message executed successfully") } } } @@ -240,9 +272,10 @@ func (r *Relay) findEventsWithFilter(opts *bind.FilterOpts, paraID uint32, start } break } - - events = append(events, iter.Event) - if iter.Event.Nonce == start { + if iter.Event.Nonce >= start { + events = append(events, iter.Event) + } + if iter.Event.Nonce == start && opts.Start != 0 { done = true iter.Close() break diff --git a/smoketest/tests/register_token.rs b/smoketest/tests/register_token.rs index 8101d36635..24c6ace8fc 100644 --- a/smoketest/tests/register_token.rs +++ b/smoketest/tests/register_token.rs @@ -52,7 +52,7 @@ async fn register_token() { let outbound_message_accepted_log = receipt.logs.last().unwrap(); // RLP-encode log and print it println!( - "receipt: {:?}", + "receipt: {:#?}", hex::encode(outbound_message_accepted_log.rlp_bytes()) ); diff --git a/smoketest/tests/send_tokens.rs b/smoketest/tests/send_tokens.rs index 3fc6b5b369..90a9b7d4ec 100644 --- a/smoketest/tests/send_tokens.rs +++ b/smoketest/tests/send_tokens.rs @@ -1,5 +1,5 @@ use hex_literal::hex; -use snowbridge_smoketest::contracts::{native_tokens, weth9}; +use snowbridge_smoketest::contracts::{i_gateway, weth9}; use std::{sync::Arc, time::Duration}; use ethers::{ @@ -10,17 +10,12 @@ use ethers::{ utils::parse_units, }; -use codec::Encode; - -use xcm::v3::{Junction, Junctions::X1, MultiLocation}; - // The deployment addresses of the following contracts are stable, unless we modify the order in // contracts are deployed in DeployScript.sol. const ETHEREUM_API: &str = "http://localhost:8545"; const ETHEREUM_KEY: &str = "0x5e002a1af63fd31f1c25258f3082dc889762664cb8f218d86da85dff8b07b342"; -const TOKEN_VAULT_CONTRACT: &str = "0xB8EA8cB425d85536b158d661da1ef0895Bb92F1D"; -const NATIVE_TOKENS_CONTRACT: &str = "0x8cF6147918A5CBb672703F879f385036f8793a24"; -const WETH_CONTRACT: &str = "0x440eDFFA1352B13227e8eE646f3Ea37456deC701"; +const GATEWAY_PROXY_CONTRACT: &str = "0xEDa338E4dC46038493b885327842fD3E301CaB39"; +const WETH_CONTRACT: &str = "0x87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d"; // SS58: DE14BzQ1bDXWPKeLoAqdLAm1GpyAWaWF1knF74cEZeomTBM const FERDIE: [u8; 32] = hex!("1cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c"); @@ -40,16 +35,14 @@ async fn send_tokens() { let client = SignerMiddleware::new(provider.clone(), wallet.clone()); let client = Arc::new(client); - let native_tokens_addr = NATIVE_TOKENS_CONTRACT.parse::
().unwrap(); - let native_tokens = native_tokens::NativeTokens::new(native_tokens_addr, client.clone()); - - let token_vault_addr = TOKEN_VAULT_CONTRACT.parse::
().unwrap(); + let gateway_addr = GATEWAY_PROXY_CONTRACT.parse::
().unwrap(); + let gateway = i_gateway::IGateway::new(gateway_addr, client.clone()); let weth_addr = WETH_CONTRACT.parse::
().unwrap(); let weth = weth9::WETH9::new(weth_addr, client.clone()); // Mint WETH tokens - let value = parse_units("10", "ether").unwrap(); + let value = parse_units("1", "ether").unwrap(); let receipt = weth .deposit() .value(value) @@ -62,7 +55,7 @@ async fn send_tokens() { assert_eq!(receipt.status.unwrap().as_u64(), 1u64); // Approve token spend - weth.approve(token_vault_addr, value.into()) + weth.approve(gateway_addr, value.into()) .send() .await .unwrap() @@ -71,19 +64,10 @@ async fn send_tokens() { .unwrap(); assert_eq!(receipt.status.unwrap().as_u64(), 1u64); - let recipient = MultiLocation { - parents: 0, - interior: X1(Junction::AccountId32 { - network: None, - id: FERDIE, - }), - } - .encode(); - // Lock tokens into vault let value1: u128 = U256::from(value).low_u128(); - native_tokens - .lock(weth.address(), 0, recipient.into(), value1) + let receipt = gateway + .send_token(weth.address(), 1000.into(), FERDIE, value1) .value(1000) .send() .await