diff --git a/package.json b/package.json index a8b37c076..237bea508 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "airswap-protocols", - "version": "0.0.1", + "version": "5.0.0", "private": true, "license": "MIT", "workspaces": { @@ -13,12 +13,14 @@ "check:owners": "node ./scripts/owners-report.js", "check:receivers": "node ./scripts/receivers-report.js", "check:wrappers": "node ./scripts/wrappers-report.js", + "check:delegates": "node ./scripts/delegates-report.js", "clean": "lerna run clean", "compile": "lerna run compile", "linter": "yarn eslint . --ext .js,.ts --fix", "test": "lerna run test", "test:ci": "lerna run test:ci", "drain": "node ./scripts/drain-deployer.js", + "migrate-pool": "node ./scripts/migrate-pool.js", "prepare": "husky install", "prettier": "prettier --write \"./**/*.sol\" \"./**/*.ts\" \"./**/*.js\" \"./**/*.json\"" }, @@ -52,6 +54,7 @@ "nx": "^16.5.5", "prettier": "^2.8.4", "prettier-plugin-solidity": "^1.1.2", + "prompt-confirm": "^2.0.4", "solidity-coverage": "^0.8.5", "ts-node": "^10.9.1", "typechain": "^8.1.1", diff --git a/scripts/delegates-report.js b/scripts/delegates-report.js new file mode 100644 index 000000000..de1d3fecc --- /dev/null +++ b/scripts/delegates-report.js @@ -0,0 +1,59 @@ +require('dotenv').config({ path: './.env' }) +const { ethers } = require('ethers') +const { mainnets, chainNames, apiUrls } = require('@airswap/utils') +const swapERC20Deploys = require('@airswap/swap-erc20/deploys.js') + +async function main() { + console.log() + for (const chainId of mainnets) { + const apiUrl = apiUrls[chainId] + const provider = new ethers.providers.JsonRpcProvider(apiUrl) + const deployer = new ethers.Wallet(process.env.PRIVATE_KEY, provider) + + const deploys = require('../source/delegate/deploys.js') + const { + Delegate__factory, + } = require('@airswap/delegate/typechain/factories/contracts') + if (deploys[chainId]) { + const contract = Delegate__factory.connect(deploys[chainId], deployer) + const currentSwapERC20 = await contract.swapERC20Contract() + + const intendedSwapERC20 = swapERC20Deploys[chainId] + + if (intendedSwapERC20) { + console.log( + chainNames[chainId].toUpperCase(), + `(${chainId})`, + '· Intended SwapERC20: ', + intendedSwapERC20 + ) + + let label = 'Delegate has correct SwapERC20' + if (currentSwapERC20 !== intendedSwapERC20) { + label = `Delegate has incorrect SwapERC20: ${currentSwapERC20}` + } + console.log(currentSwapERC20 === intendedSwapERC20 ? '✔' : '✘', label) + } else { + console.log( + chainNames[chainId].toUpperCase(), + `(${chainId})`, + '✘ SwapERC20 not deployed' + ) + } + } else { + console.log( + chainNames[chainId].toUpperCase(), + `(${chainId})`, + '✘ Delegate not deployed' + ) + } + console.log() + } +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error) + process.exit(1) + }) diff --git a/scripts/deployer-info.js b/scripts/deployer-info.js index d9f4bec78..6740dee82 100644 --- a/scripts/deployer-info.js +++ b/scripts/deployer-info.js @@ -1,3 +1,4 @@ +const Confirm = require('prompt-confirm') const { chainNames, chainCurrencies, @@ -5,23 +6,30 @@ const { } = require('@airswap/utils') module.exports = { - displayDeployerInfo: async function (deployer) { + confirmDeployment: async function (deployer, targetAddress) { const gasPrice = await deployer.getGasPrice() const chainId = await deployer.getChainId() const balance = ethers.utils.formatUnits( (await deployer.getBalance()).toString(), EVM_NATIVE_TOKEN_DECIMALS ) - console.log(`\nNetwork: ${chainNames[chainId].toUpperCase()}`) - console.log(`Gas price: ${gasPrice / 10 ** 9} gwei`) - console.log(`\nDeployer: ${deployer.address}`) - console.log(`Balance: ${balance} ${chainCurrencies[chainId]}`) + console.log(`To ${chainNames[chainId].toUpperCase()}`) + console.log(`· Gas price ${gasPrice / 10 ** 9} gwei`) + console.log(`· Deployer wallet ${deployer.address}`) + console.log(`· Deployer balance ${balance} ${chainCurrencies[chainId]}`) - console.log( - `\nNext contract address will be:\n${ethers.utils.getContractAddress({ - from: deployer.address, - nonce: await deployer.provider.getTransactionCount(deployer.address), - })}\n` + const nextAddress = ethers.utils.getContractAddress({ + from: deployer.address, + nonce: await deployer.provider.getTransactionCount(deployer.address), + }) + + console.log(`· Contract address ${nextAddress}\n`) + + const prompt = new Confirm( + nextAddress === targetAddress || !targetAddress + ? 'Proceed to deploy?' + : 'Address would not match mainnet. Proceed anyway?' ) + return await prompt.run() }, } diff --git a/source/pool/scripts/migrate.js b/scripts/migrate-pool.js similarity index 69% rename from source/pool/scripts/migrate.js rename to scripts/migrate-pool.js index 2df9753fd..16b06ebf3 100644 --- a/source/pool/scripts/migrate.js +++ b/scripts/migrate-pool.js @@ -1,30 +1,48 @@ +require('dotenv').config({ path: './.env' }) const Confirm = require('prompt-confirm') const { ethers } = require('hardhat') -const { chainNames, ChainIds } = require('@airswap/utils') -const { getReceiptUrl } = require('@airswap/utils') +const { + ChainIds, + chainNames, + apiUrls, + getReceiptUrl, +} = require('@airswap/utils') -const { Pool__factory } = require('../typechain/factories/contracts') -const { abi } = require('./migrate-abis/4-1-1.js') -const deploys = require('../deploys.js') +const { Pool__factory } = require('@airswap/pool/typechain/factories/contracts') +const { abi } = require('@airswap/pool/legacy-abis/4-1-1.js') +const deploys = require('@airswap/pool/deploys.js') const CONFIRMATIONS = 2 const PREVIOUS_POOL = '0xEEcD248D977Fd4D392915b4AdeF8154BA3aE9c02' const NEW_POOL = '0xbbcec987E4C189FCbAB0a2534c77b3ba89229F11' async function main() { - const [account] = await ethers.getSigners() - const chainId = await account.getChainId() - if (chainId === ChainIds.HARDHAT) { + let chainId + if (process.argv[2] === '--network') { + chainId = ChainIds[process.argv[3].toUpperCase()] + } + + if (!chainId) { console.log('Value for --network flag is required') return } + + const provider = new ethers.providers.JsonRpcProvider(apiUrls[chainId]) + const account = new ethers.Wallet(process.env.PRIVATE_KEY, provider) + console.log(`Account: ${account.address}`) console.log(`Network: ${chainNames[chainId].toUpperCase()}\n`) console.log(`From-pool: ${PREVIOUS_POOL}`) console.log(`To-pool: ${NEW_POOL}`) const previousPool = new ethers.Contract(PREVIOUS_POOL, abi, account.provider) - const logs = await previousPool.queryFilter(previousPool.filters.UseClaim()) + let logs + try { + logs = await previousPool.queryFilter(previousPool.filters.UseClaim()) + } catch (error) { + console.log('\n✘ Error querying claim events on from-pool.\n\n', error.body) + return + } if (!logs.length) { console.log('\n✘ No claim events found on from-pool.\n') diff --git a/scripts/owners-report.js b/scripts/owners-report.js index 9d66b075d..8169ea653 100644 --- a/scripts/owners-report.js +++ b/scripts/owners-report.js @@ -9,6 +9,7 @@ const { } = require('@airswap/utils') const contracts = [ + ['delegate', 'Delegate'], ['pool', 'Pool'], ['staking', 'Staking'], ['swap', 'Swap'], diff --git a/scripts/owners-update.js b/scripts/owners-update.js index ab422829d..968bf2569 100644 --- a/scripts/owners-update.js +++ b/scripts/owners-update.js @@ -1,7 +1,11 @@ const Confirm = require('prompt-confirm') const { ethers } = require('hardhat') -const { chainNames, ChainIds, ownerAddresses } = require('@airswap/utils') -const { getReceiptUrl } = require('@airswap/utils') +const { + chainNames, + ChainIds, + ownerAddresses, + getReceiptUrl, +} = require('@airswap/utils') const CONFIRMATIONS = 2 const TRANSFER_STARTED = diff --git a/scripts/receivers-report.js b/scripts/receivers-report.js index 095e2e125..f010620e1 100644 --- a/scripts/receivers-report.js +++ b/scripts/receivers-report.js @@ -5,9 +5,9 @@ const { chainNames, apiUrls, protocolFeeReceiverAddresses, + ADDRESS_ZERO, } = require('@airswap/utils') const poolDeploys = require('@airswap/pool/deploys.js') -const { ADDRESS_ZERO } = require('@airswap/utils') const contracts = [ ['swap', 'Swap'], diff --git a/scripts/wrappers-report.js b/scripts/wrappers-report.js index 84344bca7..68a2d8300 100644 --- a/scripts/wrappers-report.js +++ b/scripts/wrappers-report.js @@ -1,14 +1,11 @@ require('dotenv').config({ path: './.env' }) const { ethers } = require('ethers') -const { mainnets, testnets, chainNames, apiUrls } = require('@airswap/utils') +const { mainnets, chainNames, apiUrls } = require('@airswap/utils') const swapERC20Deploys = require('@airswap/swap-erc20/deploys.js') -const chains = mainnets.concat(testnets) - async function main() { console.log() - for (let c = 0; c < chains.length; c++) { - const chainId = chains[c] + for (const chainId of mainnets) { const apiUrl = apiUrls[chainId] const provider = new ethers.providers.JsonRpcProvider(apiUrl) const deployer = new ethers.Wallet(process.env.PRIVATE_KEY, provider) @@ -40,11 +37,17 @@ async function main() { console.log( chainNames[chainId].toUpperCase(), `(${chainId})`, - '✘ Not deployed' + '✘ SwapERC20 not deployed' ) } - console.log() + } else { + console.log( + chainNames[chainId].toUpperCase(), + `(${chainId})`, + '✘ Wrapper not deployed' + ) } + console.log() } } diff --git a/source/batch-call/deploys-commits.js b/source/batch-call/deploys-commits.js new file mode 100644 index 000000000..039485113 --- /dev/null +++ b/source/batch-call/deploys-commits.js @@ -0,0 +1,19 @@ +module.exports = { + 1: '863586ae7594b5088619f243dbee83c8d8e0075f', + 31: '863586ae7594b5088619f243dbee83c8d8e0075f', + 41: '863586ae7594b5088619f243dbee83c8d8e0075f', + 56: '863586ae7594b5088619f243dbee83c8d8e0075f', + 97: '863586ae7594b5088619f243dbee83c8d8e0075f', + 137: '863586ae7594b5088619f243dbee83c8d8e0075f', + 8453: '863586ae7594b5088619f243dbee83c8d8e0075f', + 17000: '863586ae7594b5088619f243dbee83c8d8e0075f', + 42161: '863586ae7594b5088619f243dbee83c8d8e0075f', + 43113: '863586ae7594b5088619f243dbee83c8d8e0075f', + 43114: '863586ae7594b5088619f243dbee83c8d8e0075f', + 59140: '863586ae7594b5088619f243dbee83c8d8e0075f', + 59144: '863586ae7594b5088619f243dbee83c8d8e0075f', + 80001: '863586ae7594b5088619f243dbee83c8d8e0075f', + 84532: '863586ae7594b5088619f243dbee83c8d8e0075f', + 421614: '863586ae7594b5088619f243dbee83c8d8e0075f', + 11155111: '863586ae7594b5088619f243dbee83c8d8e0075f', +} diff --git a/source/batch-call/deploys-commits.js.d.ts b/source/batch-call/deploys-commits.js.d.ts new file mode 100644 index 000000000..cc21f2ea2 --- /dev/null +++ b/source/batch-call/deploys-commits.js.d.ts @@ -0,0 +1 @@ +declare module '@airswap/batch-call/deploys-commits.js' diff --git a/source/batch-call/package.json b/source/batch-call/package.json index d1e3a8c9e..fab0337e8 100644 --- a/source/batch-call/package.json +++ b/source/batch-call/package.json @@ -1,7 +1,7 @@ { "name": "@airswap/batch-call", - "version": "4.2.3", - "description": "Batch balance, allowance, order validity checks", + "version": "5.0.0", + "description": "AirSwap: Balance, Allowance, Validity Checks", "license": "MIT", "repository": { "type": "git", @@ -10,9 +10,7 @@ "files": [ "./build", "./typechain", - "./deploys.js", - "./deploys-blocks.js", - "./deploys.js.d.ts" + "./deploys*" ], "scripts": { "clean": "rm -rf ./cache && rm -rf ./build && rm -rf ./typechain", @@ -26,11 +24,10 @@ "owners": "hardhat run ./scripts/owner.js" }, "devDependencies": { - "@airswap/utils": "4.3.4", - "@airswap/swap": "4.2.2", - "@airswap/swap-erc20": "4.3.1", - "@openzeppelin/contracts": "^4.8.3", - "prompt-confirm": "^2.0.4" + "@airswap/utils": "5.0.0", + "@airswap/swap": "5.0.0", + "@airswap/swap-erc20": "5.0.0", + "@openzeppelin/contracts": "^4.8.3" }, "publishConfig": { "access": "public" diff --git a/source/batch-call/scripts/deploy.js b/source/batch-call/scripts/deploy.js index e53a39435..562d86de6 100644 --- a/source/batch-call/scripts/deploy.js +++ b/source/batch-call/scripts/deploy.js @@ -1,13 +1,12 @@ /* eslint-disable no-console */ const fs = require('fs') const prettier = require('prettier') -const Confirm = require('prompt-confirm') const { ethers, run } = require('hardhat') -const { ChainIds, chainLabels } = require('@airswap/utils') -const { getReceiptUrl } = require('@airswap/utils') +const { ChainIds, chainLabels, getReceiptUrl } = require('@airswap/utils') const batchCallDeploys = require('../deploys.js') const batchCallBlocks = require('../deploys-blocks.js') -const { displayDeployerInfo } = require('../../../scripts/deployer-info') +const batchCallCommits = require('../deploys-commits.js') +const { confirmDeployment } = require('../../../scripts/deployer-info') async function main() { await run('compile') @@ -18,10 +17,9 @@ async function main() { console.log('Value for --network flag is required') return } - await displayDeployerInfo(deployer) + console.log(`\nDeploy BATCHCALL\n`) - const prompt = new Confirm('Proceed to deploy?') - if (await prompt.run()) { + if (await confirmDeployment(deployer, batchCallDeploys)) { const batchFactory = await ethers.getContractFactory('BatchCall') const batchCallContract = await batchFactory.deploy() console.log( @@ -48,6 +46,30 @@ async function main() { { ...prettierConfig, parser: 'babel' } ) ) + + batchCallBlocks[chainId] = ( + await batchCallContract.deployTransaction.wait() + ).blockNumber + fs.writeFileSync( + './deploys-blocks.js', + prettier.format( + `module.exports = ${JSON.stringify(batchCallBlocks, null, '\t')}`, + { ...prettierConfig, parser: 'babel' } + ) + ) + + batchCallCommits[chainId] = require('child_process') + .execSync('git rev-parse HEAD') + .toString() + .trim() + fs.writeFileSync( + './deploys-commits.js', + prettier.format( + `module.exports = ${JSON.stringify(batchCallCommits, null, '\t')}`, + { ...prettierConfig, parser: 'babel' } + ) + ) + console.log( `Deployed: ${batchCallDeploys[chainId]} @ ${batchCallBlocks[chainId]}` ) diff --git a/source/batch-call/test/BatchCall.js b/source/batch-call/test/BatchCall.js index 7fe37bab7..d70316ed9 100644 --- a/source/batch-call/test/BatchCall.js +++ b/source/batch-call/test/BatchCall.js @@ -13,8 +13,8 @@ const { createOrderSignature, createOrderERC20, createOrderERC20Signature, + TokenKinds, } = require('@airswap/utils') -const { TokenKinds } = require('@airswap/utils') const CHAIN_ID = 31337 const PROTOCOL_FEE = '30' diff --git a/source/delegate/LICENSE b/source/delegate/LICENSE new file mode 100644 index 000000000..f963da81e --- /dev/null +++ b/source/delegate/LICENSE @@ -0,0 +1,19 @@ +Copyright 2024 AirSwap + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/source/delegate/README.md b/source/delegate/README.md new file mode 100644 index 000000000..ed1a9091b --- /dev/null +++ b/source/delegate/README.md @@ -0,0 +1,37 @@ +# Delegate + +[AirSwap](https://www.airswap.io/) is an open-source peer-to-peer trading network. + +[![Discord](https://img.shields.io/discord/590643190281928738.svg)](https://discord.gg/ecQbV7H) +[![License](https://img.shields.io/badge/License-MIT-blue)](https://opensource.org/licenses/MIT) +![Twitter Follow](https://img.shields.io/twitter/follow/airswap?style=social) + +## Resources + +- About → https://about.airswap.io/ +- Website → https://www.airswap.io/ +- Twitter → https://twitter.com/airswap +- Chat → https://chat.airswap.io/ + +## Usage + +:warning: This package may contain unaudited code. For all AirSwap contract deployments see [Deployed Contracts](https://about.airswap.io/technology/deployments). + +## Commands + +Environment variables are set in an `.env` file in the repository root. + +| Command | Description | +| :-------------- | :--------------------------------------- | +| `yarn` | Install dependencies | +| `yarn clean` | Delete the contract `build` folder | +| `yarn compile` | Compile all contracts to `build` folder | +| `yarn coverage` | Report test coverage | +| `yarn test` | Run all tests in `test` folder | +| `yarn test:ci` | Run CI tests in `test` folder | +| `yarn deploy` | Deploy on a network using --network flag | +| `yarn verify` | Verify on a network using --network flag | + +## Running Tests + +:bulb: Prior to testing locally, run `yarn compile` in the `airswap-protocols` project root to build required artifacts. diff --git a/source/delegate/contracts/Delegate.sol b/source/delegate/contracts/Delegate.sol new file mode 100644 index 000000000..42e3d35e8 --- /dev/null +++ b/source/delegate/contracts/Delegate.sol @@ -0,0 +1,221 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.23; + +import "./interfaces/IDelegate.sol"; +import "@airswap/swap-erc20/contracts/interfaces/ISwapERC20.sol"; +import { Ownable } from "solady/src/auth/Ownable.sol"; +import { SafeTransferLib } from "solady/src/utils/SafeTransferLib.sol"; + +/** + * @title AirSwap: Delegated On-chain Trading Rules + * @notice Supports ERC-20 tokens + * @dev inherits IDelegate, Ownable; uses SafeTransferLib + */ +contract Delegate is IDelegate, Ownable { + // The SwapERC20 contract to be used to execute orders + ISwapERC20 public swapERC20Contract; + + // Mapping of senderWallet to senderToken to to signerToken to Rule + mapping(address => mapping(address => mapping(address => Rule))) public rules; + + // Mapping of senderWallet to an authorized manager + mapping(address => address) public authorized; + + /** + * @notice Constructor + * @param _swapERC20Contract address + */ + constructor(address _swapERC20Contract) { + _initializeOwner(msg.sender); + swapERC20Contract = ISwapERC20(_swapERC20Contract); + } + + /** + * @notice Set a Rule + * @param _senderWallet address Address of the sender wallet + * @param _senderToken address ERC-20 token the sender would transfer + * @param _senderAmount uint256 Maximum sender amount for the rule + * @param _signerToken address ERC-20 token the signer would transfer + * @param _signerAmount uint256 Maximum signer amount for the rule + * @param _expiry uint256 Expiry in seconds since 1 January 1970 + */ + function setRule( + address _senderWallet, + address _senderToken, + uint256 _senderAmount, + address _signerToken, + uint256 _signerAmount, + uint256 _expiry + ) external { + if (authorized[_senderWallet] != address(0)) { + // If an authorized manager is set, message sender must be the manager + if (msg.sender != authorized[_senderWallet]) revert SenderInvalid(); + } else { + // Otherwise message sender must be the sender wallet + if (msg.sender != _senderWallet) revert SenderInvalid(); + } + + // Set the rule. Overwrites an existing rule. + rules[_senderWallet][_senderToken][_signerToken] = Rule( + _senderWallet, + _senderToken, + _senderAmount, + 0, + _signerToken, + _signerAmount, + _expiry + ); + + // Emit a SetRule event + emit SetRule( + _senderWallet, + _senderToken, + _senderAmount, + _signerToken, + _signerAmount, + _expiry + ); + } + + /** + * @notice Unset a Rule + * @param _senderWallet The address of the sender's wallet + * @param _senderToken address ERC-20 token the sender would transfer + * @param _signerToken address ERC-20 token the signer would transfer + */ + function unsetRule( + address _senderWallet, + address _senderToken, + address _signerToken + ) external { + if (authorized[_senderWallet] != address(0)) { + // If an authorized manager is set, the message sender must be the manager + if (msg.sender != authorized[_senderWallet]) revert SenderInvalid(); + } else { + // Otherwise the message sender must be the sender wallet + if (msg.sender != _senderWallet) revert SenderInvalid(); + } + + // Delete the rule + delete rules[_senderWallet][_senderToken][_signerToken]; + + // Emit an UnsetRule event + emit UnsetRule(_senderWallet, _senderToken, _signerToken); + } + + /** + * @notice Perform an atomic ERC-20 swap + * @dev Forwards to underlying SwapERC20 contract + * @param _senderWallet address Wallet to receive sender proceeds + * @param _nonce uint256 Unique and should be sequential + * @param _expiry uint256 Expiry in seconds since 1 January 1970 + * @param _signerWallet address Wallet of the signer + * @param _signerToken address ERC-20 token transferred from the signer + * @param _signerAmount uint256 Amount transferred from the signer + * @param _senderToken address ERC-20 token transferred from the sender + * @param _senderAmount uint256 Amount transferred from the sender + * @param _v uint8 "v" value of the ECDSA signature + * @param _r bytes32 "r" value of the ECDSA signature + * @param _s bytes32 "s" value of the ECDSA signature + */ + function swap( + address _senderWallet, + uint256 _nonce, + uint256 _expiry, + address _signerWallet, + address _signerToken, + uint256 _signerAmount, + address _senderToken, + uint256 _senderAmount, + uint8 _v, + bytes32 _r, + bytes32 _s + ) external { + Rule storage rule = rules[_senderWallet][_senderToken][_signerToken]; + // Ensure the signer amount is valid + if ( + _signerAmount < + (rule.signerAmount * (rule.senderAmount - rule.senderFilledAmount)) / + rule.senderAmount + ) { + revert SignerAmountInvalid(); + } + // Ensure the rule has not expired + if (rule.expiry < block.timestamp) revert RuleExpired(); + + // Ensure the sender amount is valid + if (_senderAmount > (rule.senderAmount - rule.senderFilledAmount)) { + revert SenderAmountInvalid(); + } + + // Transfer the sender token to this contract + SafeTransferLib.safeTransferFrom( + _senderToken, + _senderWallet, + address(this), + _senderAmount + ); + + // Approve the SwapERC20 contract to transfer the sender token + SafeTransferLib.safeApprove( + _senderToken, + address(swapERC20Contract), + _senderAmount + ); + + // Execute the swap + swapERC20Contract.swapLight( + _nonce, + _expiry, + _signerWallet, + _signerToken, + _signerAmount, + _senderToken, + _senderAmount, + _v, + _r, + _s + ); + + // Transfer the signer token to the sender wallet + SafeTransferLib.safeTransfer(_signerToken, _senderWallet, _signerAmount); + + // Update the filled amount + rules[_senderWallet][_senderToken][_signerToken] + .senderFilledAmount += _senderAmount; + + // Emit a DelegateSwap event + emit DelegateSwap(_nonce, _signerWallet); + } + + /** + * @notice Authorize a wallet to manage rules + * @param _manager address Wallet of the manager to authorize + * @dev Emits an Authorize event + */ + function authorize(address _manager) external { + if (_manager == address(0)) revert ManagerInvalid(); + authorized[msg.sender] = _manager; + emit Authorize(_manager, msg.sender); + } + + /** + * @notice Revoke a manager + * @dev Emits a Revoke event + */ + function revoke() external { + address _tmp = authorized[msg.sender]; + delete authorized[msg.sender]; + emit Revoke(_tmp, msg.sender); + } + + /** + * @notice Sets the SwapERC20 contract + * @param _swapERC20Contract address + */ + function setSwapERC20Contract(address _swapERC20Contract) external onlyOwner { + if (_swapERC20Contract == address(0)) revert AddressInvalid(); + swapERC20Contract = ISwapERC20(_swapERC20Contract); + } +} diff --git a/source/delegate/contracts/interfaces/IDelegate.sol b/source/delegate/contracts/interfaces/IDelegate.sol new file mode 100644 index 000000000..7f1cb0c3f --- /dev/null +++ b/source/delegate/contracts/interfaces/IDelegate.sol @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.23; + +interface IDelegate { + struct Rule { + address senderWallet; + address senderToken; + uint256 senderAmount; + uint256 senderFilledAmount; + address signerToken; + uint256 signerAmount; + uint256 expiry; + } + + event Authorize(address signatory, address signer); + event DelegateSwap(uint256 nonce, address signerWallet); + event Revoke(address tmp, address signer); + + event SetRule( + address senderWallet, + address senderToken, + uint256 senderAmount, + address signerToken, + uint256 signerAmount, + uint256 expiry + ); + + event UnsetRule( + address senderWallet, + address senderToken, + address signerToken + ); + + error AddressInvalid(); + error RuleExpired(); + error SenderAmountInvalid(); + error SignerAmountInvalid(); + error SenderInvalid(); + error ManagerInvalid(); + error TransferFromFailed(); + + function setRule( + address senderWallet, + address senderToken, + uint256 senderAmount, + address signerToken, + uint256 signerAmount, + uint256 expiry + ) external; + + function swap( + address senderWallet, + uint256 nonce, + uint256 expiry, + address signerWallet, + address signerToken, + uint256 signerAmount, + address senderToken, + uint256 senderAmount, + uint8 v, + bytes32 r, + bytes32 s + ) external; + + function unsetRule( + address senderWallet, + address senderToken, + address signerToken + ) external; + + function authorize(address manager) external; + + function revoke() external; + + function setSwapERC20Contract(address _swapERC20Contract) external; +} diff --git a/source/delegate/deploys-blocks.js b/source/delegate/deploys-blocks.js new file mode 100644 index 000000000..74abdacc8 --- /dev/null +++ b/source/delegate/deploys-blocks.js @@ -0,0 +1,10 @@ +module.exports = { + 1: 20856519, + 56: 42678795, + 137: 62421566, + 8453: 20414686, + 42161: 258599054, + 43114: 51146988, + 59144: 10096855, + 11155111: 6781500, +} diff --git a/source/delegate/deploys-blocks.js.d.ts b/source/delegate/deploys-blocks.js.d.ts new file mode 100644 index 000000000..71bf3bd4b --- /dev/null +++ b/source/delegate/deploys-blocks.js.d.ts @@ -0,0 +1 @@ +declare module '@airswap/delegate/deploys-blocks.js' diff --git a/source/delegate/deploys-commits.js b/source/delegate/deploys-commits.js new file mode 100644 index 000000000..e897d2e1d --- /dev/null +++ b/source/delegate/deploys-commits.js @@ -0,0 +1,10 @@ +module.exports = { + 1: '57b971f929f783db5ee9d9121fcf76be394485a5', + 56: '57b971f929f783db5ee9d9121fcf76be394485a5', + 137: '57b971f929f783db5ee9d9121fcf76be394485a5', + 8453: '57b971f929f783db5ee9d9121fcf76be394485a5', + 42161: '57b971f929f783db5ee9d9121fcf76be394485a5', + 43114: '57b971f929f783db5ee9d9121fcf76be394485a5', + 59144: '57b971f929f783db5ee9d9121fcf76be394485a5', + 11155111: '57b971f929f783db5ee9d9121fcf76be394485a5', +} diff --git a/source/delegate/deploys-commits.js.d.ts b/source/delegate/deploys-commits.js.d.ts new file mode 100644 index 000000000..c530602c2 --- /dev/null +++ b/source/delegate/deploys-commits.js.d.ts @@ -0,0 +1 @@ +declare module '@airswap/delegate/deploys-commits.js' diff --git a/source/delegate/deploys.js b/source/delegate/deploys.js new file mode 100644 index 000000000..956d43659 --- /dev/null +++ b/source/delegate/deploys.js @@ -0,0 +1,10 @@ +module.exports = { + 1: '0xC4ad1c6fBe2794a276C09dc8AadF7F4cF79Df9E1', + 56: '0xC4ad1c6fBe2794a276C09dc8AadF7F4cF79Df9E1', + 137: '0xC4ad1c6fBe2794a276C09dc8AadF7F4cF79Df9E1', + 8453: '0xC4ad1c6fBe2794a276C09dc8AadF7F4cF79Df9E1', + 42161: '0xC4ad1c6fBe2794a276C09dc8AadF7F4cF79Df9E1', + 43114: '0xC4ad1c6fBe2794a276C09dc8AadF7F4cF79Df9E1', + 59144: '0xC4ad1c6fBe2794a276C09dc8AadF7F4cF79Df9E1', + 11155111: '0xC4ad1c6fBe2794a276C09dc8AadF7F4cF79Df9E1', +} diff --git a/source/delegate/deploys.js.d.ts b/source/delegate/deploys.js.d.ts new file mode 100644 index 000000000..d73edcc6e --- /dev/null +++ b/source/delegate/deploys.js.d.ts @@ -0,0 +1 @@ +declare module '@airswap/delegate/deploys.js' diff --git a/source/delegate/hardhat.config.js b/source/delegate/hardhat.config.js new file mode 100644 index 000000000..4b553c55b --- /dev/null +++ b/source/delegate/hardhat.config.js @@ -0,0 +1,6 @@ +module.exports = { + typechain: { + outDir: 'typechain', + }, + ...require('../../hardhat.config.js'), +} diff --git a/source/delegate/package.json b/source/delegate/package.json new file mode 100644 index 000000000..58591e0e1 --- /dev/null +++ b/source/delegate/package.json @@ -0,0 +1,33 @@ +{ + "name": "@airswap/delegate", + "version": "5.0.0", + "description": "AirSwap: Delegated On-chain Trading Rules", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/airswap/airswap-protocols" + }, + "files": [ + "./build", + "./typechain", + "./deploys*" + ], + "scripts": { + "clean": "rm -rf ./cache && rm -rf ./build && rm -rf ./typechain", + "compile": "hardhat compile; yarn typechain", + "typechain": "tsc -b", + "coverage": "hardhat coverage", + "test": "hardhat test", + "test:ci": "hardhat test", + "deploy": "hardhat run ./scripts/deploy.js", + "verify": "hardhat run ./scripts/verify.js", + "owners": "hardhat run ./scripts/owner.js" + }, + "devDependencies": { + "@airswap/utils": "5.0.0", + "@airswap/swap-erc20": "5.0.0" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/source/delegate/scripts/deploy.js b/source/delegate/scripts/deploy.js new file mode 100644 index 000000000..85bab522a --- /dev/null +++ b/source/delegate/scripts/deploy.js @@ -0,0 +1,84 @@ +/* eslint-disable no-console */ +const fs = require('fs') +const prettier = require('prettier') + +const { ethers, run } = require('hardhat') +const swapERC20Deploys = require('@airswap/swap-erc20/deploys.js') +const { ChainIds, chainLabels, getReceiptUrl } = require('@airswap/utils') +const delegateDeploys = require('../deploys.js') +const delegateBlocks = require('../deploys-blocks.js') +const delegateCommits = require('../deploys-commits.js') +const { confirmDeployment } = require('../../../scripts/deployer-info') + +async function main() { + await run('compile') + const prettierConfig = await prettier.resolveConfig('../deploys.js') + const [deployer] = await ethers.getSigners() + const chainId = await deployer.getChainId() + if (chainId === ChainIds.HARDHAT) { + console.log('Value for --network flag is required') + return + } + + console.log(`\nDeploy DELEGATE`) + + console.log(`· swapERC20Contract ${swapERC20Deploys[chainId]}\n`) + + if (await confirmDeployment(deployer, delegateDeploys[ChainIds.MAINNET])) { + const delegateFactory = await ethers.getContractFactory('Delegate') + const delegateContract = await delegateFactory.deploy( + swapERC20Deploys[chainId] + ) + console.log( + 'Deploying...', + getReceiptUrl(chainId, delegateContract.deployTransaction.hash) + ) + await delegateContract.deployed() + + delegateDeploys[chainId] = delegateContract.address + fs.writeFileSync( + './deploys.js', + prettier.format( + `module.exports = ${JSON.stringify(delegateDeploys, null, '\t')}`, + { ...prettierConfig, parser: 'babel' } + ) + ) + delegateBlocks[chainId] = ( + await delegateContract.deployTransaction.wait() + ).blockNumber + fs.writeFileSync( + './deploys-blocks.js', + prettier.format( + `module.exports = ${JSON.stringify(delegateBlocks, null, '\t')}`, + { ...prettierConfig, parser: 'babel' } + ) + ) + delegateCommits[chainId] = require('child_process') + .execSync('git rev-parse HEAD') + .toString() + .trim() + fs.writeFileSync( + './deploys-commits.js', + prettier.format( + `module.exports = ${JSON.stringify(delegateCommits, null, '\t')}`, + { ...prettierConfig, parser: 'babel' } + ) + ) + console.log( + `Deployed: ${delegateDeploys[chainId]} @ ${delegateBlocks[chainId]}` + ) + + console.log( + `\nVerify with "yarn verify --network ${chainLabels[ + chainId + ].toLowerCase()}"\n` + ) + } +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error) + process.exit(1) + }) diff --git a/source/delegate/scripts/owner.js b/source/delegate/scripts/owner.js new file mode 100644 index 000000000..28bd96f6f --- /dev/null +++ b/source/delegate/scripts/owner.js @@ -0,0 +1,14 @@ +const { check } = require('../../../scripts/owners-update') +const { Delegate__factory } = require('../typechain/factories/contracts') +const delegateDeploys = require('../deploys.js') + +async function main() { + await check('Delegate', Delegate__factory, delegateDeploys) +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error) + process.exit(1) + }) diff --git a/source/delegate/scripts/verify.js b/source/delegate/scripts/verify.js new file mode 100644 index 000000000..93fefecec --- /dev/null +++ b/source/delegate/scripts/verify.js @@ -0,0 +1,26 @@ +/* eslint-disable no-console */ +const { ethers, run } = require('hardhat') +const delegateDeploys = require('../deploys.js') +const swapERC20Deploys = require('@airswap/swap-erc20/deploys.js') +const { chainNames } = require('@airswap/utils') + +async function main() { + await run('compile') + const [deployer] = await ethers.getSigners() + console.log(`Deployer: ${deployer.address}`) + + const chainId = await deployer.getChainId() + + console.log(`Verifying on ${chainNames[chainId]}`) + await run('verify:verify', { + address: delegateDeploys[chainId], + constructorArguments: [swapERC20Deploys[chainId]], + }) +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error) + process.exit(1) + }) diff --git a/source/delegate/test/Delegate.js b/source/delegate/test/Delegate.js new file mode 100644 index 000000000..d2a8ea79a --- /dev/null +++ b/source/delegate/test/Delegate.js @@ -0,0 +1,565 @@ +const { expect } = require('chai') + +const { ethers, waffle } = require('hardhat') +const { deployMockContract } = waffle +const IERC20 = require('@openzeppelin/contracts/build/contracts/IERC20.json') +const SWAP_ERC20 = require('@airswap/swap-erc20/build/contracts/SwapERC20.sol/SwapERC20.json') +const { + ADDRESS_ZERO, + createOrderERC20, + orderERC20ToParams, + createOrderERC20Signature, + SECONDS_IN_DAY, +} = require('@airswap/utils') +const CHAIN_ID = 31337 +const DEFAULT_BALANCE = '100000' +const DEFAULT_SENDER_AMOUNT = '10000' +const DEFAULT_SIGNER_AMOUNT = '10000' +const PROTOCOL_FEE = '5' +const REBATE_SCALE = '10' +const REBATE_MAX = '100' +const UPDATE_SWAP_ERC20_ADDRESS = '0x0000000000000000000000000000000000001337' +const RULE_EXPIRY = + Math.round(Date.now() / 1000 + SECONDS_IN_DAY).toString() + 1 + +describe('Delegate Unit', () => { + let deployer + let sender + let signer + let swapERC20 + let senderToken + let signerToken + let delegate + let manager + let snapshotId + + async function createSignedOrderERC20(params, signatory) { + const unsignedOrder = createOrderERC20({ + protocolFee: PROTOCOL_FEE, + signerWallet: signer.address, + signerToken: signerToken.address, + signerAmount: DEFAULT_SIGNER_AMOUNT, + senderWallet: delegate.address, + senderToken: senderToken.address, + senderAmount: DEFAULT_SENDER_AMOUNT, + ...params, + }) + return orderERC20ToParams({ + ...unsignedOrder, + ...(await createOrderERC20Signature( + unsignedOrder, + signatory, + swapERC20.address, + CHAIN_ID + )), + }) + } + + async function setUpAllowances( + senderWallet, + senderAmount, + signerWallet, + signerAmount + ) { + await senderToken.mock.allowance + .withArgs(senderWallet, delegate.address) + .returns(senderAmount) + await signerToken.mock.allowance + .withArgs(signerWallet, swapERC20.address) + .returns(signerAmount) + } + + async function setUpBalances(senderWallet, signerWallet) { + await senderToken.mock.balanceOf + .withArgs(senderWallet) + .returns(DEFAULT_BALANCE) + await signerToken.mock.balanceOf + .withArgs(signerWallet) + .returns(DEFAULT_BALANCE) + await signerToken.mock.balanceOf + .withArgs(delegate.address) + .returns(DEFAULT_SIGNER_AMOUNT) + } + + async function setUpApprovals() { + await senderToken.mock.approve + .withArgs(delegate.address, DEFAULT_SENDER_AMOUNT) + .returns(true) + + await senderToken.mock.approve + .withArgs(swapERC20.address, DEFAULT_SENDER_AMOUNT) + .returns(true) + + await signerToken.mock.approve + .withArgs(swapERC20.address, DEFAULT_SIGNER_AMOUNT) + .returns(true) + } + + beforeEach(async () => { + snapshotId = await ethers.provider.send('evm_snapshot') + }) + + afterEach(async () => { + await ethers.provider.send('evm_revert', [snapshotId]) + }) + + before(async () => { + ;[deployer, sender, signer, manager, anyone] = await ethers.getSigners() + + const swapERC20Factory = await ethers.getContractFactory( + SWAP_ERC20.abi, + SWAP_ERC20.bytecode + ) + swapERC20 = await swapERC20Factory.deploy( + PROTOCOL_FEE, + PROTOCOL_FEE, + deployer.address, + REBATE_SCALE, + REBATE_MAX + ) + + delegate = await ( + await ethers.getContractFactory('Delegate') + ).deploy(swapERC20.address) + await delegate.deployed() + + senderToken = await deployMockContract(deployer, IERC20.abi) + signerToken = await deployMockContract(deployer, IERC20.abi) + await senderToken.mock.transferFrom.returns(true) + await signerToken.mock.transferFrom.returns(true) + await senderToken.mock.transfer.returns(true) + await signerToken.mock.transfer.returns(true) + + setUpApprovals() + }) + + describe('Constructor and admin functions', async () => { + it('swap ERC20 address is set', async () => { + expect(await delegate.swapERC20Contract()).to.equal(swapERC20.address) + }) + + it('sets the swapERC20Contract address', async () => { + await delegate.setSwapERC20Contract(UPDATE_SWAP_ERC20_ADDRESS) + expect(await delegate.swapERC20Contract()).to.equal( + UPDATE_SWAP_ERC20_ADDRESS + ) + }) + + it('the swapERC20Contract address cannot be address(0)', async () => { + await expect( + delegate.setSwapERC20Contract(ADDRESS_ZERO) + ).to.be.revertedWith('AddressInvalid') + }) + + it('only the owner can set the swapERC20Contract address', async () => { + await expect( + delegate.connect(anyone).setSwapERC20Contract(UPDATE_SWAP_ERC20_ADDRESS) + ).to.be.revertedWith('Unauthorized') + }) + }) + + describe('Rules', async () => { + it('sets a Rule', async () => { + await expect( + delegate + .connect(sender) + .setRule( + sender.address, + senderToken.address, + DEFAULT_SENDER_AMOUNT, + signerToken.address, + DEFAULT_SIGNER_AMOUNT, + RULE_EXPIRY + ) + ) + .to.emit(delegate, 'SetRule') + .withArgs( + sender.address, + senderToken.address, + DEFAULT_SENDER_AMOUNT, + signerToken.address, + DEFAULT_SIGNER_AMOUNT, + RULE_EXPIRY + ) + }) + + it('unsets a Rule', async () => { + await expect( + delegate + .connect(sender) + .unsetRule(sender.address, senderToken.address, signerToken.address) + ) + .to.emit(delegate, 'UnsetRule') + .withArgs(sender.address, senderToken.address, signerToken.address) + }) + + it('a manager can set a Rule', async () => { + await delegate.connect(sender).authorize(manager.address) + await expect( + delegate + .connect(manager) + .setRule( + sender.address, + senderToken.address, + DEFAULT_SENDER_AMOUNT, + signerToken.address, + DEFAULT_SIGNER_AMOUNT, + RULE_EXPIRY + ) + ) + .to.emit(delegate, 'SetRule') + .withArgs( + sender.address, + senderToken.address, + DEFAULT_SENDER_AMOUNT, + signerToken.address, + DEFAULT_SIGNER_AMOUNT, + RULE_EXPIRY + ) + }) + + it('an unauthorized manager cannot set a rule', async () => { + await delegate.connect(sender).authorize(manager.address) + await expect( + delegate + .connect(signer) + .setRule( + sender.address, + senderToken.address, + DEFAULT_SENDER_AMOUNT, + signerToken.address, + DEFAULT_SIGNER_AMOUNT, + RULE_EXPIRY + ) + ).to.be.revertedWith('SenderInvalid') + }) + + it('a manager cannot set a rule without prior authorization', async () => { + await expect( + delegate + .connect(manager) + .setRule( + sender.address, + senderToken.address, + DEFAULT_SENDER_AMOUNT, + signerToken.address, + DEFAULT_SIGNER_AMOUNT, + RULE_EXPIRY + ) + ).to.be.revertedWith('SenderInvalid') + }) + + it('a manager can unset a Rule', async () => { + await delegate.connect(sender).authorize(manager.address) + await delegate + .connect(manager) + .setRule( + sender.address, + senderToken.address, + DEFAULT_SENDER_AMOUNT, + signerToken.address, + DEFAULT_SIGNER_AMOUNT, + RULE_EXPIRY + ) + + await expect( + delegate + .connect(manager) + .unsetRule(sender.address, senderToken.address, signerToken.address) + ) + .to.emit(delegate, 'UnsetRule') + .withArgs(sender.address, senderToken.address, signerToken.address) + }) + + it('an unauthorized manager cannot unset a rule', async () => { + await delegate.connect(sender).authorize(manager.address) + await delegate + .connect(manager) + .setRule( + sender.address, + senderToken.address, + DEFAULT_SENDER_AMOUNT, + signerToken.address, + DEFAULT_SIGNER_AMOUNT, + RULE_EXPIRY + ) + + await expect( + delegate + .connect(signer) + .unsetRule(sender.address, senderToken.address, signerToken.address) + ).to.be.revertedWith('SenderInvalid') + }) + + it('a revoked manager cannot unset a rule', async () => { + await delegate.connect(sender).authorize(manager.address) + await delegate + .connect(manager) + .setRule( + sender.address, + senderToken.address, + DEFAULT_SENDER_AMOUNT, + signerToken.address, + DEFAULT_SIGNER_AMOUNT, + RULE_EXPIRY + ) + + await delegate.connect(sender).revoke() + + await expect( + delegate + .connect(manager) + .unsetRule(sender.address, senderToken.address, signerToken.address) + ).to.be.revertedWith('SenderInvalid') + }) + + it('setting a Rule updates the rule balance', async () => { + await delegate + .connect(sender) + .setRule( + sender.address, + senderToken.address, + DEFAULT_SENDER_AMOUNT, + signerToken.address, + DEFAULT_SIGNER_AMOUNT, + RULE_EXPIRY + ) + + const rule = await delegate.rules( + sender.address, + senderToken.address, + signerToken.address + ) + + expect(rule.senderAmount.toString()).to.equal(DEFAULT_SENDER_AMOUNT) + }) + + it('unsetting a Rule updates the rule balance', async () => { + await delegate + .connect(sender) + .setRule( + sender.address, + senderToken.address, + DEFAULT_SENDER_AMOUNT, + signerToken.address, + DEFAULT_SIGNER_AMOUNT, + RULE_EXPIRY + ) + + let rule = await delegate.rules( + sender.address, + senderToken.address, + signerToken.address + ) + + await delegate + .connect(sender) + .unsetRule(sender.address, senderToken.address, signerToken.address) + + rule = await delegate.rules( + sender.address, + senderToken.address, + signerToken.address + ) + + expect(rule.senderAmount.toString()).to.equal('0') + }) + }) + + describe('Test authorization', async () => { + it('test authorized is set', async () => { + await delegate.connect(anyone).authorize(signer.address) + expect(await delegate.authorized(anyone.address)).to.equal(signer.address) + }) + + it('test authorize with zero address', async () => { + await expect( + delegate.connect(deployer).authorize(ADDRESS_ZERO) + ).to.be.revertedWith('ManagerInvalid') + }) + + it('test revoke', async () => { + await delegate.connect(anyone).revoke() + expect(await delegate.authorized(anyone.address)).to.equal(ADDRESS_ZERO) + }) + }) + + describe('Swap', async () => { + it('successfully swaps', async () => { + await delegate + .connect(sender) + .setRule( + sender.address, + senderToken.address, + DEFAULT_SENDER_AMOUNT, + signerToken.address, + DEFAULT_SIGNER_AMOUNT, + RULE_EXPIRY + ) + + const order = await createSignedOrderERC20({}, signer) + + await setUpAllowances( + sender.address, + DEFAULT_SENDER_AMOUNT, + signer.address, + DEFAULT_SIGNER_AMOUNT + PROTOCOL_FEE + ) + await setUpBalances(signer.address, sender.address) + + await expect( + delegate.connect(signer).swap(sender.address, ...order) + ).to.emit(delegate, 'DelegateSwap') + }) + + it('successfully swaps with a manager', async () => { + await delegate.connect(sender).authorize(manager.address) + + await delegate + .connect(manager) + .setRule( + sender.address, + senderToken.address, + DEFAULT_SENDER_AMOUNT, + signerToken.address, + DEFAULT_SIGNER_AMOUNT, + RULE_EXPIRY + ) + + const order = await createSignedOrderERC20({}, signer) + + await setUpAllowances( + sender.address, + DEFAULT_SENDER_AMOUNT, + signer.address, + DEFAULT_SIGNER_AMOUNT + PROTOCOL_FEE + ) + await setUpBalances(signer.address, sender.address) + + await expect( + delegate.connect(signer).swap(sender.address, ...order) + ).to.emit(delegate, 'DelegateSwap') + }) + + it('fails to swap with no rule', async () => { + const order = await createSignedOrderERC20({}, signer) + + await setUpAllowances( + signer.address, + DEFAULT_SENDER_AMOUNT + PROTOCOL_FEE, + sender.address, + DEFAULT_SIGNER_AMOUNT + ) + await setUpBalances(signer.address, sender.address) + + await signerToken.mock.balanceOf + .withArgs(delegate.address) + .returns(DEFAULT_SIGNER_AMOUNT) + + await expect(delegate.connect(signer).swap(sender.address, ...order)).to + .be.reverted + }) + + it('fails to swap with insufficient remaining sender amount on Rule', async () => { + await senderToken.mock.approve + .withArgs(delegate.address, DEFAULT_SENDER_AMOUNT - 1) + .returns(true) + + await delegate + .connect(sender) + .setRule( + sender.address, + senderToken.address, + DEFAULT_SENDER_AMOUNT - 1, + signerToken.address, + DEFAULT_SIGNER_AMOUNT, + RULE_EXPIRY + ) + + const order = await createSignedOrderERC20({}, signer) + + await setUpAllowances( + sender.address, + DEFAULT_SENDER_AMOUNT, + signer.address, + DEFAULT_SIGNER_AMOUNT + PROTOCOL_FEE + ) + await setUpBalances(signer.address, sender.address) + + await signerToken.mock.balanceOf + .withArgs(signer.address) + .returns(DEFAULT_SIGNER_AMOUNT - 1) + + await expect( + delegate.connect(signer).swap(sender.address, ...order) + ).to.be.revertedWith('SenderAmountInvalid') + }) + + it('fails to swap with insufficient signer amount on Rule', async () => { + await senderToken.mock.approve + .withArgs(delegate.address, DEFAULT_SENDER_AMOUNT - 1) + .returns(true) + + await delegate + .connect(sender) + .setRule( + sender.address, + senderToken.address, + DEFAULT_SENDER_AMOUNT, + signerToken.address, + DEFAULT_SIGNER_AMOUNT, + RULE_EXPIRY + ) + + const order = await createSignedOrderERC20( + { + signerAmount: DEFAULT_SIGNER_AMOUNT - 1, + }, + signer + ) + + await setUpAllowances( + sender.address, + DEFAULT_SENDER_AMOUNT, + signer.address, + DEFAULT_SIGNER_AMOUNT + PROTOCOL_FEE + ) + await setUpBalances(signer.address, sender.address) + + await signerToken.mock.balanceOf + .withArgs(signer.address) + .returns(DEFAULT_SIGNER_AMOUNT - 1) + + await expect( + delegate.connect(signer).swap(sender.address, ...order) + ).to.be.revertedWith('SignerAmountInvalid') + }) + + it('fails to swap with a rule expired', async () => { + await delegate + .connect(sender) + .setRule( + sender.address, + senderToken.address, + DEFAULT_SENDER_AMOUNT, + signerToken.address, + DEFAULT_SIGNER_AMOUNT, + 0 + ) + + const order = await createSignedOrderERC20({}, signer) + + await setUpAllowances( + sender.address, + DEFAULT_SENDER_AMOUNT, + signer.address, + DEFAULT_SIGNER_AMOUNT + PROTOCOL_FEE + ) + await setUpBalances(signer.address, sender.address) + + await expect( + delegate.connect(signer).swap(sender.address, ...order) + ).to.revertedWith('RuleExpired') + }) + }) +}) diff --git a/source/delegate/test/DelegateIntegration.js b/source/delegate/test/DelegateIntegration.js new file mode 100644 index 000000000..7b1519719 --- /dev/null +++ b/source/delegate/test/DelegateIntegration.js @@ -0,0 +1,139 @@ +const { expect } = require('chai') +const { + createOrderERC20, + orderERC20ToParams, + createOrderERC20Signature, + SECONDS_IN_DAY, +} = require('@airswap/utils') +const { ethers } = require('hardhat') +const ERC20 = require('@openzeppelin/contracts/build/contracts/ERC20PresetMinterPauser.json') +const SWAP_ERC20 = require('@airswap/swap-erc20/build/contracts/SwapERC20.sol/SwapERC20.json') + +describe('Delegate Integration', () => { + let snapshotId + let signerToken + let senderToken + + let deployer + let sender + let signer + + const CHAIN_ID = 31337 + const BONUS_SCALE = '10' + const BONUS_MAX = '100' + const PROTOCOL_FEE = '5' + const DEFAULT_SENDER_AMOUNT = '10000' + const DEFAULT_SIGNER_AMOUNT = '10000' + const DEFAULT_BALANCE = '1000000' + const RULE_EXPIRY = + Math.round(Date.now() / 1000 + SECONDS_IN_DAY).toString() + 1 + + async function createSignedOrderERC20(params, signer) { + const unsignedOrder = createOrderERC20({ + protocolFee: PROTOCOL_FEE, + signerWallet: signer.address, + signerToken: signerToken.address, + signerAmount: DEFAULT_SIGNER_AMOUNT, + senderWallet: delegate.address, + senderToken: senderToken.address, + senderAmount: DEFAULT_SENDER_AMOUNT, + ...params, + }) + return orderERC20ToParams({ + ...unsignedOrder, + ...(await createOrderERC20Signature( + unsignedOrder, + signer, + swapERC20.address, + CHAIN_ID + )), + }) + } + + beforeEach(async () => { + snapshotId = await ethers.provider.send('evm_snapshot') + }) + + afterEach(async () => { + await ethers.provider.send('evm_revert', [snapshotId]) + }) + + before('get signers and deploy', async () => { + ;[deployer, sender, signer, protocolFeeWallet] = await ethers.getSigners() + + swapERC20 = await ( + await ethers.getContractFactory(SWAP_ERC20.abi, SWAP_ERC20.bytecode) + ).deploy( + PROTOCOL_FEE, + PROTOCOL_FEE, + deployer.address, + BONUS_SCALE, + BONUS_MAX + ) + await swapERC20.deployed() + + delegate = await ( + await ethers.getContractFactory('Delegate') + ).deploy(swapERC20.address) + await delegate.deployed() + + signerToken = await ( + await ethers.getContractFactory(ERC20.abi, ERC20.bytecode) + ).deploy('A', 'A') + await signerToken.deployed() + await signerToken.mint(signer.address, DEFAULT_BALANCE) + + senderToken = await ( + await ethers.getContractFactory(ERC20.abi, ERC20.bytecode) + ).deploy('B', 'B') + await senderToken.deployed() + await senderToken.mint(sender.address, DEFAULT_BALANCE) + + signerToken.connect(signer).approve(swapERC20.address, DEFAULT_BALANCE) + senderToken.connect(sender).approve(delegate.address, DEFAULT_BALANCE) + }) + + describe('Test transfers', async () => { + it('test a delegated swap', async () => { + await delegate + .connect(sender) + .setRule( + sender.address, + senderToken.address, + DEFAULT_SENDER_AMOUNT, + signerToken.address, + DEFAULT_SIGNER_AMOUNT, + RULE_EXPIRY + ) + + signerToken + .connect(signer) + .approve(swapERC20.address, DEFAULT_SIGNER_AMOUNT + PROTOCOL_FEE) + + const order = await createSignedOrderERC20({}, signer) + + await expect( + delegate.connect(signer).swap(sender.address, ...order) + ).to.emit(delegate, 'DelegateSwap') + + expect(await signerToken.balanceOf(sender.address)).to.equal( + DEFAULT_SIGNER_AMOUNT + ) + + expect(await signerToken.balanceOf(signer.address)).to.equal( + DEFAULT_BALANCE - DEFAULT_SIGNER_AMOUNT - PROTOCOL_FEE + ) + + expect(await senderToken.balanceOf(signer.address)).to.equal( + DEFAULT_SENDER_AMOUNT + ) + + expect(await senderToken.balanceOf(sender.address)).to.equal( + DEFAULT_BALANCE - DEFAULT_SENDER_AMOUNT + ) + + expect(await senderToken.balanceOf(delegate.address)).to.equal(0) + expect(await signerToken.balanceOf(delegate.address)).to.equal(0) + }) + }) +}) diff --git a/source/delegate/tsconfig.json b/source/delegate/tsconfig.json new file mode 100644 index 000000000..ce170734c --- /dev/null +++ b/source/delegate/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./typechain" + }, + "files": ["./typechain/index.ts"] +} diff --git a/source/pool/deploys-commits.js b/source/pool/deploys-commits.js new file mode 100644 index 000000000..5cd4eeb77 --- /dev/null +++ b/source/pool/deploys-commits.js @@ -0,0 +1,22 @@ +module.exports = { + 1: 'a535d0bb280fdb603a903dd2ae94e6b27d66b3d4', + 5: 'a535d0bb280fdb603a903dd2ae94e6b27d66b3d4', + 30: 'a535d0bb280fdb603a903dd2ae94e6b27d66b3d4', + 31: 'a535d0bb280fdb603a903dd2ae94e6b27d66b3d4', + 40: 'a535d0bb280fdb603a903dd2ae94e6b27d66b3d4', + 41: 'a535d0bb280fdb603a903dd2ae94e6b27d66b3d4', + 56: 'a535d0bb280fdb603a903dd2ae94e6b27d66b3d4', + 97: 'a535d0bb280fdb603a903dd2ae94e6b27d66b3d4', + 137: 'a535d0bb280fdb603a903dd2ae94e6b27d66b3d4', + 8453: 'a535d0bb280fdb603a903dd2ae94e6b27d66b3d4', + 17000: 'a535d0bb280fdb603a903dd2ae94e6b27d66b3d4', + 42161: 'a535d0bb280fdb603a903dd2ae94e6b27d66b3d4', + 43113: 'a535d0bb280fdb603a903dd2ae94e6b27d66b3d4', + 43114: 'a535d0bb280fdb603a903dd2ae94e6b27d66b3d4', + 59140: 'a535d0bb280fdb603a903dd2ae94e6b27d66b3d4', + 59144: 'a535d0bb280fdb603a903dd2ae94e6b27d66b3d4', + 80001: 'a535d0bb280fdb603a903dd2ae94e6b27d66b3d4', + 84531: 'a535d0bb280fdb603a903dd2ae94e6b27d66b3d4', + 421613: 'a535d0bb280fdb603a903dd2ae94e6b27d66b3d4', + 11155111: 'a535d0bb280fdb603a903dd2ae94e6b27d66b3d4', +} diff --git a/source/pool/deploys-commits.js.d.ts b/source/pool/deploys-commits.js.d.ts new file mode 100644 index 000000000..6ecf5882d --- /dev/null +++ b/source/pool/deploys-commits.js.d.ts @@ -0,0 +1 @@ +declare module '@airswap/pool/deploys-commits.js' diff --git a/source/pool/scripts/migrate-abis/4-1-1.js b/source/pool/legacy-abis/4-1-1.js similarity index 100% rename from source/pool/scripts/migrate-abis/4-1-1.js rename to source/pool/legacy-abis/4-1-1.js diff --git a/source/pool/package.json b/source/pool/package.json index 6a53ffbeb..7917886b6 100644 --- a/source/pool/package.json +++ b/source/pool/package.json @@ -1,6 +1,6 @@ { "name": "@airswap/pool", - "version": "4.2.2", + "version": "5.0.0", "description": "AirSwap: Withdrawable Token Pool", "license": "MIT", "repository": { @@ -10,9 +10,7 @@ "files": [ "./build", "./typechain", - "./deploys.js", - "./deploys-blocks.js", - "./deploys.js.d.ts" + "./deploys*" ], "scripts": { "clean": "rm -rf ./cache && rm -rf ./build && rm -rf ./typechain", @@ -24,14 +22,12 @@ "deploy": "hardhat run ./scripts/deploy.js", "verify": "hardhat run ./scripts/verify.js", "owners": "hardhat run ./scripts/owner.js", - "migrate": "hardhat run ./scripts/migrate.js", "balances": "hardhat run ./scripts/balances.js" }, "devDependencies": { - "@airswap/utils": "4.3.4", + "@airswap/utils": "5.0.0", "@airswap/merkle": "0.0.2", - "@openzeppelin/contracts": "^4.8.3", - "prompt-confirm": "^2.0.4" + "@openzeppelin/contracts": "^4.8.3" }, "publishConfig": { "access": "public" diff --git a/source/pool/scripts/deploy.js b/source/pool/scripts/deploy.js index 520e7e6d6..9dbf15240 100644 --- a/source/pool/scripts/deploy.js +++ b/source/pool/scripts/deploy.js @@ -1,13 +1,12 @@ /* eslint-disable no-console */ const fs = require('fs') const prettier = require('prettier') -const Confirm = require('prompt-confirm') const { ethers, run } = require('hardhat') -const { chainLabels, ChainIds } = require('@airswap/utils') -const { getReceiptUrl } = require('@airswap/utils') +const { chainLabels, ChainIds, getReceiptUrl } = require('@airswap/utils') const poolDeploys = require('../deploys.js') const poolBlocks = require('../deploys-blocks.js') -const { displayDeployerInfo } = require('../../../scripts/deployer-info') +const poolCommits = require('../deploys-commits.js') +const { confirmDeployment } = require('../../../scripts/deployer-info') async function main() { await run('compile') @@ -18,16 +17,16 @@ async function main() { console.log('Value for --network flag is required') return } - await displayDeployerInfo(deployer) const scale = 10 const max = 100 - console.log(`scale: ${scale}`) - console.log(`max: ${max}`) + console.log(`\nDeploy POOL`) - const prompt = new Confirm('Proceed to deploy?') - if (await prompt.run()) { + console.log(`· max ${max}`) + console.log(`· scale ${scale}\n`) + + if (await confirmDeployment(deployer, poolDeploys[ChainIds.MAINNET])) { const poolFactory = await ethers.getContractFactory('Pool') const poolContract = await poolFactory.deploy(scale, max) console.log( @@ -54,6 +53,17 @@ async function main() { { ...prettierConfig, parser: 'babel' } ) ) + poolCommits[chainId] = require('child_process') + .execSync('git rev-parse HEAD') + .toString() + .trim() + fs.writeFileSync( + './deploys-commits.js', + prettier.format( + `module.exports = ${JSON.stringify(poolCommits, null, '\t')}`, + { ...prettierConfig, parser: 'babel' } + ) + ) console.log(`Deployed: ${poolDeploys[chainId]} @ ${poolBlocks[chainId]}`) console.log( diff --git a/source/pool/test/Pool.js b/source/pool/test/Pool.js index 551180487..15c893b1e 100644 --- a/source/pool/test/Pool.js +++ b/source/pool/test/Pool.js @@ -1,5 +1,5 @@ const { expect } = require('chai') -const { toAtomicString } = require('@airswap/utils') +const { toAtomicString, ADDRESS_ZERO } = require('@airswap/utils') const { generateTreeFromData, getRoot, getProof } = require('@airswap/merkle') const { soliditySha3 } = require('web3-utils') @@ -7,7 +7,6 @@ const { ethers, waffle } = require('hardhat') const { deployMockContract } = waffle const IERC20 = require('@openzeppelin/contracts/build/contracts/IERC20.json') const STAKING = require('@airswap/staking/build/contracts/Staking.sol/Staking.json') -const { ADDRESS_ZERO } = require('@airswap/utils') function toWei(value, places) { return toAtomicString(value, places || 18) diff --git a/source/registry/deploys-commits.js b/source/registry/deploys-commits.js new file mode 100644 index 000000000..039485113 --- /dev/null +++ b/source/registry/deploys-commits.js @@ -0,0 +1,19 @@ +module.exports = { + 1: '863586ae7594b5088619f243dbee83c8d8e0075f', + 31: '863586ae7594b5088619f243dbee83c8d8e0075f', + 41: '863586ae7594b5088619f243dbee83c8d8e0075f', + 56: '863586ae7594b5088619f243dbee83c8d8e0075f', + 97: '863586ae7594b5088619f243dbee83c8d8e0075f', + 137: '863586ae7594b5088619f243dbee83c8d8e0075f', + 8453: '863586ae7594b5088619f243dbee83c8d8e0075f', + 17000: '863586ae7594b5088619f243dbee83c8d8e0075f', + 42161: '863586ae7594b5088619f243dbee83c8d8e0075f', + 43113: '863586ae7594b5088619f243dbee83c8d8e0075f', + 43114: '863586ae7594b5088619f243dbee83c8d8e0075f', + 59140: '863586ae7594b5088619f243dbee83c8d8e0075f', + 59144: '863586ae7594b5088619f243dbee83c8d8e0075f', + 80001: '863586ae7594b5088619f243dbee83c8d8e0075f', + 84532: '863586ae7594b5088619f243dbee83c8d8e0075f', + 421614: '863586ae7594b5088619f243dbee83c8d8e0075f', + 11155111: '863586ae7594b5088619f243dbee83c8d8e0075f', +} diff --git a/source/registry/deploys-commits.js.d.ts b/source/registry/deploys-commits.js.d.ts new file mode 100644 index 000000000..a7ccb3ee2 --- /dev/null +++ b/source/registry/deploys-commits.js.d.ts @@ -0,0 +1 @@ +declare module '@airswap/registry/deploys-commits.js' diff --git a/source/registry/package.json b/source/registry/package.json index 4204cbaae..fa86a7429 100644 --- a/source/registry/package.json +++ b/source/registry/package.json @@ -1,6 +1,6 @@ { "name": "@airswap/registry", - "version": "4.2.2", + "version": "5.0.0", "description": "AirSwap: Server Registry", "license": "MIT", "repository": { @@ -10,9 +10,7 @@ "files": [ "./build", "./typechain", - "./deploys.js", - "./deploys-blocks.js", - "./deploys.js.d.ts" + "./deploys*" ], "scripts": { "clean": "rm -rf ./cache && rm -rf ./build && rm -rf ./typechain", @@ -25,12 +23,11 @@ "verify": "hardhat run ./scripts/verify.js", "owners": "hardhat run ./scripts/owner.js" }, + "devDependencies": { + "@airswap/utils": "5.0.0", + "@openzeppelin/contracts": "^4.8.3" + }, "publishConfig": { "access": "public" - }, - "devDependencies": { - "@airswap/utils": "4.3.4", - "@openzeppelin/contracts": "^4.8.3", - "prompt-confirm": "^2.0.4" } } diff --git a/source/registry/scripts/deploy.js b/source/registry/scripts/deploy.js index 7ef092c20..45275d550 100644 --- a/source/registry/scripts/deploy.js +++ b/source/registry/scripts/deploy.js @@ -1,14 +1,13 @@ /* eslint-disable no-console */ const fs = require('fs') const prettier = require('prettier') -const Confirm = require('prompt-confirm') const { ethers, run } = require('hardhat') -const { ChainIds, chainLabels } = require('@airswap/utils') -const { getReceiptUrl } = require('@airswap/utils') +const { ChainIds, chainLabels, getReceiptUrl } = require('@airswap/utils') const registryDeploys = require('../deploys.js') const registryBlocks = require('../deploys-blocks.js') +const registryCommits = require('../deploys-commits.js') const config = require('./config.js') -const { displayDeployerInfo } = require('../../../scripts/deployer-info') +const { confirmDeployment } = require('../../../scripts/deployer-info') async function main() { await run('compile') @@ -19,7 +18,6 @@ async function main() { console.log('Value for --network flag is required') return } - await displayDeployerInfo(deployer) let stakingToken let stakingCost @@ -31,12 +29,13 @@ async function main() { ;({ stakingToken, stakingCost, supportCost } = config[ChainIds.MAINNET]) } - console.log(`stakingToken: ${stakingToken}`) - console.log(`stakingCost: ${stakingCost}`) - console.log(`supportCost: ${supportCost}\n`) + console.log(`\nDeploy REGISTRY`) - const prompt = new Confirm('Proceed to deploy?') - if (await prompt.run()) { + console.log(`· stakingCost ${stakingCost}`) + console.log(`· supportCost ${supportCost}`) + console.log(`· stakingToken ${stakingToken}\n`) + + if (await confirmDeployment(deployer, registryDeploys)) { const registryFactory = await ethers.getContractFactory('Registry') const registryContract = await registryFactory.deploy( stakingToken, @@ -67,6 +66,17 @@ async function main() { { ...prettierConfig, parser: 'babel' } ) ) + registryCommits[chainId] = require('child_process') + .execSync('git rev-parse HEAD') + .toString() + .trim() + fs.writeFileSync( + './deploys-commits.js', + prettier.format( + `module.exports = ${JSON.stringify(registryCommits, null, '\t')}`, + { ...prettierConfig, parser: 'babel' } + ) + ) console.log( `Deployed: ${registryDeploys[chainId]} @ ${registryBlocks[chainId]}` ) diff --git a/source/staking/deploys-commits.js b/source/staking/deploys-commits.js new file mode 100644 index 000000000..065e5af3d --- /dev/null +++ b/source/staking/deploys-commits.js @@ -0,0 +1,5 @@ +module.exports = { + 1: '6ede424741588b8e3fbb8060cc9ee8d343eac4ab', + 17000: '6ede424741588b8e3fbb8060cc9ee8d343eac4ab', + 11155111: '6ede424741588b8e3fbb8060cc9ee8d343eac4ab', +} diff --git a/source/staking/deploys-commits.js.d.ts b/source/staking/deploys-commits.js.d.ts new file mode 100644 index 000000000..9b529be60 --- /dev/null +++ b/source/staking/deploys-commits.js.d.ts @@ -0,0 +1 @@ +declare module '@airswap/staking/deploys-commits.js' diff --git a/source/staking/package.json b/source/staking/package.json index 039751254..04564049f 100644 --- a/source/staking/package.json +++ b/source/staking/package.json @@ -1,6 +1,6 @@ { "name": "@airswap/staking", - "version": "4.2.2", + "version": "5.0.0", "description": "AirSwap: Stake Tokens", "license": "MIT", "repository": { @@ -10,12 +10,10 @@ "files": [ "./build", "./typechain", - "./deploys.js", - "./deploys-blocks.js", - "./deploys.js.d.ts" + "./deploys*" ], "scripts": { - "clean": "rm -rf cache && rm -rf ./build && rm -rf ./typechain", + "clean": "rm -rf ./cache && rm -rf ./build && rm -rf ./typechain", "compile": "hardhat compile; yarn typechain", "typechain": "tsc -b", "coverage": "hardhat coverage", @@ -25,12 +23,11 @@ "verify": "hardhat run ./scripts/verify.js", "owners": "hardhat run ./scripts/owner.js" }, + "devDependencies": { + "@airswap/utils": "5.0.0", + "@openzeppelin/contracts": "^4.8.3" + }, "publishConfig": { "access": "public" - }, - "devDependencies": { - "@airswap/utils": "4.3.4", - "@openzeppelin/contracts": "^4.8.3", - "prompt-confirm": "^2.0.4" } } diff --git a/source/staking/scripts/deploy.js b/source/staking/scripts/deploy.js index 60dd798d7..3fda5b639 100644 --- a/source/staking/scripts/deploy.js +++ b/source/staking/scripts/deploy.js @@ -1,14 +1,13 @@ /* eslint-disable no-console */ const fs = require('fs') const prettier = require('prettier') -const Confirm = require('prompt-confirm') const { ethers, run } = require('hardhat') -const { chainLabels, ChainIds } = require('@airswap/utils') -const { getReceiptUrl } = require('@airswap/utils') +const { chainLabels, ChainIds, getReceiptUrl } = require('@airswap/utils') const stakingDeploys = require('../deploys.js') const stakingBlocks = require('../deploys-blocks.js') +const stakingCommits = require('../deploys-commits.js') const config = require('./config.js') -const { displayDeployerInfo } = require('../../../scripts/deployer-info') +const { confirmDeployment } = require('../../../scripts/deployer-info') async function main() { await run('compile') @@ -19,7 +18,6 @@ async function main() { console.log('Value for --network flag is required') return } - await displayDeployerInfo(deployer) const { name, @@ -29,14 +27,15 @@ async function main() { minDurationChangeDelay, } = config[chainId] - console.log(`name: ${name}`) - console.log(`symbol: ${symbol}`) - console.log(`stakingToken: ${stakingToken}`) - console.log(`stakingDuration: ${stakingDuration}`) - console.log(`minDurationChangeDelay: ${minDurationChangeDelay}\n`) + console.log(`\nDeploy STAKING`) - const prompt = new Confirm('Proceed to deploy?') - if (await prompt.run()) { + console.log(`· name ${name}`) + console.log(`· symbol ${symbol}`) + console.log(`· stakingToken ${stakingToken}`) + console.log(`· stakingDuration ${stakingDuration}`) + console.log(`· minDurationChangeDelay ${minDurationChangeDelay}\n`) + + if (await confirmDeployment(deployer, stakingDeploys[ChainIds.MAINNET])) { const stakingFactory = await ethers.getContractFactory('Staking') const stakingContract = await stakingFactory.deploy( name, @@ -69,6 +68,17 @@ async function main() { { ...prettierConfig, parser: 'babel' } ) ) + stakingCommits[chainId] = require('child_process') + .execSync('git rev-parse HEAD') + .toString() + .trim() + fs.writeFileSync( + './deploys-commits.js', + prettier.format( + `module.exports = ${JSON.stringify(stakingCommits, null, '\t')}`, + { ...prettierConfig, parser: 'babel' } + ) + ) console.log( `Deployed: ${stakingDeploys[chainId]} @ ${stakingBlocks[chainId]}` ) diff --git a/source/swap-erc20/contracts/SwapERC20.sol b/source/swap-erc20/contracts/SwapERC20.sol index 4b8720297..a201838f6 100644 --- a/source/swap-erc20/contracts/SwapERC20.sol +++ b/source/swap-erc20/contracts/SwapERC20.sol @@ -15,7 +15,6 @@ import "./interfaces/ISwapERC20.sol"; * @notice https://www.airswap.io/ */ contract SwapERC20 is ISwapERC20, Ownable, EIP712 { - uint256 public immutable DOMAIN_CHAIN_ID; bytes32 public immutable DOMAIN_SEPARATOR; bytes32 public constant ORDER_TYPEHASH = @@ -27,7 +26,7 @@ contract SwapERC20 is ISwapERC20, Ownable, EIP712 { ); uint256 public constant FEE_DIVISOR = 10000; - uint256 private constant MAX_ERROR_COUNT = 8; + uint256 private constant MAX_ERROR_COUNT = 7; uint256 private constant MAX_MAX = 100; uint256 private constant MAX_SCALE = 77; @@ -63,15 +62,14 @@ contract SwapERC20 is ISwapERC20, Ownable, EIP712 { uint256 _bonusScale, uint256 _bonusMax ) { - if (_protocolFee >= FEE_DIVISOR) revert InvalidFee(); - if (_protocolFeeLight >= FEE_DIVISOR) revert InvalidFeeLight(); - if (_protocolFeeWallet == address(0)) revert InvalidFeeWallet(); + if (_protocolFee >= FEE_DIVISOR) revert ProtocolFeeInvalid(); + if (_protocolFeeLight >= FEE_DIVISOR) revert ProtocolFeeLightInvalid(); + if (_protocolFeeWallet == address(0)) revert ProtocolFeeWalletInvalid(); if (_bonusMax > MAX_MAX) revert MaxTooHigh(); if (_bonusScale > MAX_SCALE) revert ScaleTooHigh(); _initializeOwner(msg.sender); - DOMAIN_CHAIN_ID = block.chainid; DOMAIN_SEPARATOR = _domainSeparator(); protocolFee = _protocolFee; @@ -257,23 +255,19 @@ contract SwapERC20 is ISwapERC20, Ownable, EIP712 { // Recover the signatory from the hash and signature address signatory = ECDSA.tryRecover( - keccak256( - abi.encodePacked( - "\x19\x01", // EIP191: Indicates EIP712 - DOMAIN_SEPARATOR, - keccak256( - abi.encode( - ORDER_TYPEHASH, - nonce, - expiry, - signerWallet, - signerToken, - signerAmount, - protocolFeeLight, - msg.sender, - senderToken, - senderAmount - ) + _hashTypedData( + keccak256( + abi.encode( + ORDER_TYPEHASH, + nonce, + expiry, + signerWallet, + signerToken, + signerAmount, + protocolFeeLight, + msg.sender, + senderToken, + senderAmount ) ) ), @@ -330,7 +324,7 @@ contract SwapERC20 is ISwapERC20, Ownable, EIP712 { */ function setProtocolFee(uint256 _protocolFee) external onlyOwner { // Ensure the fee is less than divisor - if (_protocolFee >= FEE_DIVISOR) revert InvalidFee(); + if (_protocolFee >= FEE_DIVISOR) revert ProtocolFeeInvalid(); protocolFee = _protocolFee; emit SetProtocolFee(_protocolFee); } @@ -341,7 +335,7 @@ contract SwapERC20 is ISwapERC20, Ownable, EIP712 { */ function setProtocolFeeLight(uint256 _protocolFeeLight) external onlyOwner { // Ensure the fee is less than divisor - if (_protocolFeeLight >= FEE_DIVISOR) revert InvalidFeeLight(); + if (_protocolFeeLight >= FEE_DIVISOR) revert ProtocolFeeLightInvalid(); protocolFeeLight = _protocolFeeLight; emit SetProtocolFeeLight(_protocolFeeLight); } @@ -352,7 +346,7 @@ contract SwapERC20 is ISwapERC20, Ownable, EIP712 { */ function setProtocolFeeWallet(address _protocolFeeWallet) external onlyOwner { // Ensure the new fee wallet is not null - if (_protocolFeeWallet == address(0)) revert InvalidFeeWallet(); + if (_protocolFeeWallet == address(0)) revert ProtocolFeeWalletInvalid(); protocolFeeWallet = _protocolFeeWallet; emit SetProtocolFeeWallet(_protocolFeeWallet); } @@ -385,7 +379,7 @@ contract SwapERC20 is ISwapERC20, Ownable, EIP712 { */ function setStaking(address _stakingToken) external onlyOwner { // Ensure the new staking token is not null - if (_stakingToken == address(0)) revert InvalidStaking(); + if (_stakingToken == address(0)) revert StakingInvalid(); stakingToken = _stakingToken; emit SetStaking(_stakingToken); } @@ -474,10 +468,6 @@ contract SwapERC20 is ISwapERC20, Ownable, EIP712 { order.s = s; order.senderWallet = senderWallet; - if (DOMAIN_CHAIN_ID != block.chainid) { - errors[count++] = "ChainIdChanged"; - } - // Validate as the authorized signatory if set address signatory = order.signerWallet; if (authorized[signatory] != address(0)) { @@ -655,9 +645,6 @@ contract SwapERC20 is ISwapERC20, Ownable, EIP712 { bytes32 r, bytes32 s ) private { - // Ensure execution on the intended chain - if (DOMAIN_CHAIN_ID != block.chainid) revert ChainIdChanged(); - // Ensure the expiry is not passed if (expiry <= block.timestamp) revert OrderExpired(); @@ -711,23 +698,19 @@ contract SwapERC20 is ISwapERC20, Ownable, EIP712 { uint256 senderAmount ) private view returns (bytes32) { return - keccak256( - abi.encodePacked( - "\x19\x01", // EIP191: Indicates EIP712 - DOMAIN_SEPARATOR, - keccak256( - abi.encode( - ORDER_TYPEHASH, - nonce, - expiry, - signerWallet, - signerToken, - signerAmount, - protocolFee, - senderWallet, - senderToken, - senderAmount - ) + _hashTypedData( + keccak256( + abi.encode( + ORDER_TYPEHASH, + nonce, + expiry, + signerWallet, + signerToken, + signerAmount, + protocolFee, + senderWallet, + senderToken, + senderAmount ) ) ); diff --git a/source/swap-erc20/contracts/interfaces/ISwapERC20.sol b/source/swap-erc20/contracts/interfaces/ISwapERC20.sol index 70a62308f..bc8722b50 100644 --- a/source/swap-erc20/contracts/interfaces/ISwapERC20.sol +++ b/source/swap-erc20/contracts/interfaces/ISwapERC20.sol @@ -28,17 +28,16 @@ interface ISwapERC20 { event SetBonusMax(uint256 bonusMax); event SetStaking(address indexed staking); - error ChainIdChanged(); - error InvalidFee(); - error InvalidFeeLight(); - error InvalidFeeWallet(); - error InvalidStaking(); - error OrderExpired(); error MaxTooHigh(); error NonceAlreadyUsed(uint256); + error OrderExpired(); + error ProtocolFeeInvalid(); + error ProtocolFeeLightInvalid(); + error ProtocolFeeWalletInvalid(); error ScaleTooHigh(); error SignatoryInvalid(); error SignatureInvalid(); + error StakingInvalid(); error TransferFromFailed(); function swap( diff --git a/source/swap-erc20/deploys-commits.js b/source/swap-erc20/deploys-commits.js new file mode 100644 index 000000000..6958ffd06 --- /dev/null +++ b/source/swap-erc20/deploys-commits.js @@ -0,0 +1,17 @@ +module.exports = { + 1: '8870acc9b7241bc6d5bb7a1e094ddb37d2d2a927', + 56: '8870acc9b7241bc6d5bb7a1e094ddb37d2d2a927', + 97: '8870acc9b7241bc6d5bb7a1e094ddb37d2d2a927', + 137: '8870acc9b7241bc6d5bb7a1e094ddb37d2d2a927', + 8453: '8870acc9b7241bc6d5bb7a1e094ddb37d2d2a927', + 17000: '8870acc9b7241bc6d5bb7a1e094ddb37d2d2a927', + 42161: '8870acc9b7241bc6d5bb7a1e094ddb37d2d2a927', + 43113: '8870acc9b7241bc6d5bb7a1e094ddb37d2d2a927', + 43114: '8870acc9b7241bc6d5bb7a1e094ddb37d2d2a927', + 59140: '8870acc9b7241bc6d5bb7a1e094ddb37d2d2a927', + 59144: '8870acc9b7241bc6d5bb7a1e094ddb37d2d2a927', + 80001: '8870acc9b7241bc6d5bb7a1e094ddb37d2d2a927', + 84532: '8870acc9b7241bc6d5bb7a1e094ddb37d2d2a927', + 421614: '8870acc9b7241bc6d5bb7a1e094ddb37d2d2a927', + 11155111: '8870acc9b7241bc6d5bb7a1e094ddb37d2d2a927', +} diff --git a/source/swap-erc20/deploys-commits.js.d.ts b/source/swap-erc20/deploys-commits.js.d.ts new file mode 100644 index 000000000..17f79173d --- /dev/null +++ b/source/swap-erc20/deploys-commits.js.d.ts @@ -0,0 +1 @@ +declare module '@airswap/swap-erc20/deploys-commits.js' diff --git a/source/swap-erc20/package.json b/source/swap-erc20/package.json index 69344afed..5b1375967 100644 --- a/source/swap-erc20/package.json +++ b/source/swap-erc20/package.json @@ -1,6 +1,6 @@ { "name": "@airswap/swap-erc20", - "version": "4.3.1", + "version": "5.0.0", "description": "AirSwap: Atomic ERC20 Token Swap", "license": "MIT", "repository": { @@ -10,9 +10,7 @@ "files": [ "./build", "./typechain", - "./deploys.js", - "./deploys-blocks.js", - "./deploys.js.d.ts" + "./deploys*" ], "scripts": { "clean": "rm -rf ./cache && rm -rf ./build && rm -rf ./typechain", @@ -26,9 +24,8 @@ "owners": "hardhat run ./scripts/owner.js" }, "devDependencies": { - "@airswap/staking": "4.2.2", - "@airswap/utils": "4.3.4", - "prompt-confirm": "^2.0.4", + "@airswap/staking": "5.0.0", + "@airswap/utils": "5.0.0", "solady": "0.0.173" }, "publishConfig": { diff --git a/source/swap-erc20/scripts/deploy.js b/source/swap-erc20/scripts/deploy.js index 424febabc..f7dc20094 100644 --- a/source/swap-erc20/scripts/deploy.js +++ b/source/swap-erc20/scripts/deploy.js @@ -1,19 +1,19 @@ /* eslint-disable no-console */ const fs = require('fs') const prettier = require('prettier') -const Confirm = require('prompt-confirm') const { ethers, run } = require('hardhat') const poolDeploys = require('@airswap/pool/deploys.js') const { ChainIds, chainLabels, protocolFeeReceiverAddresses, + getReceiptUrl, } = require('@airswap/utils') -const { getReceiptUrl } = require('@airswap/utils') const swapERC20Deploys = require('../deploys.js') const swapERC20Blocks = require('../deploys-blocks.js') +const swapERC20Commits = require('../deploys-commits.js') const config = require('./config.js') -const { displayDeployerInfo } = require('../../../scripts/deployer-info') +const { confirmDeployment } = require('../../../scripts/deployer-info') async function main() { await run('compile') @@ -24,7 +24,6 @@ async function main() { console.log('Value for --network flag is required') return } - await displayDeployerInfo(deployer) let protocolFeeReceiver = poolDeploys[chainId] if (protocolFeeReceiverAddresses[chainId]) { @@ -43,12 +42,13 @@ async function main() { config[ChainIds.MAINNET]) } - console.log(`protocolFee: ${protocolFee}`) - console.log(`protocolFeeLight: ${protocolFeeLight}`) - console.log(`protocolFeeReceiver: ${protocolFeeReceiver}\n`) + console.log(`\nDeploy SWAPERC20`) - const prompt = new Confirm('Proceed to deploy?') - if (await prompt.run()) { + console.log(`· protocolFee ${protocolFee}`) + console.log(`· protocolFeeLight ${protocolFeeLight}`) + console.log(`· protocolFeeReceiver ${protocolFeeReceiver}\n`) + + if (await confirmDeployment(deployer, swapERC20Deploys[ChainIds.MAINNET])) { const swapFactory = await ethers.getContractFactory('SwapERC20') const swapContract = await swapFactory.deploy( protocolFee, @@ -81,6 +81,17 @@ async function main() { { ...prettierConfig, parser: 'babel' } ) ) + swapERC20Commits[chainId] = require('child_process') + .execSync('git rev-parse HEAD') + .toString() + .trim() + fs.writeFileSync( + './deploys-commits.js', + prettier.format( + `module.exports = ${JSON.stringify(swapERC20Commits, null, '\t')}`, + { ...prettierConfig, parser: 'babel' } + ) + ) console.log( `Deployed: ${swapERC20Deploys[chainId]} @ ${swapERC20Blocks[chainId]}` ) diff --git a/source/swap-erc20/test/SwapERC20.js b/source/swap-erc20/test/SwapERC20.js index e0084068b..2440ab275 100644 --- a/source/swap-erc20/test/SwapERC20.js +++ b/source/swap-erc20/test/SwapERC20.js @@ -1,9 +1,10 @@ const { expect } = require('chai') -const { ADDRESS_ZERO, SECONDS_IN_DAY } = require('@airswap/utils') const { createOrderERC20, orderERC20ToParams, createOrderERC20Signature, + ADDRESS_ZERO, + SECONDS_IN_DAY, } = require('@airswap/utils') const { ethers, waffle } = require('hardhat') const { deployMockContract } = waffle @@ -180,7 +181,7 @@ describe('SwapERC20 Unit', () => { BONUS_SCALE, BONUS_MAX ) - ).to.be.revertedWith('InvalidFeeWallet') + ).to.be.revertedWith('ProtocolFeeWalletInvalid') }) it('test invalid fee', async () => { @@ -194,7 +195,7 @@ describe('SwapERC20 Unit', () => { BONUS_SCALE, BONUS_MAX ) - ).to.be.revertedWith('InvalidFee') + ).to.be.revertedWith('ProtocolFeeInvalid') }) it('test invalid fee light', async () => { @@ -208,7 +209,7 @@ describe('SwapERC20 Unit', () => { BONUS_SCALE, BONUS_MAX ) - ).to.be.revertedWith('InvalidFeeLight') + ).to.be.revertedWith('ProtocolFeeLightInvalid') }) it('test invalid bonus scale', async () => { @@ -250,7 +251,7 @@ describe('SwapERC20 Unit', () => { it('test setProtocolFee with invalid input', async () => { await expect( swap.connect(deployer).setProtocolFee(FEE_DIVISOR) - ).to.be.revertedWith('InvalidFee') + ).to.be.revertedWith('ProtocolFeeInvalid') }) it('test setProtocolFee as non-owner', async () => { await expect( @@ -265,7 +266,7 @@ describe('SwapERC20 Unit', () => { it('test setProtocolFeeLight with invalid input', async () => { await expect( swap.connect(deployer).setProtocolFeeLight(FEE_DIVISOR) - ).to.be.revertedWith('InvalidFeeLight') + ).to.be.revertedWith('ProtocolFeeLightInvalid') }) it('test setProtocolFeeLight as non-owner', async () => { await expect( @@ -318,7 +319,7 @@ describe('SwapERC20 Unit', () => { it('test setStaking with zero address', async () => { await expect( swap.connect(deployer).setStaking(ADDRESS_ZERO) - ).to.be.revertedWith('InvalidStaking') + ).to.be.revertedWith('StakingInvalid') }) }) @@ -688,7 +689,7 @@ describe('SwapERC20 Unit', () => { it('test invalid fee wallet', async () => { await expect( swap.connect(deployer).setProtocolFeeWallet(ADDRESS_ZERO) - ).to.be.revertedWith('InvalidFeeWallet') + ).to.be.revertedWith('ProtocolFeeWalletInvalid') }) it('test changing fee', async () => { @@ -721,7 +722,7 @@ describe('SwapERC20 Unit', () => { it('test invalid fee', async () => { await expect( swap.connect(deployer).setProtocolFee(FEE_DIVISOR + 1) - ).to.be.revertedWith('InvalidFee') + ).to.be.revertedWith('ProtocolFeeInvalid') }) it('test when signed with incorrect fee', async () => { diff --git a/source/swap/contracts/Swap.sol b/source/swap/contracts/Swap.sol index b2b136d1b..86f432981 100644 --- a/source/swap/contracts/Swap.sol +++ b/source/swap/contracts/Swap.sol @@ -28,11 +28,10 @@ contract Swap is ISwap, Ownable2Step, EIP712 { // Domain name and version for use in EIP712 signatures string public constant DOMAIN_NAME = "SWAP"; string public constant DOMAIN_VERSION = "4.2"; - uint256 public immutable DOMAIN_CHAIN_ID; bytes32 public immutable DOMAIN_SEPARATOR; uint256 public constant FEE_DIVISOR = 10000; - uint256 private constant MAX_ERROR_COUNT = 16; + uint256 private constant MAX_ERROR_COUNT = 15; /** * @notice Double mapping of signers to nonce groups to nonce states @@ -71,7 +70,6 @@ contract Swap is ISwap, Ownable2Step, EIP712 { if (_protocolFeeWallet == address(0)) revert FeeWalletInvalid(); if (_adapters.length == 0) revert AdaptersInvalid(); - DOMAIN_CHAIN_ID = block.chainid; DOMAIN_SEPARATOR = _domainSeparatorV4(); uint256 adaptersLength = _adapters.length; @@ -268,10 +266,6 @@ contract Swap is ISwap, Ownable2Step, EIP712 { bytes32[] memory errors = new bytes32[](MAX_ERROR_COUNT); uint256 count; - if (DOMAIN_CHAIN_ID != block.chainid) { - errors[count++] = "ChainIdChanged"; - } - // Validate as the authorized signatory if set address signatory = order.signer.wallet; if (authorized[signatory] != address(0)) { @@ -423,9 +417,6 @@ contract Swap is ISwap, Ownable2Step, EIP712 { * @param order Order to validate */ function _check(Order calldata order) private { - // Ensure execution on the intended chain - if (DOMAIN_CHAIN_ID != block.chainid) revert ChainIdChanged(); - // Ensure the sender token is the required kind if (order.sender.kind != requiredSenderKind) revert SenderTokenInvalid(); @@ -466,21 +457,17 @@ contract Swap is ISwap, Ownable2Step, EIP712 { */ function _getOrderHash(Order calldata order) private view returns (bytes32) { return - keccak256( - abi.encodePacked( - "\x19\x01", // EIP191: Indicates EIP712 - DOMAIN_SEPARATOR, - keccak256( - abi.encode( - ORDER_TYPEHASH, - order.nonce, - order.expiry, - protocolFee, - keccak256(abi.encode(PARTY_TYPEHASH, order.signer)), - keccak256(abi.encode(PARTY_TYPEHASH, order.sender)), - order.affiliateWallet, - order.affiliateAmount - ) + _hashTypedDataV4( + keccak256( + abi.encode( + ORDER_TYPEHASH, + order.nonce, + order.expiry, + protocolFee, + keccak256(abi.encode(PARTY_TYPEHASH, order.signer)), + keccak256(abi.encode(PARTY_TYPEHASH, order.sender)), + order.affiliateWallet, + order.affiliateAmount ) ) ); diff --git a/source/swap/contracts/interfaces/ISwap.sol b/source/swap/contracts/interfaces/ISwap.sol index 27a7888bd..c123d1de6 100644 --- a/source/swap/contracts/interfaces/ISwap.sol +++ b/source/swap/contracts/interfaces/ISwap.sol @@ -36,7 +36,6 @@ interface ISwap { event Authorize(address indexed signer, address indexed signerWallet); event Revoke(address indexed signer, address indexed signerWallet); - error ChainIdChanged(); error AdaptersInvalid(); error FeeInvalid(); error FeeWalletInvalid(); diff --git a/source/swap/deploys-adapters-commits.js b/source/swap/deploys-adapters-commits.js new file mode 100644 index 000000000..4ba52ba2c --- /dev/null +++ b/source/swap/deploys-adapters-commits.js @@ -0,0 +1 @@ +module.exports = {} diff --git a/source/swap/deploys-adapters-commits.js.d.ts b/source/swap/deploys-adapters-commits.js.d.ts new file mode 100644 index 000000000..44145ddfd --- /dev/null +++ b/source/swap/deploys-adapters-commits.js.d.ts @@ -0,0 +1 @@ +declare module '@airswap/swap/deploys-commits.js' diff --git a/source/swap/deploys-commits.js b/source/swap/deploys-commits.js new file mode 100644 index 000000000..a419bbde0 --- /dev/null +++ b/source/swap/deploys-commits.js @@ -0,0 +1,19 @@ +module.exports = { + 1: '15fc7c8ac53cf44f1e2d450edabb456d2f2d1ff0', + 31: '15fc7c8ac53cf44f1e2d450edabb456d2f2d1ff0', + 41: '15fc7c8ac53cf44f1e2d450edabb456d2f2d1ff0', + 56: '15fc7c8ac53cf44f1e2d450edabb456d2f2d1ff0', + 97: '15fc7c8ac53cf44f1e2d450edabb456d2f2d1ff0', + 137: '15fc7c8ac53cf44f1e2d450edabb456d2f2d1ff0', + 8453: '15fc7c8ac53cf44f1e2d450edabb456d2f2d1ff0', + 17000: '15fc7c8ac53cf44f1e2d450edabb456d2f2d1ff0', + 42161: '15fc7c8ac53cf44f1e2d450edabb456d2f2d1ff0', + 43113: '15fc7c8ac53cf44f1e2d450edabb456d2f2d1ff0', + 43114: '15fc7c8ac53cf44f1e2d450edabb456d2f2d1ff0', + 59140: '15fc7c8ac53cf44f1e2d450edabb456d2f2d1ff0', + 59144: '15fc7c8ac53cf44f1e2d450edabb456d2f2d1ff0', + 80001: '15fc7c8ac53cf44f1e2d450edabb456d2f2d1ff0', + 84532: '15fc7c8ac53cf44f1e2d450edabb456d2f2d1ff0', + 421614: '15fc7c8ac53cf44f1e2d450edabb456d2f2d1ff0', + 11155111: '15fc7c8ac53cf44f1e2d450edabb456d2f2d1ff0', +} diff --git a/source/swap/deploys-commits.js.d.ts b/source/swap/deploys-commits.js.d.ts new file mode 100644 index 000000000..44145ddfd --- /dev/null +++ b/source/swap/deploys-commits.js.d.ts @@ -0,0 +1 @@ +declare module '@airswap/swap/deploys-commits.js' diff --git a/source/swap/package.json b/source/swap/package.json index f082be7aa..c328403a1 100644 --- a/source/swap/package.json +++ b/source/swap/package.json @@ -1,6 +1,6 @@ { "name": "@airswap/swap", - "version": "4.2.2", + "version": "5.0.0", "description": "AirSwap: Atomic Token Swap", "license": "MIT", "repository": { @@ -10,10 +10,7 @@ "files": [ "./build", "./typechain", - "./deploys.js", - "./deploys-blocks.js", - "./deploys.js.d.ts", - "./deploys-adapters.js" + "./deploys*" ], "scripts": { "clean": "rm -rf ./cache && rm -rf ./build && rm -rf ./typechain", @@ -29,7 +26,7 @@ "owners": "hardhat run ./scripts/owner.js" }, "devDependencies": { - "@airswap/utils": "4.3.4", + "@airswap/utils": "5.0.0", "@nomicfoundation/hardhat-network-helpers": "^1.0.7", "@openzeppelin/contracts": "^4.8.3" }, diff --git a/source/swap/scripts/deploy-adapters.js b/source/swap/scripts/deploy-adapters.js index 832f8446b..d8e71439c 100644 --- a/source/swap/scripts/deploy-adapters.js +++ b/source/swap/scripts/deploy-adapters.js @@ -1,13 +1,12 @@ /* eslint-disable no-console */ const fs = require('fs') const prettier = require('prettier') -const Confirm = require('prompt-confirm') const { ethers, run } = require('hardhat') -const { chainLabels, ChainIds } = require('@airswap/utils') -const { getReceiptUrl } = require('@airswap/utils') +const { chainLabels, ChainIds, getReceiptUrl } = require('@airswap/utils') const adapterDeploys = require('../deploys-adapters.js') const adapterBlocks = require('../deploys-adapters-blocks.js') -const { displayDeployerInfo } = require('../../../scripts/deployer-info') +const adapterCommits = require('../deploys-adapters-commits.js') +const { confirmDeployment } = require('../../../scripts/deployer-info') async function main() { await run('compile') @@ -18,13 +17,14 @@ async function main() { console.log('Value for --network flag is required') return } - await displayDeployerInfo(deployer) const adapters = ['ERC20Adapter', 'ERC721Adapter', 'ERC1155Adapter'] - console.log(`adapters: ${JSON.stringify(adapters)}`) - const prompt = new Confirm('Proceed to deploy?') - if (await prompt.run()) { + console.log(`\nDeploy ADAPTERS`) + + console.log(`· adapters ${adapters.join(', ')}\n`) + + if (await confirmDeployment(deployer, adapterDeploys[ChainIds.MAINNET][0])) { const blocks = [] for (let i = 0; i < adapters.length; i++) { const adapterContract = await ( @@ -55,7 +55,17 @@ async function main() { { ...prettierConfig, parser: 'babel' } ) ) - + adapterCommits[chainId] = require('child_process') + .execSync('git rev-parse HEAD') + .toString() + .trim() + fs.writeFileSync( + './deploys-adapters-commits.js', + prettier.format( + `module.exports = ${JSON.stringify(adapterCommits, null, '\t')}`, + { ...prettierConfig, parser: 'babel' } + ) + ) console.log( `\nVerify with "yarn verify-adapters --network ${chainLabels[ chainId diff --git a/source/swap/scripts/deploy.js b/source/swap/scripts/deploy.js index b081f69ec..ad1134f7a 100644 --- a/source/swap/scripts/deploy.js +++ b/source/swap/scripts/deploy.js @@ -1,21 +1,21 @@ /* eslint-disable no-console */ const fs = require('fs') const prettier = require('prettier') -const Confirm = require('prompt-confirm') const { ethers, run } = require('hardhat') const { chainLabels, ChainIds, protocolFeeReceiverAddresses, ADDRESS_ZERO, + getReceiptUrl, } = require('@airswap/utils') -const { getReceiptUrl } = require('@airswap/utils') const poolDeploys = require('@airswap/pool/deploys.js') const swapDeploys = require('../deploys.js') const swapBlocks = require('../deploys-blocks.js') +const swapCommits = require('../deploys-commits.js') const adapterDeploys = require('../deploys-adapters.js') const config = require('./config.js') -const { displayDeployerInfo } = require('../../../scripts/deployer-info') +const { confirmDeployment } = require('../../../scripts/deployer-info') async function main() { await run('compile') @@ -26,7 +26,6 @@ async function main() { console.log('Value for --network flag is required') return } - await displayDeployerInfo(deployer) let requiredSenderKind let protocolFee @@ -41,13 +40,14 @@ async function main() { protocolFeeReceiver = protocolFeeReceiverAddresses[chainId] } - console.log(`adapters: ${JSON.stringify(adapterDeploys[chainId])}`) - console.log(`requiredSenderKind: ${requiredSenderKind}`) - console.log(`protocolFee: ${protocolFee}`) - console.log(`protocolFeeReceiver: ${protocolFeeReceiver}`) + console.log(`\nDeploy SWAP`) - const prompt = new Confirm('Proceed to deploy?') - if (await prompt.run()) { + console.log(`· adapters ${adapterDeploys[chainId].join(', ')}`) + console.log(`· protocolFee ${protocolFee}`) + console.log(`· requiredSenderKind ${requiredSenderKind}`) + console.log(`· protocolFeeReceiver ${protocolFeeReceiver}\n`) + + if (await confirmDeployment(deployer, swapDeploys)) { const swapFactory = await ethers.getContractFactory('Swap') const swapContract = await swapFactory.deploy( adapterDeploys[chainId], @@ -79,6 +79,17 @@ async function main() { { ...prettierConfig, parser: 'babel' } ) ) + swapCommits[chainId] = require('child_process') + .execSync('git rev-parse HEAD') + .toString() + .trim() + fs.writeFileSync( + './deploys-commits.js', + prettier.format( + `module.exports = ${JSON.stringify(swapCommits, null, '\t')}`, + { ...prettierConfig, parser: 'babel' } + ) + ) console.log(`Deployed: ${swapDeploys[chainId]} @ ${swapBlocks[chainId]}`) console.log( diff --git a/source/swap/test/Swap.js b/source/swap/test/Swap.js index 14c108e4e..21a7eb6f6 100644 --- a/source/swap/test/Swap.js +++ b/source/swap/test/Swap.js @@ -5,8 +5,12 @@ const { deployMockContract } = waffle const IERC20 = require('@openzeppelin/contracts/build/contracts/IERC20.json') const IERC721 = require('@openzeppelin/contracts/build/contracts/ERC721Royalty.json') const IERC1155 = require('@openzeppelin/contracts/build/contracts/IERC1155.json') -const { createOrder, createOrderSignature } = require('@airswap/utils') -const { TokenKinds, ADDRESS_ZERO } = require('@airswap/utils') +const { + createOrder, + createOrderSignature, + TokenKinds, + ADDRESS_ZERO, +} = require('@airswap/utils') const CHAIN_ID = 31337 const PROTOCOL_FEE = '30' diff --git a/source/swap/test/SwapIntegration.js b/source/swap/test/SwapIntegration.js index a40d22cff..cdc2636ef 100644 --- a/source/swap/test/SwapIntegration.js +++ b/source/swap/test/SwapIntegration.js @@ -1,7 +1,11 @@ const { expect } = require('chai') const { ethers } = require('hardhat') -const { createOrder, createOrderSignature } = require('@airswap/utils') -const { TokenKinds, ADDRESS_ZERO } = require('@airswap/utils') +const { + createOrder, + createOrderSignature, + TokenKinds, + ADDRESS_ZERO, +} = require('@airswap/utils') const ERC20PresetMinterPauser = require('@openzeppelin/contracts/build/contracts/ERC20PresetMinterPauser.json') const ERC1155PresetMinterPauser = require('@openzeppelin/contracts/build/contracts/ERC1155PresetMinterPauser.json') diff --git a/source/wrapper/deploys-commits.js b/source/wrapper/deploys-commits.js new file mode 100644 index 000000000..47f637366 --- /dev/null +++ b/source/wrapper/deploys-commits.js @@ -0,0 +1,21 @@ +module.exports = { + 1: 'e2dd551b1684cf2f9e4494386c9e3f6ff614fe4f', + 5: 'e2dd551b1684cf2f9e4494386c9e3f6ff614fe4f', + 30: 'e2dd551b1684cf2f9e4494386c9e3f6ff614fe4f', + 31: 'e2dd551b1684cf2f9e4494386c9e3f6ff614fe4f', + 40: 'e2dd551b1684cf2f9e4494386c9e3f6ff614fe4f', + 41: 'e2dd551b1684cf2f9e4494386c9e3f6ff614fe4f', + 56: 'e2dd551b1684cf2f9e4494386c9e3f6ff614fe4f', + 97: 'e2dd551b1684cf2f9e4494386c9e3f6ff614fe4f', + 137: 'e2dd551b1684cf2f9e4494386c9e3f6ff614fe4f', + 8453: 'e2dd551b1684cf2f9e4494386c9e3f6ff614fe4f', + 17000: 'e2dd551b1684cf2f9e4494386c9e3f6ff614fe4f', + 42161: 'e2dd551b1684cf2f9e4494386c9e3f6ff614fe4f', + 43113: 'e2dd551b1684cf2f9e4494386c9e3f6ff614fe4f', + 43114: 'e2dd551b1684cf2f9e4494386c9e3f6ff614fe4f', + 59140: 'e2dd551b1684cf2f9e4494386c9e3f6ff614fe4f', + 59144: 'e2dd551b1684cf2f9e4494386c9e3f6ff614fe4f', + 80001: 'e2dd551b1684cf2f9e4494386c9e3f6ff614fe4f', + 421613: 'e2dd551b1684cf2f9e4494386c9e3f6ff614fe4f', + 11155111: 'e2dd551b1684cf2f9e4494386c9e3f6ff614fe4f', +} diff --git a/source/wrapper/deploys-commits.js.d.ts b/source/wrapper/deploys-commits.js.d.ts new file mode 100644 index 000000000..54a10ee54 --- /dev/null +++ b/source/wrapper/deploys-commits.js.d.ts @@ -0,0 +1 @@ +declare module '@airswap/wrapper/deploys-commits.js' diff --git a/source/wrapper/package.json b/source/wrapper/package.json index 876df3e64..6a38cf7b3 100644 --- a/source/wrapper/package.json +++ b/source/wrapper/package.json @@ -1,6 +1,6 @@ { "name": "@airswap/wrapper", - "version": "4.2.3", + "version": "5.0.0", "description": "AirSwap: Wrap and Unwrap Native Tokens", "license": "MIT", "repository": { @@ -10,11 +10,7 @@ "files": [ "./build", "./typechain", - "./deploys.js", - "./deploys-blocks.js", - "./deploys-weth.js", - "./deploys-blocks-weth.js", - "./deploys.js.d.ts" + "./deploys*" ], "scripts": { "clean": "rm -rf ./cache && rm -rf ./build && rm -rf ./typechain", @@ -22,17 +18,16 @@ "typechain": "tsc -b", "coverage": "hardhat coverage", "test": "hardhat test", - "test:ci": "hardhat", + "test:ci": "hardhat test", "deploy": "hardhat run ./scripts/deploy.js", "verify": "hardhat run ./scripts/verify.js", "owners": "hardhat run ./scripts/owner.js" }, "devDependencies": { - "@airswap/utils": "4.3.4", - "@airswap/swap-erc20": "4.3.1", + "@airswap/utils": "5.0.0", + "@airswap/swap-erc20": "5.0.0", "@openzeppelin/contracts": "^4.8.3", - "@uniswap/v2-periphery": "^1.1.0-beta.0", - "prompt-confirm": "^2.0.4" + "@uniswap/v2-periphery": "^1.1.0-beta.0" }, "publishConfig": { "access": "public" diff --git a/source/wrapper/scripts/deploy.js b/source/wrapper/scripts/deploy.js index 220eecb38..15d6940d8 100644 --- a/source/wrapper/scripts/deploy.js +++ b/source/wrapper/scripts/deploy.js @@ -1,14 +1,14 @@ /* eslint-disable no-console */ const fs = require('fs') const prettier = require('prettier') -const Confirm = require('prompt-confirm') const { ethers, run } = require('hardhat') +const { chainLabels, ChainIds, getReceiptUrl } = require('@airswap/utils') const swapDeploys = require('@airswap/swap-erc20/deploys.js') const wrapperDeploys = require('../deploys.js') const wrapperBlocks = require('../deploys-blocks.js') +const wrapperCommits = require('../deploys-commits.js') const wethDeploys = require('../deploys-weth.js') -const { ChainIds, chainNames, chainLabels } = require('@airswap/utils') -const { getReceiptUrl } = require('@airswap/utils') +const { confirmDeployment } = require('../../../scripts/deployer-info') async function main() { await run('compile') @@ -21,9 +21,6 @@ async function main() { console.log('Value for --network flag is required') return } - console.log(`Deployer: ${deployer.address}`) - console.log(`Network: ${chainNames[chainId].toUpperCase()}`) - console.log(`Gas price: ${gasPrice / 10 ** 9} gwei\n`) const swapERC20Address = swapDeploys[chainId] const wrappedTokenAddress = wethDeploys[chainId] @@ -33,11 +30,12 @@ async function main() { return } - console.log(`SwapERC20: ${swapERC20Address}`) - console.log(`Wrapped: ${wrappedTokenAddress}`) + console.log(`\nDeploy WRAPPER`) - const prompt = new Confirm('Proceed to deploy?') - if (await prompt.run()) { + console.log(`· swapERC20Address ${swapERC20Address}`) + console.log(`· wrappedTokenAddress ${wrappedTokenAddress}\n`) + + if (await confirmDeployment(deployer, wrapperDeploys[ChainIds.MAINNET])) { const wrapperFactory = await ethers.getContractFactory('Wrapper') const wrapperContract = await wrapperFactory.deploy( swapERC20Address, @@ -70,6 +68,17 @@ async function main() { { ...prettierConfig, parser: 'babel' } ) ) + wrapperCommits[chainId] = require('child_process') + .execSync('git rev-parse HEAD') + .toString() + .trim() + fs.writeFileSync( + './deploys-commits.js', + prettier.format( + `module.exports = ${JSON.stringify(wrapperCommits, null, '\t')}`, + { ...prettierConfig, parser: 'babel' } + ) + ) console.log( `Deployed: ${wrapperDeploys[chainId]} @ ${wrapperBlocks[chainId]}` ) diff --git a/source/wrapper/test/Wrapper.js b/source/wrapper/test/Wrapper.js index be3c910df..2caad88d2 100644 --- a/source/wrapper/test/Wrapper.js +++ b/source/wrapper/test/Wrapper.js @@ -3,10 +3,10 @@ const { createOrderERC20, orderERC20ToParams, createOrderERC20Signature, + ADDRESS_ZERO, } = require('@airswap/utils') const { ethers, waffle } = require('hardhat') const { deployMockContract } = waffle -const { ADDRESS_ZERO } = require('@airswap/utils') const IERC20 = require('@openzeppelin/contracts/build/contracts/IERC20.json') const IWETH = require('../build/contracts/interfaces/IWETH.sol/IWETH.json') diff --git a/tools/libraries/package.json b/tools/libraries/package.json index 75e62e850..7afffdee6 100644 --- a/tools/libraries/package.json +++ b/tools/libraries/package.json @@ -1,6 +1,6 @@ { "name": "@airswap/libraries", - "version": "4.3.6", + "version": "5.0.0", "description": "AirSwap: Libraries for Developers", "repository": { "type": "git", @@ -22,15 +22,15 @@ "test:ci": "yarn test" }, "dependencies": { - "@airswap/batch-call": "4.2.3", + "@airswap/batch-call": "5.0.0", "@airswap/jsonrpc-client-websocket": "0.0.1", - "@airswap/pool": "4.2.2", - "@airswap/registry": "4.2.2", - "@airswap/staking": "4.2.2", - "@airswap/swap": "4.2.2", - "@airswap/swap-erc20": "4.3.1", - "@airswap/utils": "4.3.4", - "@airswap/wrapper": "4.2.3", + "@airswap/pool": "5.0.0", + "@airswap/registry": "5.0.0", + "@airswap/staking": "5.0.0", + "@airswap/swap": "5.0.0", + "@airswap/swap-erc20": "5.0.0", + "@airswap/utils": "5.0.0", + "@airswap/wrapper": "5.0.0", "browser-or-node": "^2.1.1", "ethers": "^5.7.2", "jayson": "^4.0.0", diff --git a/tools/libraries/src/Contracts.ts b/tools/libraries/src/Contracts.ts index 1dae7c96c..491e8f126 100644 --- a/tools/libraries/src/Contracts.ts +++ b/tools/libraries/src/Contracts.ts @@ -1,5 +1,6 @@ import { ethers } from 'ethers' +import { Delegate__factory } from '@airswap/delegate/typechain/factories/contracts' import { Pool__factory } from '@airswap/pool/typechain/factories/contracts' import { Staking__factory } from '@airswap/staking/typechain/factories/contracts' import { Swap__factory } from '@airswap/swap/typechain/factories/contracts' @@ -7,6 +8,7 @@ import { SwapERC20__factory } from '@airswap/swap-erc20/typechain/factories/cont import { Wrapper__factory } from '@airswap/wrapper/typechain/factories/contracts' import { WETH9__factory } from '@airswap/wrapper/typechain/factories/contracts' +import delegateDeploys from '@airswap/delegate/deploys.js' import poolDeploys from '@airswap/pool/deploys.js' import stakingDeploys from '@airswap/staking/deploys.js' import swapERC20Deploys from '@airswap/swap-erc20/deploys.js' @@ -14,6 +16,7 @@ import swapDeploys from '@airswap/swap/deploys.js' import wrapperDeploys from '@airswap/wrapper/deploys.js' import wethDeploys from '@airswap/wrapper/deploys-weth.js' +import delegateBlocks from '@airswap/delegate/deploys-blocks.js' import poolBlocks from '@airswap/pool/deploys-blocks.js' import stakingBlocks from '@airswap/staking/deploys-blocks.js' import swapERC20Blocks from '@airswap/swap-erc20/deploys-blocks.js' @@ -56,6 +59,12 @@ export class Contract { } } +export const Delegate = new Contract( + 'Delegate', + delegateDeploys, + delegateBlocks, + Delegate__factory +) export const Pool = new Contract('Pool', poolDeploys, poolBlocks, Pool__factory) export const Staking = new Contract( 'Staking', diff --git a/tools/stores/package.json b/tools/stores/package.json index a99377b79..8fa6dbc16 100644 --- a/tools/stores/package.json +++ b/tools/stores/package.json @@ -1,6 +1,6 @@ { "name": "@airswap/stores", - "version": "4.3.4", + "version": "5.0.0", "description": "AirSwap: Storage for Indexing", "repository": { "type": "git", @@ -22,7 +22,7 @@ "test": "REDISCLOUD_URL=redis://localhost:6379 TS_NODE_COMPILER_OPTIONS='{\"strict\":false}' yarn mocha -r ts-node/esm test/*.ts" }, "dependencies": { - "@airswap/utils": "4.3.4", + "@airswap/utils": "5.0.0", "redis": "^4.6.13" }, "devDependencies": { diff --git a/tools/utils/package.json b/tools/utils/package.json index f484ae0ab..6bf6bd8df 100644 --- a/tools/utils/package.json +++ b/tools/utils/package.json @@ -1,6 +1,6 @@ { "name": "@airswap/utils", - "version": "4.3.4", + "version": "5.0.0", "description": "AirSwap: Utilities for Developers", "repository": { "type": "git", diff --git a/tools/utils/src/constants.ts b/tools/utils/src/constants.ts index 7d6e8009e..cd04d2a0e 100644 --- a/tools/utils/src/constants.ts +++ b/tools/utils/src/constants.ts @@ -260,6 +260,7 @@ export const ownerAddresses: Record = { [ChainIds.AVALANCHE]: '0xed669F5fe2A37Ef204DB178c7a982717B9f03Ec2', [ChainIds.LINEA]: '0xed669F5fe2A37Ef204DB178c7a982717B9f03Ec2', [ChainIds.SEPOLIA]: '0xed669F5fe2A37Ef204DB178c7a982717B9f03Ec2', + [ChainIds.NEON]: '0xed669F5fe2A37Ef204DB178c7a982717B9f03Ec2', } export const protocolFeeReceiverAddresses: Record = { diff --git a/yarn.lock b/yarn.lock index 296f6acad..296fb230f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2719,11 +2719,11 @@ aws4@^1.8.0: integrity sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg== axios@^1.0.0, axios@^1.5.1: - version "1.6.7" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.7.tgz#7b48c2e27c96f9c68a2f8f31e2ab19f59b06b0a7" - integrity sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA== + version "1.7.7" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.7.tgz#2f554296f9892a72ac8d8e4c5b79c14a91d0a47f" + integrity sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q== dependencies: - follow-redirects "^1.15.4" + follow-redirects "^1.15.6" form-data "^4.0.0" proxy-from-env "^1.1.0" @@ -2820,10 +2820,10 @@ bn.js@^5.1.2, bn.js@^5.2.0, bn.js@^5.2.1: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== -body-parser@1.20.2, body-parser@^1.20.1: - version "1.20.2" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd" - integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA== +body-parser@1.20.3, body-parser@^1.20.1: + version "1.20.3" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6" + integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g== dependencies: bytes "3.1.2" content-type "~1.0.5" @@ -2833,7 +2833,7 @@ body-parser@1.20.2, body-parser@^1.20.1: http-errors "2.0.0" iconv-lite "0.4.24" on-finished "2.4.1" - qs "6.11.0" + qs "6.13.0" raw-body "2.5.2" type-is "~1.6.18" unpipe "1.0.0" @@ -2867,12 +2867,12 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" -braces@^3.0.2, braces@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== +braces@^3.0.3, braces@~3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== dependencies: - fill-range "^7.0.1" + fill-range "^7.1.1" brorand@^1.0.1, brorand@^1.1.0: version "1.1.0" @@ -3545,10 +3545,10 @@ cookie-signature@1.0.6: resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== -cookie@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" - integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== +cookie@0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051" + integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== cookie@^0.4.1: version "0.4.2" @@ -3896,9 +3896,9 @@ ee-first@1.1.1: integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== ejs@^3.1.7: - version "3.1.9" - resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.9.tgz#03c9e8777fe12686a9effcef22303ca3d8eeb361" - integrity sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ== + version "3.1.10" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.10.tgz#69ab8358b14e896f80cc39e62087b88500c3ac3b" + integrity sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA== dependencies: jake "^10.8.5" @@ -3935,6 +3935,11 @@ encodeurl@~1.0.2: resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== +encodeurl@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" + integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== + encoding-down@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/encoding-down/-/encoding-down-6.3.0.tgz#b1c4eb0e1728c146ecaef8e32963c549e76d082b" @@ -4462,36 +4467,36 @@ exponential-backoff@^3.1.1: integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw== express@^4.18.2: - version "4.18.3" - resolved "https://registry.yarnpkg.com/express/-/express-4.18.3.tgz#6870746f3ff904dee1819b82e4b51509afffb0d4" - integrity sha512-6VyCijWQ+9O7WuVMTRBTl+cjNNIzD5cY5mQ1WM8r/LEkI2u8EYpOotESNwzNlyCn3g+dmjKYI6BmNneSr/FSRw== + version "4.20.0" + resolved "https://registry.yarnpkg.com/express/-/express-4.20.0.tgz#f1d08e591fcec770c07be4767af8eb9bcfd67c48" + integrity sha512-pLdae7I6QqShF5PnNTCVn4hI91Dx0Grkn2+IAsMTgMIKuQVte2dN9PeGSSAME2FR8anOhVA62QDIUaWVfEXVLw== dependencies: accepts "~1.3.8" array-flatten "1.1.1" - body-parser "1.20.2" + body-parser "1.20.3" content-disposition "0.5.4" content-type "~1.0.4" - cookie "0.5.0" + cookie "0.6.0" cookie-signature "1.0.6" debug "2.6.9" depd "2.0.0" - encodeurl "~1.0.2" + encodeurl "~2.0.0" escape-html "~1.0.3" etag "~1.8.1" finalhandler "1.2.0" fresh "0.5.2" http-errors "2.0.0" - merge-descriptors "1.0.1" + merge-descriptors "1.0.3" methods "~1.1.2" on-finished "2.4.1" parseurl "~1.3.3" - path-to-regexp "0.1.7" + path-to-regexp "0.1.10" proxy-addr "~2.0.7" qs "6.11.0" range-parser "~1.2.1" safe-buffer "5.2.1" - send "0.18.0" - serve-static "1.15.0" + send "0.19.0" + serve-static "1.16.0" setprototypeof "1.2.0" statuses "2.0.1" type-is "~1.6.18" @@ -4614,10 +4619,10 @@ filelist@^1.0.4: dependencies: minimatch "^5.0.1" -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== dependencies: to-regex-range "^5.0.1" @@ -4691,10 +4696,10 @@ flatted@^3.2.9: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== -follow-redirects@^1.12.1, follow-redirects@^1.15.4: - version "1.15.5" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020" - integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw== +follow-redirects@^1.12.1, follow-redirects@^1.15.6: + version "1.15.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== for-in@^0.1.3: version "0.1.8" @@ -6710,10 +6715,10 @@ meow@^8.1.2: type-fest "^0.18.0" yargs-parser "^20.2.3" -merge-descriptors@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" - integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== +merge-descriptors@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" + integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ== merge-stream@^2.0.0: version "2.0.0" @@ -6748,11 +6753,11 @@ micro-ftch@^0.3.1: integrity sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg== micromatch@^4.0.4: - version "4.0.5" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" - integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== dependencies: - braces "^3.0.2" + braces "^3.0.3" picomatch "^2.3.1" miller-rabin@^4.0.0: @@ -7761,10 +7766,10 @@ path-scurry@^1.10.1, path-scurry@^1.6.1: lru-cache "^9.1.1 || ^10.0.0" minipass "^5.0.0 || ^6.0.2 || ^7.0.0" -path-to-regexp@0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" - integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== +path-to-regexp@0.1.10: + version "0.1.10" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.10.tgz#67e9108c5c0551b9e5326064387de4763c4d5f8b" + integrity sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w== path-to-regexp@^6.2.1: version "6.2.1" @@ -8059,12 +8064,12 @@ qs@6.11.0: dependencies: side-channel "^1.0.4" -qs@^6.11.2, qs@^6.4.0: - version "6.11.2" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9" - integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA== +qs@6.13.0, qs@^6.11.2, qs@^6.4.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" + integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== dependencies: - side-channel "^1.0.4" + side-channel "^1.0.6" qs@~6.5.2: version "6.5.3" @@ -8607,6 +8612,25 @@ send@0.18.0: range-parser "~1.2.1" statuses "2.0.1" +send@0.19.0: + version "0.19.0" + resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8" + integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw== + dependencies: + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "2.0.0" + mime "1.6.0" + ms "2.1.3" + on-finished "2.4.1" + range-parser "~1.2.1" + statuses "2.0.1" + serialize-javascript@6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" @@ -8614,10 +8638,10 @@ serialize-javascript@6.0.0: dependencies: randombytes "^2.1.0" -serve-static@1.15.0: - version "1.15.0" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" - integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== +serve-static@1.16.0: + version "1.16.0" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.0.tgz#2bf4ed49f8af311b519c46f272bf6ac3baf38a92" + integrity sha512-pDLK8zwl2eKaYrs8mrPZBJua4hMplRWJ1tIFksVC3FtBEBnl8dxgeHtsaMS8DhS9i4fLObaon6ABoc4/hQGdPA== dependencies: encodeurl "~1.0.2" escape-html "~1.0.3" @@ -8718,7 +8742,7 @@ shelljs@^0.8.3: interpret "^1.0.0" rechoir "^0.6.2" -side-channel@^1.0.4: +side-channel@^1.0.4, side-channel@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== @@ -9015,7 +9039,7 @@ string-format@^2.0.0: resolved "https://registry.yarnpkg.com/string-format/-/string-format-2.0.0.tgz#f2df2e7097440d3b65de31b6d40d54c96eaffb9b" integrity sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA== -"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -9033,6 +9057,15 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^2.0.0, string-width@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" @@ -9064,7 +9097,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -9085,6 +9118,13 @@ strip-ansi@^4.0.0: dependencies: ansi-regex "^3.0.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -9602,9 +9642,9 @@ undici-types@~5.26.4: integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== undici@^5.14.0: - version "5.28.3" - resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.3.tgz#a731e0eff2c3fcfd41c1169a869062be222d1e5b" - integrity sha512-3ItfzbrhDlINjaP0duwnNsKpDQk3acHI3gVJ1z4fmwMK31k5G9OVIAMLSIaP6w4FaGkaAkN6zaQO9LUvZ1t7VA== + version "5.28.4" + resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.4.tgz#6b280408edb6a1a604a9b20340f45b422e373068" + integrity sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g== dependencies: "@fastify/busboy" "^2.0.0" @@ -9898,7 +9938,7 @@ workerpool@6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -9924,6 +9964,15 @@ wrap-ansi@^6.0.1: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" @@ -9982,9 +10031,9 @@ ws@7.4.6: integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== ws@^7.4.1, ws@^7.4.5, ws@^7.4.6: - version "7.5.9" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" - integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== + version "7.5.10" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" + integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== ws@^8.16.0: version "8.16.0"