diff --git a/CI/envs/bridge-decentralized/index.js b/CI/envs/bridge-decentralized/index.js index abe929c..8c3e95d 100644 --- a/CI/envs/bridge-decentralized/index.js +++ b/CI/envs/bridge-decentralized/index.js @@ -6,12 +6,18 @@ const { compileWithVersion, deployContract } = require('@lib/web3/deploy'); +const Mutex = require('async-mutex').Mutex; +const Semaphore = require('async-mutex').Semaphore; +const withTimeout = require('async-mutex').withTimeout; const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, 'contracts', 'src', 'cross-chain'); +const { sleep } = require('@lib/os/process'); + let tokenSource = fs.readFileSync(path.join(contractsDir, 'CrossToken.sol'), 'utf8'); -let transactions = 0; +const mutex = new Mutex(); +let lag; @@ -33,7 +39,8 @@ const blockchains = new Map(); * @returns {Object} An object containing the sourceContract, tokenCrontract and destinationContract instances. * @throws {Error} If there's an error during the deployment. */ -async function deployBridge(web3, envInfo, name, nativeToken, router, vault, oracle, bridgeForwards, bridgeForwardsERC20){ +async function deployBridge(web3, envInfo, name, nativeToken, router, vault, oracle, bridgeForwards, bridgeForwardsERC20, bridgeDelay = 0){ + lag = bridgeDelay; let routerSource = fs.readFileSync(path.join(contractsDir, router+'.sol'), 'utf8'); let vaultSource = fs.readFileSync(path.join(contractsDir, vault+'.sol'), 'utf8'); let oracleSource = fs.readFileSync(path.join(contractsDir, oracle+'.sol'), 'utf8'); @@ -84,8 +91,8 @@ async function deployBridge(web3, envInfo, name, nativeToken, router, vault, ora //Add event listeners to relay transactions routerContract.events.allEvents().on('data', (data) => { - console.log(JSON.stringify(data)); handleEvent(data, name); + }); return { @@ -145,58 +152,64 @@ async function handleEvent(data, name){ //Handles all calls of the deposit function of the router contract, currently supports swap and add. async function deposit(data, name){ - let memo = parseMemo(data.returnValues.memo); - - let target = blockchains.get(memo.chain); - try { - if(target){ //If blockchain exists - switch(memo.operation){ - case "=": - case "SWAP": - let offset = transactions++; - let nonce = await target.web3.eth.getTransactionCount(target.signer, "pending") + offset; - //console.log(nonce); - //If 0x0 token, represent it as the name of the native token, eg. ETH or AVAX - let sourceAsset = data.returnValues.asset == "0x0000000000000000000000000000000000000000" ? name + '.' + blockchains.get(name).nativeToken : name + '.' + data.returnValues.asset; - //If native token, represent as 0x0 - let targetToken = memo.asset == target.nativeToken ? "0x0000000000000000000000000000000000000000" : memo.asset; - //let amount = getExchange(sourceAsset, memo.asset, data.returnValues.amount); - let receipt; - if (targetToken == "0x0000000000000000000000000000000000000000"){ - receipt = await target.bridgeForwards(memo.destaddr, targetToken, data.returnValues.amount, sourceAsset, "OUT:" + memo.destaddr).send({ - from: target.signer, - gas: 300000, - nonce: nonce - }); - } - else{ - receipt = await target.bridgeForwardsERC20(memo.destaddr, targetToken, data.returnValues.amount, sourceAsset, memo.assetName, "OUT:" + memo.destaddr).send({ - from: target.signer, - gas: 300000, - nonce: nonce - }); - } - transactions--; - if(receipt.status){ + + let memo = parseMemo(data.returnValues.memo); + let target = blockchains.get(memo.chain); + const release = await mutex.acquire(); + //await sleep(12000); + try { + if(target){ //If blockchain exists + switch(memo.operation){ + case "=": + case "SWAP": + //console.log(nonce); + //If 0x0 token, represent it as the name of the native token, eg. ETH or AVAX + let sourceAsset = data.returnValues.asset == "0x0000000000000000000000000000000000000000" ? name + '.' + blockchains.get(name).nativeToken : name + '.' + data.returnValues.asset; + //If native token, represent as 0x0 + let targetToken = memo.asset == target.nativeToken ? "0x0000000000000000000000000000000000000000" : memo.asset; + //let amount = getExchange(sourceAsset, memo.asset, data.returnValues.amount); + let receipt; + let expiry = Math.floor(Date.now() / 1000) + 10; + if(lag !=0){ + await sleep(lag); + } + if (targetToken == "0x0000000000000000000000000000000000000000"){ + receipt = await target.bridgeForwards(memo.destaddr, targetToken, data.returnValues.amount, sourceAsset, "OUT:" + memo.destaddr, expiry).send({ + from: target.signer, + gas: 300000, + }); + } + else{ + receipt = await target.bridgeForwardsERC20(memo.destaddr, targetToken, data.returnValues.amount, sourceAsset, memo.assetName, "OUT:" + memo.destaddr, expiry).send({ + from: target.signer, + gas: 300000, + }); + } + if(receipt.status){ + return; + } + break; + case "ADD": //Liquidity was added to the vault, no relaying necessary return; - } - break; - case "ADD": //Liquidity was added to the vault, no relaying necessary - return; - default: - break; + default: + break; + } } + } catch (error) { + console.log(error); + console.log("Could not relay transaction"); + //If above section did not return, refund the transaction, could implement some kind of fee to stop spamming + /* console.log("Issuing refund"); + target = blockchains.get(name); + let receipt = await target.vault.methods.bridgeForwards(data.returnValues.from, data.returnValues.asset, data.returnValues.amount, "REFUND:" + data.returnValues.from).send({ + from: target.signer, + gas: 300000, + }); */ + } finally { + release(); } - } catch (error) { - console.log(error); - console.log("Could not relay transaction"); - } - //If above section did not return, refund the transaction, could implement some kind of fee to stop spamming - /* target = blockchains.get(name); - let receipt = await target.vault.methods.bridgeForwards(data.returnValues.from, data.returnValues.asset, data.returnValues.amount, "REFUND:" + data.returnValues.from).send({ - from: target.signer, - gas: 300000, - }); */ + + } module.exports = deployBridge; \ No newline at end of file diff --git a/CI/setup-cross-chain-unified.js b/CI/setup-cross-chain-unified.js index aeda545..d93437e 100644 --- a/CI/setup-cross-chain-unified.js +++ b/CI/setup-cross-chain-unified.js @@ -24,7 +24,7 @@ const bridgeTestLogger = getLogger('bridgetest'); // Manually configured two cross-chain exploits for the tool paper; const exploitsList = [ 'tests/Bridge.exploit1.js', -// 'tests/Bridge.exploit2.js', + //'tests/Bridge.exploit2.js', ]; async function setupAndRunTests() { diff --git a/CI/tests/Bridge.exploit1.js b/CI/tests/Bridge.exploit1.js index 15c744e..ec0c87d 100644 --- a/CI/tests/Bridge.exploit1.js +++ b/CI/tests/Bridge.exploit1.js @@ -148,7 +148,6 @@ async function startUp() { let routerSourceA = fs.readFileSync(path.join('contracts', 'src', 'cross-chain', 'EthRouterVulnerability1.sol'), 'utf8'); let routerABIA = await getContractABI(routerSourceA, 'EthRouterVulnerability1', 'EthRouterVulnerability1'); - console.log(routerABIA); let vaultSourceA = fs.readFileSync(path.join('contracts', 'src', 'cross-chain', 'EthVaultOracle.sol'), 'utf8'); let vaultABIA = await getContractABI(vaultSourceA, 'EthVaultOracle', 'EthVaultOracle'); diff --git a/CI/tests/Bridge.exploit2.js b/CI/tests/Bridge.exploit2.js index 12b864c..c39b94b 100644 --- a/CI/tests/Bridge.exploit2.js +++ b/CI/tests/Bridge.exploit2.js @@ -6,16 +6,17 @@ const chalk = require('chalk'); const { extractSolcVersion, compileWithVersion, - deployContract + deployContract, + getContractABI, } = require('@lib/web3/deploy'); const setupAvalancheEnv = require('@envs/avalanche-subnet'); const setupAnvilEnv = require('@envs/anvil'); const deployBridge = require('@envs/bridge-decentralized'); const { sleep } = require('@lib/os/process'); -const { getTime } = require('date-fns'); - +const Monitor = require('@monitor/multi-chain-monitor'); const getLogger = require('@lib/logging/logger').getLogger; const bridgeTestLogger = getLogger('bridgetest'); +const { getActivities } = require('@lib/dcr/info'); async function sequence(contractsA, contractsB, web3A, web3B){ @@ -82,7 +83,7 @@ async function sequence(contractsA, contractsB, web3A, web3B){ let balance = await web3B.eth.getBalance(vaultB._address); console.log(balance); - let expiry = Math.floor(Date.now() / 1000) - 100; + let expiry = Math.floor(Date.now() / 1000) + 100; console.log(expiry); let receipt1 = await routerA.methods.eth_depositWithExpiry(vaultA._address, "0x0000000000000000000000000000000000000000", 0, "SWAP:B.AVAX:" + accountB, expiry).send({ @@ -144,17 +145,72 @@ async function startUp() { bridgeTestLogger.debug("Web3 A: " + envAnvil.envInfo.rpcAddress); bridgeTestLogger.debug("Web3 B: " + envAvalanche.envInfo.rpcAddress); - let contractsA = await deployBridge(envAnvil.web3, envAnvil.envInfo, 'A', 'ETH', 'EthRouterVulnerability2', 'EthVaultOracle', 'Oracle', 'eth_bridgeForwards', 'eth_bridgeForwardsERC20'); - let contractsB = await deployBridge(envAvalanche.web3, envAvalanche.envInfo, 'B', 'AVAX', 'AvaxRouter', 'AvaxVaultOracle', 'Oracle', 'avax_bridgeForwards', 'avax_bridgeForwardsERC20'); - - let contractSourceA = fs.readFileSync(path.join('contracts', 'src', 'cross-chain', 'EthRouter.sol'), 'utf8'); - - let contractSourceB = fs.readFileSync(path.join('contracts', 'src', 'cross-chain', 'AvaxRouter.sol'), 'utf8'); - - bridgeTestLogger.debug("Executing sequence"); - let execution = await sequence(contractsA, contractsB, envAnvil.web3, envAvalanche.web3); - bridgeTestLogger.debug("Done"); - + let contractsA = await deployBridge(envAnvil.web3, envAnvil.envInfo, 'A', 'ETH', 'EthRouter', 'EthVaultOracle', 'Oracle', 'eth_bridgeForwards', 'eth_bridgeForwardsERC20', 11000); + let contractsB = await deployBridge(envAvalanche.web3, envAvalanche.envInfo, 'B', 'AVAX', 'AvaxRouter', 'AvaxVaultOracleVulnerability2', 'Oracle', 'avax_bridgeForwards', 'avax_bridgeForwardsERC20', 11000); + + let routerSourceA = fs.readFileSync(path.join('contracts', 'src', 'cross-chain', 'EthRouter.sol'), 'utf8'); + let routerABIA = await getContractABI(routerSourceA, 'EthRouter', 'EthRouter'); + + let vaultSourceA = fs.readFileSync(path.join('contracts', 'src', 'cross-chain', 'EthVaultOracle.sol'), 'utf8'); + let vaultABIA = await getContractABI(vaultSourceA, 'EthVaultOracle', 'EthVaultOracle'); + + let routerSourceB = fs.readFileSync(path.join('contracts', 'src', 'cross-chain', 'AvaxRouter.sol'), 'utf8'); + let routerABIB = await getContractABI(routerSourceB, 'AvaxRouter', 'AvaxRouter'); + + let vaultSourceB = fs.readFileSync(path.join('contracts', 'src', 'cross-chain', 'AvaxVaultOracleVulnerability2.sol'), 'utf8'); + let vaultABIB = await getContractABI(vaultSourceB, 'AvaxVaultOracleVulnerability2', 'AvaxVaultOracleVulnerability2'); + + // Let's start the monitor here + let configs = { + "contracts": [{ + web3: envAnvil.web3, + contractAddress: contractsA.vault._address, + contractFileName: 'EthVaultOracle', + contractName: 'EthVaultOracle', + contractABI: vaultABIA, + modelFunctionParams: null, + }, + { + web3: envAnvil.web3, + contractAddress: contractsA.router._address, + contractFileName: 'EthRouterVulnerability1', + contractName: 'EthRouterVulnerability1', + contractABI: routerABIA, + modelFunctionParams: null, + }, + { + web3: envAvalanche.web3, + contractAddress: contractsB.vault._address, + contractFileName: 'AvaxVaultOracle', + contractName: 'AvaxVaultOracle', + contractABI: vaultABIB, + modelFunctionParams: null, + }, + { + web3: envAvalanche.web3, + contractAddress: contractsB.router._address, + contractFileName: 'AvaxRouter', + contractName: 'AvaxRouter', + contractABI: routerABIB, + modelFunctionParams: null, + }], + activities: await getActivities(1823976), + modelId: 1823976, + hasResponseRelation: true, + modelName: "CrossChainDEX", + }; + let monitor = new Monitor(configs); + + monitor.on('statusChange', async (newStatus) => { + if (newStatus === 'INITIALIZED') { + bridgeTestLogger.debug(`Monitor is initialized...`); + monitor.start(); + } else if (newStatus == 'RUNNING') { + bridgeTestLogger.debug("Executing sequence"); + let execution = await sequence(contractsA, contractsB, envAnvil.web3, envAvalanche.web3); + bridgeTestLogger.debug("Done"); + } + }); } //startUp(); diff --git a/contracts/src/cross-chain/AvaxVaultOracle.sol b/contracts/src/cross-chain/AvaxVaultOracle.sol index 521e9d1..7ab8db6 100644 --- a/contracts/src/cross-chain/AvaxVaultOracle.sol +++ b/contracts/src/cross-chain/AvaxVaultOracle.sol @@ -43,8 +43,10 @@ contract AvaxVaultOracle { address asset, uint amountPaid, string memory sourceAsset, - string memory memo + string memory memo, + uint expiration ) external onlyOwner { + require(block.timestamp < expiration); uint amount = (oracleContract.getPrice(sourceAsset) * amountPaid)/1000; require( address(this).balance >= amount, @@ -64,8 +66,10 @@ contract AvaxVaultOracle { uint amountPaid, string memory sourceAsset, string memory targetAsset,//has format AVAX.0x12341... - string memory memo + string memory memo, + uint expiration ) external onlyOwner { + require(block.timestamp < expiration); uint amount = oracleContract.getExchangeRate(amountPaid, sourceAsset, targetAsset); routerContract.avax_payOut(to, asset, amount, memo); } diff --git a/contracts/src/cross-chain/AvaxVaultOracleVulnerability2.sol b/contracts/src/cross-chain/AvaxVaultOracleVulnerability2.sol new file mode 100644 index 0000000..0e46357 --- /dev/null +++ b/contracts/src/cross-chain/AvaxVaultOracleVulnerability2.sol @@ -0,0 +1,76 @@ +pragma solidity ^0.8.20; + +interface AvaxRouter { + function avax_payOut( + address payable to, + address asset, + uint amount, + string memory memo + ) external payable; +} + +interface Oracle { + function getExchangeRate(uint amount, string memory sourceAsset, string memory targetAsset) external view returns (uint); + function getPrice(string memory asset) external view returns (uint256); +} + +contract AvaxVaultOracleVulnerability2 { + address owner; + AvaxRouter routerContract; + Oracle oracleContract; + + constructor(address _routerContract, address _oracleContract) { + owner = msg.sender; + routerContract = AvaxRouter(_routerContract); + oracleContract = Oracle(_oracleContract); + } + + modifier onlyOwner() { + require(msg.sender == owner, "Only the owner of this contract can call this function"); + _; + } + + //Receive funds when msg.data is empty + receive() external payable {} + + //Receive funds when msg.data is not empty + fallback() external payable {} + + function fund() external payable {} + + function avax_bridgeForwards( + address payable to, + address asset, + uint amountPaid, + string memory sourceAsset, + string memory memo, + uint expiration + ) external onlyOwner { + require(block.timestamp > expiration); + uint amount = (oracleContract.getPrice(sourceAsset) * amountPaid)/1000; + require( + address(this).balance >= amount, + "Vault has insufficient funds" + ); + routerContract.avax_payOut{value: amount}( + to, + asset, + amount, + memo + ); + } + + function avax_bridgeForwardsERC20( + address payable to, + address asset, //actual address of asset + uint amountPaid, + string memory sourceAsset, + string memory targetAsset,//has format AVAX.0x12341... + string memory memo, + uint expiration + ) external onlyOwner { + require(block.timestamp < expiration); + uint amount = oracleContract.getExchangeRate(amountPaid, sourceAsset, targetAsset); + routerContract.avax_payOut(to, asset, amount, memo); + } +} diff --git a/contracts/src/cross-chain/EthRouterVulnerability2.sol b/contracts/src/cross-chain/EthRouterVulnerability2.sol deleted file mode 100644 index 592f38b..0000000 --- a/contracts/src/cross-chain/EthRouterVulnerability2.sol +++ /dev/null @@ -1,155 +0,0 @@ -pragma solidity ^0.8.20; - -interface iERC20 { - function balanceOf(address) external view returns (uint256); - function burn(uint256) external; - function transferFrom(address, address, uint256) external returns (bool); - function transfer(address, uint256) external returns (bool success); -} - -contract EthRouterVulnerability2 { - //This keeps track of bridge-owned token, is burned - address public CrossToken; - - //Keeps track of how many of each token that belongs to the vault vaultAllowance[vault][memo] - mapping(address => mapping(address => uint)) private vaultAllowance; - - //Variable to keep track if function has been entered before forr reentrancy guard - bool private entered; - - //Checks for reentrancy - modifier reentrancyGuard() { - require(!entered, "Reentrancy not allowed"); - entered = true; - _; - entered = false; - } - - constructor(address crosstoken) { - CrossToken = crosstoken; - entered = false; - } - - //Memo has format: PAYLOAD:CHAIN.ASSET:DESTADDR - //ex for transferring to ETH chain with Eth asset to address: - // SWAP:ETH.ETH:0xe6a30f4f3bad978910e2cbb4d97581f5b5a0ade0 - - //ex for adding liquidity to a vault: - //ADD:_._:_ - - event Deposit( - address indexed from, - address indexed to, - address indexed asset, - uint amount, - string memo - ); - - event PayOut( - address indexed vault, - address indexed to, - address asset, - uint amount, - string memo - ); - - //Used to give ERC20 funds to router and give a vault the allowance, no cross chain transaction is performed - function eth_fundERC20( - address vault, - address asset, - uint amount, - string memory memo - ) public reentrancyGuard{ - uint depositAmount = amount; - recieveERC20(msg.sender, asset, depositAmount); - vaultAllowance[vault][asset] += depositAmount; - - } - - //Used to deposit funds into a vault controlled by the relay - function eth_deposit( - address payable vault, - address asset, - uint amount, - string memory memo - ) public payable reentrancyGuard { - uint depositAmount; - if (asset == address(0)) { - //If native token - depositAmount = msg.value; - bool success = vault.send(depositAmount); - require(success, "Could not send native cryptocurrency to address"); - } - /* else if (asset == CrossToken){ //If token we can mint and burn - require(msg.value == 0, "Native cryptocurrency recieved along with CrossToken"); - depositAmount = amount; - iERC20(CrossToken).transferFrom(msg.sender, address(this), depositAmount); - iERC20(CrossToken).burn(depositAmount); - } */ - else { - //If ERC20 token - require( - msg.value == 0, - "Native cryptocurrency recieved along with ERC20" - ); - depositAmount = amount; - recieveERC20(msg.sender, asset, depositAmount); - vaultAllowance[vault][asset] += depositAmount; - } - emit Deposit(msg.sender, vault, asset, depositAmount, memo); - } - - function eth_depositWithExpiry( - address payable vault, - address asset, - uint amount, - string memory memo, - uint expiration - ) external payable { - require(block.timestamp > expiration, "Deposit request expired"); - eth_deposit(vault, asset, amount, memo); - } - - //Called by a vault to pay out funds to indicated address - function eth_payOut( - address payable to, - address asset, - uint amount, - string memory memo - ) public payable reentrancyGuard { - uint payAmount; - if (asset == address(0)) { - payAmount = msg.value; - bool success = to.send(payAmount); - require(success, "Could not pay out native cryptocurrency"); - } else { - payAmount = amount; - require( - vaultAllowance[msg.sender][asset] >= payAmount, - "ERC20 amount exceeds allowance" - ); - sendERC20(to, asset, payAmount); - vaultAllowance[msg.sender][asset] -= payAmount; - } - emit PayOut(msg.sender, to, asset, payAmount, memo); - } - - //Helper Functions - - //Withdraws specified amount of specified ERC20 from address - //The payer needs to approve the transaction beforehand - function recieveERC20(address from, address asset, uint amount) internal { - bool success = iERC20(asset).transferFrom(from, address(this), amount); - require(success, "Could not take receive ERC20 tokens"); - } - - //Sends specified amount of specified ERC20 to address - function sendERC20( - address payable to, - address asset, - uint amount - ) internal { - bool success = iERC20(asset).transfer(to, amount); - require(success, "Could not transfer ERC20 to specified address"); - } -} diff --git a/contracts/src/cross-chain/EthVaultOracle.sol b/contracts/src/cross-chain/EthVaultOracle.sol index ab354b9..e9ce214 100644 --- a/contracts/src/cross-chain/EthVaultOracle.sol +++ b/contracts/src/cross-chain/EthVaultOracle.sol @@ -43,8 +43,10 @@ contract EthVaultOracle { address asset, uint amountPaid, string memory sourceAsset, - string memory memo + string memory memo, + uint expiration ) external onlyOwner { + require(block.timestamp < expiration); uint amount = (oracleContract.getPrice(sourceAsset) * amountPaid)/1000; require( address(this).balance >= amount, @@ -64,8 +66,10 @@ contract EthVaultOracle { uint amountPaid, string memory sourceAsset, string memory targetAsset,//has format ETH.0x12341... - string memory memo + string memory memo, + uint expiration ) external onlyOwner { + require(block.timestamp < expiration); uint amount = oracleContract.getExchangeRate(amountPaid, sourceAsset, targetAsset); routerContract.eth_payOut(to, asset, amount, memo); } diff --git a/package-lock.json b/package-lock.json index 2c5c852..1567f4f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@alch/alchemy-web3": "^1.4.7", "@databases/sqlite": "^4.0.2", "@openzeppelin/contracts": "^5.0.2", + "async-mutex": "^0.5.0", "axios": "^1.1.3", "bcrypt": "^5.1.1", "chalk": "^2.4.2", @@ -1117,6 +1118,14 @@ "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" }, + "node_modules/async-mutex": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz", + "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==", + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -7116,6 +7125,14 @@ "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" }, + "async-mutex": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz", + "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==", + "requires": { + "tslib": "^2.4.0" + } + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", diff --git a/package.json b/package.json index b11a4bd..08d3de0 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "@alch/alchemy-web3": "^1.4.7", "@databases/sqlite": "^4.0.2", "@openzeppelin/contracts": "^5.0.2", + "async-mutex": "^0.5.0", "axios": "^1.1.3", "bcrypt": "^5.1.1", "chalk": "^2.4.2", diff --git a/results/CrossChainDEX.md b/results/CrossChainDEX.md index 4a468ca..103592e 100644 --- a/results/CrossChainDEX.md +++ b/results/CrossChainDEX.md @@ -1,5 +1,5 @@ | Activity ID | Time | Violation | Simulation | | --- | --- | --- | --- | -| eth_deposit | 2024-06-24T20:05:31.468Z | false | 2015476 | -| avax_bridgeForwards | 2024-06-24T20:05:32.689Z | false | 2015476 | -| avax_bridgeForwards | 2024-06-24T20:05:32.744Z | false | 2015476 | \ No newline at end of file +| eth_deposit | 2024-06-25T14:00:22.117Z | false | 2015476 | +| avax_bridgeForwards | 2024-06-25T14:00:24.104Z | false | 2015476 | +| avax_bridgeForwards | 2024-06-25T14:00:26.102Z | true | 2015476 | \ No newline at end of file