Skip to content

Commit

Permalink
feat(ERC2612+ERC1271): adding permit function and helper ; removing s…
Browse files Browse the repository at this point in the history
…ignature points from burn function in favor of a byte array
  • Loading branch information
KristenPire committed Feb 9, 2024

Verified

This commit was signed with the committer’s verified signature. The key has expired.
1 parent 0e66fff commit 179d77a
Showing 44 changed files with 4,380 additions and 36,991 deletions.
45 changes: 45 additions & 0 deletions contracts/BytesLib.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/* SPDX-License-Identifier: apache-2.0 */
/**
* Copyright 2022 Monerium ehf.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

pragma solidity 0.8.11;

library BytesLib {
/*
* @dev Reads a bytes32 from a specific position in a byte array.
* @param b Byte array to read the bytes32 value from.
* @param index Index in byte array to start reading the bytes32 value.
* @return result The bytes32 value read from the specified index in the byte array.
*/
function readBytes32(
bytes memory b,
uint256 index
) internal pure returns (bytes32 result) {
require(
b.length >= index + 32,
"GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED"
);

// Arrays are prefixed by a 256 bit length parameter
index += 32;

// Read the bytes32 from array memory
assembly {
result := mload(add(b, index))
}
return result;
}
}
40 changes: 34 additions & 6 deletions contracts/MintableController.sol
Original file line number Diff line number Diff line change
@@ -20,6 +20,17 @@ pragma solidity 0.8.11;
import "./StandardController.sol";
import "./MintableTokenLib.sol";

interface ITokenFrontend {
function burnFrom(
address from,
uint256 amount,
bytes32 h,
uint8 v,
bytes32 r,
bytes32 s
) external returns (bool);
}

/**
* @title MintableController
* @dev This contracts implements functionality allowing for minting and burning of tokens.
@@ -115,10 +126,17 @@ contract MintableController is StandardController {
uint8 v,
bytes32 r,
bytes32 s
) public onlyFrontend onlySystemAccount(caller) returns (bool) {
) public view onlyFrontend returns (bool) {
// Explicitly mark parameters as unused to silence compiler warnings
from;
amount;
h;
v;
r;
s;
require(
token.burn(from, amount, h, v, r, s),
"MintableController: burn failed"
caller == address(this),
"only allow this contract to be the caller"
);
return true;
}
@@ -131,9 +149,19 @@ contract MintableController is StandardController {
*/
function burnFrom(
address from,
uint256 amount
) public onlyFrontend onlySystemAccount(msg.sender) returns (bool) {
require(token.burn(from, amount), "MintableController: burn failed");
uint256 amount,
bytes32 h,
bytes memory signature
) public onlySystemAccount(msg.sender) returns (bool) {
require(
token.burn(from, amount, h, signature),
"MintableController: burn failed"
);
ITokenFrontend tokenFrontend = ITokenFrontend(frontend);
require(
tokenFrontend.burnFrom(from, amount, h, 0, 0, 0),
"MintableController: TokenFrontend burn call failed"
);
return true;
}

12 changes: 2 additions & 10 deletions contracts/MintableTokenLib.sol
Original file line number Diff line number Diff line change
@@ -69,23 +69,15 @@ library MintableTokenLib {
* @param from The address holding tokens.
* @param amount The amount of tokens to burn.
* @param h Hash which the token owner signed.
* @param v Signature component.
* @param r Signature component.
* @param s Sigature component.
* @param signature Signature component.
*/
function burn(
TokenStorage db,
address from,
uint256 amount,
bytes32 h,
uint8 v,
bytes32 r,
bytes32 s
bytes memory signature
) external returns (bool) {
bytes memory signature;
if (r != bytes32(0) || s != bytes32(0)) {
signature = bytes(abi.encodePacked(r, s, v));
}
require(
from.isValidSignatureNow(h, signature),
"signature/hash does not match"
5 changes: 3 additions & 2 deletions contracts/PolygonPosTokenFrontend.sol
Original file line number Diff line number Diff line change
@@ -77,12 +77,13 @@ abstract contract PolygonPosTokenFrontend is
}

/**
* This Function has beed left out. The controller doesn't implement this function anymore.
* @notice Polygon Bridge Mechanism. Called when user wants to withdraw tokens back to root chain
* @dev Should burn user's tokens. This transaction will be verified when exiting on root chain
* @param amount amount of tokens to withdraw
*/
function withdraw(uint256 amount) external override {
controller.burnFrom(msg.sender, amount);
emit Transfer(msg.sender, address(0x0), amount);
// controller.burnFrom(msg.sender, amount);
// emit Transfer(msg.sender, address(0x0), amount);
}
}
100 changes: 100 additions & 0 deletions contracts/StandardController.sol
Original file line number Diff line number Diff line change
@@ -37,6 +37,9 @@ contract StandardController is ClaimableSystemRole {
address internal frontend;
mapping(address => bool) internal bridgeFrontends;
uint8 public decimals = 18;

bytes32 private constant PERMIT_TYPEHASH =
keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");

/**
* @dev Emitted when updating the frontend.
@@ -74,6 +77,17 @@ contract StandardController is ClaimableSystemRole {
_;
}

/*//////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/

uint256 internal immutable INITIAL_CHAIN_ID;

bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

mapping(address => uint256) public nonces;


/**
* @dev Contract constructor.
* @param storage_ Address of the token storage for the controller.
@@ -92,6 +106,9 @@ contract StandardController is ClaimableSystemRole {
token = TokenStorage(storage_);
}
frontend = frontend_;

INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}

/**
@@ -236,6 +253,89 @@ contract StandardController is ClaimableSystemRole {
return token.approve(caller, spender, amount);
}

function getPermitDigest(
address owner,
address spender,
uint256 value,
uint256 nonce,
uint256 deadline
) public view returns (bytes32) {
return keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"),
owner,
spender,
value,
nonce,
deadline
)
)
)
);
}

function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");

// Unchecked because the only math done is incrementing
// the owner's nonce which cannot realistically overflow.
unchecked {
address recoveredAddress = ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);

require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
}
token.approve(owner, spender, value);
}

function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}

function computeDomainSeparator() internal view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes("standardController")),
keccak256("1"),
block.chainid,
address(this)
)
);
}

/**
* @dev Transfers tokens and subsequently calls a method on the recipient [ERC677].
* If the recipient is a non-contract address this method behaves just like transfer.
9 changes: 9 additions & 0 deletions contracts/SystemRole.sol
Original file line number Diff line number Diff line change
@@ -109,6 +109,15 @@ abstract contract SystemRole is AccessControl, Ownable {
return hasRole(SYSTEM_ROLE, account);
}

/**
* @dev Checks wether an address is a admin account.
* @param account The address of the account.
* @return true if admin account.
*/
function isAdminAccount(address account) public view returns (bool) {
return hasRole(ADMIN_ROLE, account);
}

/**
* @dev add system account.
* @param account The address of the account.
Loading

0 comments on commit 179d77a

Please sign in to comment.