diff --git a/l1-contracts/src/core/ProofCommitmentEscrow.sol b/l1-contracts/src/core/ProofCommitmentEscrow.sol index 7f63cd17aa3..a29f92ac0c4 100644 --- a/l1-contracts/src/core/ProofCommitmentEscrow.sol +++ b/l1-contracts/src/core/ProofCommitmentEscrow.sol @@ -13,11 +13,6 @@ import {Timestamp} from "@aztec/core/libraries/TimeMath.sol"; contract ProofCommitmentEscrow is IProofCommitmentEscrow { using SafeERC20 for IERC20; - struct Stake { - uint256 amount; - address prover; - } - struct WithdrawRequest { uint256 amount; Timestamp executableAt; @@ -29,7 +24,6 @@ contract ProofCommitmentEscrow is IProofCommitmentEscrow { mapping(address => uint256) public deposits; mapping(address => WithdrawRequest) public withdrawRequests; IERC20 public immutable token; - Stake public stake; modifier onlyRollup() { require(msg.sender == ROLLUP, Errors.ProofCommitmentEscrow__NotOwner(msg.sender)); @@ -103,9 +97,8 @@ contract ProofCommitmentEscrow is IProofCommitmentEscrow { * The prover must have sufficient balance * The prover's balance will be reduced by the bond amount */ - function stakeBond(uint256 _amount, address _prover) external override onlyRollup { + function stakeBond(address _prover, uint256 _amount) external override onlyRollup { deposits[_prover] -= _amount; - stake = Stake({amount: _amount, prover: _prover}); emit StakeBond(_prover, _amount); } @@ -115,9 +108,10 @@ contract ProofCommitmentEscrow is IProofCommitmentEscrow { * * @dev Only callable by the owner */ - function unstakeBond() external override onlyRollup { - deposits[stake.prover] += stake.amount; - delete stake; + function unstakeBond(address _prover, uint256 _amount) external override onlyRollup { + deposits[_prover] += _amount; + + emit UnstakeBond(_prover, _amount); } /** diff --git a/l1-contracts/src/core/Rollup.sol b/l1-contracts/src/core/Rollup.sol index e856da11b2e..de9e5c88ea7 100644 --- a/l1-contracts/src/core/Rollup.sol +++ b/l1-contracts/src/core/Rollup.sol @@ -117,16 +117,6 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup { return _hashTypedDataV4(EpochProofQuoteLib.hash(quote)); } - function verifySignedQuote(EpochProofQuoteLib.SignedEpochProofQuote memory signedQuote) - public - view - override(IRollup) - { - bytes32 digest = quoteToDigest(signedQuote.quote); - address recoveredSigner = ECDSA.recover(digest, SignatureLib.toBytes(signedQuote.signature)); - require(recoveredSigner == signedQuote.quote.prover); - } - /** * @notice Prune the pending chain up to the last proven block * @@ -358,7 +348,7 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup { // We don't currently unstake, // but we will as part of https://github.com/AztecProtocol/aztec-packages/issues/8652. // Blocked on submitting epoch proofs to this contract. - PROOF_COMMITMENT_ESCROW.stakeBond(_quote.quote.bondAmount, _quote.quote.prover); + PROOF_COMMITMENT_ESCROW.stakeBond(_quote.quote.prover, _quote.quote.bondAmount); proofClaim = DataStructures.EpochProofClaim({ epochToProve: epochToProve, @@ -586,7 +576,7 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup { view override(IRollup) { - verifySignedQuote(_quote); + SignatureLib.verify(_quote.signature, _quote.quote.prover, quoteToDigest(_quote.quote)); Slot currentSlot = getCurrentSlot(); address currentProposer = getCurrentProposer(); diff --git a/l1-contracts/src/core/interfaces/IProofCommitmentEscrow.sol b/l1-contracts/src/core/interfaces/IProofCommitmentEscrow.sol index e49547dd9fc..e844400fba5 100644 --- a/l1-contracts/src/core/interfaces/IProofCommitmentEscrow.sol +++ b/l1-contracts/src/core/interfaces/IProofCommitmentEscrow.sol @@ -9,12 +9,12 @@ interface IProofCommitmentEscrow { event StartWithdraw(address indexed withdrawer, uint256 amount, Timestamp executableAt); event ExecuteWithdraw(address indexed withdrawer, uint256 amount); event StakeBond(address indexed prover, uint256 amount); - event UnstakeBond(address indexed prover); + event UnstakeBond(address indexed prover, uint256 amount); function deposit(uint256 _amount) external; function startWithdraw(uint256 _amount) external; function executeWithdraw() external; - function stakeBond(uint256 _bondAmount, address _prover) external; - function unstakeBond() external; + function stakeBond(address _prover, uint256 _amount) external; + function unstakeBond(address _prover, uint256 _amount) external; function minBalanceAtTime(Timestamp _timestamp, address _prover) external view returns (uint256); } diff --git a/l1-contracts/src/core/interfaces/IRollup.sol b/l1-contracts/src/core/interfaces/IRollup.sol index 5b224bd522b..6aa9f616938 100644 --- a/l1-contracts/src/core/interfaces/IRollup.sol +++ b/l1-contracts/src/core/interfaces/IRollup.sol @@ -96,9 +96,6 @@ interface IRollup { external view returns (bytes32); - function verifySignedQuote(EpochProofQuoteLib.SignedEpochProofQuote calldata _quote) - external - view; function archive() external view returns (bytes32); function archiveAt(uint256 _blockNumber) external view returns (bytes32); diff --git a/l1-contracts/src/mock/MockProofCommitmentEscrow.sol b/l1-contracts/src/mock/MockProofCommitmentEscrow.sol index d4f1db360aa..4568f3392eb 100644 --- a/l1-contracts/src/mock/MockProofCommitmentEscrow.sol +++ b/l1-contracts/src/mock/MockProofCommitmentEscrow.sol @@ -18,11 +18,11 @@ contract MockProofCommitmentEscrow is IProofCommitmentEscrow { // do nothing } - function unstakeBond() external override { + function unstakeBond(address _prover, uint256 _amount) external override { // do nothing } - function stakeBond(uint256 _amount, address _prover) external override { + function stakeBond(address _prover, uint256 _amount) external override { // do nothing } diff --git a/l1-contracts/test/prover-coordination/ProofCommitmentEscrow.t.sol b/l1-contracts/test/prover-coordination/ProofCommitmentEscrow.t.sol index e0fcea53610..401fd67a2bc 100644 --- a/l1-contracts/test/prover-coordination/ProofCommitmentEscrow.t.sol +++ b/l1-contracts/test/prover-coordination/ProofCommitmentEscrow.t.sol @@ -13,51 +13,45 @@ import {TestERC20} from "../TestERC20.sol"; // solhint-disable comprehensive-interface contract TestProofCommitmentEscrow is Test { - ProofCommitmentEscrow internal _escrow; - TestERC20 internal _token; + // solhint-disable-next-line var-name-mixedcase + ProofCommitmentEscrow internal ESCROW; + // solhint-disable-next-line var-name-mixedcase + TestERC20 internal TOKEN; address internal prover; uint256 internal depositAmount; - modifier setup() { - _token = new TestERC20(); - _escrow = new ProofCommitmentEscrow(_token, address(this)); - _; - } - modifier setupWithApproval(address _prover, uint256 _depositAmount) { - _token = new TestERC20(); - _escrow = new ProofCommitmentEscrow(_token, address(this)); - - _token.mint(_prover, _depositAmount); + TOKEN.mint(_prover, _depositAmount); vm.prank(_prover); - _token.approve(address(_escrow), _depositAmount); + TOKEN.approve(address(ESCROW), _depositAmount); prover = _prover; depositAmount = _depositAmount; _; } + function setUp() public { + TOKEN = new TestERC20(); + ESCROW = new ProofCommitmentEscrow(TOKEN, address(this)); + } + function testDeposit() public setupWithApproval(address(42), 100) { vm.prank(prover); - _escrow.deposit(depositAmount); + ESCROW.deposit(depositAmount); assertEq( - _token.balanceOf(address(_escrow)), - depositAmount, - "Escrow balance should match deposit amount" + TOKEN.balanceOf(address(ESCROW)), depositAmount, "Escrow balance should match deposit amount" ); - assertEq(_token.balanceOf(prover), 0, "Prover balance should be 0 after deposit"); + assertEq(TOKEN.balanceOf(prover), 0, "Prover balance should be 0 after deposit"); } function testCannotWithdrawWithoutMatureRequest() public setupWithApproval(address(42), 100) { vm.prank(prover); - _escrow.deposit(depositAmount); - uint256 withdrawReadyAt = block.timestamp + _escrow.WITHDRAW_DELAY(); - - _mintAndDeposit(prover, depositAmount); + ESCROW.deposit(depositAmount); + uint256 withdrawReadyAt = block.timestamp + ESCROW.WITHDRAW_DELAY(); vm.prank(prover); - _escrow.startWithdraw(depositAmount); + ESCROW.startWithdraw(depositAmount); vm.prank(prover); vm.expectRevert( @@ -67,9 +61,9 @@ contract TestProofCommitmentEscrow is Test { withdrawReadyAt ) ); - _escrow.executeWithdraw(); + ESCROW.executeWithdraw(); - vm.warp(block.timestamp + _escrow.WITHDRAW_DELAY() - 1); + vm.warp(block.timestamp + ESCROW.WITHDRAW_DELAY() - 1); vm.prank(prover); vm.expectRevert( abi.encodeWithSelector( @@ -78,115 +72,108 @@ contract TestProofCommitmentEscrow is Test { withdrawReadyAt ) ); - _escrow.executeWithdraw(); + ESCROW.executeWithdraw(); } function testWithdrawAfterDelay() public setupWithApproval(address(42), 100) { vm.prank(prover); - _escrow.deposit(depositAmount); + ESCROW.deposit(depositAmount); uint256 withdrawAmount = 50; - uint256 withdrawReadyAt = block.timestamp + _escrow.WITHDRAW_DELAY(); - - _mintAndDeposit(prover, depositAmount); + uint256 withdrawReadyAt = block.timestamp + ESCROW.WITHDRAW_DELAY(); vm.prank(prover); - _escrow.startWithdraw(withdrawAmount); + ESCROW.startWithdraw(withdrawAmount); vm.warp(withdrawReadyAt); vm.prank(prover); - _escrow.executeWithdraw(); + ESCROW.executeWithdraw(); assertEq( - _token.balanceOf(address(_escrow)), + TOKEN.balanceOf(address(ESCROW)), depositAmount - withdrawAmount, "Escrow balance should be reduced after withdrawal" ); - assertEq(_token.balanceOf(prover), withdrawAmount, "Prover balance should match deposit amount"); + assertEq(TOKEN.balanceOf(prover), withdrawAmount, "Prover balance should match deposit amount"); } - function testCannotReplayWithdrawRequest() public setupWithApproval(address(42), 100) { + function testCannotReplayWithdrawRequest(uint256 _withdrawAmount) + public + setupWithApproval(address(42), 100) + { vm.prank(prover); - _escrow.deposit(depositAmount); - uint256 withdrawAmount = 50; - uint256 withdrawReadyAt = block.timestamp + _escrow.WITHDRAW_DELAY(); - - _mintAndDeposit(prover, depositAmount); + ESCROW.deposit(depositAmount); + uint256 withdrawAmount = bound(_withdrawAmount, 1, depositAmount); + uint256 withdrawReadyAt = block.timestamp + ESCROW.WITHDRAW_DELAY(); vm.prank(prover); - _escrow.startWithdraw(withdrawAmount); + ESCROW.startWithdraw(withdrawAmount); vm.warp(withdrawReadyAt); vm.prank(prover); - _escrow.executeWithdraw(); + ESCROW.executeWithdraw(); vm.prank(prover); - _escrow.executeWithdraw(); + ESCROW.executeWithdraw(); assertEq( - _token.balanceOf(address(_escrow)), + TOKEN.balanceOf(address(ESCROW)), depositAmount - withdrawAmount, "Escrow balance should be reduced after withdrawal" ); } - function testOnlyOwnerCanStake(address nonOwner) public setup { + function testOnlyOwnerCanStake(address nonOwner) public { vm.assume(nonOwner != address(this)); vm.prank(nonOwner); vm.expectRevert( abi.encodeWithSelector(Errors.ProofCommitmentEscrow__NotOwner.selector, nonOwner) ); - _escrow.stakeBond(0, address(0)); + ESCROW.stakeBond(address(0), 0); } function testCannotStakeMoreThanProverBalance() public setupWithApproval(address(42), 100) { vm.prank(prover); - _escrow.deposit(depositAmount); + ESCROW.deposit(depositAmount); uint256 stakeAmount = depositAmount + 1; - _mintAndDeposit(prover, depositAmount); - vm.expectRevert(); - _escrow.stakeBond(stakeAmount, prover); + ESCROW.stakeBond(prover, stakeAmount); assertEq( - _token.balanceOf(address(_escrow)), - depositAmount, - "Escrow balance should match deposit amount" + TOKEN.balanceOf(address(ESCROW)), depositAmount, "Escrow balance should match deposit amount" ); - assertEq(_escrow.deposits(prover), depositAmount, "Prover balance should match deposit amount"); + assertEq(ESCROW.deposits(prover), depositAmount, "Prover balance should match deposit amount"); } - function testOnlyOwnerCanUnstake(address nonOwner) public setup { + function testOnlyOwnerCanUnstake(address nonOwner) public { vm.assume(nonOwner != address(this)); vm.prank(nonOwner); vm.expectRevert( abi.encodeWithSelector(Errors.ProofCommitmentEscrow__NotOwner.selector, nonOwner) ); - _escrow.unstakeBond(); + ESCROW.unstakeBond(address(0), 0); } function testStakeAndUnstake() public setupWithApproval(address(42), 100) { vm.prank(prover); - _escrow.deposit(depositAmount); + ESCROW.deposit(depositAmount); uint256 stakeAmount = 50; - _mintAndDeposit(prover, depositAmount); - - _escrow.stakeBond(stakeAmount, prover); + ESCROW.stakeBond(prover, stakeAmount); assertEq( - _escrow.deposits(prover), depositAmount - stakeAmount, "Prover balance should be reduced" + ESCROW.deposits(prover), depositAmount - stakeAmount, "Prover balance should be reduced" ); - _escrow.unstakeBond(); + ESCROW.unstakeBond(prover, stakeAmount); assertEq( - _escrow.deposits(prover), depositAmount, "Prover balance should be restored after unstake" + ESCROW.deposits(prover), depositAmount, "Prover balance should be restored after unstake" ); } - function testOverwritingStakeSlashesPreviousProver() public setup { + function testOverwritingStakeSlashesPreviousProver() public { address proverA = address(42); address proverB = address(43); uint256 depositAmountA = 100; @@ -194,64 +181,62 @@ contract TestProofCommitmentEscrow is Test { uint256 depositAmountB = 200; uint256 stakeAmountB = 100; - _token.mint(proverA, depositAmountA); + TOKEN.mint(proverA, depositAmountA); vm.prank(proverA); - _token.approve(address(_escrow), depositAmountA); + TOKEN.approve(address(ESCROW), depositAmountA); vm.prank(proverA); - _escrow.deposit(depositAmountA); + ESCROW.deposit(depositAmountA); - _token.mint(proverB, depositAmountB); + TOKEN.mint(proverB, depositAmountB); vm.prank(proverB); - _token.approve(address(_escrow), depositAmountB); + TOKEN.approve(address(ESCROW), depositAmountB); vm.prank(proverB); - _escrow.deposit(depositAmountB); + ESCROW.deposit(depositAmountB); - // Prover A deposits and is staked - _escrow.stakeBond(stakeAmountA, proverA); + // Prover A is staked + ESCROW.stakeBond(proverA, stakeAmountA); - // Prover B deposits and owner overwrites the stake - _escrow.stakeBond(stakeAmountB, proverB); + // Prover B is staked + ESCROW.stakeBond(proverB, stakeAmountB); - // Prover A cannot recover the staked amount + // Prover A is missing the stake uint256 expectedDepositA = depositAmountA - stakeAmountA; assertEq( - _escrow.deposits(proverA), + ESCROW.deposits(proverA), expectedDepositA, "Prover A's deposit should reflect the slashed stake" ); - // Owner cannot unstake Prover A's stake anymore - _escrow.unstakeBond(); + // Prover B gets unstaked + ESCROW.unstakeBond(proverB, stakeAmountB); assertEq( - _escrow.deposits(proverB), + ESCROW.deposits(proverB), depositAmountB, "Prover B's deposit should be restored after unstake" ); assertEq( - _escrow.deposits(proverA), - expectedDepositA, - "Prover A's deposit remains slashed after unstake" + ESCROW.deposits(proverA), expectedDepositA, "Prover A's deposit remains slashed after unstake" ); } function testWithdrawRequestOverwriting() public setupWithApproval(address(42), 100) { uint256 withdrawAmountA = 40; uint256 withdrawAmountB = 60; - uint256 withdrawReadyAtA = block.timestamp + _escrow.WITHDRAW_DELAY(); - uint256 withdrawReadyAtB = block.timestamp + 2 * _escrow.WITHDRAW_DELAY(); + uint256 withdrawReadyAtA = block.timestamp + ESCROW.WITHDRAW_DELAY(); + uint256 withdrawReadyAtB = block.timestamp + 2 * ESCROW.WITHDRAW_DELAY(); vm.prank(prover); - _escrow.deposit(depositAmount); + ESCROW.deposit(depositAmount); // Prover starts first withdraw request vm.prank(prover); - _escrow.startWithdraw(withdrawAmountA); + ESCROW.startWithdraw(withdrawAmountA); // Prover starts second withdraw request before executing first vm.warp(withdrawReadyAtA); vm.prank(prover); - _escrow.startWithdraw(withdrawAmountB); + ESCROW.startWithdraw(withdrawAmountB); // Attempt to execute first withdraw request after its delay vm.prank(prover); @@ -262,51 +247,51 @@ contract TestProofCommitmentEscrow is Test { withdrawReadyAtB ) ); - _escrow.executeWithdraw(); + ESCROW.executeWithdraw(); // Execute second withdraw request after its delay vm.warp(withdrawReadyAtB); vm.prank(prover); - _escrow.executeWithdraw(); + ESCROW.executeWithdraw(); // Assert assertEq( - _escrow.deposits(prover), + ESCROW.deposits(prover), depositAmount - withdrawAmountB, "Prover's deposit should be reduced by the withdrawn amount" ); } - function testMinBalanceAtSlot() public setupWithApproval(address(42), 100) { + function testMinBalanceAtTime() public setupWithApproval(address(42), 100) { uint256 withdrawAmount = 25; - Timestamp withdrawReadyAt = Timestamp.wrap(block.timestamp + _escrow.WITHDRAW_DELAY()); + Timestamp withdrawReadyAt = Timestamp.wrap(block.timestamp + ESCROW.WITHDRAW_DELAY()); vm.prank(prover); - _escrow.deposit(depositAmount); + ESCROW.deposit(depositAmount); assertEq( - _escrow.minBalanceAtTime(Timestamp.wrap(block.timestamp), prover), + ESCROW.minBalanceAtTime(Timestamp.wrap(block.timestamp), prover), depositAmount, "Min balance should match deposit amount before any withdraw request" ); assertEq( - _escrow.minBalanceAtTime(withdrawReadyAt - Timestamp.wrap(1), prover), + ESCROW.minBalanceAtTime(withdrawReadyAt - Timestamp.wrap(1), prover), depositAmount, "Min balance should match deposit amount before withdraw request matures" ); vm.prank(prover); - _escrow.startWithdraw(withdrawAmount); + ESCROW.startWithdraw(withdrawAmount); assertEq( - _escrow.minBalanceAtTime(Timestamp.wrap(block.timestamp), prover), + ESCROW.minBalanceAtTime(Timestamp.wrap(block.timestamp), prover), depositAmount, "Min balance should be unaffected by pending withdraw request before maturity" ); assertEq( - _escrow.minBalanceAtTime(Timestamp.wrap(block.timestamp + _escrow.WITHDRAW_DELAY()), prover), + ESCROW.minBalanceAtTime(Timestamp.wrap(block.timestamp + ESCROW.WITHDRAW_DELAY()), prover), 0, "Min balance should be 0 at or beyond the delay window" ); @@ -314,17 +299,15 @@ contract TestProofCommitmentEscrow is Test { vm.warp(block.timestamp + 1); assertEq( - _escrow.minBalanceAtTime(withdrawReadyAt, prover), + ESCROW.minBalanceAtTime(withdrawReadyAt, prover), depositAmount - withdrawAmount, "Min balance should be 75 after withdraw request matures" ); assertEq( - _escrow.minBalanceAtTime(withdrawReadyAt + Timestamp.wrap(1), prover), + ESCROW.minBalanceAtTime(withdrawReadyAt + Timestamp.wrap(1), prover), 0, "Min balance should be 0 at or beyond the delay window" ); } - - function _mintAndDeposit(address _prover, uint256 _amount) internal {} } diff --git a/yarn-project/circuit-types/src/prover_coordination/epoch_proof_quote.ts b/yarn-project/circuit-types/src/prover_coordination/epoch_proof_quote.ts index 43824aaf105..7bfd6070bd2 100644 --- a/yarn-project/circuit-types/src/prover_coordination/epoch_proof_quote.ts +++ b/yarn-project/circuit-types/src/prover_coordination/epoch_proof_quote.ts @@ -32,6 +32,7 @@ export class EpochProofQuote extends Gossipable { return new EpochProofQuote(reader.readObject(EpochProofQuotePayload), reader.readObject(Signature)); } + // TODO: https://github.com/AztecProtocol/aztec-packages/issues/8911 /** * Creates a new quote with a signature. * The digest provided must match what the rollup contract will produce i.e. `_hashTypedDataV4(EpochProofQuoteLib.hash(quote))` diff --git a/yarn-project/end-to-end/src/prover-coordination/e2e_quote_signing.test.ts b/yarn-project/end-to-end/src/prover-coordination/e2e_quote_signing.test.ts deleted file mode 100644 index 5dbdffcf3d7..00000000000 --- a/yarn-project/end-to-end/src/prover-coordination/e2e_quote_signing.test.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { EpochProofQuote, EpochProofQuotePayload } from '@aztec/aztec.js'; -import { EthAddress } from '@aztec/circuits.js'; -import { Buffer32 } from '@aztec/foundation/buffer'; -import { Secp256k1Signer, keccak256 } from '@aztec/foundation/crypto'; -import { RollupAbi } from '@aztec/l1-artifacts'; - -import { beforeAll } from '@jest/globals'; -import { - type Chain, - type GetContractReturnType, - type HttpTransport, - type PublicClient, - getAddress, - getContract, -} from 'viem'; - -import { type ISnapshotManager, type SubsystemsContext, createSnapshotManager } from '../fixtures/snapshot_manager.js'; - -/** - * Tests the creation of epoch proof quotes and their validation on L1 - */ -describe('e2e_quote_signature_validation', () => { - let ctx: SubsystemsContext; - let rollupContract: GetContractReturnType>; - - // let logger: DebugLogger; - let snapshotManager: ISnapshotManager; - - beforeAll(async () => { - snapshotManager = createSnapshotManager(`prover_coordination/e2e_quote_signing`, process.env.E2E_DATA_PATH); - - ctx = await snapshotManager.setup(); - - await ctx.proverNode.stop(); - - rollupContract = getContract({ - address: getAddress(ctx.deployL1ContractsValues.l1ContractAddresses.rollupAddress.toString()), - abi: RollupAbi, - client: ctx.deployL1ContractsValues.walletClient, - }); - }); - - it('can get domain information from the rollup', async () => { - const [, name, version, chainId, address] = await rollupContract.read.eip712Domain(); - expect(name).toBe('Aztec Rollup'); - expect(version).toBe('1'); - expect(chainId).toBe(31337n); - expect(address).toBe(rollupContract.address); - }); - - it('can verify a signed quote on L1', async () => { - const payload = EpochProofQuotePayload.fromFields({ - epochToProve: 42n, - validUntilSlot: 100n, - bondAmount: 1000000000000000000n, - prover: EthAddress.fromString('0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826'), - basisPointFee: 5000, - }); - - const signer = new Secp256k1Signer(Buffer32.fromBuffer(keccak256(Buffer.from('cow')))); - const digest = await rollupContract.read.quoteToDigest([payload.toViemArgs()]); - const quote = EpochProofQuote.new(Buffer32.fromString(digest), payload, signer); - - await rollupContract.read.verifySignedQuote([quote.toViemArgs()]); - }); -});