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

feat: Add LSP7Votes and LSP8Votes extension to support LSP-based Governance #980

Open
wants to merge 5 commits into
base: develop
Choose a base branch
from
Open
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
93 changes: 50 additions & 43 deletions packages/lsp7-contracts/contracts/LSP7DigitalAsset.sol
Original file line number Diff line number Diff line change
Expand Up @@ -602,19 +602,7 @@ abstract contract LSP7DigitalAsset is

_beforeTokenTransfer(address(0), to, amount, data);

// tokens being minted
_existingTokens += amount;

_tokenOwnerBalances[to] += amount;

emit Transfer({
operator: msg.sender,
from: address(0),
to: to,
amount: amount,
force: force,
data: data
});
_update(address(0), to, amount, force, data);

_afterTokenTransfer(address(0), to, amount, data);

Expand Down Expand Up @@ -664,23 +652,7 @@ abstract contract LSP7DigitalAsset is

_beforeTokenTransfer(from, address(0), amount, data);

uint256 balance = _tokenOwnerBalances[from];
if (amount > balance) {
revert LSP7AmountExceedsBalance(balance, from, amount);
}
// tokens being burnt
_existingTokens -= amount;

_tokenOwnerBalances[from] -= amount;

emit Transfer({
operator: msg.sender,
from: from,
to: address(0),
amount: amount,
force: false,
data: data
});
_update(from, address(0), amount, false, data);

_afterTokenTransfer(from, address(0), amount, data);

Expand Down Expand Up @@ -775,29 +747,64 @@ abstract contract LSP7DigitalAsset is

_beforeTokenTransfer(from, to, amount, data);

uint256 balance = _tokenOwnerBalances[from];
if (amount > balance) {
revert LSP7AmountExceedsBalance(balance, from, amount);
_update(from, to, amount, force, data);

_afterTokenTransfer(from, to, amount, data);

bytes memory lsp1Data = abi.encode(msg.sender, from, to, amount, data);

_notifyTokenSender(from, lsp1Data);
_notifyTokenReceiver(to, force, lsp1Data);
}

/**
* @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
* (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
* this function.
*
* Emits a {Transfer} event.
*/
function _update(
address from,
address to,
uint256 value,
bool force,
bytes memory data
) internal virtual {
if (from == address(0)) {
// Overflow check required: The rest of the code assumes that totalSupply never overflows
_existingTokens += value;
} else {
uint256 fromBalance = _tokenOwnerBalances[from];
if (fromBalance < value) {
revert LSP7AmountExceedsBalance(fromBalance, from, value);
}
unchecked {
// Overflow not possible: value <= fromBalance <= totalSupply.
_tokenOwnerBalances[from] = fromBalance - value;
}
}

_tokenOwnerBalances[from] -= amount;
_tokenOwnerBalances[to] += amount;
if (to == address(0)) {
unchecked {
// Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
_existingTokens -= value;
}
} else {
unchecked {
// Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
_tokenOwnerBalances[to] += value;
}
}

emit Transfer({
operator: msg.sender,
from: from,
to: to,
amount: amount,
amount: value,
force: force,
data: data
});

_afterTokenTransfer(from, to, amount, data);

bytes memory lsp1Data = abi.encode(msg.sender, from, to, amount, data);

_notifyTokenSender(from, lsp1Data);
_notifyTokenReceiver(to, force, lsp1Data);
}

