From fe6ba6f25838e448552c7ff95d49516154c26b8f Mon Sep 17 00:00:00 2001 From: don mosites Date: Mon, 24 Jun 2024 16:27:32 +0200 Subject: [PATCH 1/4] libraries: export RegistryV3 for legacy support (#1315) --- tools/libraries/index.ts | 1 + tools/libraries/package.json | 5 +- tools/libraries/src/RegistryV3.ts | 84 +++++++++++++++++++++++++ tools/libraries/test/RegistryV3.test.ts | 72 +++++++++++++++++++++ yarn.lock | 9 ++- 5 files changed, 168 insertions(+), 3 deletions(-) create mode 100644 tools/libraries/src/RegistryV3.ts create mode 100644 tools/libraries/test/RegistryV3.test.ts diff --git a/tools/libraries/index.ts b/tools/libraries/index.ts index 659e7f0f5..3978b03be 100644 --- a/tools/libraries/index.ts +++ b/tools/libraries/index.ts @@ -1,3 +1,4 @@ export * from './src/Contracts' export * from './src/Registry' +export * from './src/RegistryV3' export * from './src/Server' diff --git a/tools/libraries/package.json b/tools/libraries/package.json index ba2914056..717379d73 100644 --- a/tools/libraries/package.json +++ b/tools/libraries/package.json @@ -1,6 +1,6 @@ { "name": "@airswap/libraries", - "version": "4.3.5", + "version": "4.3.6", "description": "AirSwap: Libraries for Developers", "repository": { "type": "git", @@ -24,8 +24,8 @@ "dependencies": { "@airswap/batch-call": "4.2.3", "@airswap/jsonrpc-client-websocket": "0.0.1", - "@airswap/registry": "4.2.2", "@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", @@ -34,6 +34,7 @@ "browser-or-node": "^2.1.1", "ethers": "^5.7.2", "jayson": "^4.0.0", + "registry-v3": "npm:@airswap/registry@3.1.2", "tiny-typed-emitter": "^2.1.0", "websocket": "^1.0.34" }, diff --git a/tools/libraries/src/RegistryV3.ts b/tools/libraries/src/RegistryV3.ts new file mode 100644 index 000000000..44424b755 --- /dev/null +++ b/tools/libraries/src/RegistryV3.ts @@ -0,0 +1,84 @@ +import { ethers } from 'ethers' +import { Registry__factory } from 'registry-v3/typechain/factories/contracts' + +import { Server } from './Server' +import { Contract, SwapERC20 } from './Contracts' + +const registryDeploys = { + 1: '0x8F9DA6d38939411340b19401E8c54Ea1f51B8f95', + 5: '0x05545815a5579d80Bd4c380da3487EAC2c4Ce299', + 30: '0xE0EE84592b12cfcd03843DE12b58852879ee6FF5', + 31: '0x517d482F686f11b922EED764692f2b42663ce2fa', + 56: '0x9F11691FA842856E44586380b27Ac331ab7De93d', + 97: '0x05545815a5579d80Bd4c380da3487EAC2c4Ce299', + 137: '0x9F11691FA842856E44586380b27Ac331ab7De93d', + 42161: '0xaBF694A434E0fE3b951409C01aa2db50Af4D2E3A', + 43113: '0x4F290e83B414097C107F5AD483a9ae15434B43d3', + 43114: '0xE40feb39fcb941A633deC965Abc9921b3FE962b2', + 80001: '0x05545815a5579d80Bd4c380da3487EAC2c4Ce299', + 421613: '0x517d482F686f11b922EED764692f2b42663ce2fa', +} + +const registryBlocks = { + 1: 0, + 5: 0, + 30: 0, + 31: 0, + 56: 0, + 97: 0, + 137: 0, + 42161: 0, + 43113: 0, + 43114: 0, + 80001: 0, + 421613: 0, +} + +class ServerRegistry extends Contract { + public constructor() { + super('Registry', registryDeploys, registryBlocks, Registry__factory) + } + public async getServerURLs( + providerOrSigner: ethers.providers.Provider | ethers.Signer, + chainId: number, + baseToken: string, + quoteToken: string, + address: any = registryDeploys[chainId] + ): Promise { + const contract = Registry__factory.connect(address, providerOrSigner) + const baseTokenURLs: string[] = await contract.getURLsForToken(baseToken) + const quoteTokenURLs: string[] = await contract.getURLsForToken(quoteToken) + return baseTokenURLs.filter((baseTokenURL) => + quoteTokenURLs.includes(baseTokenURL) + ) + } + public async getServers( + providerOrSigner: ethers.providers.Provider | ethers.Signer, + chainId: number, + baseToken: string, + quoteToken: string, + address: any = registryDeploys[chainId] + ): Promise> { + const urls = await this.getServerURLs( + providerOrSigner, + chainId, + baseToken, + quoteToken, + address + ) + const serverPromises = await Promise.allSettled( + urls.map((url) => { + return Server.at(url, { + chainId, + swapContract: SwapERC20.addresses[chainId], + }) + }) + ) + const servers: PromiseFulfilledResult[] = serverPromises.filter( + (value): value is PromiseFulfilledResult => + value.status === 'fulfilled' + ) + return servers.map((value) => value.value) + } +} +export const RegistryV3 = new ServerRegistry() diff --git a/tools/libraries/test/RegistryV3.test.ts b/tools/libraries/test/RegistryV3.test.ts new file mode 100644 index 000000000..45422bb1c --- /dev/null +++ b/tools/libraries/test/RegistryV3.test.ts @@ -0,0 +1,72 @@ +import { expect } from 'chai' + +const { ChainIds } = require('@airswap/utils') +const { ethers } = require('hardhat') +const ERC20PresetFixedSupply = require('@openzeppelin/contracts/build/contracts/ERC20PresetFixedSupply.json') +const RegistryABI = require('registry-v3/build/contracts/Registry.sol/Registry.json') + +const { RegistryV3 } = require('../index') + +let deployer +let staker +let stakingToken +let baseToken +let quoteToken +let registryFactory +let registry + +const SERVER_URL = 'https://maker.com/xyz' +const STAKING_COST = 1000 +const SUPPORT_COST = 10 +const TOKEN_BALANCE = 10000 + +describe('Registry', async () => { + before(async () => { + ;[deployer, staker] = await ethers.getSigners() + stakingToken = await ( + await ethers.getContractFactory( + ERC20PresetFixedSupply.abi, + ERC20PresetFixedSupply.bytecode + ) + ).deploy('TestERC20', 'TERC20', TOKEN_BALANCE, staker.address) + baseToken = await ( + await ethers.getContractFactory( + ERC20PresetFixedSupply.abi, + ERC20PresetFixedSupply.bytecode + ) + ).deploy('TestERC20', 'TERC20', TOKEN_BALANCE, staker.address) + quoteToken = await ( + await ethers.getContractFactory( + ERC20PresetFixedSupply.abi, + ERC20PresetFixedSupply.bytecode + ) + ).deploy('TestERC20', 'TERC20', TOKEN_BALANCE, staker.address) + registryFactory = await ethers.getContractFactory( + RegistryABI.abi, + RegistryABI.bytecode + ) + registry = await registryFactory + .connect(deployer) + .deploy(stakingToken.address, STAKING_COST, SUPPORT_COST) + await registry.deployed() + + stakingToken.connect(staker).approve(registry.address, STAKING_COST * 2) + + await registry.connect(staker).setURL(SERVER_URL) + await registry + .connect(staker) + .addTokens([quoteToken.address, baseToken.address]) + }) + + it('get URLs: succeeds', async () => { + const servers = await RegistryV3.getServerURLs( + ethers.provider, + ChainIds.HARDHAT, + baseToken.address, + quoteToken.address, + registry.address + ) + expect(servers.length).to.be.equal(1) + expect(servers[0]).to.be.equal(SERVER_URL) + }) +}) diff --git a/yarn.lock b/yarn.lock index b640fa052..296f6acad 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1358,7 +1358,7 @@ dependencies: "@octokit/openapi-types" "^18.0.0" -"@openzeppelin/contracts@^4.8.3": +"@openzeppelin/contracts@^4.3.1", "@openzeppelin/contracts@^4.8.3": version "4.9.6" resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.9.6.tgz#2a880a24eb19b4f8b25adc2a5095f2aa27f39677" integrity sha512-xSmezSupL+y9VkHZJGDoCBpmnB2ogM13ccaYDWqJTfS3dbuHkgjuwDFUmaFauBCboQMGB/S5UqUl2y54X99BmA== @@ -8293,6 +8293,13 @@ reduce-flatten@^2.0.0: resolved "https://registry.yarnpkg.com/reduce-flatten/-/reduce-flatten-2.0.0.tgz#734fd84e65f375d7ca4465c69798c25c9d10ae27" integrity sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w== +"registry-v3@npm:@airswap/registry@3.1.2": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@airswap/registry/-/registry-3.1.2.tgz#c963424c61a47bd1ec6c939a379531f6e9cc7abe" + integrity sha512-saicMqmZaOHbvTZZyfndSd6vmWUFHxUSpZ3qO35mDgzzez5Zg6D6rkWixmfery0cV0398NeNGXtyOj5XzD8jnw== + dependencies: + "@openzeppelin/contracts" "^4.3.1" + remove-trailing-separator@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" From f221e7082d69b228403e3cd2c068710b31b89bd3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 14:19:11 +0200 Subject: [PATCH 2/4] Bump ws from 7.5.9 to 7.5.10 (#1314) Bumps [ws](https://github.com/websockets/ws) from 7.5.9 to 7.5.10. - [Release notes](https://github.com/websockets/ws/releases) - [Commits](https://github.com/websockets/ws/compare/7.5.9...7.5.10) --- updated-dependencies: - dependency-name: ws dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/yarn.lock b/yarn.lock index 58520b632..b1debc7cf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9015,7 +9015,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 +9033,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 +9073,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 +9094,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" @@ -9898,7 +9914,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 +9940,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 +10007,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" From 89b5de3bc5e5582fc8eba7698f979b7182bd1df1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 14:19:28 +0200 Subject: [PATCH 3/4] Bump braces from 3.0.2 to 3.0.3 (#1312) Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3. - [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md) - [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3) --- updated-dependencies: - dependency-name: braces dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/yarn.lock b/yarn.lock index b1debc7cf..d0a1a8d08 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2868,11 +2868,11 @@ brace-expansion@^2.0.1: 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== + 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" @@ -4614,10 +4614,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" From 431724260296f4d88fc9f97e17689693c3850811 Mon Sep 17 00:00:00 2001 From: Smartcontrart <100962328+smartcontrart@users.noreply.github.com> Date: Wed, 21 Aug 2024 03:21:39 -0400 Subject: [PATCH 4/4] Delegates (#1320) * Imported Delegate and Indexer contracts and Fixed Delegate code to allow compilation * Updated Indexer code to compile with Solidity 0.8.17 * Updated licenses for Delegates and Indexer contracts * Removed constructor visibility and updated Delegate/Indexer interactions to ensure both contracts compile simultaneously * Removed the indexer boilerplate and adjusted the delegate placeholder contract accordingly. Removed the delegateFactory contract * Removed the indexer boilerplate and adjusted the delegate placeholder contract accordingly. Removed the delegateFactory contract * publish libraries 4.2.1-beta.0 * Removed IDelegateFactoryContract * Created basics of the swap function for the delegate contract * Fixed token transfers * Removed unecessary variable * Removed unused imports in Delegate tests * Removed unused imports in Delegate tests * Fixed transfer amounts * Updated recipient to delegator in swapERC20 * Renamed signer to delegator and sender to taker * Renamed signer and sender to delegator and taker for clarity and added tracking of remaining rule allowance * Created delegate integration test * Removed unused variables * updated delegates contract to solady library * delegates: cleanup deps; remove redundants * name updates; test quickfix * prettier * Renamed _maxDelegatorAmount to _maxSenderAmount for consistency * Added test to check delegate allowance * Added internal rule tests * Implemented Delegate price check * Separated setting and unsetting rules tests * Specified DEFAULT_SIGNER_AMOUNT and DEFAULT_SENDER_AMOUNT in tests. Added a test case when SENDER_AMOUNT is above requirements * Removed test to check if sender can send more token that defined in the rule * Implemented signatory logic allowing sender to delegate rule setting on their behalf * Wrote delegate deploy script * Removed unecessary scripts * updated deploy.js.d.ts * Fixed chain name import * Deployed to Sepolia * Renamed Signatory to Manager, adjusted tests accordingly * Deployed to Sepolia * prettier * restore abis * Updated copyright date * PR comments, added swapERC20 setter and fixed ownership initialization * zeroed out all fields in the rule when unsetting * Refactored rule reset on unset * Removed unused variable * Added Delegate fillSenderAmount and Expiry to Rule * Added Rule expiry test * Fixed expiry check bug --------- Co-authored-by: don mosites --- source/delegate/contracts/Delegate.sol | 29 +++++--- .../contracts/interfaces/IDelegate.sol | 64 +++++++++-------- source/delegate/test/Delegate.js | 69 +++++++++++++++---- source/delegate/test/DelegateIntegration.js | 6 +- 4 files changed, 114 insertions(+), 54 deletions(-) diff --git a/source/delegate/contracts/Delegate.sol b/source/delegate/contracts/Delegate.sol index c005d7691..779ca746b 100644 --- a/source/delegate/contracts/Delegate.sol +++ b/source/delegate/contracts/Delegate.sol @@ -34,16 +34,17 @@ contract Delegate is IDelegate, Ownable { /** * @notice Set a Trading Rule * @param _senderToken address Address of an ERC-20 token the consumer would send - * @param _senderAmount uint256 Maximum amount of ERC-20 token the sender wants to swap + * @param _senderRuleAmount uint256 Maximum amount of ERC-20 token the sender wants to swap * @param _signerToken address Address of an ERC-20 token the delegate would recieve * @param _signerAmount uint256 Minimum amount of ERC-20 token the delegate would recieve */ function setRule( address _senderWallet, address _senderToken, - uint256 _senderAmount, + uint256 _senderRuleAmount, address _signerToken, - uint256 _signerAmount + uint256 _signerAmount, + uint256 _ruleExpiry ) external { if (authorized[_senderWallet] != address(0)) { if (authorized[_senderWallet] != msg.sender) revert SenderInvalid(); @@ -54,17 +55,21 @@ contract Delegate is IDelegate, Ownable { rules[_senderWallet][_senderToken][_signerToken] = Rule( _senderWallet, _senderToken, - _senderAmount, + _senderRuleAmount, + 0, _signerToken, - _signerAmount + _signerAmount, + _ruleExpiry ); emit SetRule( _senderWallet, _senderToken, - _senderAmount, + _senderRuleAmount, + 0, _signerToken, - _signerAmount + _signerAmount, + _ruleExpiry ); } @@ -84,7 +89,6 @@ contract Delegate is IDelegate, Ownable { } else { if (_senderWallet != msg.sender) revert SenderInvalid(); } - Rule storage rule = rules[_senderWallet][_senderToken][_signerToken]; delete rules[_senderWallet][_senderToken][_signerToken]; emit UnsetRule(_senderWallet, _senderToken, _signerToken); @@ -106,12 +110,15 @@ contract Delegate is IDelegate, Ownable { Rule storage rule = rules[_senderWallet][_senderToken][_signerToken]; if ( - _senderAmount > (_signerAmount * rule.senderAmount) / rule.signerAmount + _signerAmount < + (rule.signerAmount * (rule.senderRuleAmount - rule.senderFilledAmount)) / + rule.senderRuleAmount ) { revert InvalidSignerAmount(); } + if (rule.ruleExpiry < block.timestamp) revert RuleExpired(); - if (rule.senderAmount < _senderAmount) { + if (_senderAmount > (rule.senderRuleAmount - rule.senderFilledAmount)) { revert InvalidSenderAmount(); } @@ -140,7 +147,7 @@ contract Delegate is IDelegate, Ownable { SafeTransferLib.safeTransfer(_signerToken, _senderWallet, _signerAmount); rules[_senderWallet][_senderToken][_signerToken] - .senderAmount -= _senderAmount; + .senderFilledAmount += _senderAmount; emit DelegateSwap(_nonce, _signerWallet); } diff --git a/source/delegate/contracts/interfaces/IDelegate.sol b/source/delegate/contracts/interfaces/IDelegate.sol index 825ff2690..49d7327a1 100644 --- a/source/delegate/contracts/interfaces/IDelegate.sol +++ b/source/delegate/contracts/interfaces/IDelegate.sol @@ -8,11 +8,14 @@ interface IDelegate { struct Rule { address sender; address senderToken; - uint256 senderAmount; + uint256 senderRuleAmount; + uint256 senderFilledAmount; address signerToken; uint256 signerAmount; + uint256 ruleExpiry; } + error RuleExpired(); error InvalidAddress(); error InvalidSenderAmount(); error InvalidSignerAmount(); @@ -20,45 +23,48 @@ interface IDelegate { error SenderInvalid(); error TransferFromFailed(); - event Authorize(address _signatory, address _signer); - event DelegateSwap(uint256 _nonce, address _signerWallet); - event Revoke(address _tmp, address _signer); + 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 + address senderWallet, + address senderToken, + uint256 senderRuleAmount, + uint256 senderFilledAmount, + address signerToken, + uint256 signerAmount, + uint256 ruleExpiry ); - event UnsetRule(address _signer, address _signerToken, address _senderToken); + event UnsetRule(address signer, address signerToken, address senderToken); function setRule( - address _sender, - address _senderToken, - uint256 _senderAmount, - address _signerToken, - uint256 _signerAmount + address sender, + address senderToken, + uint256 senderRuleAmount, + address signerToken, + uint256 signerAmount, + uint256 ruleExpiry ) 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 + 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 _sender, - address _signerToken, - address _senderToken + address sender, + address signerToken, + address senderToken ) external; } diff --git a/source/delegate/test/Delegate.js b/source/delegate/test/Delegate.js index 130b1ed30..a00dddeeb 100644 --- a/source/delegate/test/Delegate.js +++ b/source/delegate/test/Delegate.js @@ -9,6 +9,7 @@ const { createOrderERC20, orderERC20ToParams, createOrderERC20Signature, + SECONDS_IN_DAY, } = require('@airswap/utils') const CHAIN_ID = 31337 const DEFAULT_BALANCE = '100000' @@ -18,6 +19,8 @@ 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 @@ -157,7 +160,8 @@ describe('Delegate Unit', () => { senderToken.address, DEFAULT_SENDER_AMOUNT, signerToken.address, - DEFAULT_SIGNER_AMOUNT + DEFAULT_SIGNER_AMOUNT, + RULE_EXPIRY ) ) .to.emit(delegate, 'SetRule') @@ -165,8 +169,10 @@ describe('Delegate Unit', () => { sender.address, senderToken.address, DEFAULT_SENDER_AMOUNT, + 0, signerToken.address, - DEFAULT_SIGNER_AMOUNT + DEFAULT_SIGNER_AMOUNT, + RULE_EXPIRY ) }) @@ -190,7 +196,8 @@ describe('Delegate Unit', () => { senderToken.address, DEFAULT_SENDER_AMOUNT, signerToken.address, - DEFAULT_SIGNER_AMOUNT + DEFAULT_SIGNER_AMOUNT, + RULE_EXPIRY ) ) .to.emit(delegate, 'SetRule') @@ -198,8 +205,10 @@ describe('Delegate Unit', () => { sender.address, senderToken.address, DEFAULT_SENDER_AMOUNT, + 0, signerToken.address, - DEFAULT_SIGNER_AMOUNT + DEFAULT_SIGNER_AMOUNT, + RULE_EXPIRY ) }) @@ -212,7 +221,8 @@ describe('Delegate Unit', () => { senderToken.address, DEFAULT_SENDER_AMOUNT, signerToken.address, - DEFAULT_SIGNER_AMOUNT + DEFAULT_SIGNER_AMOUNT, + RULE_EXPIRY ) await expect( @@ -232,7 +242,8 @@ describe('Delegate Unit', () => { senderToken.address, DEFAULT_SENDER_AMOUNT, signerToken.address, - DEFAULT_SIGNER_AMOUNT + DEFAULT_SIGNER_AMOUNT, + RULE_EXPIRY ) const rule = await delegate.rules( @@ -241,7 +252,7 @@ describe('Delegate Unit', () => { signerToken.address ) - expect(rule.senderAmount.toString()).to.equal(DEFAULT_SENDER_AMOUNT) + expect(rule.senderRuleAmount.toString()).to.equal(DEFAULT_SENDER_AMOUNT) }) it('unsetting a Rule updates the rule balance', async () => { @@ -252,7 +263,8 @@ describe('Delegate Unit', () => { senderToken.address, DEFAULT_SENDER_AMOUNT, signerToken.address, - DEFAULT_SIGNER_AMOUNT + DEFAULT_SIGNER_AMOUNT, + RULE_EXPIRY ) let rule = await delegate.rules( @@ -271,7 +283,7 @@ describe('Delegate Unit', () => { signerToken.address ) - expect(rule.senderAmount.toString()).to.equal('0') + expect(rule.senderRuleAmount.toString()).to.equal('0') }) }) @@ -302,7 +314,8 @@ describe('Delegate Unit', () => { senderToken.address, DEFAULT_SENDER_AMOUNT, signerToken.address, - DEFAULT_SIGNER_AMOUNT + DEFAULT_SIGNER_AMOUNT, + RULE_EXPIRY ) const order = await createSignedOrderERC20({}, signer) @@ -330,7 +343,8 @@ describe('Delegate Unit', () => { senderToken.address, DEFAULT_SENDER_AMOUNT, signerToken.address, - DEFAULT_SIGNER_AMOUNT + DEFAULT_SIGNER_AMOUNT, + RULE_EXPIRY ) const order = await createSignedOrderERC20({}, signer) @@ -379,7 +393,8 @@ describe('Delegate Unit', () => { senderToken.address, DEFAULT_SENDER_AMOUNT - 1, signerToken.address, - DEFAULT_SIGNER_AMOUNT - 1 + DEFAULT_SIGNER_AMOUNT, + RULE_EXPIRY ) const order = await createSignedOrderERC20({}, signer) @@ -413,7 +428,8 @@ describe('Delegate Unit', () => { senderToken.address, DEFAULT_SENDER_AMOUNT, signerToken.address, - DEFAULT_SIGNER_AMOUNT + DEFAULT_SIGNER_AMOUNT, + RULE_EXPIRY ) const order = await createSignedOrderERC20( @@ -439,5 +455,32 @@ describe('Delegate Unit', () => { delegate.connect(signer).swap(sender.address, ...order) ).to.be.revertedWith('InvalidSignerAmount') }) + + 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 index 04f8f0c93..7b1519719 100644 --- a/source/delegate/test/DelegateIntegration.js +++ b/source/delegate/test/DelegateIntegration.js @@ -3,6 +3,7 @@ const { createOrderERC20, orderERC20ToParams, createOrderERC20Signature, + SECONDS_IN_DAY, } = require('@airswap/utils') const { ethers } = require('hardhat') const ERC20 = require('@openzeppelin/contracts/build/contracts/ERC20PresetMinterPauser.json') @@ -24,6 +25,8 @@ describe('Delegate Integration', () => { 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({ @@ -99,7 +102,8 @@ describe('Delegate Integration', () => { senderToken.address, DEFAULT_SENDER_AMOUNT, signerToken.address, - DEFAULT_SIGNER_AMOUNT + DEFAULT_SIGNER_AMOUNT, + RULE_EXPIRY ) signerToken