Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Introduce dynamic domain separator #21

Merged
merged 5 commits into from
Nov 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 6 additions & 20 deletions contracts/proposals/StakeTokenUpgradeProposal.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,42 +29,28 @@ contract StakeTokenUpgradeProposalExecutor {

function execute() external {
_upgradeStakedAave();
_upgradeStakedAbp();
_upgradeStakedAbpt();
}

function _upgradeStakedAave() internal {
string memory name = 'Staked AAVE';
string memory symbol = 'stkAAVE';
uint8 decimals = 18;

bytes memory params = abi.encodeWithSignature(
'initialize(address,address,address,uint256,string,string,uint8)',
'initialize(address,address,address,uint256)',
SLASHING_ADMIN,
COOLDOWN_PAUSE_ADMIN,
CLAIM_HELPER,
MAX_SLASHABLE_PERCENTAGE,
name,
symbol,
decimals
MAX_SLASHABLE_PERCENTAGE
);

STAKED_AAVE_TOKEN_PROXY.upgradeToAndCall(NEW_STAKED_AAVE_TOKEN_IMPLEMENTATION, params);
}

function _upgradeStakedAbp() internal {
string memory name = 'Staked Aave Balance Pool Token';
string memory symbol = 'stkABPT';
uint8 decimals = 18;

function _upgradeStakedAbpt() internal {
bytes memory params = abi.encodeWithSignature(
'initialize(address,address,address,uint256,string,string,uint8)',
'initialize(address,address,address,uint256)',
SLASHING_ADMIN,
COOLDOWN_PAUSE_ADMIN,
CLAIM_HELPER,
MAX_SLASHABLE_PERCENTAGE,
name,
symbol,
decimals
MAX_SLASHABLE_PERCENTAGE
);

STAKED_ABP_TOKEN_PROXY.upgradeToAndCall(NEW_STAKED_ABP_TOKEN_IMPLEMENTATION, params);
Expand Down
42 changes: 35 additions & 7 deletions contracts/stake/StakedTokenV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ contract StakedTokenV2 is
mapping(address => uint256) internal _propositionPowerSnapshotsCounts;
mapping(address => address) internal _propositionPowerDelegates;

bytes32 public DOMAIN_SEPARATOR;
bytes32 internal CACHED_DOMAIN_SEPARATOR;
bytes public constant EIP712_REVISION = bytes('1');
bytes32 internal constant EIP712_DOMAIN =
keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)');
Expand All @@ -66,6 +66,8 @@ contract StakedTokenV2 is
/// @dev owner => next valid nonce to submit with permit()
mapping(address => uint256) public _nonces;

uint256 internal immutable CACHED_CHAIN_ID;

event Staked(address indexed from, address indexed onBehalfOf, uint256 amount);
event Redeem(address indexed from, address indexed to, uint256 amount);

Expand Down Expand Up @@ -94,20 +96,25 @@ contract StakedTokenV2 is
REWARDS_VAULT = rewardsVault;
_aaveGovernance = ITransferHook(governance);
ERC20._setupDecimals(decimals);

uint256 chainId;
//solium-disable-next-line
assembly {
chainId := chainid()
}
CACHED_CHAIN_ID = chainId;
}

/**
* @dev Called by the proxy contract
**/
function initialize() external virtual initializer {
uint256 chainId;

//solium-disable-next-line
assembly {
chainId := chainid()
}

DOMAIN_SEPARATOR = keccak256(
CACHED_DOMAIN_SEPARATOR = keccak256(
abi.encode(
EIP712_DOMAIN,
keccak256(bytes(name())),
Expand All @@ -118,6 +125,27 @@ contract StakedTokenV2 is
);
}

function DOMAIN_SEPARATOR() public view returns (bytes32) {
uint256 chainId;
//solium-disable-next-line
assembly {
chainId := chainid()
}
if (chainId == CACHED_CHAIN_ID) {
return CACHED_DOMAIN_SEPARATOR;
}
return
keccak256(
abi.encode(
EIP712_DOMAIN,
keccak256(bytes(name())),
keccak256(EIP712_REVISION),
chainId,
address(this)
)
);
}

function stake(address onBehalfOf, uint256 amount) external virtual override {
require(amount != 0, 'INVALID_ZERO_AMOUNT');
uint256 balanceOfUser = balanceOf(onBehalfOf);
Expand Down Expand Up @@ -371,7 +399,7 @@ contract StakedTokenV2 is
bytes32 digest = keccak256(
abi.encodePacked(
'\x19\x01',
DOMAIN_SEPARATOR,
DOMAIN_SEPARATOR(),
keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, currentValidNonce, deadline))
)
);
Expand Down Expand Up @@ -479,7 +507,7 @@ contract StakedTokenV2 is
bytes32 structHash = keccak256(
abi.encode(DELEGATE_BY_TYPE_TYPEHASH, delegatee, uint256(delegationType), nonce, expiry)
);
bytes32 digest = keccak256(abi.encodePacked('\x19\x01', DOMAIN_SEPARATOR, structHash));
bytes32 digest = keccak256(abi.encodePacked('\x19\x01', DOMAIN_SEPARATOR(), structHash));
address signatory = ecrecover(digest, v, r, s);
require(signatory != address(0), 'INVALID_SIGNATURE');
require(nonce == _nonces[signatory]++, 'INVALID_NONCE');
Expand All @@ -505,7 +533,7 @@ contract StakedTokenV2 is
bytes32 s
) public {
bytes32 structHash = keccak256(abi.encode(DELEGATE_TYPEHASH, delegatee, nonce, expiry));
bytes32 digest = keccak256(abi.encodePacked('\x19\x01', DOMAIN_SEPARATOR, structHash));
bytes32 digest = keccak256(abi.encodePacked('\x19\x01', DOMAIN_SEPARATOR(), structHash));
address signatory = ecrecover(digest, v, r, s);
require(signatory != address(0), 'INVALID_SIGNATURE');
require(nonce == _nonces[signatory]++, 'INVALID_NONCE');
Expand Down
16 changes: 3 additions & 13 deletions contracts/stake/StakedTokenV3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -128,23 +128,19 @@ contract StakedTokenV3 is StakedTokenV2, IStakedTokenV3, RoleManager {
address slashingAdmin,
address cooldownPauseAdmin,
address claimHelper,
uint256 maxSlashablePercentage,
string calldata name,
string calldata symbol,
uint8 decimals
uint256 maxSlashablePercentage
) external initializer {
require(
maxSlashablePercentage <= PercentageMath.PERCENTAGE_FACTOR,
'INVALID_SLASHING_PERCENTAGE'
);
uint256 chainId;

uint256 chainId;
//solium-disable-next-line
assembly {
chainId := chainid()
}

DOMAIN_SEPARATOR = keccak256(
CACHED_DOMAIN_SEPARATOR = keccak256(
abi.encode(
EIP712_DOMAIN,
keccak256(bytes(super.name())),
Expand All @@ -154,12 +150,6 @@ contract StakedTokenV3 is StakedTokenV2, IStakedTokenV3, RoleManager {
)
);

if (REVISION() == 1) {
_name = name;
_symbol = symbol;
_setupDecimals(decimals);
}

address[] memory adminsAddresses = new address[](3);
uint256[] memory adminsRoles = new uint256[](3);

Expand Down
37 changes: 37 additions & 0 deletions test/StakedAaveV2/delegation.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -740,6 +740,43 @@ makeSuite('StakedAaveV2. Power Delegations', (testEnv: TestEnv) => {
).to.be.revertedWith('INVALID_NONCE');
});

it('User 1 should not be able to delegate with bad chainid', async () => {
const {
users: [, user1, user2],
stakedAaveV2,
} = testEnv;

// Prepare params to sign message
const { chainId } = await DRE.ethers.provider.getNetwork();
if (!chainId) {
fail("Current network doesn't have CHAIN ID");
}
const nonce = (await stakedAaveV2._nonces(user1.address)).toString();
const expiration = MAX_UINT_AMOUNT;
const msgParams = buildDelegateByTypeParams(
chainId + 1,
stakedAaveV2.address,
user2.address,
'0',
nonce,
expiration
);
const ownerPrivateKey = require('../../test-wallets').accounts[1].secretKey;
if (!ownerPrivateKey) {
throw new Error('INVALID_OWNER_PK');
}

const { v, r, s } = getSignatureFromTypedData(ownerPrivateKey, msgParams);

// Transmit message via delegateByTypeBySig.
// Signature don't recover address 0, so will fail due to nonce.
await expect(
stakedAaveV2
.connect(user1.signer)
.delegateByTypeBySig(user2.address, '0', nonce, expiration, v, r, s)
).to.be.revertedWith('INVALID_NONCE');
});

it('User 1 should not be able to delegate if signature expired', async () => {
const {
users: [, user1, user2],
Expand Down
34 changes: 34 additions & 0 deletions test/StakedAaveV2/permit.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,40 @@ makeSuite('StakedAaveV2. Permit', (testEnv: TestEnv) => {
expect((await stakedAaveV2._nonces(owner)).toNumber()).to.be.equal(2);
});

it('Tries to submit a permit with invalid chainid', async () => {
const { deployer, users, stakedAaveV2 } = testEnv;
const owner = deployer.address;
const spender = users[1].address;

const { chainId } = await DRE.ethers.provider.getNetwork();
if (!chainId) {
fail("Current network doesn't have CHAIN ID");
}
const deadline = MAX_UINT_AMOUNT;
const nonce = (await stakedAaveV2._nonces(owner)).toNumber();
const permitAmount = '0';
const msgParams = buildPermitParams(
chainId + 1,
stakedAaveV2.address,
owner,
spender,
nonce,
deadline,
permitAmount
);

const ownerPrivateKey = require('../../test-wallets').accounts[0].secretKey;
if (!ownerPrivateKey) {
throw new Error('INVALID_OWNER_PK');
}

const { v, r, s } = getSignatureFromTypedData(ownerPrivateKey, msgParams);

await expect(
stakedAaveV2.connect(users[1].signer).permit(owner, spender, permitAmount, deadline, v, r, s)
).to.be.revertedWith('INVALID_SIGNATURE');
});

it('Tries to submit a permit with invalid nonce', async () => {
const { deployer, users, stakedAaveV2 } = testEnv;
const owner = deployer.address;
Expand Down
Loading