From 7a6b455be5ca472c9600d6ea1c5236fb951ab8b3 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Tue, 11 Jul 2023 14:40:16 +0200 Subject: [PATCH 01/27] feat: add approval with signature --- src/Blue.sol | 82 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 77 insertions(+), 5 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index 8e04d686d..2813eb3a1 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -11,6 +11,26 @@ import {SafeTransferLib} from "src/libraries/SafeTransferLib.sol"; uint256 constant WAD = 1e18; uint256 constant ALPHA = 0.5e18; +/// @dev The prefix used for EIP-712 signature. +string constant EIP712_MSG_PREFIX = "\x19\x01"; + +/// @dev The name used for EIP-712 signature. +string constant EIP712_NAME = "Blue"; + +/// @dev The version used for EIP-712 signature. +string constant EIP712_VERSION = "0"; + +/// @dev The domain typehash used for the EIP-712 signature. +bytes32 constant EIP712_DOMAIN_TYPEHASH = + keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); + +/// @dev The typehash for approveManagerWithSig Authorization used for the EIP-712 signature. +bytes32 constant EIP712_AUTHORIZATION_TYPEHASH = + keccak256("Authorization(address delegator,address manager,bool isAllowed,uint256 nonce,uint256 deadline)"); + +/// @dev The highest valid value for s in an ECDSA signature pair (0 < s < secp256k1n ÷ 2 + 1). +uint256 constant MAX_VALID_ECDSA_S = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0; + // Market id. type Id is bytes32; @@ -24,6 +44,13 @@ struct Market { uint256 lltv; } +/// @notice Contains the `v`, `r` and `s` parameters of an ECDSA signature. +struct Signature { + uint8 v; + bytes32 r; + bytes32 s; +} + using {toId} for Market; function toId(Market calldata market) pure returns (Id) { @@ -34,6 +61,10 @@ contract Blue { using MathLib for uint256; using SafeTransferLib for IERC20; + // Immutables. + + bytes32 immutable domainSeparator; + // Storage. // Owner. @@ -60,11 +91,23 @@ contract Blue { mapping(uint256 => bool) public isLltvEnabled; // User's managers. mapping(address => mapping(address => bool)) public approval; + // User's nonces. + mapping(address => uint256) public userNonce; // Constructor. constructor(address newOwner) { owner = newOwner; + + domainSeparator = keccak256( + abi.encode( + EIP712_DOMAIN_TYPEHASH, + keccak256(bytes(EIP712_NAME)), + keccak256(bytes(EIP712_VERSION)), + block.chainid, + address(this) + ) + ); } // Modifiers. @@ -127,7 +170,7 @@ contract Blue { Id id = market.toId(); require(lastUpdate[id] != 0, "unknown market"); require(amount != 0, "zero amount"); - require(isSenderApprovedFor(onBehalf), "not approved"); + require(_isSenderApprovedFor(onBehalf), "not approved"); accrueInterests(market, id); @@ -148,7 +191,7 @@ contract Blue { Id id = market.toId(); require(lastUpdate[id] != 0, "unknown market"); require(amount != 0, "zero amount"); - require(isSenderApprovedFor(onBehalf), "not approved"); + require(_isSenderApprovedFor(onBehalf), "not approved"); accrueInterests(market, id); @@ -204,7 +247,7 @@ contract Blue { Id id = market.toId(); require(lastUpdate[id] != 0, "unknown market"); require(amount != 0, "zero amount"); - require(isSenderApprovedFor(onBehalf), "not approved"); + require(_isSenderApprovedFor(onBehalf), "not approved"); accrueInterests(market, id); @@ -251,11 +294,40 @@ contract Blue { // Position management. + function setApproval( + address delegator, + address manager, + bool isAllowed, + uint256 nonce, + uint256 deadline, + Signature calldata signature + ) external { + require(uint256(signature.s) <= MAX_VALID_ECDSA_S, "invalid s"); + // v ∈ {27, 28} (source: https://ethereum.github.io/yellowpaper/paper.pdf #308) + require(signature.v == 27 || signature.v == 28, "invalid v"); + + bytes32 structHash = + keccak256(abi.encode(EIP712_AUTHORIZATION_TYPEHASH, delegator, manager, isAllowed, nonce, deadline)); + bytes32 digest = keccak256(abi.encodePacked(EIP712_MSG_PREFIX, domainSeparator, structHash)); + address signatory = ecrecover(digest, signature.v, signature.r, signature.s); + + require((signatory != address(0) && delegator == signatory), "invalid signatory"); + require(block.timestamp < deadline, "signature expired"); + + require(nonce == userNonce[signatory]++, "invalid nonce"); + + _setApproval(signatory, manager, isAllowed); + } + function setApproval(address manager, bool isAllowed) external { - approval[msg.sender][manager] = isAllowed; + _setApproval(msg.sender, manager, isAllowed); + } + + function _setApproval(address delegator, address manager, bool isAllowed) internal { + approval[delegator][manager] = isAllowed; } - function isSenderApprovedFor(address user) internal view returns (bool) { + function _isSenderApprovedFor(address user) internal view returns (bool) { return msg.sender == user || approval[user][msg.sender]; } From 7efa793a7e67d70c7bf3f9d585f47562b0df6ec7 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Tue, 11 Jul 2023 14:44:11 +0200 Subject: [PATCH 02/27] feat: add public visibility --- src/Blue.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Blue.sol b/src/Blue.sol index 2813eb3a1..5b1d4e80b 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -63,7 +63,7 @@ contract Blue { // Immutables. - bytes32 immutable domainSeparator; + bytes32 public immutable domainSeparator; // Storage. From 4063fc96b3ed69a54ce2857f005c64b8d8f483fd Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Tue, 11 Jul 2023 14:46:02 +0200 Subject: [PATCH 03/27] feat: small details --- src/Blue.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index 5b1d4e80b..9873a7c4d 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -91,7 +91,7 @@ contract Blue { mapping(uint256 => bool) public isLltvEnabled; // User's managers. mapping(address => mapping(address => bool)) public approval; - // User's nonces. + // User's nonces. Used to prevent replay attacks with EIP-712 signatures. mapping(address => uint256) public userNonce; // Constructor. @@ -311,7 +311,7 @@ contract Blue { bytes32 digest = keccak256(abi.encodePacked(EIP712_MSG_PREFIX, domainSeparator, structHash)); address signatory = ecrecover(digest, signature.v, signature.r, signature.s); - require((signatory != address(0) && delegator == signatory), "invalid signatory"); + require(signatory != address(0) && delegator == signatory, "invalid signatory"); require(block.timestamp < deadline, "signature expired"); require(nonce == userNonce[signatory]++, "invalid nonce"); From 830c0dbdd78fd3f4773f1734210a229a98df213b Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Wed, 12 Jul 2023 18:59:25 +0200 Subject: [PATCH 04/27] feat: remove version --- src/Blue.sol | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index 9873a7c4d..70bb4515e 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -17,12 +17,9 @@ string constant EIP712_MSG_PREFIX = "\x19\x01"; /// @dev The name used for EIP-712 signature. string constant EIP712_NAME = "Blue"; -/// @dev The version used for EIP-712 signature. -string constant EIP712_VERSION = "0"; - /// @dev The domain typehash used for the EIP-712 signature. bytes32 constant EIP712_DOMAIN_TYPEHASH = - keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); + keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); /// @dev The typehash for approveManagerWithSig Authorization used for the EIP-712 signature. bytes32 constant EIP712_AUTHORIZATION_TYPEHASH = @@ -99,15 +96,8 @@ contract Blue { constructor(address newOwner) { owner = newOwner; - domainSeparator = keccak256( - abi.encode( - EIP712_DOMAIN_TYPEHASH, - keccak256(bytes(EIP712_NAME)), - keccak256(bytes(EIP712_VERSION)), - block.chainid, - address(this) - ) - ); + domainSeparator = + keccak256(abi.encode(EIP712_DOMAIN_TYPEHASH, keccak256(bytes(EIP712_NAME)), block.chainid, address(this))); } // Modifiers. From c1aab0a38c620a64f8ace6da20732233caa16c37 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Fri, 21 Jul 2023 16:19:23 +0200 Subject: [PATCH 05/27] feat: switch to lib errors --- src/Blue.sol | 10 +++++----- src/libraries/Errors.sol | 10 ++++++++++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index 1057ec73f..353400184 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -286,18 +286,18 @@ contract Blue { uint256 deadline, Signature calldata signature ) external { - require(uint256(signature.s) <= MAX_VALID_ECDSA_S, "invalid s"); + require(uint256(signature.s) <= MAX_VALID_ECDSA_S, Errors.INVALID_S); // v ∈ {27, 28} (source: https://ethereum.github.io/yellowpaper/paper.pdf #308) - require(signature.v == 27 || signature.v == 28, "invalid v"); + require(signature.v == 27 || signature.v == 28, Errors.INVALID_V); bytes32 structHash = keccak256(abi.encode(EIP712_AUTHORIZATION_TYPEHASH, delegator, manager, isAllowed, nonce, deadline)); bytes32 digest = keccak256(abi.encodePacked(EIP712_MSG_PREFIX, domainSeparator, structHash)); address signatory = ecrecover(digest, signature.v, signature.r, signature.s); - require(signatory != address(0) && delegator == signatory, "invalid signatory"); - require(block.timestamp < deadline, "signature expired"); - require(nonce == userNonce[signatory]++, "invalid nonce"); + require(signatory != address(0) && delegator == signatory, Errors.INVALID_SIGNATORY); + require(block.timestamp < deadline, Errors.SIGNATURE_EXPIRED); + require(nonce == userNonce[signatory]++, Errors.INVALID_NONCE); _setApproval(signatory, manager, isAllowed); } diff --git a/src/libraries/Errors.sol b/src/libraries/Errors.sol index c7c2235d8..c8a42ba95 100644 --- a/src/libraries/Errors.sol +++ b/src/libraries/Errors.sol @@ -23,4 +23,14 @@ library Errors { string internal constant INSUFFICIENT_LIQUIDITY = "insufficient liquidity"; string internal constant HEALTHY_POSITION = "position is healthy"; + + string internal constant INVALID_S = "invalid s"; + + string internal constant INVALID_V = "invalid v"; + + string internal constant INVALID_SIGNATORY = "invalid signatory"; + + string internal constant SIGNATURE_EXPIRED = "signature expired"; + + string internal constant INVALID_NONCE = "invalid nonce"; } From baad8a0b187428ea9abeef5d9580956e9895f556 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Thu, 27 Jul 2023 17:05:20 +0200 Subject: [PATCH 06/27] refactor: implement part of suggestions --- src/Blue.sol | 10 +++++----- src/libraries/Errors.sol | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index 353400184..89adc1c58 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -23,7 +23,7 @@ string constant EIP712_NAME = "Blue"; bytes32 constant EIP712_DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); -/// @dev The typehash for approveManagerWithSig Authorization used for the EIP-712 signature. +/// @dev The EIP-712 typeHash for Authorization. bytes32 constant EIP712_AUTHORIZATION_TYPEHASH = keccak256("Authorization(address delegator,address manager,bool isAllowed,uint256 nonce,uint256 deadline)"); @@ -276,7 +276,7 @@ contract Blue { market.borrowableAsset.safeTransferFrom(msg.sender, address(this), repaid); } - // Position management. + // Position approvals. function setApproval( address delegator, @@ -290,12 +290,12 @@ contract Blue { // v ∈ {27, 28} (source: https://ethereum.github.io/yellowpaper/paper.pdf #308) require(signature.v == 27 || signature.v == 28, Errors.INVALID_V); - bytes32 structHash = + bytes32 hashStruct = keccak256(abi.encode(EIP712_AUTHORIZATION_TYPEHASH, delegator, manager, isAllowed, nonce, deadline)); - bytes32 digest = keccak256(abi.encodePacked(EIP712_MSG_PREFIX, domainSeparator, structHash)); + bytes32 digest = keccak256(abi.encodePacked(EIP712_MSG_PREFIX, domainSeparator, hashStruct)); address signatory = ecrecover(digest, signature.v, signature.r, signature.s); - require(signatory != address(0) && delegator == signatory, Errors.INVALID_SIGNATORY); + require(delegator == signatory, Errors.INVALID_SIGNATURE); require(block.timestamp < deadline, Errors.SIGNATURE_EXPIRED); require(nonce == userNonce[signatory]++, Errors.INVALID_NONCE); diff --git a/src/libraries/Errors.sol b/src/libraries/Errors.sol index c8a42ba95..0f65f368d 100644 --- a/src/libraries/Errors.sol +++ b/src/libraries/Errors.sol @@ -28,7 +28,7 @@ library Errors { string internal constant INVALID_V = "invalid v"; - string internal constant INVALID_SIGNATORY = "invalid signatory"; + string internal constant INVALID_SIGNATURE = "invalid signature"; string internal constant SIGNATURE_EXPIRED = "signature expired"; From f323f2c8c7e17b988f21fcd701ec6041eeeb0cfb Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Thu, 27 Jul 2023 17:34:43 +0200 Subject: [PATCH 07/27] test: add first test --- test/forge/Blue.t.sol | 36 +++++++++++++++++++++++++++++++ test/forge/helpers/SigUtils.sol | 38 +++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 test/forge/helpers/SigUtils.sol diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index 2f5b7eeef..de4db83af 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -4,6 +4,8 @@ pragma solidity 0.8.20; import "forge-std/Test.sol"; import "forge-std/console.sol"; +import {SigUtils} from "./helpers/SigUtils.sol"; + import "src/Blue.sol"; import {ERC20Mock as ERC20} from "src/mocks/ERC20Mock.sol"; import {OracleMock as Oracle} from "src/mocks/OracleMock.sol"; @@ -18,6 +20,7 @@ contract BlueTest is Test { address private constant OWNER = address(0xdead); Blue private blue; + SigUtils internal sigUtils; ERC20 private borrowableAsset; ERC20 private collateralAsset; Oracle private borrowableOracle; @@ -30,6 +33,8 @@ contract BlueTest is Test { // Create Blue. blue = new Blue(OWNER); + sigUtils = new SigUtils(blue.domainSeparator()); + // List a market. borrowableAsset = new ERC20("borrowable", "B", 18); collateralAsset = new ERC20("collateral", "C", 18); @@ -685,6 +690,37 @@ contract BlueTest is Test { vm.stopPrank(); } + + function testApprovalWithSig(uint128 deadline, address manager, uint256 privateKey, bool isAllowed) public { + vm.assume(deadline > block.timestamp); + privateKey = bound(privateKey, 1, type(uint32).max); // "Private key must be less than the secp256k1 curve order (115792089237316195423570985008687907852837564279074904382605163141518161494337)." + address delegator = vm.addr(privateKey); + + SigUtils.Authorization memory authorization = SigUtils.Authorization({ + delegator: delegator, + manager: manager, + isAllowed: isAllowed, + nonce: blue.userNonce(delegator), + deadline: block.timestamp + deadline + }); + + bytes32 digest = sigUtils.getTypedDataHash(authorization); + + Signature memory sig; + (sig.v, sig.r, sig.s) = vm.sign(privateKey, digest); + + blue.setApproval( + authorization.delegator, + authorization.manager, + authorization.isAllowed, + authorization.nonce, + authorization.deadline, + sig + ); + + assertEq(blue.isApproved(delegator, manager), isAllowed); + assertEq(blue.userNonce(delegator), 1); + } } function neq(Market memory a, Market memory b) pure returns (bool) { diff --git a/test/forge/helpers/SigUtils.sol b/test/forge/helpers/SigUtils.sol new file mode 100644 index 000000000..caccfcc73 --- /dev/null +++ b/test/forge/helpers/SigUtils.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity ^0.8.0; + +import {EIP712_AUTHORIZATION_TYPEHASH} from "src/Blue.sol"; + +contract SigUtils { + struct Authorization { + address delegator; + address manager; + bool isAllowed; + uint256 nonce; + uint256 deadline; + } + + bytes32 internal domainSeparator; + + constructor(bytes32 _domainSeparator) { + domainSeparator = _domainSeparator; + } + + /// @dev Computes the hash of the fully encoded EIP-712 message for the domain, which can be used to recover the signer + function getTypedDataHash(Authorization memory authorization) public view returns (bytes32) { + return keccak256(abi.encodePacked("\x19\x01", domainSeparator, getStructHash(authorization))); + } + + function getStructHash(Authorization memory authorization) internal pure returns (bytes32) { + return keccak256( + abi.encode( + EIP712_AUTHORIZATION_TYPEHASH, + authorization.delegator, + authorization.manager, + authorization.isAllowed, + authorization.nonce, + authorization.deadline + ) + ); + } +} From 86ccb5e1b0a76ca9e82292f3139d8b1c48022cbf Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Thu, 27 Jul 2023 17:35:31 +0200 Subject: [PATCH 08/27] docs: apply suggestion --- src/Blue.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Blue.sol b/src/Blue.sol index 89adc1c58..ea5ef8521 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -19,7 +19,7 @@ string constant EIP712_MSG_PREFIX = "\x19\x01"; /// @dev The name used for EIP-712 signature. string constant EIP712_NAME = "Blue"; -/// @dev The domain typehash used for the EIP-712 signature. +/// @dev The EIP-712 typeHash for EIP712Domain. bytes32 constant EIP712_DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); From 79eae36ab195cfd297c5fe01b4fd9fc2b245cca3 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Thu, 27 Jul 2023 19:16:45 +0200 Subject: [PATCH 09/27] refactor: remove _setApproval for now --- src/Blue.sol | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index 874b4a045..35c2eee50 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -301,15 +301,11 @@ contract Blue { require(block.timestamp < deadline, Errors.SIGNATURE_EXPIRED); require(nonce == userNonce[signatory]++, Errors.INVALID_NONCE); - _setApproval(signatory, manager, isAllowed); + isApproved[signatory][manager] = isAllowed; } function setApproval(address manager, bool isAllowed) external { - _setApproval(msg.sender, manager, isAllowed); - } - - function _setApproval(address delegator, address manager, bool isAllowed) internal { - isApproved[delegator][manager] = isAllowed; + isApproved[msg.sender][manager] = isAllowed; } function _isSenderOrIsApproved(address user) internal view returns (bool) { From 88806e23ec5b4ccc182bfeb7bc886706471a094e Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Fri, 28 Jul 2023 09:39:44 +0200 Subject: [PATCH 10/27] refactor: move deadline require up --- src/Blue.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Blue.sol b/src/Blue.sol index 35c2eee50..0ddffd69f 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -291,6 +291,7 @@ contract Blue { require(uint256(signature.s) <= MAX_VALID_ECDSA_S, Errors.INVALID_S); // v ∈ {27, 28} (source: https://ethereum.github.io/yellowpaper/paper.pdf #308) require(signature.v == 27 || signature.v == 28, Errors.INVALID_V); + require(block.timestamp < deadline, Errors.SIGNATURE_EXPIRED); bytes32 hashStruct = keccak256(abi.encode(EIP712_AUTHORIZATION_TYPEHASH, delegator, manager, isAllowed, nonce, deadline)); @@ -298,7 +299,6 @@ contract Blue { address signatory = ecrecover(digest, signature.v, signature.r, signature.s); require(delegator == signatory, Errors.INVALID_SIGNATURE); - require(block.timestamp < deadline, Errors.SIGNATURE_EXPIRED); require(nonce == userNonce[signatory]++, Errors.INVALID_NONCE); isApproved[signatory][manager] = isAllowed; From 30dd70f77dfded0e8f56e97d3a6a7158aa189fe0 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Fri, 28 Jul 2023 09:49:09 +0200 Subject: [PATCH 11/27] test: update tests --- test/forge/Blue.t.sol | 5 +---- test/forge/helpers/SigUtils.sol | 22 ++++++++++------------ 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index be71aa3c0..b2c469e69 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -21,7 +21,6 @@ contract BlueTest is Test { address private constant OWNER = address(0xdead); Blue private blue; - SigUtils internal sigUtils; ERC20 private borrowableAsset; ERC20 private collateralAsset; Oracle private borrowableOracle; @@ -34,8 +33,6 @@ contract BlueTest is Test { // Create Blue. blue = new Blue(OWNER); - sigUtils = new SigUtils(blue.domainSeparator()); - // List a market. borrowableAsset = new ERC20("borrowable", "B", 18); collateralAsset = new ERC20("collateral", "C", 18); @@ -706,7 +703,7 @@ contract BlueTest is Test { deadline: block.timestamp + deadline }); - bytes32 digest = sigUtils.getTypedDataHash(authorization); + bytes32 digest = SigUtils.getTypedDataHash(blue.domainSeparator(), authorization); Signature memory sig; (sig.v, sig.r, sig.s) = vm.sign(privateKey, digest); diff --git a/test/forge/helpers/SigUtils.sol b/test/forge/helpers/SigUtils.sol index caccfcc73..717f6cfd1 100644 --- a/test/forge/helpers/SigUtils.sol +++ b/test/forge/helpers/SigUtils.sol @@ -1,9 +1,9 @@ -// SPDX-License-Identifier: AGPL-3.0-only +// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; import {EIP712_AUTHORIZATION_TYPEHASH} from "src/Blue.sol"; -contract SigUtils { +library SigUtils { struct Authorization { address delegator; address manager; @@ -12,18 +12,16 @@ contract SigUtils { uint256 deadline; } - bytes32 internal domainSeparator; - - constructor(bytes32 _domainSeparator) { - domainSeparator = _domainSeparator; - } - - /// @dev Computes the hash of the fully encoded EIP-712 message for the domain, which can be used to recover the signer - function getTypedDataHash(Authorization memory authorization) public view returns (bytes32) { - return keccak256(abi.encodePacked("\x19\x01", domainSeparator, getStructHash(authorization))); + /// @dev Computes the hash of the EIP-712 encoded data. + function getTypedDataHash(bytes32 domainSeparator, Authorization memory authorization) + public + pure + returns (bytes32) + { + return keccak256(abi.encodePacked("\x19\x01", domainSeparator, hashStruct(authorization))); } - function getStructHash(Authorization memory authorization) internal pure returns (bytes32) { + function hashStruct(Authorization memory authorization) internal pure returns (bytes32) { return keccak256( abi.encode( EIP712_AUTHORIZATION_TYPEHASH, From 96a8f286f7a2f7ebfccb036b9b9d7ac8660a1029 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Fri, 28 Jul 2023 10:20:03 +0200 Subject: [PATCH 12/27] style: approve with sig --- src/Blue.sol | 18 +++++------------- test/forge/helpers/SigUtils.sol | 4 ++-- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index 68f5c07fb..6c01cfd69 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -15,18 +15,11 @@ import {SafeTransferLib} from "src/libraries/SafeTransferLib.sol"; uint256 constant MAX_FEE = 0.25e18; uint256 constant ALPHA = 0.5e18; -/// @dev The prefix used for EIP-712 signature. -string constant EIP712_MSG_PREFIX = "\x19\x01"; - -/// @dev The name used for EIP-712 signature. -string constant EIP712_NAME = "Blue"; - /// @dev The EIP-712 typeHash for EIP712Domain. -bytes32 constant EIP712_DOMAIN_TYPEHASH = - keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); +bytes32 constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(uint256 chainId,address verifyingContract)"); /// @dev The EIP-712 typeHash for Authorization. -bytes32 constant EIP712_AUTHORIZATION_TYPEHASH = +bytes32 constant AUTHORIZATION_TYPEHASH = keccak256("Authorization(address delegator,address manager,bool isAllowed,uint256 nonce,uint256 deadline)"); /// @dev The highest valid value for s in an ECDSA signature pair (0 < s < secp256k1n ÷ 2 + 1). @@ -87,8 +80,7 @@ contract Blue is IFlashLender { constructor(address newOwner) { owner = newOwner; - domainSeparator = - keccak256(abi.encode(EIP712_DOMAIN_TYPEHASH, keccak256(bytes(EIP712_NAME)), block.chainid, address(this))); + domainSeparator = keccak256(abi.encode(DOMAIN_TYPEHASH, block.chainid, address(this))); } // Modifiers. @@ -296,8 +288,8 @@ contract Blue is IFlashLender { require(block.timestamp < deadline, Errors.SIGNATURE_EXPIRED); bytes32 hashStruct = - keccak256(abi.encode(EIP712_AUTHORIZATION_TYPEHASH, delegator, manager, isAllowed, nonce, deadline)); - bytes32 digest = keccak256(abi.encodePacked(EIP712_MSG_PREFIX, domainSeparator, hashStruct)); + keccak256(abi.encode(AUTHORIZATION_TYPEHASH, delegator, manager, isAllowed, nonce, deadline)); + bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, hashStruct)); address signatory = ecrecover(digest, signature.v, signature.r, signature.s); require(delegator == signatory, Errors.INVALID_SIGNATURE); diff --git a/test/forge/helpers/SigUtils.sol b/test/forge/helpers/SigUtils.sol index 717f6cfd1..1cd71f2ef 100644 --- a/test/forge/helpers/SigUtils.sol +++ b/test/forge/helpers/SigUtils.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; -import {EIP712_AUTHORIZATION_TYPEHASH} from "src/Blue.sol"; +import {AUTHORIZATION_TYPEHASH} from "src/Blue.sol"; library SigUtils { struct Authorization { @@ -24,7 +24,7 @@ library SigUtils { function hashStruct(Authorization memory authorization) internal pure returns (bytes32) { return keccak256( abi.encode( - EIP712_AUTHORIZATION_TYPEHASH, + AUTHORIZATION_TYPEHASH, authorization.delegator, authorization.manager, authorization.isAllowed, From 0872299ca9a1ba1d06b07a51d480c3e3a00d4ad4 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Fri, 28 Jul 2023 10:26:14 +0200 Subject: [PATCH 13/27] perf: remove nonce from args --- src/Blue.sol | 7 +++---- src/libraries/Errors.sol | 2 -- test/forge/Blue.t.sol | 7 +------ 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index 6c01cfd69..c50f50f47 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -278,7 +278,6 @@ contract Blue is IFlashLender { address delegator, address manager, bool isAllowed, - uint256 nonce, uint256 deadline, Signature calldata signature ) external { @@ -287,13 +286,13 @@ contract Blue is IFlashLender { require(signature.v == 27 || signature.v == 28, Errors.INVALID_V); require(block.timestamp < deadline, Errors.SIGNATURE_EXPIRED); - bytes32 hashStruct = - keccak256(abi.encode(AUTHORIZATION_TYPEHASH, delegator, manager, isAllowed, nonce, deadline)); + bytes32 hashStruct = keccak256( + abi.encode(AUTHORIZATION_TYPEHASH, delegator, manager, isAllowed, userNonce[delegator]++, deadline) + ); bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, hashStruct)); address signatory = ecrecover(digest, signature.v, signature.r, signature.s); require(delegator == signatory, Errors.INVALID_SIGNATURE); - require(nonce == userNonce[signatory]++, Errors.INVALID_NONCE); isApproved[signatory][manager] = isAllowed; } diff --git a/src/libraries/Errors.sol b/src/libraries/Errors.sol index 7bca552cb..f00116709 100644 --- a/src/libraries/Errors.sol +++ b/src/libraries/Errors.sol @@ -34,7 +34,5 @@ library Errors { string internal constant SIGNATURE_EXPIRED = "signature expired"; - string internal constant INVALID_NONCE = "invalid nonce"; - string internal constant INVALID_SUCCESS_HASH = "invalid success hash"; } diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index bd22bc435..f87b4f538 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -714,12 +714,7 @@ contract BlueTest is Test { (sig.v, sig.r, sig.s) = vm.sign(privateKey, digest); blue.setApproval( - authorization.delegator, - authorization.manager, - authorization.isAllowed, - authorization.nonce, - authorization.deadline, - sig + authorization.delegator, authorization.manager, authorization.isAllowed, authorization.deadline, sig ); assertEq(blue.isApproved(delegator, manager), isAllowed); From 0961afaa317e5d55e21a775d2aa7dff275cb0cd8 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Fri, 28 Jul 2023 10:31:59 +0200 Subject: [PATCH 14/27] style: signature error wording --- src/Blue.sol | 2 +- src/libraries/Errors.sol | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index c50f50f47..5dfe66cc0 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -292,7 +292,7 @@ contract Blue is IFlashLender { bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, hashStruct)); address signatory = ecrecover(digest, signature.v, signature.r, signature.s); - require(delegator == signatory, Errors.INVALID_SIGNATURE); + require(delegator == signatory, Errors.WRONG_SIGNATURE); isApproved[signatory][manager] = isAllowed; } diff --git a/src/libraries/Errors.sol b/src/libraries/Errors.sol index f00116709..660f11a2d 100644 --- a/src/libraries/Errors.sol +++ b/src/libraries/Errors.sol @@ -30,9 +30,7 @@ library Errors { string internal constant INVALID_V = "invalid v"; - string internal constant INVALID_SIGNATURE = "invalid signature"; + string internal constant WRONG_SIGNATURE = "wrong signature"; string internal constant SIGNATURE_EXPIRED = "signature expired"; - - string internal constant INVALID_SUCCESS_HASH = "invalid success hash"; } From 7e58d627ce36375dc4186b4c458f83877f21a18e Mon Sep 17 00:00:00 2001 From: MathisGD Date: Fri, 28 Jul 2023 10:43:49 +0200 Subject: [PATCH 15/27] style: immutable case --- src/Blue.sol | 6 +++--- test/forge/Blue.t.sol | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index 5dfe66cc0..b5644f70b 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -40,7 +40,7 @@ contract Blue is IFlashLender { // Immutables. - bytes32 public immutable domainSeparator; + bytes32 public immutable DOMAIN_SEPARATOR; // Storage. @@ -80,7 +80,7 @@ contract Blue is IFlashLender { constructor(address newOwner) { owner = newOwner; - domainSeparator = keccak256(abi.encode(DOMAIN_TYPEHASH, block.chainid, address(this))); + DOMAIN_SEPARATOR = keccak256(abi.encode(DOMAIN_TYPEHASH, block.chainid, address(this))); } // Modifiers. @@ -289,7 +289,7 @@ contract Blue is IFlashLender { bytes32 hashStruct = keccak256( abi.encode(AUTHORIZATION_TYPEHASH, delegator, manager, isAllowed, userNonce[delegator]++, deadline) ); - bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, hashStruct)); + bytes32 digest = keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, hashStruct)); address signatory = ecrecover(digest, signature.v, signature.r, signature.s); require(delegator == signatory, Errors.WRONG_SIGNATURE); diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index f87b4f538..756390f92 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -708,7 +708,7 @@ contract BlueTest is Test { deadline: block.timestamp + deadline }); - bytes32 digest = SigUtils.getTypedDataHash(blue.domainSeparator(), authorization); + bytes32 digest = SigUtils.getTypedDataHash(blue.DOMAIN_SEPARATOR(), authorization); Signature memory sig; (sig.v, sig.r, sig.s) = vm.sign(privateKey, digest); From 391ab286c2b30dfe7e37df9233cb129fdfd8d619 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Fri, 28 Jul 2023 10:53:08 +0200 Subject: [PATCH 16/27] style: harmonize authorizations naming --- src/Blue.sol | 48 ++++++++++++++++----------------- src/libraries/Errors.sol | 2 +- test/forge/Blue.t.sol | 42 +++++++++++++++-------------- test/forge/helpers/SigUtils.sol | 12 ++++----- 4 files changed, 53 insertions(+), 51 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index b5644f70b..4bf670c69 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -20,7 +20,7 @@ bytes32 constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(uint256 chainId,addre /// @dev The EIP-712 typeHash for Authorization. bytes32 constant AUTHORIZATION_TYPEHASH = - keccak256("Authorization(address delegator,address manager,bool isAllowed,uint256 nonce,uint256 deadline)"); + keccak256("Authorization(address authoriser,address authorizee,bool isAuthorized,uint256 nonce,uint256 deadline)"); /// @dev The highest valid value for s in an ECDSA signature pair (0 < s < secp256k1n ÷ 2 + 1). uint256 constant MAX_VALID_ECDSA_S = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0; @@ -70,8 +70,8 @@ contract Blue is IFlashLender { mapping(IIrm => bool) public isIrmEnabled; // Enabled LLTVs. mapping(uint256 => bool) public isLltvEnabled; - // User's managers. - mapping(address => mapping(address => bool)) public isApproved; + // User's authorizations. + mapping(address => mapping(address => bool)) public isAuthorized; // User's nonces. Used to prevent replay attacks with EIP-712 signatures. mapping(address => uint256) public userNonce; @@ -150,7 +150,7 @@ contract Blue is IFlashLender { Id id = market.id(); require(lastUpdate[id] != 0, Errors.MARKET_NOT_CREATED); require(amount != 0, Errors.ZERO_AMOUNT); - require(_isSenderOrIsApproved(onBehalf), Errors.MANAGER_NOT_APPROVED); + require(_isSenderOrIsAuthorized(onBehalf), Errors.NOT_SENDER_AND_NOT_AUTHORIZED); _accrueInterests(market, id); @@ -171,7 +171,7 @@ contract Blue is IFlashLender { Id id = market.id(); require(lastUpdate[id] != 0, Errors.MARKET_NOT_CREATED); require(amount != 0, Errors.ZERO_AMOUNT); - require(_isSenderOrIsApproved(onBehalf), Errors.MANAGER_NOT_APPROVED); + require(_isSenderOrIsAuthorized(onBehalf), Errors.NOT_SENDER_AND_NOT_AUTHORIZED); _accrueInterests(market, id); @@ -222,7 +222,7 @@ contract Blue is IFlashLender { Id id = market.id(); require(lastUpdate[id] != 0, Errors.MARKET_NOT_CREATED); require(amount != 0, Errors.ZERO_AMOUNT); - require(_isSenderOrIsApproved(onBehalf), Errors.MANAGER_NOT_APPROVED); + require(_isSenderOrIsAuthorized(onBehalf), Errors.NOT_SENDER_AND_NOT_AUTHORIZED); _accrueInterests(market, id); @@ -272,12 +272,12 @@ contract Blue is IFlashLender { market.borrowableAsset.safeTransferFrom(msg.sender, address(this), repaid); } - // Position approvals. + // Position authorizations. - function setApproval( - address delegator, - address manager, - bool isAllowed, + function setAuthorization( + address authorizer, + address authorizee, + bool newIsAuthorized, uint256 deadline, Signature calldata signature ) external { @@ -287,14 +287,24 @@ contract Blue is IFlashLender { require(block.timestamp < deadline, Errors.SIGNATURE_EXPIRED); bytes32 hashStruct = keccak256( - abi.encode(AUTHORIZATION_TYPEHASH, delegator, manager, isAllowed, userNonce[delegator]++, deadline) + abi.encode( + AUTHORIZATION_TYPEHASH, authorizer, authorizee, newIsAuthorized, userNonce[authorizer]++, deadline + ) ); bytes32 digest = keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, hashStruct)); address signatory = ecrecover(digest, signature.v, signature.r, signature.s); - require(delegator == signatory, Errors.WRONG_SIGNATURE); + require(authorizer == signatory, Errors.WRONG_SIGNATURE); - isApproved[signatory][manager] = isAllowed; + isAuthorized[signatory][authorizee] = newIsAuthorized; + } + + function setAuthorization(address authorizee, bool newIsAuthorized) external { + isAuthorized[msg.sender][authorizee] = newIsAuthorized; + } + + function _isSenderOrIsAuthorized(address user) internal view returns (bool) { + return msg.sender == user || isAuthorized[user][msg.sender]; } // Flash Loans. @@ -308,16 +318,6 @@ contract Blue is IFlashLender { IERC20(token).safeTransferFrom(address(receiver), address(this), amount); } - // Position management. - - function setApproval(address manager, bool isAllowed) external { - isApproved[msg.sender][manager] = isAllowed; - } - - function _isSenderOrIsApproved(address user) internal view returns (bool) { - return msg.sender == user || isApproved[user][msg.sender]; - } - // Interests management. function _accrueInterests(Market memory market, Id id) internal { diff --git a/src/libraries/Errors.sol b/src/libraries/Errors.sol index 660f11a2d..c190c1288 100644 --- a/src/libraries/Errors.sol +++ b/src/libraries/Errors.sol @@ -18,7 +18,7 @@ library Errors { string internal constant ZERO_AMOUNT = "zero amount"; - string internal constant MANAGER_NOT_APPROVED = "not approved"; + string internal constant NOT_SENDER_AND_NOT_AUTHORIZED = "not sender and not authorized"; string internal constant INSUFFICIENT_COLLATERAL = "insufficient collateral"; diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index 756390f92..5067d0301 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -657,36 +657,36 @@ contract BlueTest is Test { blue.withdrawCollateral(market, amount, address(this)); } - function testSetApproval(address manager, bool isAllowed) public { - blue.setApproval(manager, isAllowed); - assertEq(blue.isApproved(address(this), manager), isAllowed); + function testSetAuthorization(address authorizee, bool isAuthorized) public { + blue.setAuthorization(authorizee, isAuthorized); + assertEq(blue.isAuthorized(address(this), authorizee), isAuthorized); } - function testNotApproved(address attacker) public { + function testNotAuthorized(address attacker) public { vm.assume(attacker != address(this)); vm.startPrank(attacker); - vm.expectRevert("not approved"); + vm.expectRevert("not sender and not authorized"); blue.withdraw(market, 1, address(this)); - vm.expectRevert("not approved"); + vm.expectRevert("not sender and not authorized"); blue.withdrawCollateral(market, 1, address(this)); - vm.expectRevert("not approved"); + vm.expectRevert("not sender and not authorized"); blue.borrow(market, 1, address(this)); vm.stopPrank(); } - function testApproved(address manager) public { + function testAuthorization(address authorizee) public { borrowableAsset.setBalance(address(this), 100 ether); collateralAsset.setBalance(address(this), 100 ether); blue.supply(market, 100 ether, address(this)); blue.supplyCollateral(market, 100 ether, address(this)); - blue.setApproval(manager, true); + blue.setAuthorization(authorizee, true); - vm.startPrank(manager); + vm.startPrank(authorizee); blue.withdraw(market, 1 ether, address(this)); blue.withdrawCollateral(market, 1 ether, address(this)); @@ -695,16 +695,18 @@ contract BlueTest is Test { vm.stopPrank(); } - function testApprovalWithSig(uint128 deadline, address manager, uint256 privateKey, bool isAllowed) public { + function testAuthorizationWithSig(uint128 deadline, address authorizee, uint256 privateKey, bool isAuthorized) + public + { vm.assume(deadline > block.timestamp); privateKey = bound(privateKey, 1, type(uint32).max); // "Private key must be less than the secp256k1 curve order (115792089237316195423570985008687907852837564279074904382605163141518161494337)." - address delegator = vm.addr(privateKey); + address authorizer = vm.addr(privateKey); SigUtils.Authorization memory authorization = SigUtils.Authorization({ - delegator: delegator, - manager: manager, - isAllowed: isAllowed, - nonce: blue.userNonce(delegator), + authorizer: authorizer, + authorizee: authorizee, + isAuthorized: isAuthorized, + nonce: blue.userNonce(authorizer), deadline: block.timestamp + deadline }); @@ -713,12 +715,12 @@ contract BlueTest is Test { Signature memory sig; (sig.v, sig.r, sig.s) = vm.sign(privateKey, digest); - blue.setApproval( - authorization.delegator, authorization.manager, authorization.isAllowed, authorization.deadline, sig + blue.setAuthorization( + authorization.authorizer, authorization.authorizee, authorization.isAuthorized, authorization.deadline, sig ); - assertEq(blue.isApproved(delegator, manager), isAllowed); - assertEq(blue.userNonce(delegator), 1); + assertEq(blue.isAuthorized(authorizer, authorizee), isAuthorized); + assertEq(blue.userNonce(authorizer), 1); } function testFlashLoan(uint256 amount) public { diff --git a/test/forge/helpers/SigUtils.sol b/test/forge/helpers/SigUtils.sol index 1cd71f2ef..5917a5db0 100644 --- a/test/forge/helpers/SigUtils.sol +++ b/test/forge/helpers/SigUtils.sol @@ -5,9 +5,9 @@ import {AUTHORIZATION_TYPEHASH} from "src/Blue.sol"; library SigUtils { struct Authorization { - address delegator; - address manager; - bool isAllowed; + address authorizer; + address authorizee; + bool isAuthorized; uint256 nonce; uint256 deadline; } @@ -25,9 +25,9 @@ library SigUtils { return keccak256( abi.encode( AUTHORIZATION_TYPEHASH, - authorization.delegator, - authorization.manager, - authorization.isAllowed, + authorization.authorizer, + authorization.authorizee, + authorization.isAuthorized, authorization.nonce, authorization.deadline ) From fe70678031d425d3285264f00515f083657aecb6 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Fri, 28 Jul 2023 11:19:50 +0200 Subject: [PATCH 17/27] perf: remove redondant checks on sig --- src/Blue.sol | 6 ------ src/libraries/Errors.sol | 4 ---- 2 files changed, 10 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index 4bf670c69..1525841e9 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -22,9 +22,6 @@ bytes32 constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(uint256 chainId,addre bytes32 constant AUTHORIZATION_TYPEHASH = keccak256("Authorization(address authoriser,address authorizee,bool isAuthorized,uint256 nonce,uint256 deadline)"); -/// @dev The highest valid value for s in an ECDSA signature pair (0 < s < secp256k1n ÷ 2 + 1). -uint256 constant MAX_VALID_ECDSA_S = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0; - /// @notice Contains the `v`, `r` and `s` parameters of an ECDSA signature. struct Signature { uint8 v; @@ -281,9 +278,6 @@ contract Blue is IFlashLender { uint256 deadline, Signature calldata signature ) external { - require(uint256(signature.s) <= MAX_VALID_ECDSA_S, Errors.INVALID_S); - // v ∈ {27, 28} (source: https://ethereum.github.io/yellowpaper/paper.pdf #308) - require(signature.v == 27 || signature.v == 28, Errors.INVALID_V); require(block.timestamp < deadline, Errors.SIGNATURE_EXPIRED); bytes32 hashStruct = keccak256( diff --git a/src/libraries/Errors.sol b/src/libraries/Errors.sol index c190c1288..9c847b98d 100644 --- a/src/libraries/Errors.sol +++ b/src/libraries/Errors.sol @@ -26,10 +26,6 @@ library Errors { string internal constant HEALTHY_POSITION = "position is healthy"; - string internal constant INVALID_S = "invalid s"; - - string internal constant INVALID_V = "invalid v"; - string internal constant WRONG_SIGNATURE = "wrong signature"; string internal constant SIGNATURE_EXPIRED = "signature expired"; From 6ce91a5793acf62e273775d2110705d2dc62c0cc Mon Sep 17 00:00:00 2001 From: MathisGD Date: Fri, 28 Jul 2023 11:54:28 +0200 Subject: [PATCH 18/27] style: fixes after review --- src/Blue.sol | 10 ++++------ src/libraries/Errors.sol | 2 +- test/forge/Blue.t.sol | 4 ++-- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index 1525841e9..80d533b45 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -20,7 +20,7 @@ bytes32 constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(uint256 chainId,addre /// @dev The EIP-712 typeHash for Authorization. bytes32 constant AUTHORIZATION_TYPEHASH = - keccak256("Authorization(address authoriser,address authorizee,bool isAuthorized,uint256 nonce,uint256 deadline)"); + keccak256("Authorization(address authorizer,address authorizee,bool isAuthorized,uint256 nonce,uint256 deadline)"); /// @notice Contains the `v`, `r` and `s` parameters of an ECDSA signature. struct Signature { @@ -70,7 +70,7 @@ contract Blue is IFlashLender { // User's authorizations. mapping(address => mapping(address => bool)) public isAuthorized; // User's nonces. Used to prevent replay attacks with EIP-712 signatures. - mapping(address => uint256) public userNonce; + mapping(address => uint256) public nonce; // Constructor. @@ -281,14 +281,12 @@ contract Blue is IFlashLender { require(block.timestamp < deadline, Errors.SIGNATURE_EXPIRED); bytes32 hashStruct = keccak256( - abi.encode( - AUTHORIZATION_TYPEHASH, authorizer, authorizee, newIsAuthorized, userNonce[authorizer]++, deadline - ) + abi.encode(AUTHORIZATION_TYPEHASH, authorizer, authorizee, newIsAuthorized, nonce[authorizer]++, deadline) ); bytes32 digest = keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, hashStruct)); address signatory = ecrecover(digest, signature.v, signature.r, signature.s); - require(authorizer == signatory, Errors.WRONG_SIGNATURE); + require(authorizer == signatory, Errors.INVALID_SIGNATURE); isAuthorized[signatory][authorizee] = newIsAuthorized; } diff --git a/src/libraries/Errors.sol b/src/libraries/Errors.sol index 9c847b98d..cfd51a62d 100644 --- a/src/libraries/Errors.sol +++ b/src/libraries/Errors.sol @@ -26,7 +26,7 @@ library Errors { string internal constant HEALTHY_POSITION = "position is healthy"; - string internal constant WRONG_SIGNATURE = "wrong signature"; + string internal constant INVALID_SIGNATURE = "invalid signature"; string internal constant SIGNATURE_EXPIRED = "signature expired"; } diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index 5067d0301..6879d02d6 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -706,7 +706,7 @@ contract BlueTest is Test { authorizer: authorizer, authorizee: authorizee, isAuthorized: isAuthorized, - nonce: blue.userNonce(authorizer), + nonce: blue.nonce(authorizer), deadline: block.timestamp + deadline }); @@ -720,7 +720,7 @@ contract BlueTest is Test { ); assertEq(blue.isAuthorized(authorizer, authorizee), isAuthorized); - assertEq(blue.userNonce(authorizer), 1); + assertEq(blue.nonce(authorizer), 1); } function testFlashLoan(uint256 amount) public { From 40a385566d29bf5135c4a721a4998bff84292c2f Mon Sep 17 00:00:00 2001 From: MathisGD Date: Fri, 28 Jul 2023 12:11:47 +0200 Subject: [PATCH 19/27] fix: readd name in domain --- src/Blue.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index 80d533b45..bd92c10d5 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -16,7 +16,7 @@ uint256 constant MAX_FEE = 0.25e18; uint256 constant ALPHA = 0.5e18; /// @dev The EIP-712 typeHash for EIP712Domain. -bytes32 constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(uint256 chainId,address verifyingContract)"); +bytes32 constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); /// @dev The EIP-712 typeHash for Authorization. bytes32 constant AUTHORIZATION_TYPEHASH = @@ -77,7 +77,7 @@ contract Blue is IFlashLender { constructor(address newOwner) { owner = newOwner; - DOMAIN_SEPARATOR = keccak256(abi.encode(DOMAIN_TYPEHASH, block.chainid, address(this))); + DOMAIN_SEPARATOR = keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256("Blue"), block.chainid, address(this))); } // Modifiers. From 7ed097579ed7998d9493d54c4afde57e6bcf882b Mon Sep 17 00:00:00 2001 From: MathisGD Date: Fri, 28 Jul 2023 12:16:40 +0200 Subject: [PATCH 20/27] fix: check that signatory is not address zero --- src/Blue.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Blue.sol b/src/Blue.sol index bd92c10d5..12a721de7 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -286,7 +286,7 @@ contract Blue is IFlashLender { bytes32 digest = keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, hashStruct)); address signatory = ecrecover(digest, signature.v, signature.r, signature.s); - require(authorizer == signatory, Errors.INVALID_SIGNATURE); + require(signatory != address(0) && authorizer == signatory, Errors.INVALID_SIGNATURE); isAuthorized[signatory][authorizee] = newIsAuthorized; } From af2d66f7b40cb57b383815423371a8f724fb7a18 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Mon, 31 Jul 2023 10:25:39 +0200 Subject: [PATCH 21/27] style: errors wording and tests --- src/libraries/Errors.sol | 6 +++--- test/forge/Blue.t.sol | 40 ++++++++++++++++++++-------------------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/libraries/Errors.sol b/src/libraries/Errors.sol index cfd51a62d..830f15aa6 100644 --- a/src/libraries/Errors.sol +++ b/src/libraries/Errors.sol @@ -6,15 +6,15 @@ library Errors { string internal constant LLTV_TOO_HIGH = "LLTV too high"; - string internal constant MAX_FEE_EXCEEDED = "fee must be <= MAX_FEE"; + string internal constant MAX_FEE_EXCEEDED = "MAX_FEE exceeded"; string internal constant IRM_NOT_ENABLED = "IRM not enabled"; string internal constant LLTV_NOT_ENABLED = "LLTV not enabled"; - string internal constant MARKET_CREATED = "market already exists"; + string internal constant MARKET_CREATED = "market created"; - string internal constant MARKET_NOT_CREATED = "unknown market"; + string internal constant MARKET_NOT_CREATED = "market not created"; string internal constant ZERO_AMOUNT = "zero amount"; diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index 6879d02d6..003c64288 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -218,7 +218,7 @@ contract BlueTest is Test { fee = bound(fee, 0, FixedPointMathLib.WAD); vm.prank(OWNER); - vm.expectRevert("unknown market"); + vm.expectRevert(bytes(Errors.MARKET_NOT_CREATED)); blue.setFee(marketFuzz, fee); } @@ -226,7 +226,7 @@ contract BlueTest is Test { vm.assume(caller != OWNER); fee = bound(fee, 0, FixedPointMathLib.WAD); - vm.expectRevert("not owner"); + vm.expectRevert(bytes(Errors.NOT_OWNER)); blue.setFee(market, fee); } @@ -240,7 +240,7 @@ contract BlueTest is Test { function testSetFeeRecipientShouldRevertIfNotOwner(address caller, address recipient) public { vm.assume(caller != OWNER); - vm.expectRevert("not owner"); + vm.expectRevert(bytes(Errors.NOT_OWNER)); vm.prank(caller); blue.setFeeRecipient(recipient); } @@ -599,48 +599,48 @@ contract BlueTest is Test { function testUnknownMarket(Market memory marketFuzz) public { vm.assume(neq(marketFuzz, market)); - vm.expectRevert("unknown market"); + vm.expectRevert(bytes(Errors.MARKET_NOT_CREATED)); blue.supply(marketFuzz, 1, address(this)); - vm.expectRevert("unknown market"); + vm.expectRevert(bytes(Errors.MARKET_NOT_CREATED)); blue.withdraw(marketFuzz, 1, address(this)); - vm.expectRevert("unknown market"); + vm.expectRevert(bytes(Errors.MARKET_NOT_CREATED)); blue.borrow(marketFuzz, 1, address(this)); - vm.expectRevert("unknown market"); + vm.expectRevert(bytes(Errors.MARKET_NOT_CREATED)); blue.repay(marketFuzz, 1, address(this)); - vm.expectRevert("unknown market"); + vm.expectRevert(bytes(Errors.MARKET_NOT_CREATED)); blue.supplyCollateral(marketFuzz, 1, address(this)); - vm.expectRevert("unknown market"); + vm.expectRevert(bytes(Errors.MARKET_NOT_CREATED)); blue.withdrawCollateral(marketFuzz, 1, address(this)); - vm.expectRevert("unknown market"); + vm.expectRevert(bytes(Errors.MARKET_NOT_CREATED)); blue.liquidate(marketFuzz, address(0), 1); } function testAmountZero() public { - vm.expectRevert("zero amount"); + vm.expectRevert(bytes(Errors.ZERO_AMOUNT)); blue.supply(market, 0, address(this)); - vm.expectRevert("zero amount"); + vm.expectRevert(bytes(Errors.ZERO_AMOUNT)); blue.withdraw(market, 0, address(this)); - vm.expectRevert("zero amount"); + vm.expectRevert(bytes(Errors.ZERO_AMOUNT)); blue.borrow(market, 0, address(this)); - vm.expectRevert("zero amount"); + vm.expectRevert(bytes(Errors.ZERO_AMOUNT)); blue.repay(market, 0, address(this)); - vm.expectRevert("zero amount"); + vm.expectRevert(bytes(Errors.ZERO_AMOUNT)); blue.supplyCollateral(market, 0, address(this)); - vm.expectRevert("zero amount"); + vm.expectRevert(bytes(Errors.ZERO_AMOUNT)); blue.withdrawCollateral(market, 0, address(this)); - vm.expectRevert("zero amount"); + vm.expectRevert(bytes(Errors.ZERO_AMOUNT)); blue.liquidate(market, address(0), 0); } @@ -667,11 +667,11 @@ contract BlueTest is Test { vm.startPrank(attacker); - vm.expectRevert("not sender and not authorized"); + vm.expectRevert(bytes(Errors.NOT_SENDER_AND_NOT_AUTHORIZED)); blue.withdraw(market, 1, address(this)); - vm.expectRevert("not sender and not authorized"); + vm.expectRevert(bytes(Errors.NOT_SENDER_AND_NOT_AUTHORIZED)); blue.withdrawCollateral(market, 1, address(this)); - vm.expectRevert("not sender and not authorized"); + vm.expectRevert(bytes(Errors.NOT_SENDER_AND_NOT_AUTHORIZED)); blue.borrow(market, 1, address(this)); vm.stopPrank(); From 541c8833d91faf7b209895fc92d1b36b45e19e28 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Mon, 31 Jul 2023 10:31:01 +0200 Subject: [PATCH 22/27] style: sender is authorized by default --- src/Blue.sol | 10 +++++----- src/libraries/Errors.sol | 2 +- test/forge/Blue.t.sol | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index 12a721de7..6e5c7daf0 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -67,7 +67,7 @@ contract Blue is IFlashLender { mapping(IIrm => bool) public isIrmEnabled; // Enabled LLTVs. mapping(uint256 => bool) public isLltvEnabled; - // User's authorizations. + // User's authorizations. Note that by default, msg.sender is authorized by themself. mapping(address => mapping(address => bool)) public isAuthorized; // User's nonces. Used to prevent replay attacks with EIP-712 signatures. mapping(address => uint256) public nonce; @@ -147,7 +147,7 @@ contract Blue is IFlashLender { Id id = market.id(); require(lastUpdate[id] != 0, Errors.MARKET_NOT_CREATED); require(amount != 0, Errors.ZERO_AMOUNT); - require(_isSenderOrIsAuthorized(onBehalf), Errors.NOT_SENDER_AND_NOT_AUTHORIZED); + require(_isAuthorized(onBehalf), Errors.UNAUTHORIZED); _accrueInterests(market, id); @@ -168,7 +168,7 @@ contract Blue is IFlashLender { Id id = market.id(); require(lastUpdate[id] != 0, Errors.MARKET_NOT_CREATED); require(amount != 0, Errors.ZERO_AMOUNT); - require(_isSenderOrIsAuthorized(onBehalf), Errors.NOT_SENDER_AND_NOT_AUTHORIZED); + require(_isAuthorized(onBehalf), Errors.UNAUTHORIZED); _accrueInterests(market, id); @@ -219,7 +219,7 @@ contract Blue is IFlashLender { Id id = market.id(); require(lastUpdate[id] != 0, Errors.MARKET_NOT_CREATED); require(amount != 0, Errors.ZERO_AMOUNT); - require(_isSenderOrIsAuthorized(onBehalf), Errors.NOT_SENDER_AND_NOT_AUTHORIZED); + require(_isAuthorized(onBehalf), Errors.UNAUTHORIZED); _accrueInterests(market, id); @@ -295,7 +295,7 @@ contract Blue is IFlashLender { isAuthorized[msg.sender][authorizee] = newIsAuthorized; } - function _isSenderOrIsAuthorized(address user) internal view returns (bool) { + function _isAuthorized(address user) internal view returns (bool) { return msg.sender == user || isAuthorized[user][msg.sender]; } diff --git a/src/libraries/Errors.sol b/src/libraries/Errors.sol index 830f15aa6..44b7d0bad 100644 --- a/src/libraries/Errors.sol +++ b/src/libraries/Errors.sol @@ -18,7 +18,7 @@ library Errors { string internal constant ZERO_AMOUNT = "zero amount"; - string internal constant NOT_SENDER_AND_NOT_AUTHORIZED = "not sender and not authorized"; + string internal constant UNAUTHORIZED = "unauthorized"; string internal constant INSUFFICIENT_COLLATERAL = "insufficient collateral"; diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index 003c64288..748cb5009 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -667,11 +667,11 @@ contract BlueTest is Test { vm.startPrank(attacker); - vm.expectRevert(bytes(Errors.NOT_SENDER_AND_NOT_AUTHORIZED)); + vm.expectRevert(bytes(Errors.UNAUTHORIZED)); blue.withdraw(market, 1, address(this)); - vm.expectRevert(bytes(Errors.NOT_SENDER_AND_NOT_AUTHORIZED)); + vm.expectRevert(bytes(Errors.UNAUTHORIZED)); blue.withdrawCollateral(market, 1, address(this)); - vm.expectRevert(bytes(Errors.NOT_SENDER_AND_NOT_AUTHORIZED)); + vm.expectRevert(bytes(Errors.UNAUTHORIZED)); blue.borrow(market, 1, address(this)); vm.stopPrank(); From f45f4d5fe9db7e2f4fa934870f8380dcda6dd8d4 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Mon, 31 Jul 2023 11:05:16 +0200 Subject: [PATCH 23/27] style: isSenderApproved function naming --- src/Blue.sol | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index 6e5c7daf0..f097e8796 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -147,7 +147,7 @@ contract Blue is IFlashLender { Id id = market.id(); require(lastUpdate[id] != 0, Errors.MARKET_NOT_CREATED); require(amount != 0, Errors.ZERO_AMOUNT); - require(_isAuthorized(onBehalf), Errors.UNAUTHORIZED); + require(_isSenderAuthorized(onBehalf), Errors.UNAUTHORIZED); _accrueInterests(market, id); @@ -168,7 +168,7 @@ contract Blue is IFlashLender { Id id = market.id(); require(lastUpdate[id] != 0, Errors.MARKET_NOT_CREATED); require(amount != 0, Errors.ZERO_AMOUNT); - require(_isAuthorized(onBehalf), Errors.UNAUTHORIZED); + require(_isSenderAuthorized(onBehalf), Errors.UNAUTHORIZED); _accrueInterests(market, id); @@ -219,7 +219,7 @@ contract Blue is IFlashLender { Id id = market.id(); require(lastUpdate[id] != 0, Errors.MARKET_NOT_CREATED); require(amount != 0, Errors.ZERO_AMOUNT); - require(_isAuthorized(onBehalf), Errors.UNAUTHORIZED); + require(_isSenderAuthorized(onBehalf), Errors.UNAUTHORIZED); _accrueInterests(market, id); @@ -295,7 +295,7 @@ contract Blue is IFlashLender { isAuthorized[msg.sender][authorizee] = newIsAuthorized; } - function _isAuthorized(address user) internal view returns (bool) { + function _isSenderAuthorized(address user) internal view returns (bool) { return msg.sender == user || isAuthorized[user][msg.sender]; } From 200c50fcd0fdb5773fe686faa38b6da95d7d1525 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Mon, 31 Jul 2023 13:55:06 +0200 Subject: [PATCH 24/27] style: replace flashloans above authorizations --- src/Blue.sol | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index bc8ff2e1b..38ca6940d 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -286,6 +286,17 @@ contract Blue is IFlashLender { market.borrowableAsset.safeTransferFrom(msg.sender, address(this), repaid); } + // Flash Loans. + + /// @inheritdoc IFlashLender + function flashLoan(IFlashBorrower receiver, address token, uint256 amount, bytes calldata data) external { + IERC20(token).safeTransfer(address(receiver), amount); + + receiver.onBlueFlashLoan(msg.sender, token, amount, data); + + IERC20(token).safeTransferFrom(address(receiver), address(this), amount); + } + // Position authorizations. function setAuthorization( @@ -316,17 +327,6 @@ contract Blue is IFlashLender { return msg.sender == user || isAuthorized[user][msg.sender]; } - // Flash Loans. - - /// @inheritdoc IFlashLender - function flashLoan(IFlashBorrower receiver, address token, uint256 amount, bytes calldata data) external { - IERC20(token).safeTransfer(address(receiver), amount); - - receiver.onBlueFlashLoan(msg.sender, token, amount, data); - - IERC20(token).safeTransferFrom(address(receiver), address(this), amount); - } - // Interests management. function _accrueInterests(Market memory market, Id id) internal { From 2a68fdd3a58ae73eb159c10047f5e8b750a98dd0 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Mon, 31 Jul 2023 13:56:42 +0200 Subject: [PATCH 25/27] docs: simplify authorization doc --- src/Blue.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Blue.sol b/src/Blue.sol index 38ca6940d..866a315bc 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -297,7 +297,7 @@ contract Blue is IFlashLender { IERC20(token).safeTransferFrom(address(receiver), address(this), amount); } - // Position authorizations. + // Authorizations. function setAuthorization( address authorizer, From a901b95fa4ffeb4dd32b6776500dca255417d557 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Mon, 31 Jul 2023 14:04:31 +0200 Subject: [PATCH 26/27] docs: add a comment on signature malleability --- src/Blue.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Blue.sol b/src/Blue.sol index 866a315bc..09a05d39b 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -299,6 +299,7 @@ contract Blue is IFlashLender { // Authorizations. + /// @dev The signature is malleable, but it has no impact on the security here. function setAuthorization( address authorizer, address authorizee, From 7bf9f1529476a4f9b8be466a3df06cd99f976d37 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Mon, 31 Jul 2023 16:19:18 +0200 Subject: [PATCH 27/27] style: naming authorizee => authorized --- src/Blue.sol | 12 ++++++------ test/forge/Blue.t.sol | 20 ++++++++++---------- test/forge/helpers/SigUtils.sol | 4 ++-- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index 09a05d39b..06abdb4a6 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -26,7 +26,7 @@ bytes32 constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 c /// @dev The EIP-712 typeHash for Authorization. bytes32 constant AUTHORIZATION_TYPEHASH = - keccak256("Authorization(address authorizer,address authorizee,bool isAuthorized,uint256 nonce,uint256 deadline)"); + keccak256("Authorization(address authorizer,address authorized,bool isAuthorized,uint256 nonce,uint256 deadline)"); /// @notice Contains the `v`, `r` and `s` parameters of an ECDSA signature. struct Signature { @@ -302,7 +302,7 @@ contract Blue is IFlashLender { /// @dev The signature is malleable, but it has no impact on the security here. function setAuthorization( address authorizer, - address authorizee, + address authorized, bool newIsAuthorized, uint256 deadline, Signature calldata signature @@ -310,18 +310,18 @@ contract Blue is IFlashLender { require(block.timestamp < deadline, Errors.SIGNATURE_EXPIRED); bytes32 hashStruct = keccak256( - abi.encode(AUTHORIZATION_TYPEHASH, authorizer, authorizee, newIsAuthorized, nonce[authorizer]++, deadline) + abi.encode(AUTHORIZATION_TYPEHASH, authorizer, authorized, newIsAuthorized, nonce[authorizer]++, deadline) ); bytes32 digest = keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, hashStruct)); address signatory = ecrecover(digest, signature.v, signature.r, signature.s); require(signatory != address(0) && authorizer == signatory, Errors.INVALID_SIGNATURE); - isAuthorized[signatory][authorizee] = newIsAuthorized; + isAuthorized[signatory][authorized] = newIsAuthorized; } - function setAuthorization(address authorizee, bool newIsAuthorized) external { - isAuthorized[msg.sender][authorizee] = newIsAuthorized; + function setAuthorization(address authorized, bool newIsAuthorized) external { + isAuthorized[msg.sender][authorized] = newIsAuthorized; } function _isSenderAuthorized(address user) internal view returns (bool) { diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index 84dc4c5a2..457cfc93c 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -669,9 +669,9 @@ contract BlueTest is blue.withdrawCollateral(market, amount, address(this)); } - function testSetAuthorization(address authorizee, bool isAuthorized) public { - blue.setAuthorization(authorizee, isAuthorized); - assertEq(blue.isAuthorized(address(this), authorizee), isAuthorized); + function testSetAuthorization(address authorized, bool isAuthorized) public { + blue.setAuthorization(authorized, isAuthorized); + assertEq(blue.isAuthorized(address(this), authorized), isAuthorized); } function testNotAuthorized(address attacker) public { @@ -689,16 +689,16 @@ contract BlueTest is vm.stopPrank(); } - function testAuthorization(address authorizee) public { + function testAuthorization(address authorized) public { borrowableAsset.setBalance(address(this), 100 ether); collateralAsset.setBalance(address(this), 100 ether); blue.supply(market, 100 ether, address(this), hex""); blue.supplyCollateral(market, 100 ether, address(this), hex""); - blue.setAuthorization(authorizee, true); + blue.setAuthorization(authorized, true); - vm.startPrank(authorizee); + vm.startPrank(authorized); blue.withdraw(market, 1 ether, address(this)); blue.withdrawCollateral(market, 1 ether, address(this)); @@ -707,7 +707,7 @@ contract BlueTest is vm.stopPrank(); } - function testAuthorizationWithSig(uint128 deadline, address authorizee, uint256 privateKey, bool isAuthorized) + function testAuthorizationWithSig(uint128 deadline, address authorized, uint256 privateKey, bool isAuthorized) public { vm.assume(deadline > block.timestamp); @@ -716,7 +716,7 @@ contract BlueTest is SigUtils.Authorization memory authorization = SigUtils.Authorization({ authorizer: authorizer, - authorizee: authorizee, + authorized: authorized, isAuthorized: isAuthorized, nonce: blue.nonce(authorizer), deadline: block.timestamp + deadline @@ -728,10 +728,10 @@ contract BlueTest is (sig.v, sig.r, sig.s) = vm.sign(privateKey, digest); blue.setAuthorization( - authorization.authorizer, authorization.authorizee, authorization.isAuthorized, authorization.deadline, sig + authorization.authorizer, authorization.authorized, authorization.isAuthorized, authorization.deadline, sig ); - assertEq(blue.isAuthorized(authorizer, authorizee), isAuthorized); + assertEq(blue.isAuthorized(authorizer, authorized), isAuthorized); assertEq(blue.nonce(authorizer), 1); } diff --git a/test/forge/helpers/SigUtils.sol b/test/forge/helpers/SigUtils.sol index 5917a5db0..7c13b2450 100644 --- a/test/forge/helpers/SigUtils.sol +++ b/test/forge/helpers/SigUtils.sol @@ -6,7 +6,7 @@ import {AUTHORIZATION_TYPEHASH} from "src/Blue.sol"; library SigUtils { struct Authorization { address authorizer; - address authorizee; + address authorized; bool isAuthorized; uint256 nonce; uint256 deadline; @@ -26,7 +26,7 @@ library SigUtils { abi.encode( AUTHORIZATION_TYPEHASH, authorization.authorizer, - authorization.authorizee, + authorization.authorized, authorization.isAuthorized, authorization.nonce, authorization.deadline