diff --git a/package.json b/package.json index 0278ac70..18fd7558 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "lint-solidity": "yarn solhint ./contracts/**/*.sol", "lint-js": "yarn prettier --list-different && yarn lint-rust", "lint-rust": "cargo fmt --all -- --check && cargo clippy", - "lint-fix": "yarn prettier --write", + "lint-fix": "yarn prettier --write && cargo fmt --all", "prettier": "prettier .", "clean-fast": "for dir in node_modules cache cache-zk artifacts artifacts-zk dist typechain; do mv \"${dir}\" \"_${dir}\"; rm -rf \"_${dir}\" &; done", "clean": "rm -rf node_modules cache cache-zk artifacts artifacts-zk dist typechain", diff --git a/programs/svm-spoke/src/event.rs b/programs/svm-spoke/src/event.rs new file mode 100644 index 00000000..428e0bc7 --- /dev/null +++ b/programs/svm-spoke/src/event.rs @@ -0,0 +1,107 @@ +use anchor_lang::prelude::*; + +// Admin events +#[event] +pub struct SetXDomainAdmin { + pub new_admin: Pubkey, +} + +#[event] +pub struct PausedDeposits { + pub is_paused: bool, +} + +#[event] +pub struct PausedFills { + pub is_paused: bool, +} + +#[event] +pub struct EnabledDepositRoute { + pub origin_token: Pubkey, + pub destination_chain_id: u64, + pub enabled: bool, +} + +// Deposit events +#[event] +pub struct V3FundsDeposited { + pub input_token: Pubkey, + pub output_token: Pubkey, + pub input_amount: u64, + pub output_amount: u64, + pub destination_chain_id: u64, + pub deposit_id: u32, + pub quote_timestamp: u32, + pub fill_deadline: u32, + pub exclusivity_deadline: u32, + pub depositor: Pubkey, + pub recipient: Pubkey, + pub exclusive_relayer: Pubkey, + pub message: Vec, +} + +// Fill events +#[derive(AnchorSerialize, AnchorDeserialize, Clone, PartialEq)] +pub enum FillType { + FastFill, + ReplacedSlowFill, + SlowFill, +} + +#[derive(AnchorSerialize, AnchorDeserialize, Clone)] +pub struct V3RelayExecutionEventInfo { + pub updated_recipient: Pubkey, + pub updated_message: Vec, + pub updated_output_amount: u64, + pub fill_type: FillType, +} + +#[event] +pub struct FilledV3Relay { + pub input_token: Pubkey, + pub output_token: Pubkey, + pub input_amount: u64, + pub output_amount: u64, + pub repayment_chain_id: u64, + pub origin_chain_id: u64, + pub deposit_id: u32, + pub fill_deadline: u32, + pub exclusivity_deadline: u32, + pub exclusive_relayer: Pubkey, + pub relayer: Pubkey, + pub depositor: Pubkey, + pub recipient: Pubkey, + pub message: Vec, + pub relay_execution_info: V3RelayExecutionEventInfo, +} + +// Slow fill events +#[event] +pub struct RequestedV3SlowFill { + pub input_token: Pubkey, + pub output_token: Pubkey, + pub input_amount: u64, + pub output_amount: u64, + pub origin_chain_id: u64, + pub deposit_id: u32, + pub fill_deadline: u32, + pub exclusivity_deadline: u32, + pub exclusive_relayer: Pubkey, + pub depositor: Pubkey, + pub recipient: Pubkey, + pub message: Vec, +} + +// Relayer refund events +#[event] +pub struct ExecutedRelayerRefundRoot { + pub amount_to_return: u64, + pub chain_id: u64, + pub refund_amounts: Vec, + pub root_bundle_id: u32, + pub leaf_id: u32, + pub l2_token_address: Pubkey, + pub refund_addresses: Vec, + pub caller: Pubkey, +} diff --git a/programs/svm-spoke/src/instructions/admin.rs b/programs/svm-spoke/src/instructions/admin.rs index cf35de45..39a683a5 100644 --- a/programs/svm-spoke/src/instructions/admin.rs +++ b/programs/svm-spoke/src/instructions/admin.rs @@ -8,6 +8,7 @@ use crate::constraints::is_local_or_remote_owner; use crate::{ error::CustomError, + event::{EnabledDepositRoute, PausedDeposits, PausedFills, SetXDomainAdmin}, state::{RootBundle, Route, State}, }; @@ -32,7 +33,7 @@ pub struct Initialize<'info> { pub fn initialize( ctx: Context, seed: u64, - initial_number_of_deposits: u64, + initial_number_of_deposits: u32, chain_id: u64, // Across definition of chainId for Solana. remote_domain: u32, // CCTP domain for Mainnet Ethereum. cross_domain_admin: Pubkey, // HubPool on Mainnet Ethereum. @@ -239,24 +240,3 @@ pub fn relay_root_bundle( state.root_bundle_id += 1; Ok(()) } -#[event] -pub struct SetXDomainAdmin { - pub new_admin: Pubkey, -} - -#[event] -pub struct PausedDeposits { - pub is_paused: bool, -} - -#[event] -pub struct PausedFills { - pub is_paused: bool, -} - -#[event] -pub struct EnabledDepositRoute { - pub origin_token: Pubkey, - pub destination_chain_id: u64, - pub enabled: bool, -} diff --git a/programs/svm-spoke/src/instructions/bundle.rs b/programs/svm-spoke/src/instructions/bundle.rs index 23bc7bfc..25306bbe 100644 --- a/programs/svm-spoke/src/instructions/bundle.rs +++ b/programs/svm-spoke/src/instructions/bundle.rs @@ -4,6 +4,7 @@ use anchor_lang::solana_program::keccak; use crate::{ constants::DISCRIMINATOR_SIZE, error::CustomError, + event::ExecutedRelayerRefundRoot, state::{RootBundle, State, TransferLiability}, utils::{is_claimed, set_claimed, verify_merkle_proof}, }; @@ -12,6 +13,7 @@ use anchor_spl::token_interface::{ transfer_checked, Mint, TokenAccount, TokenInterface, TransferChecked, }; +#[event_cpi] #[derive(Accounts)] #[instruction(root_bundle_id: u32, relayer_refund_leaf: RelayerRefundLeaf)] pub struct ExecuteRelayerRefundLeaf<'info> { @@ -162,5 +164,17 @@ pub fn execute_relayer_refund_leaf<'info>( ctx.accounts.transfer_liability.pending_to_hub_pool += relayer_refund_leaf.amount_to_return; } + // Emit the ExecutedRelayerRefundRoot event + emit_cpi!(ExecutedRelayerRefundRoot { + amount_to_return: relayer_refund_leaf.amount_to_return, + chain_id: relayer_refund_leaf.chain_id, + refund_amounts: relayer_refund_leaf.refund_amounts, + root_bundle_id, + leaf_id: relayer_refund_leaf.leaf_id, + l2_token_address: ctx.accounts.mint.key(), + refund_addresses: relayer_refund_leaf.refund_accounts, + caller: ctx.accounts.signer.key(), + }); + Ok(()) } diff --git a/programs/svm-spoke/src/instructions/deposit.rs b/programs/svm-spoke/src/instructions/deposit.rs index ee9051ea..78bbdef3 100644 --- a/programs/svm-spoke/src/instructions/deposit.rs +++ b/programs/svm-spoke/src/instructions/deposit.rs @@ -2,6 +2,7 @@ use anchor_lang::prelude::*; use crate::{ error::CustomError, + event::V3FundsDeposited, state::{Route, State}, }; @@ -122,20 +123,3 @@ pub fn deposit_v3( Ok(()) } - -#[event] -pub struct V3FundsDeposited { - pub input_token: Pubkey, - pub output_token: Pubkey, - pub input_amount: u64, - pub output_amount: u64, - pub destination_chain_id: u64, - pub deposit_id: u64, - pub quote_timestamp: u32, - pub fill_deadline: u32, - pub exclusivity_deadline: u32, - pub depositor: Pubkey, - pub recipient: Pubkey, - pub exclusive_relayer: Pubkey, - pub message: Vec, -} diff --git a/programs/svm-spoke/src/instructions/fill.rs b/programs/svm-spoke/src/instructions/fill.rs index 1b001d90..a7cec49a 100644 --- a/programs/svm-spoke/src/instructions/fill.rs +++ b/programs/svm-spoke/src/instructions/fill.rs @@ -9,6 +9,7 @@ use crate::{ constants::DISCRIMINATOR_SIZE, constraints::is_relay_hash_valid, error::CustomError, + event::{FillType, FilledV3Relay, V3RelayExecutionEventInfo}, state::{FillStatus, FillStatusAccount, State}, }; @@ -91,13 +92,6 @@ pub struct V3RelayData { pub message: Vec, } -#[derive(AnchorSerialize, AnchorDeserialize, Clone, PartialEq)] -pub enum FillType { - FastFill, - ReplacedSlowFill, - SlowFill, -} - pub fn fill_v3_relay( ctx: Context, relay_hash: [u8; 32], // include in props, while not using it, to enable us to access it from the #Instruction Attribute within the accounts. This enables us to pass in the relay_hash PDA. @@ -238,29 +232,3 @@ pub fn close_fill_pda( } // Events. -#[derive(AnchorSerialize, AnchorDeserialize, Clone)] -pub struct V3RelayExecutionEventInfo { - pub updated_recipient: Pubkey, - pub updated_message: Vec, - pub updated_output_amount: u64, - pub fill_type: FillType, -} - -#[event] -pub struct FilledV3Relay { - pub input_token: Pubkey, - pub output_token: Pubkey, - pub input_amount: u64, - pub output_amount: u64, - pub repayment_chain_id: u64, - pub origin_chain_id: u64, - pub deposit_id: u32, - pub fill_deadline: u32, - pub exclusivity_deadline: u32, - pub exclusive_relayer: Pubkey, - pub relayer: Pubkey, - pub depositor: Pubkey, - pub recipient: Pubkey, - pub message: Vec, - pub relay_execution_info: V3RelayExecutionEventInfo, -} diff --git a/programs/svm-spoke/src/instructions/slow_fill.rs b/programs/svm-spoke/src/instructions/slow_fill.rs index 08bde24b..6db354f6 100644 --- a/programs/svm-spoke/src/instructions/slow_fill.rs +++ b/programs/svm-spoke/src/instructions/slow_fill.rs @@ -15,7 +15,8 @@ use crate::{ }; // TODO: We can likely move some of the common exports to better locations. we are pulling a lot of these from fill.rs -use crate::{FillType, FilledV3Relay, V3RelayData, V3RelayExecutionEventInfo}; +use crate::event::{FillType, FilledV3Relay, RequestedV3SlowFill, V3RelayExecutionEventInfo}; +use crate::V3RelayData; // Pulled type definition from fill.rs. #[event_cpi] #[derive(Accounts)] @@ -270,20 +271,3 @@ pub fn execute_v3_slow_relay_leaf( Ok(()) } - -// Events. -#[event] -pub struct RequestedV3SlowFill { - pub input_token: Pubkey, - pub output_token: Pubkey, - pub input_amount: u64, - pub output_amount: u64, - pub origin_chain_id: u64, - pub deposit_id: u32, - pub fill_deadline: u32, - pub exclusivity_deadline: u32, - pub exclusive_relayer: Pubkey, - pub depositor: Pubkey, - pub recipient: Pubkey, - pub message: Vec, -} diff --git a/programs/svm-spoke/src/lib.rs b/programs/svm-spoke/src/lib.rs index e11a5547..590d5afc 100644 --- a/programs/svm-spoke/src/lib.rs +++ b/programs/svm-spoke/src/lib.rs @@ -9,6 +9,7 @@ declare_program!(token_messenger_minter); pub mod constants; mod constraints; pub mod error; +pub mod event; mod instructions; mod state; pub mod utils; @@ -24,7 +25,7 @@ pub mod svm_spoke { pub fn initialize( ctx: Context, seed: u64, - initial_number_of_deposits: u64, + initial_number_of_deposits: u32, chain_id: u64, remote_domain: u32, cross_domain_admin: Pubkey, diff --git a/programs/svm-spoke/src/state/state.rs b/programs/svm-spoke/src/state/state.rs index 0064c86a..7572aa50 100644 --- a/programs/svm-spoke/src/state/state.rs +++ b/programs/svm-spoke/src/state/state.rs @@ -7,7 +7,7 @@ pub struct State { pub paused_fills: bool, pub owner: Pubkey, pub seed: u64, // Add a seed to the state to enable multiple deployments. - pub number_of_deposits: u64, + pub number_of_deposits: u32, pub chain_id: u64, // Across definition of chainId for Solana. pub current_time: u32, // Only used in testable mode, else set to 0 on mainnet. pub remote_domain: u32, // CCTP domain for Mainnet Ethereum. diff --git a/test/svm/SvmSpoke.Bundle.ts b/test/svm/SvmSpoke.Bundle.ts index d8438c2c..deac3bd1 100644 --- a/test/svm/SvmSpoke.Bundle.ts +++ b/test/svm/SvmSpoke.Bundle.ts @@ -13,9 +13,10 @@ import { RelayerRefundLeaf, RelayerRefundLeafSolana, RelayerRefundLeafType, + readProgramEvents, } from "./utils"; -const { provider, program, owner, initializeState, connection, chainId } = common; +const { provider, program, owner, initializeState, connection, chainId, assertSE } = common; describe("svm_spoke.bundle", () => { anchor.setProvider(provider); @@ -139,7 +140,7 @@ describe("svm_spoke.bundle", () => { isSolana: true, leafId: new BN(0), chainId: chainId, - amountToReturn: new BN(0), + amountToReturn: new BN(69420), mintPublicKey: mint, refundAccounts: [relayerTA, relayerTB], refundAmounts: [relayerARefund, relayerBRefund], @@ -191,6 +192,23 @@ describe("svm_spoke.bundle", () => { .remainingAccounts(remainingAccounts) .rpc(); + // Verify the ExecutedRelayerRefundRoot event + await new Promise((resolve) => setTimeout(resolve, 500)); // Wait for event processing + let events = await readProgramEvents(connection, program); + let event = events.find((event) => event.name === "executedRelayerRefundRoot").data; + + // Remove the expectedValues object and use direct assertions + assertSE(event.amountToReturn, relayerRefundLeaves[0].amountToReturn, "amountToReturn should match"); + assertSE(event.chainId, chainId, "chainId should match"); + assertSE(event.refundAmounts[0], relayerARefund, "Relayer A refund amount should match"); + assertSE(event.refundAmounts[1], relayerBRefund, "Relayer B refund amount should match"); + assertSE(event.rootBundleId, stateAccountData.rootBundleId, "rootBundleId should match"); + assertSE(event.leafId, leaf.leafId, "leafId should match"); + assertSE(event.l2TokenAddress, mint, "l2TokenAddress should match"); + assertSE(event.refundAddresses[0], relayerTA, "Relayer A address should match"); + assertSE(event.refundAddresses[1], relayerTB, "Relayer B address should match"); + assertSE(event.caller, owner, "caller should match"); + const fVaultBal = (await connection.getTokenAccountBalance(vault)).value.amount; const fRelayerABal = (await connection.getTokenAccountBalance(relayerTA)).value.amount; const fRelayerBBal = (await connection.getTokenAccountBalance(relayerTB)).value.amount; diff --git a/test/svm/SvmSpoke.Deposit.ts b/test/svm/SvmSpoke.Deposit.ts index ecdfc904..2f29b606 100644 --- a/test/svm/SvmSpoke.Deposit.ts +++ b/test/svm/SvmSpoke.Deposit.ts @@ -9,8 +9,8 @@ import { getAccount, } from "@solana/spl-token"; import { PublicKey, Keypair } from "@solana/web3.js"; -import { readProgramEvents } from "../../src/SvmUtils"; import { common } from "./SvmSpoke.common"; +import { readProgramEvents } from "./utils"; const { provider, connection, program, owner, seedBalance, initializeState, depositData } = common; const { createRoutePda, getVaultAta, assertSE, assert } = common; diff --git a/test/svm/SvmSpoke.Fill.ts b/test/svm/SvmSpoke.Fill.ts index 27ee7df6..3aa7b091 100644 --- a/test/svm/SvmSpoke.Fill.ts +++ b/test/svm/SvmSpoke.Fill.ts @@ -155,7 +155,7 @@ describe("svm_spoke.fill", () => { }); it("Allows fill by non-exclusive relayer after exclusivity deadline", async () => { - updateRelayData({ ...relayData, exclusivityDeadline: new BN(Math.floor(Date.now() / 1000) - 30) }); + updateRelayData({ ...relayData, exclusivityDeadline: new BN(Math.floor(Date.now() / 1000) - 100) }); accounts.signer = otherRelayer.publicKey; accounts.relayer = otherRelayer.publicKey; diff --git a/test/svm/SvmSpoke.Ownership.ts b/test/svm/SvmSpoke.Ownership.ts index 127e2c13..f064231a 100644 --- a/test/svm/SvmSpoke.Ownership.ts +++ b/test/svm/SvmSpoke.Ownership.ts @@ -2,7 +2,7 @@ import * as anchor from "@coral-xyz/anchor"; import { Keypair, PublicKey } from "@solana/web3.js"; import { assert } from "chai"; import { common } from "./SvmSpoke.common"; -import { readProgramEvents } from "../../src/SvmUtils"; +import { readProgramEvents } from "./utils"; const { provider, program, owner, initializeState } = common; diff --git a/test/svm/SvmSpoke.Routes.ts b/test/svm/SvmSpoke.Routes.ts index 37ddc5af..4e957f99 100644 --- a/test/svm/SvmSpoke.Routes.ts +++ b/test/svm/SvmSpoke.Routes.ts @@ -4,7 +4,7 @@ import { ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, createMint, getAccount } import { PublicKey, Keypair } from "@solana/web3.js"; import { assert } from "chai"; import { common } from "./SvmSpoke.common"; -import { readProgramEvents } from "../../src/SvmUtils"; +import { readProgramEvents } from "./utils"; const { provider, program, owner, initializeState, createRoutePda, getVaultAta } = common; diff --git a/test/svm/SvmSpoke.SlowFill.ts b/test/svm/SvmSpoke.SlowFill.ts index b3d02780..3f25e836 100644 --- a/test/svm/SvmSpoke.SlowFill.ts +++ b/test/svm/SvmSpoke.SlowFill.ts @@ -10,10 +10,9 @@ import { getAccount, } from "@solana/spl-token"; import { PublicKey, Keypair } from "@solana/web3.js"; -import { readProgramEvents, calculateRelayHashUint8Array } from "../../src/SvmUtils"; import { common } from "./SvmSpoke.common"; import { MerkleTree } from "@uma/common/dist/MerkleTree"; -import { slowFillHashFn, SlowFillLeaf } from "./utils"; +import { slowFillHashFn, SlowFillLeaf, readProgramEvents, calculateRelayHashUint8Array } from "./utils"; const { provider, connection, program, owner, chainId, seedBalance, initializeState } = common; const { recipient, setCurrentTime, assertSE, assert } = common; diff --git a/test/svm/SvmSpoke.TokenBridge.ts b/test/svm/SvmSpoke.TokenBridge.ts index de785752..19e661c9 100644 --- a/test/svm/SvmSpoke.TokenBridge.ts +++ b/test/svm/SvmSpoke.TokenBridge.ts @@ -5,8 +5,7 @@ import { MerkleTree } from "@uma/common/dist/MerkleTree"; import { common } from "./SvmSpoke.common"; import { MessageTransmitter } from "../../target/types/message_transmitter"; import { TokenMessengerMinter } from "../../target/types/token_messenger_minter"; -import { findProgramAddress } from "../../src/SvmUtils"; -import { RelayerRefundLeafSolana, RelayerRefundLeafType, relayerRefundHashFn } from "./utils"; +import { RelayerRefundLeafSolana, RelayerRefundLeafType, relayerRefundHashFn, findProgramAddress } from "./utils"; import { assert } from "chai"; import { decodeMessageSentData } from "./cctpHelpers"; diff --git a/test/svm/utils.ts b/test/svm/utils.ts index def91f23..0a4af94b 100644 --- a/test/svm/utils.ts +++ b/test/svm/utils.ts @@ -3,6 +3,10 @@ import { PublicKey } from "@solana/web3.js"; import { ethers } from "ethers"; import * as crypto from "crypto"; +import { readProgramEvents, calculateRelayHashUint8Array, findProgramAddress } from "../../src/SvmUtils"; + +export { readProgramEvents, calculateRelayHashUint8Array, findProgramAddress }; + export async function printLogs(connection: any, program: any, tx: any) { const latestBlockHash = await connection.getLatestBlockhash(); await connection.confirmTransaction(