diff --git a/.env.sample b/.env.sample index 251c116..66b17b6 100644 --- a/.env.sample +++ b/.env.sample @@ -1,17 +1,22 @@ # RPCs EVMX_RPC="https://rpc-evmx-devnet.socket.tech/" + SEPOLIA_RPC="https://ethereum-sepolia-rpc.publicnode.com" ARBITRUM_SEPOLIA_RPC="https://sepolia-rollup.arbitrum.io/rpc" OPTIMISM_SEPOLIA_RPC="https://sepolia.optimism.io" BASE_SEPOLIA_RPC="https://sepolia.base.org" +ARBITRUM_RPC="https://arbitrum.drpc.org" +OPTIMISM_RPC="https://optimism.drpc.org" +BASE_RPC="https://base.drpc.org" + # EVMx key addresses # Find the most up to date addresses at: # https://github.com/SocketDotTech/socket-protocol/blob/master/deployments/stage_addresses.json -ADDRESS_RESOLVER="0x21a9AFDfbEb0399D4a12f3AA1324042Be2B57F8e" -FEES_MANAGER="0x30e07016eB24570629Bc8765CA307394Af90B27C" -ARBITRUM_FEES_PLUG="0xDfE94B9b14de382Ed13C8A7F387884808D0f7E0b" -ARBITRUM_TEST_USDC="0xa03Cbf13f331aF7c0fD7F2E28E6Cbc13F879E3F3" +ADDRESS_RESOLVER="0x935b06902cA5C8bb4C76e18738561c294D377A93" +FEES_MANAGER="0xA07208F9e7aE243F922317ab6604DC9F86822406" +ARBITRUM_FEES_PLUG="0x501bdF8C7163ddD32172575C2836c5A7F556cbE7" +ARBITRUM_USDC="0xaf88d065e77c8cC2239327C5EDb3A432268e5831" # Add your deployer private key here # or remove it from this file if it is already an env var diff --git a/lib/socket-protocol b/lib/socket-protocol index 38accd5..b5b5c7f 160000 --- a/lib/socket-protocol +++ b/lib/socket-protocol @@ -1 +1 @@ -Subproject commit 38accd5f8a4144297048940122d56a08578c1783 +Subproject commit b5b5c7f86430b984f91892ee7097ba5355ba231a diff --git a/package.json b/package.json index 5d21ddc..fe1a19d 100644 --- a/package.json +++ b/package.json @@ -1,4 +1,5 @@ { + "license": "MIT", "type": "module", "dependencies": { "dotenv": "^16.5.0", diff --git a/script/runIntegrationTests.ts b/script/runIntegrationTests.ts index ae73688..3fe86e0 100644 --- a/script/runIntegrationTests.ts +++ b/script/runIntegrationTests.ts @@ -1,6 +1,6 @@ -import { setupClients } from './utils/client-setup.js'; +import { getAvailableChains } from './utils/client-setup.js'; import { buildContracts } from './utils/deployer.js'; -import { ChainConfig, TestFlags } from './utils/types.js'; +import { TestFlags } from './utils/types.js'; import { COLORS } from './utils/constants.js'; // Import test modules @@ -12,19 +12,6 @@ import { executeSchedulerTests } from './tests/scheduler_tests.js'; import { executeInsufficientFeesTests } from './tests/insufficient_tests.js'; import { executeRevertTests } from './tests/revert_tests.js'; -// Global chain configurations -let evmxChain: ChainConfig; -let arbChain: ChainConfig; -let opChain: ChainConfig; - -// Initialize chains -function initializeChains(): void { - const chains = setupClients(); - evmxChain = chains.evmxChain; - arbChain = chains.arbChain; - opChain = chains.opChain; -} - // Help function function showHelp(): void { console.log('Usage: npx tsx tests.ts [OPTIONS]'); @@ -59,7 +46,7 @@ function parseFlags(args: string[]): TestFlags { async function main(): Promise { try { await buildContracts(); - initializeChains(); + const chains = getAvailableChains(); // Parse command line arguments const args = process.argv.slice(2); @@ -72,31 +59,31 @@ async function main(): Promise { // Execute tests based on flags if (flags.write) { - await executeWriteTests(evmxChain, arbChain); + await executeWriteTests(chains.evmxChain, chains.arbChain); } if (flags.read) { - await executeReadTests(evmxChain, arbChain); + await executeReadTests(chains.evmxChain, chains.arbChain); } if (flags.trigger) { - await executeTriggerTests(evmxChain, arbChain, opChain); + await executeTriggerTests(chains.evmxChain, chains.arbChain, chains.opChain); } if (flags.upload) { - await executeUploadTests(evmxChain, arbChain); + await executeUploadTests(chains.evmxChain, chains.arbChain); } if (flags.scheduler) { - await executeSchedulerTests(evmxChain, arbChain); + await executeSchedulerTests(chains.evmxChain, chains.arbChain); } if (flags.insufficient) { - await executeInsufficientFeesTests(evmxChain, arbChain); + await executeInsufficientFeesTests(chains.evmxChain, chains.arbChain); } if (flags.revert) { - await executeRevertTests(evmxChain, arbChain); + await executeRevertTests(chains.evmxChain, chains.arbChain); } console.log(`${COLORS.GREEN}All selected tests completed successfully!${COLORS.NC}`); @@ -107,14 +94,6 @@ async function main(): Promise { } } -// Export the setup for external use -export { - initializeChains, - evmxChain, - arbChain, - opChain -}; - // Run main if this is the main module if (import.meta.url === `file://${process.argv[1]}`) { main().catch((error) => { diff --git a/script/tests/read_tests.ts b/script/tests/read_tests.ts index 9535894..d164f0e 100644 --- a/script/tests/read_tests.ts +++ b/script/tests/read_tests.ts @@ -13,6 +13,7 @@ export async function runReadTests( console.log(`${COLORS.CYAN}Running all read tests functions...${COLORS.NC}`); const abi = parseAbi([ + 'function numberOfRequests() external view returns (uint256)', 'function triggerParallelRead(address forwarder) external', 'function triggerAltRead(address forwarder1, address forwarder2) external' ]); @@ -21,6 +22,12 @@ export async function runReadTests( throw new Error('Required addresses not found'); } + const numberOfRequests = await evmxChain.client.readContract({ + address: addresses.appGateway, + abi, + functionName: 'numberOfRequests', + }); + // 1. Trigger Parallel Read await sendTransaction( addresses.appGateway, @@ -29,7 +36,7 @@ export async function runReadTests( evmxChain, abi ); - await awaitEvents(10, 'ValueRead(address,uint256,uint256)', addresses.appGateway, evmxChain); + await awaitEvents(numberOfRequests, 'ValueRead(address,uint256,uint256)', addresses.appGateway, evmxChain); // 2. Trigger Alternating Read await sendTransaction( @@ -39,7 +46,7 @@ export async function runReadTests( evmxChain, abi ); - await awaitEvents(20, 'ValueRead(address,uint256,uint256)', addresses.appGateway, evmxChain); + await awaitEvents(numberOfRequests * 2n, 'ValueRead(address,uint256,uint256)', addresses.appGateway, evmxChain); } export async function executeReadTests( diff --git a/script/tests/revert_tests.ts b/script/tests/revert_tests.ts index 0c00f22..7537839 100644 --- a/script/tests/revert_tests.ts +++ b/script/tests/revert_tests.ts @@ -35,39 +35,36 @@ export async function runRevertTests( abi ); - console.log(`${COLORS.CYAN}Waiting for transaction finalization${COLORS.NC}`); + console.log(`${COLORS.CYAN}Waiting for transaction proof${COLORS.NC}`); let attempt = 0; let status = ''; + let execStatus; while (true) { const response = await getTxDetails(endpoint, hash1); - status = response?.response?.[0]?.writePayloads?.[0]?.finalizeDetails?.finalizeStatus; - - if (status === 'FINALIZED') { - if (attempt > 0) process.stdout.write('\r\x1b[2K'); - break; + status = response?.response?.[0]?.writePayloads?.[0]?.proofUploadDetails?.proofUploadStatus; + + if (status === 'PROOF_UPLOADED') { + execStatus = response?.response?.[0]?.writePayloads?.[0]?.executeDetails?.executeStatus; + if (execStatus === 'EXECUTION_FAILED') { + console.log(`Execution status is EXECUTION_FAILED as expected`); + if (attempt > 0) process.stdout.write('\r\x1b[2K'); + break; + } } if (attempt >= maxAttempts) { console.log(); - throw new Error(`Transaction not finalized after ${maxSeconds} seconds. Current status: ${status}`); + throw new Error(`Could not validate failed execution after ${maxSeconds} seconds. Current status: ${status}`); } const elapsed = attempt * interval / 1000; - process.stdout.write(`\r${COLORS.YELLOW}Waiting for finalization:${COLORS.NC} ${elapsed}s / ${maxSeconds}s`); + process.stdout.write(`\r${COLORS.YELLOW}Waiting for failed execution:${COLORS.NC} ${elapsed}s / ${maxSeconds}s`); await new Promise(resolve => setTimeout(resolve, interval)); attempt++; } - const execStatus = (await getTxDetails(endpoint, hash1))?.response?.[0]?.writePayloads?.[0]?.executeDetails?.executeStatus; - - if (execStatus === 'EXECUTION_FAILED') { - console.log(`Execution status is EXECUTION_FAILED as expected`); - } else { - throw new Error(`Execution status is not EXECUTION_FAILED, it is: ${execStatus}`); - } - // Send callback revert transaction console.log(`${COLORS.CYAN}Testing callback revert${COLORS.NC}`); diff --git a/script/tests/scheduler_tests.ts b/script/tests/scheduler_tests.ts index 55616b1..58bf855 100644 --- a/script/tests/scheduler_tests.ts +++ b/script/tests/scheduler_tests.ts @@ -10,53 +10,53 @@ export async function runSchedulerTests( appGateway: Address, evmxChain: ChainConfig ): Promise { - console.log(`${COLORS.CYAN}Reading timeouts from the contract:${COLORS.NC}`); + console.log(`${COLORS.CYAN}Reading schedules from the contract:${COLORS.NC}`); const abi = parseAbi([ - 'function timeoutsInSeconds(uint256) external view returns (uint256)', - 'function triggerTimeouts() external' + 'function schedulesInSeconds(uint256) external view returns (uint256)', + 'function triggerSchedules() external' ]); - let maxTimeout = 0; - let numberOfTimeouts = 0; + let maxSchedule = 0n; + let numberOfSchedules = 0n; while (true) { try { - const timeout = await evmxChain.client.readContract({ + const schedule = await evmxChain.client.readContract({ address: appGateway, abi, - functionName: 'timeoutsInSeconds', - args: [numberOfTimeouts] - }) as number; + functionName: 'schedulesInSeconds', + args: [numberOfSchedules] + }); - if (timeout === 0) break; + if (schedule === 0) break; - console.log(`Timeout ${numberOfTimeouts}: ${timeout} seconds`); - numberOfTimeouts++; + console.log(`Schedule ${numberOfSchedules}: ${schedule} seconds`); + numberOfSchedules++; - if (timeout > maxTimeout) { - maxTimeout = timeout; + if (schedule > maxSchedule) { + maxSchedule = schedule; } } catch (error) { break; } } - console.log(`${COLORS.CYAN}Triggering timeouts...${COLORS.NC}`); + console.log(`${COLORS.CYAN}Triggering schedules...${COLORS.NC}`); await sendTransaction( appGateway, - 'triggerTimeouts', + 'triggerSchedules', [], evmxChain, abi ); - console.log(`${COLORS.CYAN}Fetching TimeoutResolved events...${COLORS.NC}`); + console.log(`${COLORS.CYAN}Fetching ScheduleResolved events...${COLORS.NC}`); - await awaitEvents(numberOfTimeouts, 'TimeoutResolved(uint256,uint256,uint256)', appGateway, evmxChain, Number(maxTimeout)); + await awaitEvents(numberOfSchedules, 'ScheduleResolved(uint256,uint256,uint256)', appGateway, evmxChain, Number(maxSchedule)); const logs = await evmxChain.client.getLogs({ address: appGateway, - event: parseAbi(['event TimeoutResolved(uint256,uint256,uint256)'])[0], + event: parseAbi(['event ScheduleResolved(uint256,uint256,uint256)'])[0], fromBlock: 'earliest', toBlock: 'latest' }); @@ -69,7 +69,7 @@ export async function runSchedulerTests( const creationTimestamp = BigInt('0x' + dataHex.slice(64, 128)); const executionTimestamp = BigInt('0x' + dataHex.slice(128, 192)); - console.log(`${COLORS.GREEN}Timeout Resolved:${COLORS.NC}`); + console.log(`${COLORS.GREEN}Schedule Resolved:${COLORS.NC}`); console.log(` Index: ${index}`); console.log(` Created at: ${creationTimestamp}`); console.log(` Executed at: ${executionTimestamp}`); diff --git a/script/tests/upload_tests.ts b/script/tests/upload_tests.ts index 26d2c93..002450e 100644 --- a/script/tests/upload_tests.ts +++ b/script/tests/upload_tests.ts @@ -54,7 +54,7 @@ export async function runUploadTests( uploadAbi ); - await awaitEvents(1, 'ReadOnchain(address,uint256)', appGateway, evmxChain); + await awaitEvents(1n, 'ReadOnchain(address,uint256)', appGateway, evmxChain); } export async function executeUploadTests( diff --git a/script/tests/write_tests.ts b/script/tests/write_tests.ts index a3bf380..68a5c29 100644 --- a/script/tests/write_tests.ts +++ b/script/tests/write_tests.ts @@ -13,6 +13,7 @@ export async function runWriteTests( console.log(`${COLORS.CYAN}Running all write tests functions...${COLORS.NC}`); const abi = parseAbi([ + 'function numberOfRequests() external view returns (uint256)', 'function triggerSequentialWrite(address forwarder) external', 'function triggerParallelWrite(address forwarder) external', 'function triggerAltWrite(address forwarder1, address forwarder2) external' @@ -22,6 +23,13 @@ export async function runWriteTests( throw new Error('Required addresses not found'); } + const numberOfRequests = await evmxChain.client.readContract({ + address: addresses.appGateway, + abi, + functionName: 'numberOfRequests', + }); + + // 1. Trigger Sequential Write await sendTransaction( addresses.appGateway, @@ -30,7 +38,7 @@ export async function runWriteTests( evmxChain, abi ); - await awaitEvents(10, 'CounterIncreased(address,uint256,uint256)', addresses.appGateway, evmxChain); + await awaitEvents(numberOfRequests, 'CounterIncreased(address,uint256,uint256)', addresses.appGateway, evmxChain); // 2. Trigger Parallel Write await sendTransaction( @@ -40,7 +48,7 @@ export async function runWriteTests( evmxChain, abi ); - await awaitEvents(20, 'CounterIncreased(address,uint256,uint256)', addresses.appGateway, evmxChain); + await awaitEvents(numberOfRequests * 2n, 'CounterIncreased(address,uint256,uint256)', addresses.appGateway, evmxChain); // 3. Trigger Alternating Write await sendTransaction( @@ -50,7 +58,7 @@ export async function runWriteTests( evmxChain, abi ); - await awaitEvents(30, 'CounterIncreased(address,uint256,uint256)', addresses.appGateway, evmxChain); + await awaitEvents(numberOfRequests * 3n, 'CounterIncreased(address,uint256,uint256)', addresses.appGateway, evmxChain); } export async function executeWriteTests( diff --git a/script/utils/client-setup.ts b/script/utils/client-setup.ts index 6c460bd..c321cb4 100644 --- a/script/utils/client-setup.ts +++ b/script/utils/client-setup.ts @@ -1,4 +1,3 @@ -// clients/setup.ts import { createPublicClient, createWalletClient, @@ -6,7 +5,7 @@ import { type Hash, } from 'viem'; import { privateKeyToAccount } from 'viem/accounts'; -import { arbitrumSepolia, optimismSepolia } from 'viem/chains'; +import { arbitrumSepolia, optimismSepolia, optimism, arbitrum, base } from 'viem/chains'; import dotenv from 'dotenv'; import { ChainConfig } from './types.js'; import { CHAIN_IDS } from './constants.js'; @@ -14,15 +13,17 @@ import { CHAIN_IDS } from './constants.js'; // Load environment variables dotenv.config(); -// Environment validation +// Environment validation - only check required chains export function validateEnvironment(): void { const required = [ - 'EVMX_RPC', 'PRIVATE_KEY', + // EVMx + 'EVMX_RPC', 'ADDRESS_RESOLVER', 'FEES_MANAGER', + // Testnets (always required) 'ARBITRUM_SEPOLIA_RPC', - 'OPTIMISM_SEPOLIA_RPC' + 'OPTIMISM_SEPOLIA_RPC', ]; for (const env of required) { @@ -32,68 +33,156 @@ export function validateEnvironment(): void { } } -// Setup all blockchain clients +// Helper function to create chain config +function createChainConfig( + chainInfo: any, + rpcUrl: string, + account: any, + chainId: number, + explorerUrl: string +): ChainConfig { + const client = createPublicClient({ + chain: chainInfo, + transport: http(rpcUrl) + }); + + const walletClient = createWalletClient({ + account, + chain: chainInfo, + transport: http(rpcUrl) + }); + + return { + client, + walletClient, + chainId, + explorerUrl + }; +} + +// Check if mainnet environment variables are available +export function getAvailableMainnets(): { + hasArbitrum: boolean; + hasOptimism: boolean; + hasBase: boolean; +} { + return { + hasArbitrum: !!process.env.ARBITRUM_RPC, + hasOptimism: !!process.env.OPTIMISM_RPC, + hasBase: !!process.env.BASE_RPC, + }; +} + +// Setup all blockchain clients with optional mainnets export function setupClients(): { evmxChain: ChainConfig; arbChain: ChainConfig; opChain: ChainConfig; + arbMainnetChain?: ChainConfig; + opMainnetChain?: ChainConfig; + baseMainnetChain?: ChainConfig; } { validateEnvironment(); const account = privateKeyToAccount(process.env.PRIVATE_KEY as Hash); + const availableMainnets = getAvailableMainnets(); - // EVMx Chain + // EVMx Chain (always required) const evmxClient = createPublicClient({ transport: http(process.env.EVMX_RPC) }); - const evmxWallet = createWalletClient({ account, transport: http(process.env.EVMX_RPC) }); - // Arbitrum Sepolia Chain - const arbClient = createPublicClient({ - chain: arbitrumSepolia, - transport: http(process.env.ARBITRUM_SEPOLIA_RPC) - }); - - const arbWallet = createWalletClient({ + // Testnet chains (always required) + const arbChain = createChainConfig( + arbitrumSepolia, + process.env.ARBITRUM_SEPOLIA_RPC!, account, - chain: arbitrumSepolia, - transport: http(process.env.ARBITRUM_SEPOLIA_RPC) - }); + CHAIN_IDS.ARB_SEP, + 'arbitrum-sepolia.blockscout.com' + ); - // Optimism Sepolia Chain - const opClient = createPublicClient({ - chain: optimismSepolia, - transport: http(process.env.OPTIMISM_SEPOLIA_RPC) - }); - - const opWallet = createWalletClient({ + const opChain = createChainConfig( + optimismSepolia, + process.env.OPTIMISM_SEPOLIA_RPC!, account, - chain: optimismSepolia, - transport: http(process.env.OPTIMISM_SEPOLIA_RPC) - }); + CHAIN_IDS.OP_SEP, + 'optimism-sepolia.blockscout.com' + ); - return { + const result: { + evmxChain: ChainConfig; + arbChain: ChainConfig; + opChain: ChainConfig; + arbMainnetChain?: ChainConfig; + opMainnetChain?: ChainConfig; + baseMainnetChain?: ChainConfig; + } = { evmxChain: { client: evmxClient, walletClient: evmxWallet, chainId: CHAIN_IDS.EVMX, explorerUrl: 'evmx.cloud.blockscout.com' }, - arbChain: { - client: arbClient, - walletClient: arbWallet, - chainId: CHAIN_IDS.ARB_SEP, - explorerUrl: 'arbitrum-sepolia.blockscout.com' - }, - opChain: { - client: opClient, - walletClient: opWallet, - chainId: CHAIN_IDS.OP_SEP, - explorerUrl: 'optimism-sepolia.blockscout.com' - } + arbChain, + opChain, }; + + // Optional mainnet chains + if (availableMainnets.hasArbitrum) { + result.arbMainnetChain = createChainConfig( + arbitrum, + process.env.ARBITRUM_RPC!, + account, + CHAIN_IDS.ARB, + 'arbitrum.blockscout.com' + ); + } + + if (availableMainnets.hasOptimism) { + result.opMainnetChain = createChainConfig( + optimism, + process.env.OPTIMISM_RPC!, + account, + CHAIN_IDS.OP, // Fixed: was using OP_SEP instead of OP + 'optimism.blockscout.com' + ); + } + + if (availableMainnets.hasBase) { + result.baseMainnetChain = createChainConfig( + base, + process.env.BASE_RPC!, + account, + CHAIN_IDS.BASE, + 'base.blockscout.com' + ); + } + + return result; +} + +// Helper function to get only available chains +export function getAvailableChains() { + const clients = setupClients(); + const availableMainnets = getAvailableMainnets(); + + console.log('Available chains:'); + console.log('- EVMx: ✓'); + console.log('- Arbitrum Sepolia: ✓'); + console.log('- Optimism Sepolia: ✓'); + if (availableMainnets.hasArbitrum) { + console.log(`- Arbitrum Mainnet: ✓`); + } + if (availableMainnets.hasOptimism) { + console.log(`- Optimism Mainnet: ✓`); + } + if (availableMainnets.hasBase) { + console.log(`- Base Mainnet: ✓`); + } + + return clients; } diff --git a/script/utils/constants.ts b/script/utils/constants.ts index 8e2b11f..0fd091e 100644 --- a/script/utils/constants.ts +++ b/script/utils/constants.ts @@ -3,8 +3,13 @@ import { parseEther } from 'viem'; export const CHAIN_IDS = { EVMX: 43, + // Testnets ARB_SEP: 421614, OP_SEP: 11155420, + // Mainnets + OP: 10, + ARB: 42161, + BASE: 8453, } as const; export const URLS = { @@ -12,10 +17,10 @@ export const URLS = { } as const; export const AMOUNTS = { - DEPLOY_FEES: parseEther('10'), // 10 ETH - TEST_USDC: BigInt('100000000'), // 100 TEST USDC - GAS_BUFFER: BigInt('100000000'), // 0.1 Gwei - GAS_LIMIT: BigInt('50000000000'), // Gas limit estimate + DEPLOY_FEES: parseEther('1'), // 1 ETH + TEST_USDC: 1000000n, // 1 USDC for testing + GAS_BUFFER: 100000000n, // 0.1 Gwei + GAS_LIMIT: 50000000000n, // Gas limit estimate } as const; export const COLORS = { @@ -25,4 +30,3 @@ export const COLORS = { RED: '\x1b[0;31m', NC: '\x1b[0m', // No Color } as const; - diff --git a/script/utils/deployer.ts b/script/utils/deployer.ts index 30670af..a1c43e8 100644 --- a/script/utils/deployer.ts +++ b/script/utils/deployer.ts @@ -74,7 +74,8 @@ export async function sendTransaction( functionName: string, args: any[] = [], chainConfig: ChainConfig, - abi: Abi + abi: Abi, + value: bigint = 0n ): Promise { console.log(`${COLORS.CYAN}Sending transaction to ${functionName} on ${to}${COLORS.NC}`); @@ -86,7 +87,8 @@ export async function sendTransaction( args, account: chainConfig.walletClient.account!, chain: chainConfig.walletClient.chain, - gasPrice: chainConfig.chainId === CHAIN_IDS.EVMX ? 0n : undefined + gasPrice: chainConfig.chainId === CHAIN_IDS.EVMX ? 0n : undefined, + value: value }); await new Promise(resolve => setTimeout(resolve, 2000)); @@ -107,7 +109,7 @@ export async function deployAppGateway( ): Promise
{ return deployContract( filename, - [process.env.ADDRESS_RESOLVER, deployFees], + [process.env.ADDRESS_RESOLVER, deployFees / 2n], evmxChain ); } diff --git a/script/utils/fees-manager.ts b/script/utils/fees-manager.ts index 0aefa6e..cc9d336 100644 --- a/script/utils/fees-manager.ts +++ b/script/utils/fees-manager.ts @@ -1,5 +1,5 @@ // fees/manager.ts -import { parseAbi, type Address } from 'viem'; +import { parseAbi, formatEther, type Address, parseEther } from 'viem'; import { ChainConfig } from './types.js'; import { COLORS, AMOUNTS, CHAIN_IDS } from './constants.js'; import { sendTransaction } from './deployer.js'; @@ -27,7 +27,8 @@ export async function checkAvailableFees( }) as bigint; if (availableFees > 0n) { - console.log(`Funds available: ${availableFees} wei`); + console.log(`Funds available: ${formatEther(availableFees)} Credits - ${availableFees} wei`); + await new Promise(resolve => setTimeout(resolve, 1000)); return availableFees; } } catch (error) { @@ -54,44 +55,51 @@ export async function depositFunds( ): Promise { console.log(`${COLORS.CYAN}Depositing funds${COLORS.NC}`); - const erc20Abi = parseAbi([ - 'function mint(address to, uint256 amount) external', - 'function approve(address spender, uint256 amount) external returns (bool)' - ]); + const balance = await evmxChain.client.getBalance({ + address: evmxChain.walletClient.account?.address as Address, + }) as bigint; - const feesPlugAbi = parseAbi([ - 'function depositToFeeAndNative(address token, address appGateway, uint256 amount) external' + const feesAbi = parseAbi([ + 'function depositCreditAndNative(address token, address appGateway, uint256 amount) external', + 'function wrap(address appGateway)' ]); - const walletAddress = arbChain.walletClient.account?.address; - if (!walletAddress) throw new Error('Wallet address not found'); + if (balance > AMOUNTS.DEPLOY_FEES) { + sendTransaction( + process.env.FEES_MANAGER as Address, + 'wrap', + [appGateway], + evmxChain, + feesAbi, + AMOUNTS.DEPLOY_FEES + ); + } else { + console.log(`Not enough EVMx balance. Depositing ${AMOUNTS.TEST_USDC} Arbitrum USDC in wei.`); + const erc20Abi = parseAbi([ + 'function approve(address spender, uint256 amount) external returns (bool)' + ]); - // Mint test USDC - await sendTransaction( - process.env.ARBITRUM_TEST_USDC as Address, - 'mint', - [walletAddress, AMOUNTS.TEST_USDC], - arbChain, - erc20Abi - ); + const walletAddress = arbChain.walletClient.account?.address; + if (!walletAddress) throw new Error('Wallet address not found'); - // Approve USDC for FeesPlug - await sendTransaction( - process.env.ARBITRUM_TEST_USDC as Address, - 'approve', - [process.env.ARBITRUM_FEES_PLUG as Address, AMOUNTS.TEST_USDC], - arbChain, - erc20Abi - ); + // Approve USDC for FeesPlug + await sendTransaction( + process.env.ARBITRUM_USDC as Address, + 'approve', + [process.env.ARBITRUM_FEES_PLUG as Address, AMOUNTS.TEST_USDC], + arbChain, + erc20Abi + ); - // Deposit funds - await sendTransaction( - process.env.ARBITRUM_FEES_PLUG as Address, - 'depositToFeeAndNative', - [process.env.ARBITRUM_TEST_USDC as Address, appGateway, AMOUNTS.TEST_USDC], - arbChain, - feesPlugAbi - ); + // Deposit funds + await sendTransaction( + process.env.ARBITRUM_FEES_PLUG as Address, + 'depositCreditAndNative', + [process.env.ARBITRUM_USDC as Address, evmxChain.walletClient.account?.address, AMOUNTS.TEST_USDC], + arbChain, + feesAbi + ); + } await checkAvailableFees(appGateway, evmxChain); } @@ -104,37 +112,48 @@ export async function withdrawFunds( ): Promise { console.log(`${COLORS.CYAN}Withdrawing funds${COLORS.NC}`); - const availableFees = await checkAvailableFees(appGateway, evmxChain); + let availableFees = await checkAvailableFees(appGateway, evmxChain); if (availableFees === 0n) { console.log('No available fees to withdraw.'); return; } - // Get gas price and calculate withdrawal amount - const gasPrice = await arbChain.client.getGasPrice(); - const estimatedGasCost = AMOUNTS.GAS_LIMIT * (gasPrice + AMOUNTS.GAS_BUFFER); - - let amountToWithdraw = 0n; - if (availableFees > estimatedGasCost) { - amountToWithdraw = availableFees - estimatedGasCost; - } - - console.log(`Withdrawing ${amountToWithdraw} wei`); + const abi = parseAbi([ + 'function withdrawCredits(uint32 chainId, address token, uint256 amount, address to) external', + 'function transferCredits(address to_, uint256 amount_) external' + ]); - if (amountToWithdraw > 0n) { - const abi = parseAbi([ - 'function withdrawFeeTokens(uint32 chainId, address token, uint256 amount, address to) external' - ]); + await sendTransaction( + appGateway, + 'transferCredits', + [evmxChain.walletClient.account?.address, availableFees], + evmxChain, + abi + ); - await sendTransaction( - appGateway, - 'withdrawFeeTokens', - [CHAIN_IDS.ARB_SEP, process.env.ARBITRUM_TEST_USDC, amountToWithdraw, arbChain.walletClient.account?.address], - evmxChain, - abi - ); - } else { - console.log('No funds available for withdrawal after gas cost estimation.'); - } + // TODO: Move the code below to a withdraw example as separate test that shows: + // - transfering credits + // - wraping credits + // - wraping natives + // - withdrawing to mainnet + + //let amountToWithdraw = availableFees - AMOUNTS.DEPLOY_FEES; + + //console.log(`Withdrawing ${formatEther(amountToWithdraw)} Credits - ${amountToWithdraw} wei`); + + //if (amountToWithdraw > 0n) { + // await sendTransaction( + // appGateway, + // 'withdrawCredits', + // [CHAIN_IDS.ARB_SEP, process.env.ARBITRUM_USDC, amountToWithdraw, arbChain.walletClient.account?.address], + // evmxChain, + // abi + // ); + //} else { + // console.log('No funds available for withdrawal after gas cost estimation.'); + //} + + //// transfer the rest to the EOA + //availableFees = await checkAvailableFees(appGateway, evmxChain); } diff --git a/script/utils/helpers.ts b/script/utils/helpers.ts index d4ddf6f..fa5b1a3 100644 --- a/script/utils/helpers.ts +++ b/script/utils/helpers.ts @@ -80,22 +80,23 @@ export async function fetchForwarderAndOnchainAddress( console.log(`Forwarder: ${forwarder}`); console.log(`Onchain: ${onchain}`); + await new Promise(resolve => setTimeout(resolve, 1000)); return { forwarder, onchain }; } // Await events function export async function awaitEvents( - expectedNewEvents: number, + expectedNewEvents: bigint, _eventSignature: string, appGateway: Address, evmxChain: ChainConfig, - timeout: number = 180 + timeout: number = 300 ): Promise { console.log(`${COLORS.CYAN}Waiting logs for ${expectedNewEvents} new events (up to ${timeout} seconds)...${COLORS.NC}`); const interval: number = 2000; // 2 seconds let elapsed: number = 0; - let eventCount: number = 0; + let eventCount = 0n; while (elapsed <= timeout * 1000) { try { diff --git a/src/deploy/DeploymentAppGateway.sol b/src/deploy/DeploymentAppGateway.sol index 9ef47e0..40c5ce9 100644 --- a/src/deploy/DeploymentAppGateway.sol +++ b/src/deploy/DeploymentAppGateway.sol @@ -48,13 +48,14 @@ contract DeploymentAppGateway is AppGatewayBase { * @param addressResolver_ Address of the SOCKET Protocol's AddressResolver contract * @param fees_ Fee configuration for multi-chain operations */ - constructor(address addressResolver_, uint256 fees_) AppGatewayBase(addressResolver_) { + constructor(address addressResolver_, uint256 fees_) { creationCodeWithArgs[noPlugNoInititialize] = abi.encodePacked(type(NoPlugNoInititialize).creationCode); creationCodeWithArgs[noPlugInitialize] = abi.encodePacked(type(NoPlugInitialize).creationCode); creationCodeWithArgs[plugNoInitialize] = abi.encodePacked(type(PlugNoInitialize).creationCode); creationCodeWithArgs[plugInitialize] = abi.encodePacked(type(PlugInitialize).creationCode); creationCodeWithArgs[plugInitializeTwice] = abi.encodePacked(type(PlugInitializeTwice).creationCode); creationCodeWithArgs[plugNoInitInitialize] = abi.encodePacked(type(PlugNoInitInitialize).creationCode); + _initializeAppGateway(addressResolver_); _setMaxFees(fees_); } @@ -63,7 +64,7 @@ contract DeploymentAppGateway is AppGatewayBase { * @dev Triggers asynchronous multi-chain deployments with different initialization scenarios * @param chainSlug_ The identifier of the target chain */ - function deployContracts(uint32 chainSlug_) external async(bytes("")) { + function deployContracts(uint32 chainSlug_) external async { _deploy(noPlugNoInititialize, chainSlug_, IsPlug.NO); _deploy( noPlugInitialize, chainSlug_, IsPlug.NO, abi.encodeWithSelector(NoPlugInitialize.initialise.selector, 10) @@ -84,7 +85,7 @@ contract DeploymentAppGateway is AppGatewayBase { * @dev Calls initialize functions on specific contracts after deployment * @param chainSlug_ The identifier of the chain where contracts were deployed */ - function initialize(uint32 chainSlug_) public override async(bytes("")) { + function initializeOnChain(uint32 chainSlug_) public override async { PlugInitializeTwice(forwarderAddresses[plugInitializeTwice][chainSlug_]).initialise(10); PlugNoInitInitialize(forwarderAddresses[plugNoInitInitialize][chainSlug_]).initialise(10); } @@ -94,7 +95,7 @@ contract DeploymentAppGateway is AppGatewayBase { * @dev Performs checks on each contract type to ensure proper initialization and functionality * @param chainSlug_ The identifier of the chain where contracts were deployed */ - function contractValidation(uint32 chainSlug_) external async(bytes("")) { + function contractValidation(uint32 chainSlug_) external async { address noPlugNoInititializeForwarder = forwarderAddresses[noPlugNoInititialize][chainSlug_]; address noPlugInitializeForwarder = forwarderAddresses[noPlugInitialize][chainSlug_]; address plugNoInitializeForwarder = forwarderAddresses[plugNoInitialize][chainSlug_]; @@ -105,36 +106,36 @@ contract DeploymentAppGateway is AppGatewayBase { // NoPlugNoInititialize checks _setOverrides(Read.ON); IDeployOnchain(noPlugNoInititializeForwarder).variable(); - IPromise(noPlugNoInititializeForwarder).then(this.validateVariable.selector, abi.encode(0)); + then(this.validateVariable.selector, abi.encode(0)); // NoPlugInitialize checks IDeployOnchain(noPlugInitializeForwarder).variable(); - IPromise(noPlugInitializeForwarder).then(this.validateVariable.selector, abi.encode(10)); + then(this.validateVariable.selector, abi.encode(10)); // PlugNoInitialize checks IDeployOnchain(plugNoInitializeForwarder).variable(); - IPromise(plugNoInitializeForwarder).then(this.validateVariable.selector, abi.encode(0)); + then(this.validateVariable.selector, abi.encode(0)); IDeployOnchain(plugNoInitializeForwarder).socket__(); - IPromise(plugNoInitializeForwarder).then(this.validateSocket.selector, abi.encode(0)); + then(this.validateSocket.selector, abi.encode(0)); // PlugInitialize checks IDeployOnchain(plugInitializeForwarder).variable(); - IPromise(plugInitializeForwarder).then(this.validateVariable.selector, abi.encode(10)); + then(this.validateVariable.selector, abi.encode(10)); IDeployOnchain(plugInitializeForwarder).socket__(); - IPromise(plugInitializeForwarder).then(this.validateSocket.selector, abi.encode(0)); + then(this.validateSocket.selector, abi.encode(0)); // PlugInitializeTwice checks IDeployOnchain(plugInitializeTwiceForwarder).variable(); - IPromise(plugInitializeTwiceForwarder).then(this.validateVariable.selector, abi.encode(20)); + then(this.validateVariable.selector, abi.encode(20)); IDeployOnchain(plugInitializeTwiceForwarder).socket__(); - IPromise(plugInitializeTwiceForwarder).then(this.validateSocket.selector, abi.encode(0)); + then(this.validateSocket.selector, abi.encode(0)); // PlugNoInitInitialize checks _setOverrides(Read.ON); IDeployOnchain(plugNoInitInitializeForwarder).variable(); - IPromise(plugNoInitInitializeForwarder).then(this.validateVariable.selector, abi.encode(10)); + then(this.validateVariable.selector, abi.encode(10)); IDeployOnchain(plugNoInitInitializeForwarder).socket__(); - IPromise(plugNoInitInitializeForwarder).then(this.validateSocket.selector, abi.encode(0)); + then(this.validateSocket.selector, abi.encode(0)); _setOverrides(Read.OFF); } @@ -170,7 +171,26 @@ contract DeploymentAppGateway is AppGatewayBase { * @param amount_ The amount to withdraw * @param receiver_ The address that will receive the withdrawn fees */ - function withdrawFeeTokens(uint32 chainSlug_, address token_, uint256 amount_, address receiver_) external { - _withdrawFeeTokens(chainSlug_, token_, amount_, receiver_); + function withdrawCredits(uint32 chainSlug_, address token_, uint256 amount_, address receiver_) external { + _withdrawCredits(chainSlug_, token_, amount_, receiver_); + } + + /** + * @notice Transfers fee credits from this contract to a specified address + * @dev Moves a specified amount of fee credits from the current contract to the given recipient + * @param to_ The address to transfer credits to + * @param amount_ The amount of credits to transfer + */ + function transferCredits(address to_, uint256 amount_) external { + feesManager__().transferCredits(address(this), to_, amount_); + } + + /** + * @notice Updates the fee max value + * @dev Allows modification of fee settings for multi-chain operations + * @param fees_ New fee configuration + */ + function setMaxFees(uint256 fees_) public { + maxFees = fees_; } } diff --git a/src/forwarder-on-evmx/UploadAppGateway.sol b/src/forwarder-on-evmx/UploadAppGateway.sol index 0a6ba00..65cfdc7 100644 --- a/src/forwarder-on-evmx/UploadAppGateway.sol +++ b/src/forwarder-on-evmx/UploadAppGateway.sol @@ -2,7 +2,6 @@ pragma solidity ^0.8.0; import "socket-protocol/contracts/evmx/base/AppGatewayBase.sol"; -import "socket-protocol/contracts/evmx/interfaces/IPromise.sol"; import "./ICounter.sol"; /** @@ -31,7 +30,8 @@ contract UploadAppGateway is AppGatewayBase { * @param addressResolver_ Address of the SOCKET Protocol's AddressResolver contract * @param fees_ Fee configuration for multi-chain operations */ - constructor(address addressResolver_, uint256 fees_) AppGatewayBase(addressResolver_) { + constructor(address addressResolver_, uint256 fees_) { + _initializeAppGateway(addressResolver_); _setMaxFees(fees_); } @@ -40,14 +40,14 @@ contract UploadAppGateway is AppGatewayBase { * @dev Required by AppGatewayBase but not used in this implementation * @param chainSlug_ The identifier of the target chain (unused) */ - function deployContracts(uint32 chainSlug_) external async(bytes("")) {} + function deployContracts(uint32 chainSlug_) external async {} /** * @notice Empty initialization function as no post-deployment setup is needed * @dev Required by AppGatewayBase but not used in this implementation * @param chainSlug_ The identifier of the chain (unused) */ - function initialize(uint32 chainSlug_) public override {} + function initializeOnChain(uint32 chainSlug_) public override {} /** * @notice Uploads an existing onchain contract to EVMx @@ -56,7 +56,7 @@ contract UploadAppGateway is AppGatewayBase { * @param chainSlug_ The identifier of the chain where the contract exists */ function uploadToEVMx(address onchainContract, uint32 chainSlug_) public { - counterForwarder = addressResolver__.getOrDeployForwarderContract(address(this), onchainContract, chainSlug_); + counterForwarder = asyncDeployer__().getOrDeployForwarderContract(onchainContract, chainSlug_); } /** @@ -64,12 +64,11 @@ contract UploadAppGateway is AppGatewayBase { * @dev Initiates an asynchronous read operation with parallel execution enabled * Sets up a promise to handle the read result via the handleRead function */ - function read() public async(bytes("")) { - _setOverrides(Read.ON, Parallel.ON); - // TODO: Remove Parallel.ON after new contract deployment to devnet + function read() public async { + _setOverrides(Read.ON); ICounter(counterForwarder).counter(); - IPromise(counterForwarder).then(this.handleRead.selector, abi.encode(counterForwarder)); - _setOverrides(Read.OFF, Parallel.OFF); + then(this.handleRead.selector, abi.encode(counterForwarder)); + _setOverrides(Read.OFF); } /** @@ -94,7 +93,26 @@ contract UploadAppGateway is AppGatewayBase { * @param amount_ The amount to withdraw * @param receiver_ The address that will receive the withdrawn fees */ - function withdrawFeeTokens(uint32 chainSlug_, address token_, uint256 amount_, address receiver_) external { - _withdrawFeeTokens(chainSlug_, token_, amount_, receiver_); + function withdrawCredits(uint32 chainSlug_, address token_, uint256 amount_, address receiver_) external { + _withdrawCredits(chainSlug_, token_, amount_, receiver_); + } + + /** + * @notice Transfers fee credits from this contract to a specified address + * @dev Moves a specified amount of fee credits from the current contract to the given recipient + * @param to_ The address to transfer credits to + * @param amount_ The amount of credits to transfer + */ + function transferCredits(address to_, uint256 amount_) external { + feesManager__().transferCredits(address(this), to_, amount_); + } + + /** + * @notice Updates the fee max value + * @dev Allows modification of fee settings for multi-chain operations + * @param fees_ New fee configuration + */ + function setMaxFees(uint256 fees_) public { + maxFees = fees_; } } diff --git a/src/read/IReadMultichain.sol b/src/read/IReadMultichain.sol index 851125e..b8554d2 100644 --- a/src/read/IReadMultichain.sol +++ b/src/read/IReadMultichain.sol @@ -13,13 +13,4 @@ interface IReadMultichain { * @param index The index of the value to retrieve */ function values(uint256 index) external; - - /** - * @notice Connects the contract to the SOCKET Protocol - * @dev Sets up the contract for EVMx communication by calling the parent PlugBase method - * @param appGateway_ Address of the application gateway contract - * @param socket_ Address of the SOCKET Protocol contract - * @param switchboard_ Address of the switchboard contract - */ - function connectSocket(address appGateway_, address socket_, address switchboard_) external; } diff --git a/src/read/ReadAppGateway.sol b/src/read/ReadAppGateway.sol index 285f665..b65ebce 100644 --- a/src/read/ReadAppGateway.sol +++ b/src/read/ReadAppGateway.sol @@ -3,7 +3,6 @@ pragma solidity >=0.7.0 <0.9.0; import "socket-protocol/contracts/evmx/base/AppGatewayBase.sol"; import "socket-protocol/contracts/evmx/interfaces/IForwarder.sol"; -import "socket-protocol/contracts/evmx/interfaces/IPromise.sol"; import "./IReadMultichain.sol"; import "./ReadMultichain.sol"; @@ -14,6 +13,12 @@ import "./ReadMultichain.sol"; * Inherits from AppGatewayBase for SOCKET Protocol integration. */ contract ReadAppGateway is AppGatewayBase { + /** + * @notice Number of requests to call onchain + * @dev Used to maximize number of requests done + */ + uint256 public numberOfRequests = 10; + /** * @notice Identifier for the ReadMultichain contract * @dev Used to track ReadMultichain contract instances across chains @@ -41,10 +46,11 @@ contract ReadAppGateway is AppGatewayBase { * @param addressResolver_ Address of the SOCKET Protocol's AddressResolver contract * @param fees_ Fee configuration for multi-chain operations */ - constructor(address addressResolver_, uint256 fees_) AppGatewayBase(addressResolver_) { + constructor(address addressResolver_, uint256 fees_) { creationCodeWithArgs[multichain] = abi.encodePacked(type(ReadMultichain).creationCode); + values = new uint256[](numberOfRequests); + _initializeAppGateway(addressResolver_); _setMaxFees(fees_); - values = new uint256[](10); } /** @@ -52,7 +58,7 @@ contract ReadAppGateway is AppGatewayBase { * @dev Triggers an asynchronous multi-chain deployment via SOCKET Protocol. * @param chainSlug_ The identifier of the target chain */ - function deployContracts(uint32 chainSlug_) external async(bytes("")) { + function deployContracts(uint32 chainSlug_) external async { _deploy(multichain, chainSlug_, IsPlug.YES); } @@ -61,7 +67,7 @@ contract ReadAppGateway is AppGatewayBase { * @dev No initialization needed for this application, so implementation is empty. * The chainSlug parameter is required by the interface but not used. */ - function initialize(uint32) public pure override { + function initializeOnChain(uint32) public pure override { return; } @@ -71,11 +77,11 @@ contract ReadAppGateway is AppGatewayBase { * and stores the results in the values array. * @param instance_ Address of the ReadMultichain instance to read from */ - function triggerParallelRead(address instance_) public async(bytes("")) { + function triggerParallelRead(address instance_) public async { _setOverrides(Read.ON, Parallel.ON); - for (uint256 i = 0; i < 10; i++) { + for (uint256 i = 0; i < numberOfRequests; i++) { IReadMultichain(instance_).values(i); - IPromise(instance_).then(this.handleValue.selector, abi.encode(i, instance_)); + then(this.handleValue.selector, abi.encode(i, instance_)); } _setOverrides(Read.OFF, Parallel.OFF); } @@ -87,15 +93,15 @@ contract ReadAppGateway is AppGatewayBase { * @param instance1_ Address of the first ReadMultichain instance * @param instance2_ Address of the second ReadMultichain instance */ - function triggerAltRead(address instance1_, address instance2_) public async(bytes("")) { + function triggerAltRead(address instance1_, address instance2_) public async { _setOverrides(Read.ON, Parallel.ON); - for (uint256 i = 0; i < 10; i++) { + for (uint256 i = 0; i < numberOfRequests; i++) { if (i % 2 == 0) { IReadMultichain(instance1_).values(i); - IPromise(instance1_).then(this.handleValue.selector, abi.encode(i, instance1_)); + then(this.handleValue.selector, abi.encode(i, instance1_)); } else { IReadMultichain(instance2_).values(i); - IPromise(instance2_).then(this.handleValue.selector, abi.encode(i, instance2_)); + then(this.handleValue.selector, abi.encode(i, instance2_)); } } _setOverrides(Read.OFF, Parallel.OFF); @@ -123,8 +129,27 @@ contract ReadAppGateway is AppGatewayBase { * @param amount_ The amount to withdraw * @param receiver_ The address that will receive the withdrawn fees */ - function withdrawFeeTokens(uint32 chainSlug_, address token_, uint256 amount_, address receiver_) external { - _withdrawFeeTokens(chainSlug_, token_, amount_, receiver_); + function withdrawCredits(uint32 chainSlug_, address token_, uint256 amount_, address receiver_) external { + _withdrawCredits(chainSlug_, token_, amount_, receiver_); + } + + /** + * @notice Transfers fee credits from this contract to a specified address + * @dev Moves a specified amount of fee credits from the current contract to the given recipient + * @param to_ The address to transfer credits to + * @param amount_ The amount of credits to transfer + */ + function transferCredits(address to_, uint256 amount_) external { + feesManager__().transferCredits(address(this), to_, amount_); + } + + /** + * @notice Updates the fee max value + * @dev Allows modification of fee settings for multi-chain operations + * @param fees_ New fee configuration + */ + function setMaxFees(uint256 fees_) public { + maxFees = fees_; } /** diff --git a/src/revert/RevertAppGateway.sol b/src/revert/RevertAppGateway.sol index 5c592d6..5aae3ab 100644 --- a/src/revert/RevertAppGateway.sol +++ b/src/revert/RevertAppGateway.sol @@ -2,7 +2,6 @@ pragma solidity >=0.7.0 <0.9.0; import "socket-protocol/contracts/evmx/base/AppGatewayBase.sol"; -import "socket-protocol/contracts/evmx/interfaces/IPromise.sol"; import "./ICounter.sol"; import "./Counter.sol"; @@ -26,8 +25,9 @@ contract RevertAppGateway is AppGatewayBase { * @param addressResolver_ Address of the SOCKET Protocol's AddressResolver contract * @param fees_ Fee configuration for multi-chain operations */ - constructor(address addressResolver_, uint256 fees_) AppGatewayBase(addressResolver_) { + constructor(address addressResolver_, uint256 fees_) { creationCodeWithArgs[counter] = abi.encodePacked(type(Counter).creationCode); + _initializeAppGateway(addressResolver_); _setMaxFees(fees_); } @@ -36,7 +36,7 @@ contract RevertAppGateway is AppGatewayBase { * @dev Triggers an asynchronous multi-chain deployment via SOCKET Protocol * @param chainSlug_ The identifier of the target chain */ - function deployContracts(uint32 chainSlug_) external async(bytes("")) { + function deployContracts(uint32 chainSlug_) external async { _deploy(counter, chainSlug_, IsPlug.YES); } @@ -45,7 +45,7 @@ contract RevertAppGateway is AppGatewayBase { * @dev Sets up the validity of the deployed OnchainTrigger contract on the specified chain * @param chainSlug_ The identifier of the chain where the contract was deployed */ - function initialize(uint32 chainSlug_) public override async(bytes("")) { + function initializeOnChain(uint32 chainSlug_) public override async { address instance = forwarderAddresses[counter][chainSlug_]; ICounter(instance).increment(); } @@ -57,7 +57,7 @@ contract RevertAppGateway is AppGatewayBase { * unexistentFunction exists on the interface but not on the onchain contract. This will cause an onchain revert. * @param chainSlug A uint32 identifier for the target chain to test the revert on */ - function testOnChainRevert(uint32 chainSlug) public async(bytes("")) { + function testOnChainRevert(uint32 chainSlug) public async { address instance = forwarderAddresses[counter][chainSlug]; ICounter(instance).unexistentFunction(); } @@ -69,12 +69,12 @@ contract RevertAppGateway is AppGatewayBase { * notCorrectInputArgs that will revert due to wrong input parameters * @param chainSlug A uint32 identifier for the target chain to test the callback revert on */ - function testCallbackRevertWrongInputArgs(uint32 chainSlug) public async(bytes("")) { + function testCallbackRevertWrongInputArgs(uint32 chainSlug) public async { _setOverrides(Read.ON, Parallel.ON); address instance = forwarderAddresses[counter][chainSlug]; ICounter(instance).counter(); // wrong function input parameters for a callback - IPromise(instance).then(this.notCorrectInputArgs.selector, abi.encode(chainSlug)); + then(this.notCorrectInputArgs.selector, abi.encode(chainSlug)); _setOverrides(Read.OFF, Parallel.OFF); } @@ -97,7 +97,26 @@ contract RevertAppGateway is AppGatewayBase { * @param amount_ The amount to withdraw * @param receiver_ The address that will receive the withdrawn fees */ - function withdrawFeeTokens(uint32 chainSlug_, address token_, uint256 amount_, address receiver_) external { - _withdrawFeeTokens(chainSlug_, token_, amount_, receiver_); + function withdrawCredits(uint32 chainSlug_, address token_, uint256 amount_, address receiver_) external { + _withdrawCredits(chainSlug_, token_, amount_, receiver_); + } + + /** + * @notice Transfers fee credits from this contract to a specified address + * @dev Moves a specified amount of fee credits from the current contract to the given recipient + * @param to_ The address to transfer credits to + * @param amount_ The amount of credits to transfer + */ + function transferCredits(address to_, uint256 amount_) external { + feesManager__().transferCredits(address(this), to_, amount_); + } + + /** + * @notice Updates the fee max value + * @dev Allows modification of fee settings for multi-chain operations + * @param fees_ New fee configuration + */ + function setMaxFees(uint256 fees_) public { + maxFees = fees_; } } diff --git a/src/schedule/ScheduleAppGateway.sol b/src/schedule/ScheduleAppGateway.sol index 9164f45..3476b0a 100644 --- a/src/schedule/ScheduleAppGateway.sol +++ b/src/schedule/ScheduleAppGateway.sol @@ -11,18 +11,18 @@ import "socket-protocol/contracts/evmx/base/AppGatewayBase.sol"; */ contract ScheduleAppGateway is AppGatewayBase { /** - * @notice Array of timeout durations in seconds + * @notice Array of schedule durations in seconds * @dev These values define the delay periods in seconds for scheduled executions */ - uint256[] public timeoutsInSeconds = [1, 20, 60, 120, 600, 1200, 5000]; + uint256[] public schedulesInSeconds = [1, 20, 60, 120, 600, 1200, 5000]; /** - * @notice Emitted when a scheduled timeout is resolved - * @param index The index of the timeout in the timeoutsInSeconds array - * @param creationTimestamp The timestamp when the timeout was created - * @param executionTimestamp The timestamp when the timeout was executed + * @notice Emitted when a scheduled schedule is resolved + * @param index The index of the schedule in the schedulesInSeconds array + * @param creationTimestamp The timestamp when the schedule was created + * @param executionTimestamp The timestamp when the schedule was executed */ - event TimeoutResolved(uint256 index, uint256 creationTimestamp, uint256 executionTimestamp); + event ScheduleResolved(uint256 index, uint256 creationTimestamp, uint256 executionTimestamp); /** * @notice Constructs the ScheduleAppGateway @@ -30,7 +30,8 @@ contract ScheduleAppGateway is AppGatewayBase { * @param addressResolver_ Address of the SOCKET Protocol's AddressResolver contract * @param fees_ Fee configuration for onchain operations */ - constructor(address addressResolver_, uint256 fees_) AppGatewayBase(addressResolver_) { + constructor(address addressResolver_, uint256 fees_) { + _initializeAppGateway(addressResolver_); _setMaxFees(fees_); } @@ -39,7 +40,7 @@ contract ScheduleAppGateway is AppGatewayBase { * @dev This function is a placeholder for the ScheduleAppGateway since no contracts need deployment * The chainSlug parameter is required by the interface but not used. */ - function deployContracts(uint32) external async(bytes("")) { + function deployContracts(uint32) external async { return; } @@ -48,29 +49,29 @@ contract ScheduleAppGateway is AppGatewayBase { * @dev No initialization needed for this application, so implementation is empty. * The chainSlug parameter is required by the interface but not used. */ - function initialize(uint32) public pure override { + function initializeOnChain(uint32) public pure override { return; } /** - * @notice Triggers multiple timeouts with different delay periods - * @dev Sets up scheduled calls to resolveTimeout with various delay periods defined in timeoutsInSeconds + * @notice Triggers multiple schedules with different delay periods + * @dev Sets up scheduled calls to resolveSchedule with various delay periods defined in schedulesInSeconds */ - function triggerTimeouts() public { - for (uint256 i = 0; i < timeoutsInSeconds.length; i++) { - bytes memory payload = abi.encodeWithSelector(this.resolveTimeout.selector, i, block.timestamp); - watcherPrecompile__().setTimeout(timeoutsInSeconds[i], payload); + function triggerSchedules() public async { + for (uint256 i = 0; i < schedulesInSeconds.length; i++) { + _setSchedule(schedulesInSeconds[i]); + then(this.resolveSchedule.selector, abi.encode(i, block.timestamp)); } } /** - * @notice Callback function executed when a timeout is reached - * @dev Emits a TimeoutResolved event with timing information - * @param index_ The index of the timeout in the timeoutsInSeconds array - * @param creationTimestamp_ The timestamp when the timeout was created + * @notice Callback function executed when a schedule is reached + * @dev Emits a ScheduleResolved event with timing information + * @param index_ The index of the schedule in the schedulesInSeconds array + * @param creationTimestamp_ The timestamp when the schedule was created */ - function resolveTimeout(uint256 index_, uint256 creationTimestamp_) public { - emit TimeoutResolved(index_, creationTimestamp_, block.timestamp); + function resolveSchedule(uint256 index_, uint256 creationTimestamp_) public { + emit ScheduleResolved(index_, creationTimestamp_, block.timestamp); } /** @@ -81,7 +82,26 @@ contract ScheduleAppGateway is AppGatewayBase { * @param amount_ The amount to withdraw * @param receiver_ The address that will receive the withdrawn fees */ - function withdrawFeeTokens(uint32 chainSlug_, address token_, uint256 amount_, address receiver_) external { - _withdrawFeeTokens(chainSlug_, token_, amount_, receiver_); + function withdrawCredits(uint32 chainSlug_, address token_, uint256 amount_, address receiver_) external { + _withdrawCredits(chainSlug_, token_, amount_, receiver_); + } + + /** + * @notice Transfers fee credits from this contract to a specified address + * @dev Moves a specified amount of fee credits from the current contract to the given recipient + * @param to_ The address to transfer credits to + * @param amount_ The amount of credits to transfer + */ + function transferCredits(address to_, uint256 amount_) external { + feesManager__().transferCredits(address(this), to_, amount_); + } + + /** + * @notice Updates the fee max value + * @dev Allows modification of fee settings for multi-chain operations + * @param fees_ New fee configuration + */ + function setMaxFees(uint256 fees_) public { + maxFees = fees_; } } diff --git a/src/trigger-appgateway-onchain/OnchainTriggerAppGateway.sol b/src/trigger-appgateway-onchain/OnchainTriggerAppGateway.sol index b533da9..4054550 100644 --- a/src/trigger-appgateway-onchain/OnchainTriggerAppGateway.sol +++ b/src/trigger-appgateway-onchain/OnchainTriggerAppGateway.sol @@ -48,8 +48,9 @@ contract OnchainTriggerAppGateway is AppGatewayBase { * @param addressResolver_ Address of the SOCKET Protocol's AddressResolver contract * @param fees_ Fee configuration for multi-chain operations */ - constructor(address addressResolver_, uint256 fees_) AppGatewayBase(addressResolver_) { + constructor(address addressResolver_, uint256 fees_) { creationCodeWithArgs[onchainToEVMx] = abi.encodePacked(type(OnchainTrigger).creationCode); + _initializeAppGateway(addressResolver_); _setMaxFees(fees_); } @@ -58,7 +59,7 @@ contract OnchainTriggerAppGateway is AppGatewayBase { * @dev Triggers an asynchronous multi-chain deployment via SOCKET Protocol * @param chainSlug_ The identifier of the target chain */ - function deployContracts(uint32 chainSlug_) external async(bytes("")) { + function deployContracts(uint32 chainSlug_) external async { _deploy(onchainToEVMx, chainSlug_, IsPlug.YES); } @@ -67,9 +68,8 @@ contract OnchainTriggerAppGateway is AppGatewayBase { * @dev Sets up the validity of the deployed OnchainTrigger contract on the specified chain * @param chainSlug_ The identifier of the chain where the contract was deployed */ - function initialize(uint32 chainSlug_) public override { - address onchainAddress = getOnChainAddress(onchainToEVMx, chainSlug_); - watcherPrecompileConfig().setIsValidPlug(chainSlug_, onchainAddress, true); + function initializeOnChain(uint32 chainSlug_) public override { + _setValidPlug(true, chainSlug_, onchainToEVMx); } /** @@ -77,7 +77,7 @@ contract OnchainTriggerAppGateway is AppGatewayBase { * @dev Sends the current valueOnGateway to the OnchainTrigger contract on the specified chain * @param targetChain The identifier of the destination chain */ - function updateOnchain(uint32 targetChain) public async(bytes("")) { + function updateOnchain(uint32 targetChain) public async { address onchainToEVMxForwarderAddress = forwarderAddresses[onchainToEVMx][targetChain]; IOnchainTrigger(onchainToEVMxForwarderAddress).updateFromGateway(valueOnGateway); } @@ -88,7 +88,7 @@ contract OnchainTriggerAppGateway is AppGatewayBase { * The onlyWatcherPrecompile modifier ensures the function can only be called by the watcher * @param value Value to update from the onchain contract on AppGateway */ - function callFromChain(uint256 value) external async(bytes("")) onlyWatcherPrecompile { + function callFromChain(uint256 value) external onlyWatcher { valueOnGateway += value; } @@ -99,7 +99,7 @@ contract OnchainTriggerAppGateway is AppGatewayBase { * @param value Value to update on the other OnchainTrigger contract * @param targetChain Chain where the value should be updated */ - function propagateToChain(uint256 value, uint32 targetChain) external async(bytes("")) onlyWatcherPrecompile { + function propagateToChain(uint256 value, uint32 targetChain) external async onlyWatcher { address onchainToEVMxForwarderAddress = forwarderAddresses[onchainToEVMx][targetChain]; IOnchainTrigger(onchainToEVMxForwarderAddress).updateFromGateway(value); } @@ -112,7 +112,26 @@ contract OnchainTriggerAppGateway is AppGatewayBase { * @param amount_ The amount to withdraw * @param receiver_ The address that will receive the withdrawn fees */ - function withdrawFeeTokens(uint32 chainSlug_, address token_, uint256 amount_, address receiver_) external { - _withdrawFeeTokens(chainSlug_, token_, amount_, receiver_); + function withdrawCredits(uint32 chainSlug_, address token_, uint256 amount_, address receiver_) external { + _withdrawCredits(chainSlug_, token_, amount_, receiver_); + } + + /** + * @notice Transfers fee credits from this contract to a specified address + * @dev Moves a specified amount of fee credits from the current contract to the given recipient + * @param to_ The address to transfer credits to + * @param amount_ The amount of credits to transfer + */ + function transferCredits(address to_, uint256 amount_) external { + feesManager__().transferCredits(address(this), to_, amount_); + } + + /** + * @notice Updates the fee max value + * @dev Allows modification of fee settings for multi-chain operations + * @param fees_ New fee configuration + */ + function setMaxFees(uint256 fees_) public { + maxFees = fees_; } } diff --git a/src/write/IWriteMultichain.sol b/src/write/IWriteMultichain.sol index 33a924e..1cc0f60 100644 --- a/src/write/IWriteMultichain.sol +++ b/src/write/IWriteMultichain.sol @@ -18,13 +18,4 @@ interface IWriteMultichain { * @dev Can only be called by authorized accounts via the SOCKET Protocol */ function increase() external; - - /** - * @notice Connects the contract to the SOCKET Protocol - * @dev Sets up the contract for EVMx communication - * @param appGateway_ Address of the application gateway contract - * @param socket_ Address of the SOCKET Protocol contract - * @param switchboard_ Address of the switchboard contract - */ - function connectSocket(address appGateway_, address socket_, address switchboard_) external; } diff --git a/src/write/WriteAppGateway.sol b/src/write/WriteAppGateway.sol index 1f5377f..1e89635 100644 --- a/src/write/WriteAppGateway.sol +++ b/src/write/WriteAppGateway.sol @@ -13,6 +13,12 @@ import "./WriteMultichain.sol"; * Inherits from AppGatewayBase for SOCKET Protocol integration. */ contract WriteAppGateway is AppGatewayBase { + /** + * @notice Number of requests to call onchain + * @dev Used to maximize number of requests done + */ + uint256 public numberOfRequests = 10; + /** * @notice Identifier for the WriteMultichain contract * @dev Used to track WriteMultichain contract instances across chains @@ -33,8 +39,9 @@ contract WriteAppGateway is AppGatewayBase { * @param addressResolver_ Address of the SOCKET Protocol's AddressResolver contract * @param fees_ Fee configuration for multi-chain operations */ - constructor(address addressResolver_, uint256 fees_) AppGatewayBase(addressResolver_) { + constructor(address addressResolver_, uint256 fees_) { creationCodeWithArgs[multichain] = abi.encodePacked(type(WriteMultichain).creationCode); + _initializeAppGateway(addressResolver_); _setMaxFees(fees_); } @@ -43,7 +50,7 @@ contract WriteAppGateway is AppGatewayBase { * @dev Triggers an asynchronous multi-chain deployment via SOCKET Protocol * @param chainSlug_ The identifier of the target chain */ - function deployContracts(uint32 chainSlug_) external async(bytes("")) { + function deployContracts(uint32 chainSlug_) external async { _deploy(multichain, chainSlug_, IsPlug.YES); } @@ -52,7 +59,7 @@ contract WriteAppGateway is AppGatewayBase { * @dev No initialization needed for this application, so implementation is empty. * The chainSlug parameter is required by the interface but not used. */ - function initialize(uint32) public pure override { + function initializeOnChain(uint32) public pure override { return; } @@ -61,10 +68,10 @@ contract WriteAppGateway is AppGatewayBase { * @dev Calls the increase function 10 times in sequence and processes the return values * @param instance_ Address of the WriteMultichain instance to write to */ - function triggerSequentialWrite(address instance_) public async(bytes("")) { - for (uint256 i = 0; i < 10; i++) { + function triggerSequentialWrite(address instance_) public async { + for (uint256 i = 0; i < numberOfRequests; i++) { IWriteMultichain(instance_).increase(); - IPromise(instance_).then(this.handleValue.selector, abi.encode(i, instance_)); + then(this.handleValue.selector, abi.encode(i, instance_)); } } @@ -73,11 +80,11 @@ contract WriteAppGateway is AppGatewayBase { * @dev Calls the increase function 10 times in parallel and processes the return values * @param instance_ Address of the WriteMultichain instance to write to */ - function triggerParallelWrite(address instance_) public async(bytes("")) { + function triggerParallelWrite(address instance_) public async { _setOverrides(Parallel.ON); - for (uint256 i = 0; i < 10; i++) { + for (uint256 i = 0; i < numberOfRequests; i++) { IWriteMultichain(instance_).increase(); - IPromise(instance_).then(this.handleValue.selector, abi.encode(i, instance_)); + then(this.handleValue.selector, abi.encode(i, instance_)); } _setOverrides(Parallel.OFF); } @@ -88,12 +95,15 @@ contract WriteAppGateway is AppGatewayBase { * @param instance1_ Address of the first WriteMultichain instance * @param instance2_ Address of the second WriteMultichain instance */ - function triggerAltWrite(address instance1_, address instance2_) public async(bytes("")) { - for (uint256 i = 0; i < 5; i++) { - IWriteMultichain(instance1_).increase(); - IPromise(instance1_).then(this.handleValue.selector, abi.encode(i, instance1_)); - IWriteMultichain(instance2_).increase(); - IPromise(instance2_).then(this.handleValue.selector, abi.encode(i, instance2_)); + function triggerAltWrite(address instance1_, address instance2_) public async { + for (uint256 i = 0; i < numberOfRequests; i++) { + if (i % 2 == 0) { + IWriteMultichain(instance1_).increase(); + then(this.handleValue.selector, abi.encode(i, instance1_)); + } else { + IWriteMultichain(instance2_).increase(); + then(this.handleValue.selector, abi.encode(i, instance2_)); + } } } @@ -117,8 +127,18 @@ contract WriteAppGateway is AppGatewayBase { * @param amount_ The amount to withdraw * @param receiver_ The address that will receive the withdrawn fees */ - function withdrawFeeTokens(uint32 chainSlug_, address token_, uint256 amount_, address receiver_) external { - _withdrawFeeTokens(chainSlug_, token_, amount_, receiver_); + function withdrawCredits(uint32 chainSlug_, address token_, uint256 amount_, address receiver_) external { + _withdrawCredits(chainSlug_, token_, amount_, receiver_); + } + + /** + * @notice Transfers fee credits from this contract to a specified address + * @dev Moves a specified amount of fee credits from the current contract to the given recipient + * @param to_ The address to transfer credits to + * @param amount_ The amount of credits to transfer + */ + function transferCredits(address to_, uint256 amount_) external { + feesManager__().transferCredits(address(this), to_, amount_); } /** diff --git a/test/Dummy.t.sol b/test/Dummy.t.sol index 1d0e27e..5707dd4 100644 --- a/test/Dummy.t.sol +++ b/test/Dummy.t.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.21; -import "socket-protocol/test/DeliveryHelper.t.sol"; +import "socket-protocol/test/SetupTest.t.sol"; -contract DummyTest is DeliveryHelperTest { +contract DummyTest is AppGatewayBaseSetup { function testDummy() external {} }