/**
Expand Down
95 changes: 51 additions & 44 deletions packages/lsp7-contracts/contracts/LSP7DigitalAssetInitAbstract.sol
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ abstract contract LSP7DigitalAssetInitAbstract is
*
* 2. If the data sent to this function is of length less than 4 bytes (not a function selector), revert.
*/
// solhint-disable-next-line no-complex-fallback
// solhint-disable no-complex-fallback
fallback(
bytes calldata callData
) external payable virtual returns (bytes memory) {
Expand Down Expand Up @@ -616,19 +616,7 @@ abstract contract LSP7DigitalAssetInitAbstract is

_beforeTokenTransfer(address(0), to, amount, data);

// tokens being minted
_existingTokens += amount;

_tokenOwnerBalances[to] += amount;

emit Transfer({
operator: msg.sender,
from: address(0),
to: to,
amount: amount,
force: force,
data: data
});
_update(address(0), to, amount, force, data);

_afterTokenTransfer(address(0), to, amount, data);

Expand Down Expand Up @@ -678,23 +666,7 @@ abstract contract LSP7DigitalAssetInitAbstract is

_beforeTokenTransfer(from, address(0), amount, data);

uint256 balance = _tokenOwnerBalances[from];
if (amount > balance) {
revert LSP7AmountExceedsBalance(balance, from, amount);
}
// tokens being burnt
_existingTokens -= amount;

_tokenOwnerBalances[from] -= amount;

emit Transfer({
operator: msg.sender,
from: from,
to: address(0),
amount: amount,
force: false,
data: data
});
_update(from, address(0), amount, false, data);

_afterTokenTransfer(from, address(0), amount, data);

Expand Down Expand Up @@ -789,29 +761,64 @@ abstract contract LSP7DigitalAssetInitAbstract is

_beforeTokenTransfer(from, to, amount, data);

uint256 balance = _tokenOwnerBalances[from];
if (amount > balance) {
revert LSP7AmountExceedsBalance(balance, from, amount);
_update(from, to, amount, force, data);

_afterTokenTransfer(from, to, amount, data);

bytes memory lsp1Data = abi.encode(msg.sender, from, to, amount, data);

_notifyTokenSender(from, lsp1Data);
_notifyTokenReceiver(to, force, lsp1Data);
}

/**
* @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
* (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
* this function.
*
* Emits a {Transfer} event.
*/
function _update(
address from,
address to,
uint256 value,
bool force,
bytes memory data
) internal virtual {
if (from == address(0)) {
// Overflow check required: The rest of the code assumes that totalSupply never overflows
_existingTokens += value;
} else {
uint256 fromBalance = _tokenOwnerBalances[from];
if (fromBalance < value) {
revert LSP7AmountExceedsBalance(fromBalance, from, value);
}
unchecked {
// Overflow not possible: value <= fromBalance <= totalSupply.
_tokenOwnerBalances[from] = fromBalance - value;
}
}

_tokenOwnerBalances[from] -= amount;
_tokenOwnerBalances[to] += amount;
if (to == address(0)) {
unchecked {
// Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
_existingTokens -= value;
}
} else {
unchecked {
// Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
_tokenOwnerBalances[to] += value;
}
}

emit Transfer({
operator: msg.sender,
from: from,
to: to,
amount: amount,
amount: value,
force: force,
data: data
});

_afterTokenTransfer(from, to, amount, data);

bytes memory lsp1Data = abi.encode(msg.sender, from, to, amount, data);

_notifyTokenSender(from, lsp1Data);
_notifyTokenReceiver(to, force, lsp1Data);
}

/**
Expand Down
77 changes: 77 additions & 0 deletions packages/lsp7-contracts/contracts/Mocks/MyGovernor.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {
IGovernor,
Governor
} from "@openzeppelin/contracts/governance/Governor.sol";
import {
GovernorSettings
} from "@openzeppelin/contracts/governance/extensions/GovernorSettings.sol";
import {
GovernorCountingSimple
} from "@openzeppelin/contracts/governance/extensions/GovernorCountingSimple.sol";
import {
GovernorVotes,
IVotes
} from "@openzeppelin/contracts/governance/extensions/GovernorVotes.sol";
import {
GovernorVotesQuorumFraction
} from "@openzeppelin/contracts/governance/extensions/GovernorVotesQuorumFraction.sol";

contract MyGovernor is
Governor,
GovernorSettings,
GovernorCountingSimple,
GovernorVotes,
GovernorVotesQuorumFraction
{
constructor(
IVotes _token
)
Governor("MyGovernor")
GovernorSettings(7200 /* 1 day */, 7200 /* 1 day */, 1e18)
GovernorVotes(_token)
GovernorVotesQuorumFraction(1)
{}

// The following functions are overrides required by Solidity.

function votingDelay()
public
view
override(IGovernor, GovernorSettings)
returns (uint256)
{
return super.votingDelay();
}

function votingPeriod()
public
view
override(IGovernor, GovernorSettings)
returns (uint256)
{
return super.votingPeriod();
}

function quorum(
uint256 blockNumber
)
public
view
override(IGovernor, GovernorVotesQuorumFraction)
returns (uint256)
{
return super.quorum(blockNumber);
}

function proposalThreshold()
public
view
override(Governor, GovernorSettings)
returns (uint256)
{
return super.proposalThreshold();
}
}
20 changes: 20 additions & 0 deletions packages/lsp7-contracts/contracts/Mocks/MyVotingToken.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import {LSP7Votes, LSP7DigitalAsset} from "../extensions/LSP7Votes.sol";
import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";

/**
* @dev Mock of an LSP7Votes token
*/
contract MyVotingToken is LSP7Votes {
constructor()
LSP7DigitalAsset("MyVotingToken", "MYVTKN", msg.sender, 0, false)
EIP712("MyVotingToken", "1")
{}

function mint(address rec, uint256 amount) public {
_mint(rec, amount, true, "");
}
}
Loading
Loading