Skip to content

Instantly share code, notes, and snippets.

@must479
Created December 5, 2022 14:08
Show Gist options
  • Save must479/69fd79d6e728c10c2225de9ce44135cd to your computer and use it in GitHub Desktop.
Save must479/69fd79d6e728c10c2225de9ce44135cd to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.8.15+commit.e14f2714.js&optimize=false&runs=200&gist=
// SPDX-License-Identifier: Unlicense
pragma solidity >=0.6.0 <0.9.0;
import "./Vm.sol";
import "./console.sol";
import "./console2.sol";
abstract contract Script {
address constant private VM_ADDRESS =
address(bytes20(uint160(uint256(keccak256('hevm cheat code')))));
Vm public constant vm = Vm(VM_ADDRESS);
}
PRIVATE_KEY=
RPC_URL=
ETHERSCAN_API_KEY=
CHAIN_ID=
[core]
repositoryformatversion = 0
filemode = false
bare = false
logallrefupdates = true
symlinks = false
ignorecase = true
ref: refs/heads/main
DIRC
c�RB3�c�RB3�����}�7i��$���IQ��B.�
README.txtc�RB%@�@c�RB%@�@���+j�2#�=��C��� p���contracts/1_Storage.solc�RB&�?�c�RB&�?�����I����*̛���>K���?�'contracts/2_Owner.solc�RB(;��c�RB(;��������l6 l��8Յ�%��+�Zcontracts/3_Ballot.solc�RB)���c�RB)���������!�)?�U���U-u�scripts/deploy_with_ethers.tsc�RB+']c�RB+']����W�5��XPwK��ئ��| scripts/deploy_with_web3.tsc�RB-b�c�RB-b������uۚCl�j��{�f� X�.Bscripts/ethers-lib.tsc�RB. @c�RB. @���O���:���V�)��?�(GY�scripts/web3-lib.tsc�RB/�S@c�RB/�S@����In��ˠ/�ds�9���5��[tests/Ballot_test.solc�RB1xˀc�RB1xˀ������Z+��>G����`}%wtests/storage.test.js�"ghHgn���X�.��ߥ�~
x�+)JMU041`040031Qrut�u�+�(axT��<��E��F�w����:��21��������b��A=)?dKJ�:KlY���a�W����������ׯO�bR�Y�'�k����?DEIj1P�'��=\v��zQ!g)��_��~T�9x
x�+)JMU042`040031Q0�.�/JLO�+��a�Zi���v��Glk[%
�>����(޿</��l�珳�nk���o�B;�ΣO�g�C��;%��䗀��?�cƙs�������3&iK�a�$m2�
x��R]k�0ݳ�EO64��<l��A�` ��h cY���ɒ']� ��}rlg1dz�u��БJmK8_�z6������٥�h<�>VhHm���au9[�<IZ'��o�����2�/yor~��"QMksب�[BO��aV@��Zy&�dAJ
�w��w���;Xwl~���jAsiM��^��r��$?_ܾ�j���dD����c�@\�.�[��� Zg[��$�E���P�4��=� F��J�X�ZC�AJ�d��aM4y|��)��T���Y�ʱ,����"�� ��dG�(\+c�ٮ������ݦl���l��l����z�=:��3�NF戮ivA��3`�@��`*|�|m����kA�6�������N����0E&�ٿ��v��^#g��xT���7������VgG�����`��|��� >�5�
x�+)JMU�0c040031QpJ���/�/I-.�+��a�ɛ����s)�M���0=w0���$�(1=U�6��a���(q�6��v�=Ͽ�I�U-N�#�
x�]�QK�0�}�8 a-s-�/l ��D4M�ְ4�-s��wo��������sHi}����(�Z�����3�r( m�
�Q���#�&������;J�L����� *�Dx/�T$4jG�m��}�7V(yq����s`���6�@I/��:����[�~rUg+��ŭ5z���U�|ӻ�oFC�����{��j�X�>�ny�gv��͆'�$1�^p��?_��8Hة�'I���i�K�Hq8���Id��e,���@L�C���?�?o)�~�~��q���t��C��uQ��X�H)���ʠ��y�3�s�f?�L�
x�m�Mk�0 �w���i��|������a0=���[qu$v��2�ߗ�I�Au�,������)$IrE�^}l�%e�,_9*�����j<����?�� ʉ����0��8| ��t*`
�L\ �Y7�*9��� �!�jY8R �ԐL /Μe]�O`�
lf�b#��(Ǫ���x�Eޣ�!���L+62�a7�
M8R��<�re�f�m���P�^SI#�vD�a ���{wNeLZ���j�*��5LЇǁE�̻��6�7�3��{��������>>�Ԅ�C, �� ^?8��;�����
x�+)JMU047c040031QHI-�ɯ�/�,ɈO-�H-*�+)f��F������k�Пb��BuK�b�P��d R�p��������:n�X�����b�nNfH��۳�sNf�;U��?�w�#='�B�Y0e��߳Z�=#l����o4$ݙ"[U.IE
x���A� E]s�ٛ(�1ƅ�1�S�@kp�x|� ���x粒�,`t�Hc4>E�|�=y�{4}��6�C7t.:��)Z�>7��K\���7<Q;�*�Ks=���i-lu�Z���?��NY2X#h\��볐��i)�>��Ck
x�}T�n1�y�b��T�M��� P�P�"*�oȻ��Xx�0�&�P�����MA��^<�s�g�k�ËWgg� ������Fh�^K4^�
�>�ޜ�̖I�!Q���J*��w�e�:[��e�&[^$��7�<Lւ�Z�Ea ;cƏ �'' ��{��F��3H�_����s(��T�3-��I���?I��������V�����1�2x(-���ϠmU)SEck����ϣ��{�`���[ �e~�R[o7�@�P�yK����Q��c6g�-Nt]�_#���AUS���)��FN�� 3u�ش��4FV4^Y�V&:#�A� ����P���Y��+�!ȅ�ф@�y��8Ƿ5sjƠp��2ԁJ��tQmu�ȥ�7�eFf_[£l�>JTz4 ��V��(Bn�1">ֶlLض(�Ғ�a���(����S���� �qE�,���rM��:n���J��2�w/��1�Țꐻ6�]�9�� X�ڳNar9�~ w'�1}����k�0 a�&`hw�m��K�;)LDx�=5��������l�N�����$���X V#�EPsz��rq�Тa�Y�p�Їc�C)�տ�8[����I�.���ΰ$�aP� f����v�ƣ�>j�w�4�VE?�#��t-0�'�ڛ�d�}C���� �ZGԞ�U���� � [�R�Y�d�D��=�� B��
x��RMO�0 �_a��T�&��B��aH��Ĵ]Rw0M��$��ց��!Rl?��b'�l��'�,tQ2�J�7��$��������R3$��$��d�4��x�M�k� [�G�v$Y�N��=&�:?Ɣ�T�aEQ2�g� J��1�up$R�L4��)�i�Yc+��D�6��� �2~`xGg%+��a��ñn��������e��ʮE��j~T#%�lEYe 1 �LOw���#�Ӷ���czG�4�H�i���Y��{���8ou���nnM���)ڕ�1l������:���=����Ȉ_��wd{/$.���zx��֏ۚ����;�+�/�,2�
x�mTMo�0 �ٿ��v��nQ�0��m�i ���0 ��8�l)��A��>J��;D@[�{|�He�������Qo�����KXiUC���0����MI�1����d� �V� 6V(i�����v���a$����w�TjvMP��0�8� j�ao��lG�=��=��CX�g���Z����ԥ�J� c�&�J���ɒ6�B�D0�U#-4�
�p�>>���s�S�l�t %��D-��4�FK���j{�L;�? ������]���dѴ)tBg>�)ϼ���_����* ]�Jn���A�����8�����}3�E��TEq��h٩�H�q?U�.;�$�{e�dk��v&�bc��
�7�_�`����$i�\�a�Q����Z�������L��IO0Ka�0���`����v͹/3���t2p�dtI�K3~6J.���-2�ϧ�����P�[4��5α��p�
(�$� ,����p�^��ɔ��H���Ӊy�b��{c�2�M�х�xG�`��1f�(��������K�uD��p�c:�%��lg)W�*{&�WFg��&�"�����wZX� ���Q�4�c�3v�:��*����m����?��<+��\v��:wk���)>" V��+h���ζ
x�]�aK�0���_�2��̵�A�����o"���5.MFrE����u�އ���}������UY�1Q�gh�P�H5أ���pC����� �w�f�2n �l��g�5UP�P$}��"�U;B��4�۽��*� #�w�#���N�� *�x��ι��؏��be��X�F�0[b���o���h�u�a��,.�:�G ֍�;�㉃�����:IL����G8��iQ�'�ɸi�d*�F�c��1��ø�C�DF��Y��K�Y:Ξ���-�#�
�ه�k�9��gH1ޜ>Ί������^T�R/}F���<��`��
x�uTMs�F ͙���0㺽x2�qmy���x$u���$!-��.�����R�]7͈ ������Ƶp��/����O��p������-|���ks{y���5�=�T2&�¨:
0z h�P������k��윇��`G>D�4 Tĉ�`qz �yT{�(j��K�"���q�V�GP���A �'
����~7|
˧�8������O�:g�"�z��E� �EU�5PKЫ.���p����c)��Y����G4܎s�'s������B�i�HWǖ;�X�È%6��e�PύXE�y .eUZ4nj�sF�^�:��q�zn��y����2�᧜��濉VB���T��j���nDD<M���GZ�h�IS�A�_L'�y�Rֱ��=o�\�j�d��+��-U�+���<�.�>�pi��8���X�&�y7�`�, n`O�'rBf��� �O�6�>I����ys`gfQ��w☻y��W����9RU�R>/�(s̖g3(�E�=,�}r�Vﯴ"H�b^j��B�|��H �� h�{`_�Y����|��%�^'[���C�~q�_�D~e����n�|�7������8jQyi���d�aҷ�/�\��L�M��4���;�(ym$�H>e-S)ʫt�\��J�dݴ8���is��5e} �U- L�{�b�k��"� �b&/:i�{�ؠ��!,惦��k����0��'[�$��ケ�稡�f"X+�M��%�N6��\zTJ$b��o����~ ����;�&^
x�]SMo�0 �ٿ����@ƺÀ���u(�K ����m�QgK��4 ���Q�G��`D���#�7:�O�_.?ȶ����m�X8Bet q��(����~R��=�-��Z9����;4���:#U}��V�(��UH�i(�<���_���Mm����TNc�mᴉ!�cv��ն��p^Q�r����2��0ς�;m���!�5
w�����fJ���_��׀v�
H溗л�R�p�8#�m *pO��� ����|1r�������}qݐht��{\>����4g��#�.�����ET�pq��vYl@Z�I�ADz'�
�v�A'�b-NV\A�ؿ�/Cnͬ��ҧx�U9�X�G��w�xB�a;׹�;K&1m6�d���V���r�AU{�L��JYUd���1l4���%:�R��W�g�R�;� ��]�4I\��ߨ�&/ ����M�8'�NF�;�?s�d��������H�އ���D���xYY�9�*Q�6Rt�f�ɼT�
�����p�s՞�6~\g������h��\.&�D��{G�.I��߳"��R?�����D��!���m{ٜO����}{r_aG�n= �8ـ ��T�$��F1�'}s���V��[��S
x��Xm��6 ���
�_��w���0�i�m-��m�ZtA��J�Ͷ<I��(��G�Y��\;`�r�DR$��}��o�˯=���
޾y���k��Z���֊mO�՛חO��d���H^���������-�o��r2�z�&��UL��'eɕY(�-\WMI+�+�+V�<�R)9�:0��;
Z�Q�ר|5�y�ɕ3'�G*���{���iY��@�n���q�W`xL��U[E �t�)��p^�%h]�<�^�� ��譠��\�J�B�X�Uj�v~��S?� <ʜU��x�Z�:? ��4:�$�7n+
m\o��-䤆�UL[%�w�YŁ@N�"����6�5<m�)*/ �B��$���i/�H�0oOÆ�D �A�j�=�{J0�O']�ʇ)�\(����&�5-7?M� ����p����qv<
M�)Y��&,K+P��A�ͼ��%�ܫh�B:i�囵���x Ky�8��@r � ��KX 2�s�9�f~�<�i�[h� $�&WRk�g;�+���r����7�[ъ�.�4��eVP�]&)P,{ ���Hp��ZZ�#�~�\ �2�;�%�y��Y
��Ç��oS����,ˎ�)�&�X��h�pAD7T� �o��X�.BZeִr?Ck��P ��J`] �3��=���DJ?��i�7l}q"�s�),���|8��>!�+vKajP��0��c2=qU{lz���D:\+;�\�ei{�4�����v_R�!���o�ֹCo~��㺞���M�+dLп[&�,�>�V���i������� �Av�W��:z�=�v�g���;8��k�"��>$5���d~Rh]b)� �/ݕ�����;6[����o ��,w�- �V1�ߞ#�ګXb'";
I`@w}��{v�e����� �#��0��� ��r{�v���Ho3�S��H�u���V]���|ز��>�X+2m ���(v�� ����xQp�j��w��1�`lz��Z��c��=��r��G��|yQ/�!�?H�4�e�y#��z'��0_ � �A���O�0 ��?}��I=�Em�.<\��ݜ���k��>�0ș��:q�y�G7�)��7W�w�tW0�@h3V�e[���d&�<��\��)�n�����vv" Sj�bRӛ�iP�n��h������.��P����G�3O���f2lAߝm?�SP�Ug�,���Ou��� ���&������,�M��V�
�_�静���)_7�X�K�+ ǰ�T���E^�i5ߴ�V����2D���ot��-�t�D&p=��#�U���?n}�;9�pp`v&F����n��c?W�ߝV�}�G=�Lʍ���hRSrs:%��#���С�~F��<�p@�T�x�Y h��#�� v�~�&��Z���G~�bэn��������{������y�X�ϡ��ڍN�$v$F�$'���;� ��[�
935cbd68330e3673ddbe2709f7421d552807de8b
name: test
on:
push:
branches:
- main
pull_request:
workflow_dispatch:
env:
FOUNDRY_PROFILE: ci
jobs:
check:
strategy:
fail-fast: true
name: Foundry project
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly
- name: Run Forge build
run: |
forge --version
forge build --sizes
id: build
- name: Run Forge unit tests
run: |
forge test -vvv --no-match-contract Integration
id: unit-test
- name: Run Forge integration tests
env:
FORK_URL: ${{ secrets.FORK_URL }}
run: |
forge test -vvv --match-contract Integration --fork-url $FORK_URL
id: integration-test
cache/
out/
.env
broadcast/
[submodule "lib/forge-std"]
path = lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "lib/openzeppelin-contracts"]
path = lib/openzeppelin-contracts
url = https://github.com/Openzeppelin/openzeppelin-contracts
[submodule "lib/solmate"]
path = lib/solmate
url = https://github.com/Rari-Capital/solmate

Franchiser

Franchiser allows holders of checkpoint voting tokens to selectively sub-delegate voting power while retaining full custody over their funds, as described in the design document.

Running Locally

  • Ensure that foundry is installed on your machine
  • forge build
  • forge test --no-match-contract Integration
  • forge test --match-contract Integration --fork-url $FORK_URL

Deploying

  • Create and populate a .env file
  • source .env
  • forge script script/Deploy.s.sol:Deploy --broadcast --private-key $PRIVATE_KEY --rpc-url $RPC_URL [--etherscan-api-key $ETHERSCAN_API_KEY --verify --chain-id $CHAIN_ID]

Deployed Addresses

Network FranchiserFactory FranchiserLens
Mainnet 0xf754A7E347F81cFdc70AF9FbCCe9Df3D826360FA 0x3E718C61a2849FBb0181ebA83de4Ee8363014106
Görli 0xf754A7E347F81cFdc70AF9FbCCe9Df3D826360FA 0x3E718C61a2849FBb0181ebA83de4Ee8363014106
Sepolia 0xf754A7E347F81cFdc70AF9FbCCe9Df3D826360FA 0x3E718C61a2849FBb0181ebA83de4Ee8363014106

Note that the UNI is not yet deployed at 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984 on Sepolia, so this deploy is non-functional for the time being.

name: test
on:
push:
branches:
- main
pull_request:
workflow_dispatch:
env:
FOUNDRY_PROFILE: ci
jobs:
check:
strategy:
fail-fast: true
name: Foundry project
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly
- name: Run Forge build
run: |
forge --version
forge build --sizes
id: build
- name: Run Forge unit tests
run: |
forge test -vvv --no-match-contract Integration
id: unit-test
- name: Run Forge integration tests
env:
FORK_URL: ${{ secrets.FORK_URL }}
run: |
forge test -vvv --match-contract Integration --fork-url $FORK_URL
id: integration-test
{
"transactions": [
{
"hash": "0x1a4f063278d24aacf05b39df3726fd1c1b4a959779ba667e7dbf986874fcff51",
"type": "CREATE",
"contractAddress": "0xf754a7e347f81cfdc70af9fbcce9df3d826360fa",
"tx": {
"type": "0x02",
"from": "0x9f67f9af54513a1790ed159dd65e8be73fb531fd",
"gas": "0x38fb79",
"value": "0x0",
"data": "0x60c0346100e6576001600160401b0390601f61345a38819003918201601f1916830191848311848410176100d0578084926020946040528339810103126100e657516001600160a01b038116908181036100e6576080526040519161240f90818401908111848210176100d057602092849261104b843981520301906000f080156100c45760a052604051610f5f90816100ec823960805181818161058a0152818161094d0152610dae015260a0518181816106bd0152818161079f01526109ca0152f35b6040513d6000823e3d90fd5b634e487b7160e01b600052604160045260246000fd5b600080fdfe60806040526004361015610013575b600080fd5b60003560e01c806319f86ffd146100db5780635b145a75146100d25780637b1837de146100c95780638de804a7146100c0578063a1343f37146100b7578063b0340123146100ae578063b6a24b0e146100a5578063c61bdcd21461009c578063e95c4d36146100935763f5f2df861461008b57600080fd5b61000e61071c565b5061000e6106e1565b5061000e610671565b5061000e6105ae565b5061000e61053e565b5061000e6104cd565b5061000e610353565b5061000e61028a565b5061000e6101f2565b3461000e576100f26100ec36610112565b90610cbe565b005b73ffffffffffffffffffffffffffffffffffffffff81160361000e57565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc604091011261000e57600435610148816100f4565b90602435610155816100f4565b90565b9181601f8401121561000e5782359167ffffffffffffffff831161000e576020808501948460051b01011161000e57565b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc82011261000e5767ffffffffffffffff9160043583811161000e57826101d491600401610158565b9390939260243591821161000e576101ee91600401610158565b9091565b503461000e5761020136610189565b908183036102515760005b83811061021557005b8061024b6102266001938789610c7a565b35610230816100f4565b61023b838787610c7a565b3590610246826100f4565b610cbe565b0161020c565b506040517ffa5dbe0800000000000000000000000000000000000000000000000000000000815260048101929092526024820152604490fd5b503461000e5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5760206102d46004356102cb816100f4565b60243590610915565b73ffffffffffffffffffffffffffffffffffffffff60405191168152f35b6064359060ff8216820361000e57565b6020908160408183019282815285518094520193019160005b828110610329575050505090565b835173ffffffffffffffffffffffffffffffffffffffff168552938101939281019260010161031b565b503461000e5760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5767ffffffffffffffff60043581811161000e576103a4903690600401610158565b9160243590811161000e576103bd903690600401610158565b6103c56102f2565b818503610492576000805b86811061046d5750906103ee9160a435916084359160443590610d61565b6103f784610bfb565b9360005b81811061041457604051806104108882610302565b0390f35b8061046761044261043061042b600195878b610c7a565b610c92565b61043b84888a610c7a565b3590610915565b61044c838a610c9c565b9073ffffffffffffffffffffffffffffffffffffffff169052565b016103fb565b9061048761048d91610480848789610c7a565b3590610f46565b91610f0c565b6103d0565b506040517ffa5dbe08000000000000000000000000000000000000000000000000000000008152600481018590526024810191909152604490fd5b503461000e5760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5760206102d460043561050e816100f4565b6024359061052e61051d6102f2565b60a435906084359060443586610d61565b610915565b600091031261000e57565b503461000e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b503461000e576105bd36610189565b8083949303610637576105cf84610bfb565b9360005b8181106105e857604051806104108882610302565b8061060e6105f96001938589610c7a565b35610603816100f4565b61043b838789610c7a565b73ffffffffffffffffffffffffffffffffffffffff61062d838a610c9c565b91169052016105d3565b6040517ffa5dbe08000000000000000000000000000000000000000000000000000000008152600481018590526024810191909152604490fd5b503461000e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b503461000e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57602060405160088152f35b503461000e5760206102d461073036610112565b905b603761075573ffffffffffffffffffffffffffffffffffffffff9360559361089b565b604051907f3d602d80600a3d3981f3363d3d373d3d3d363d7300000000000000000000000082527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000060601b1660148301527f5af43d82803e903d91602b57fd5bf3ff0000000000000000000000000000000060288301523060601b6038830152604c820152818120606c82015201201690565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b67ffffffffffffffff811161084d57604052565b610855610809565b604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761084d57604052565b6040519060208201927fffffffffffffffffffffffffffffffffffffffff000000000000000000000000809260601b16845260601b166034820152602881526060810181811067ffffffffffffffff8211176108fb575b60405251902090565b610903610809565b6108f2565b506040513d6000823e3d90fd5b9190916109228133610732565b9273ffffffffffffffffffffffffffffffffffffffff80851690813b15610974575b610972935033907f000000000000000000000000000000000000000000000000000000000000000016610b1d565b565b8061097f853361089b565b60376040517f3d602d80600a3d3981f3363d3d373d3d3d363d7300000000000000000000000081527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000060601b1660148201527f5af43d82803e903d91602b57fd5bf3000000000000000000000000000000000060288201526000f51615610abf57813b1561000e576040517fc861c25000000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff949094166024850152600860448501526109729360008160648183875af18015610ab2575b610a99575b50610944565b80610aa6610aac92610839565b80610533565b38610a93565b610aba610908565b610a8e565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f455243313136373a2063726561746532206661696c65640000000000000000006044820152fd5b9060006064926020958295604051947f23b872dd0000000000000000000000000000000000000000000000000000000086526004860152602485015260448401525af13d15601f3d1160016000511416171615610b7657565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152fd5b60209067ffffffffffffffff8111610bee575b60051b0190565b610bf6610809565b610be7565b90610c0582610bd4565b610c12604051918261085a565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0610c408294610bd4565b0190602036910137565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b9190811015610c8a5760051b0190565b610bf6610c4a565b35610155816100f4565b6020918151811015610cb1575b60051b010190565b610cb9610c4a565b610ca9565b90610ce19173ffffffffffffffffffffffffffffffffffffffff92839133610732565b1691823b610cee57505050565b823b1561000e5760246000928360405195869485937fca4305190000000000000000000000000000000000000000000000000000000085521660048401525af18015610d45575b610d3c5750565b61097290610839565b610d4d610908565b610d35565b9081602091031261000e575190565b6040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523360048201523060248201529394909373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169291908590602081604481885afa908115610ecf575b600091610ea1575b5010610dfb575b505050505050565b823b1561000e576040517fd505accf0000000000000000000000000000000000000000000000000000000081523360048201523060248201526044810195909552606485019190915260ff92909216608484015260a483019390935260c482015290600090829060e490829084905af18015610e94575b610e81575b8080808080610df3565b80610aa6610e8e92610839565b38610e77565b610e9c610908565b610e72565b610ec2915060203d8111610ec8575b610eba818361085a565b810190610d52565b38610dec565b503d610eb0565b610ed7610908565b610de4565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6001907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114610f3a570190565b610f42610edc565b0190565b81198111610f3a57019056fea164736f6c634300080f000a60c03461010f57601f61240f38819003918201601f19168301916001600160401b038311848410176101145780849260209460405283398101031261010f57516001600160a01b038116810361010f57608052600080546001600160a01b031990811682556040519160019190807f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d768186a33060a05260025416176002556122e4908161012b823960805181818161050b01528181610b2d01528181610dea015281816110190152818161139101526117c5015260a051818181610465015281816105eb01528181610a0201528181610c41015281816117260152818161187001528181611d270152611f510152f35b600080fd5b634e487b7160e01b600052604160045260246000fdfe60806040526004361015610013575b600080fd5b60003560e01c806301ce3e9d1461016757806313af40351461015e5780631ba8d97c146101555780631e31d0531461014c5780635c2927781461014357806368afe2b71461013a57806378b440ac146101315780638da5cb5b14610128578063b03401231461011f578063c5546c3f14610116578063c61bdcd21461010d578063c861c25014610104578063ca430519146100fb578063ce9b7930146100f2578063d1ddf4ef146100e9578063e08eae5f146100e05763f2a41374146100d857600080fd5b61000e611239565b5061000e6111fe565b5061000e611111565b5061000e6110d7565b5061000e610f79565b5061000e610c65565b5061000e610bf5565b5061000e610b51565b5061000e610ae1565b5061000e610a8e565b5061000e61095b565b5061000e6107f7565b5061000e61039a565b5061000e610347565b5061000e610307565b5061000e610244565b5061000e61018e565b73ffffffffffffffffffffffffffffffffffffffff81160361000e57565b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e576004356101ca81610170565b73ffffffffffffffffffffffffffffffffffffffff600254168033036101f5576101f382611cbd565b005b6040517f2488453800000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff919091166024820152604490fd5b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5760007fffffffffffffffffffffffff00000000000000000000000000000000000000006004356102a381610170565b82549073ffffffffffffffffffffffffffffffffffffffff906102c982841633146114d5565b169182911617825560405190337f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d768484a3f35b600091031261000e57565b503461000e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57602060025460a01c604051908152f35b503461000e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57602073ffffffffffffffffffffffffffffffffffffffff60025416604051908152f35b503461000e576040807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e576004356103d781610170565b6002549173ffffffffffffffffffffffffffffffffffffffff80841680330361072757506104d3836055603773ffffffffffffffffffffffffffffffffffffffff92604051907f3d602d80600a3d3981f3363d3d373d3d3d363d7300000000000000000000000082527fffffffffffffffffffffffffffffffffffffffff00000000000000000000000090817f000000000000000000000000000000000000000000000000000000000000000060601b1660148401527f5af43d82803e903d91602b57fd5bf3ff0000000000000000000000000000000060288401523060601b603884015260601b16604c820152818120606c82015201201690565b92818116946104f66104f2876000526004602052604060002054151590565b1590565b61055a575b61055685856105316024358784167f00000000000000000000000000000000000000000000000000000000000000008916611ba1565b5173ffffffffffffffffffffffffffffffffffffffff90911681529081906020820190565b0390f35b6003549060a01c8091146106e7575061057a61057586611b0a565b611987565b838216803b156105bc575b505061053190610556947f3aadde169d4a8fa84eba250da3838e12911f6dffadeb18f092363cfbfa8a12c160008551a290386104fb565b9190946106107fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008760601b16837f0000000000000000000000000000000000000000000000000000000000000000166119bd565b5061063461062060025460a01c90565b60011c6b7fffffffffffffffffffffff1690565b92803b1561000e5784517ff2a4137400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90971660048801526bffffffffffffffffffffffff9093166024870152610556956105319360009082908183816044810103925af180156106da575b6106c1575b5094819250610585565b806106ce6106d49261156a565b806102fc565b386106b7565b6106e26115cc565b6106b2565b83517f1d7d1c310000000000000000000000000000000000000000000000000000000081526bffffffffffffffffffffffff919091166004820152602490fd5b82517f2488453800000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff919091166024820152604490fd5b9181601f8401121561000e5782359167ffffffffffffffff831161000e576020808501948460051b01011161000e57565b6020908160408183019282815285518094520193019160005b8281106107cd575050505090565b835173ffffffffffffffffffffffffffffffffffffffff16855293810193928101926001016107bf565b503461000e5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5767ffffffffffffffff60043581811161000e57610848903690600401610775565b9160243590811161000e57610861903690600401610775565b8084036109245761087184611c52565b9361087f604051958661158b565b8085527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06108ac82611c52565b0136602087013760005b8181106108cb576040518061055688826107a6565b8061091e6108f96108e76108e2600195878b611c79565b611c91565b6108f284888a611c79565b3590611698565b610903838a611c9b565b9073ffffffffffffffffffffffffffffffffffffffff169052565b016108b6565b83604491604051917ffa5dbe0800000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e576020610a7060043561099c81610170565b6055603773ffffffffffffffffffffffffffffffffffffffff92604051907f3d602d80600a3d3981f3363d3d373d3d3d363d7300000000000000000000000082527fffffffffffffffffffffffffffffffffffffffff00000000000000000000000090817f000000000000000000000000000000000000000000000000000000000000000060601b1660148401527f5af43d82803e903d91602b57fd5bf3ff0000000000000000000000000000000060288401523060601b603884015260601b16604c820152818120606c82015201201690565b73ffffffffffffffffffffffffffffffffffffffff60405191168152f35b503461000e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b503461000e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5760043567ffffffffffffffff811161000e57610ba1903690600401610775565b9073ffffffffffffffffffffffffffffffffffffffff600254168033036101f5575060005b828110610bcf57005b80610bef610be06001938686611c79565b35610bea81610170565b611cbd565b01610bc6565b503461000e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b503461000e5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57600435610ca181610170565b602435610cad81610170565b604435906bffffffffffffffffffffffff8216820361000e5773ffffffffffffffffffffffffffffffffffffffff90818116938415610f4f57610d21610d0860025473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1690565b610f2557600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001633179055828116610eda575b50610d9d8173ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff00000000000000000000000000000000000000006002541617600255565b610de78373ffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffff00000000000000000000000000000000000000006002549260a01b16911617600255565b817f000000000000000000000000000000000000000000000000000000000000000016803b1561000e576040517f5c19a95c00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9290921660048301526000908290602490829084905af18015610ecd575b610eba575b50610e786115d9565b6040516bffffffffffffffffffffffff939093168352169033907f8e063a11026e8eeb829fca77696415cbd6da2c4d6e73aaf6fc3e5c902c882a4d90602090a4005b806106ce610ec79261156a565b38610e6f565b610ed56115cc565b610e6a565b610f1f9073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff00000000000000000000000000000000000000006001541617600155565b38610d57565b60046040517f0dc149f0000000000000000000000000000000000000000000000000000000008152fd5b60046040517fcfb6286a000000000000000000000000000000000000000000000000000000008152fd5b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57600435610fb581610170565b73ffffffffffffffffffffffffffffffffffffffff90610fda826000541633146114d5565b600354805b61109557506040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526101f3929091907f000000000000000000000000000000000000000000000000000000000000000016602083602481845afa928315611088575b600093611058575b50611ba1565b61107a91935060203d8111611081575b611072818361158b565b810190612285565b9138611052565b503d611068565b6110906115cc565b61104a565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0191826110d06110cb610d08610d0884612294565b611ee7565b9092610fdf565b503461000e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e576020610a706115d9565b503461000e576000807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126111fb5760405180916003549081835260208093018092600383527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b90835b8181106111e7575050508461119391038561158b565b60405193838594850191818652518092526040850193925b8281106111ba57505050500390f35b835173ffffffffffffffffffffffffffffffffffffffff16855286955093810193928101926001016111ab565b82548452928601926001928301920161117d565b80fd5b503461000e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57602060405160028152f35b503461000e576040807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e576004359061127782610170565b6024356bffffffffffffffffffffffff8116810361000e5773ffffffffffffffffffffffffffffffffffffffff908184169182156114ac576112d1610d0860025473ffffffffffffffffffffffffffffffffffffffff1690565b61148357600080547fffffffffffffffffffffffff000000000000000000000000000000000000000016331790556113448573ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff00000000000000000000000000000000000000006002541617600255565b61138e8273ffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffff00000000000000000000000000000000000000006002549260a01b16911617600255565b807f00000000000000000000000000000000000000000000000000000000000000001694853b1561000e5784517f5c19a95c00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff91909116600482015260009586908290602490829084905af18015611476575b611463575b506114216115d9565b84516bffffffffffffffffffffffff939093168352169033907f8e063a11026e8eeb829fca77696415cbd6da2c4d6e73aaf6fc3e5c902c882a4d90602090a451f35b806106ce6114709261156a565b38611418565b61147e6115cc565b611413565b600484517f0dc149f0000000000000000000000000000000000000000000000000000000008152fd5b600484517fcfb6286a000000000000000000000000000000000000000000000000000000008152fd5b156114dc57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152fd5b507f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b67ffffffffffffffff811161157e57604052565b61158661153a565b604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761157e57604052565b506040513d6000823e3d90fd5b60015473ffffffffffffffffffffffffffffffffffffffff9081169081156115ff575090565b905060005416806116105750600090565b6020600491604051928380927f1e31d0530000000000000000000000000000000000000000000000000000000082525afa90811561168b575b600091611654575090565b906020823d8211611683575b8161166d6020938361158b565b810103126111fb57505161168081610170565b90565b3d9150611660565b6116936115cc565b611649565b6002549173ffffffffffffffffffffffffffffffffffffffff8084168033036101f55750611794836055603773ffffffffffffffffffffffffffffffffffffffff92604051907f3d602d80600a3d3981f3363d3d373d3d3d363d7300000000000000000000000082527fffffffffffffffffffffffffffffffffffffffff00000000000000000000000090817f000000000000000000000000000000000000000000000000000000000000000060601b1660148401527f5af43d82803e903d91602b57fd5bf3ff0000000000000000000000000000000060288401523060601b603884015260601b16604c820152818120606c82015201201690565b93818416906117b36104f2836000526004602052604060002054151590565b6117ea575b50506116809250808416907f000000000000000000000000000000000000000000000000000000000000000016611ba1565b6003549060a01c809114611946575061180561057582611b0a565b848216803b15611844575b5061168093507f3aadde169d4a8fa84eba250da3838e12911f6dffadeb18f092363cfbfa8a12c16000604051a238806117b8565b6118957fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008660601b16847f0000000000000000000000000000000000000000000000000000000000000000166119bd565b506118a561062060025460a01c90565b94813b1561000e576040517ff2a4137400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201526bffffffffffffffffffffffff9095166024860152611680949060009082908183816044810103925af18015611939575b1561181057806106ce6119339261156a565b38611810565b6119416115cc565b611921565b6040517f1d7d1c310000000000000000000000000000000000000000000000000000000081526bffffffffffffffffffffffff919091166004820152602490fd5b1561198e57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b603790604051907f3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000825260601b60148201527f5af43d82803e903d91602b57fd5bf3000000000000000000000000000000000060288201526000f59073ffffffffffffffffffffffffffffffffffffffff821615611a3757565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f455243313136373a2063726561746532206661696c65640000000000000000006044820152fd5b507f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600354811015611afd575b60036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0190600090565b611b05611a95565b611ad0565b80600052600460205260406000205415600014611b9b578060035468010000000000000000811015611b8e575b6001810180600355811015611b81575b7fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0155600354906000526004602052604060002055600190565b611b89611a95565b611b47565b611b9661153a565b611b37565b50600090565b60009182604492602095604051937fa9059cbb000000000000000000000000000000000000000000000000000000008552600485015260248401525af13d15601f3d1160016000511416171615611bf457565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152fd5b60209067ffffffffffffffff8111611c6c575b60051b0190565b611c7461153a565b611c65565b9190811015611c895760051b0190565b611c74611a95565b3561168081610170565b6020918151811015611cb0575b60051b010190565b611cb8611a95565b611ca8565b611d95816055603773ffffffffffffffffffffffffffffffffffffffff92604051907f3d602d80600a3d3981f3363d3d373d3d3d363d7300000000000000000000000082527fffffffffffffffffffffffffffffffffffffffff00000000000000000000000090817f000000000000000000000000000000000000000000000000000000000000000060601b1660148401527f5af43d82803e903d91602b57fd5bf3ff0000000000000000000000000000000060288401523060601b603884015260601b16604c820152818120606c82015201201690565b9073ffffffffffffffffffffffffffffffffffffffff80911691611dc6836000526004602052604060002054151590565b15611e7057611dd76105758461216c565b16803b1561000e576040517fca430519000000000000000000000000000000000000000000000000000000008152306004820152906000908290602490829084905af18015611e63575b611e50575b507f5b52cabd65626f7970ccfee89ce9d84764a149a8289e4d4356d32819e7b010876000604051a2565b806106ce611e5d9261156a565b38611e26565b611e6b6115cc565b611e21565b169050803b611e7d575b50565b803b1561000e576040517fca430519000000000000000000000000000000000000000000000000000000008152306004820152906000908290602490829084905af18015611eda575b15611e7a57806106ce611ed89261156a565b565b611ee26115cc565b611ec6565b611fbf816055603773ffffffffffffffffffffffffffffffffffffffff92604051907f3d602d80600a3d3981f3363d3d373d3d3d363d7300000000000000000000000082527fffffffffffffffffffffffffffffffffffffffff00000000000000000000000090817f000000000000000000000000000000000000000000000000000000000000000060601b1660148401527f5af43d82803e903d91602b57fd5bf3ff0000000000000000000000000000000060288401523060601b603884015260601b16604c820152818120606c82015201201690565b9073ffffffffffffffffffffffffffffffffffffffff80911691611fe56105758461216c565b16803b1561000e57600080916024604051809481937fca4305190000000000000000000000000000000000000000000000000000000083523060048401525af18015612063575b61205a57507f5b52cabd65626f7970ccfee89ce9d84764a149a8289e4d4356d32819e7b010876000604051a2565b611e5d9061156a565b61206b6115cc565b61202c565b6001811061209d577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600354801561213d5760007fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83019280841015612130575b600383520155600355565b612138611a95565b612125565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b600081815260046020526040812054909190801561228057600181106122535790817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6121e393016121bf600354612070565b908082036121e9575b5050506121d36120cc565b6000526004602052604060002090565b55600190565b6121d3612211916122096121ff61224a95611ac5565b90549060031b1c90565b928391611ac5565b90919082549060031b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811b9283911b16911916179055565b553880806121c8565b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b505090565b9081602091031261000e575190565b6003548110156122ca575b60036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b015490565b6122d2611a95565b61229f56fea164736f6c634300080f000a0000000000000000000000001f9840a85d5af5bf1d1762f925bdaddc4201f984",
"nonce": "0x0",
"accessList": []
}
},
{
"hash": "0x186e37e88742310ec2389199ab6ae9fceed21e81ebdbb8e95e2a40dc1d0bf71c",
"type": "CREATE",
"contractAddress": "0x3e718c61a2849fbb0181eba83de4ee8363014106",
"tx": {
"type": "0x02",
"from": "0x9f67f9af54513a1790ed159dd65e8be73fb531fd",
"gas": "0x17e378",
"value": "0x0",
"data": "0x60c060409080825234620001f15781816200170c8038038091620000248285620001f6565b833981010312620001f15780516001600160a01b0391908281168103620001f15760208092015190838216808303620001f157600492849260805260a0528551928380926374ae269b60e11b82525afa9081156200017257600091620001cf575b506001600160601b03906200009f90821660081462000251565b6004828460a0511686519283809263630dee6960e11b82525afa8015620001c45783916000916200017d575b50600486518096819363e08eae5f60e01b8352165afa8015620001725762000103936002936000926200013e575b5050161462000251565b5161149c908162000270823960805181818161019f0152611403015260a05181818160b901528181610235015281816105ef015261095f0152f35b620001629250803d106200016a575b620001598183620001f6565b81019062000230565b3880620000f9565b503d6200014d565b84513d6000823e3d90fd5b909181813d8311620001bc575b620001968183620001f6565b81010312620001b85751908482168203620001b55750829038620000cb565b80fd5b5080fd5b503d6200018a565b85513d6000823e3d90fd5b620001ea9150823d84116200016a57620001598183620001f6565b3862000085565b600080fd5b601f909101601f19168101906001600160401b038211908210176200021a57604052565b634e487b7160e01b600052604160045260246000fd5b90816020910312620001f157516001600160601b0381168103620001f15790565b156200025957565b634e487b7160e01b600052600160045260246000fdfe6040608081526004908136101561001557600080fd5b600091823560e01c90816308ecd5c0146106765781631cad183c1461055457816371456623146104bb57816397f2c53d146101c3578163b034012314610154578163df5fc105146100e1575063f3dc336b1461007057600080fd5b346100dd57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100dd576020905173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b5080fd5b9050346101505760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261015057359173ffffffffffffffffffffffffffffffffffffffff8316830361014d575061013e61014992610cc2565b9051918291826107bd565b0390f35b80fd5b8280fd5b5050346100dd57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100dd576020905173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b90503461015057602092837ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014d5773ffffffffffffffffffffffffffffffffffffffff938235858116810361015057845190836102248361089b565b83855b60a081106104a757505087807f000000000000000000000000000000000000000000000000000000000000000016925b16828114610435578751917fce9b793000000000000000000000000000000000000000000000000000000000835285838981855afa92831561042b5787936103f0575b5088517f1e31d05300000000000000000000000000000000000000000000000000000000815286818a81865afa9081156103e657918b8a94928c8a958c9261039c575b50908261031494939251986102f18a610834565b1688521684870152828c870152600181019561030d828a610bb0565b5287610bb0565b508951928380927f8da5cb5b0000000000000000000000000000000000000000000000000000000082525afa80156103925789908790610358575b81925016610257565b50508481813d831161038b575b61036f81836108b7565b8101031261038757886103828192610917565b61034f565b8580fd5b503d610365565b88513d88823e3d90fd5b9496505050509181813d83116103df575b6103b781836108b7565b810103126103db579161031487928c8c816103d28e98610917565b929394506102dd565b8780fd5b503d6103ad565b8a513d8a823e3d90fd5b9092508581813d8311610424575b61040881836108b7565b810103126104205761041990610917565b913861029a565b8680fd5b503d6103fe565b89513d89823e3d90fd5b8786858461044281610c08565b925b8181106104585784518061014986826107bd565b806104a061046860019386610bb0565b517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83860301906104998289610c82565b5286610c82565b5001610444565b6104af6108f8565b81860152018490610227565b9050346101505760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261015057359173ffffffffffffffffffffffffffffffffffffffff8316830361014d575061055261051a60609361093d565b915180926040908173ffffffffffffffffffffffffffffffffffffffff91828151168552826020820151166020860152015116910152565bf35b90503461015057817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101505780359073ffffffffffffffffffffffffffffffffffffffff91828116809103610672576024359280841680940361038757602092604491865195869485937ff5f2df8600000000000000000000000000000000000000000000000000000000855284015260248301527f0000000000000000000000000000000000000000000000000000000000000000165afa908115610666579061062d916101499491610638575b50610fe8565b9051918291826106d3565b610659915060203d811161065f575b61065181836108b7565b810190610c96565b38610627565b503d610647565b505051903d90823e3d90fd5b8480fd5b9050346101505760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261015057359173ffffffffffffffffffffffffffffffffffffffff8316830361014d575061062d61014992610fe8565b602080820190808352835180925260409283810182858560051b840101960194600080935b86851061070a57505050505050505090565b9091929394887fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08383999a9b0301865289519082808351928381520192019085905b80821061076d5750505090806001929a0195019501939695949291906106f8565b9193608060019294865173ffffffffffffffffffffffffffffffffffffffff808251168352808583015116858401528b820151168b8301526060809101519082015201940192018993929161074c565b6020908160408183019282815285518094520193019160005b8281106107e4575050505090565b909192938260608261082860019489516040908173ffffffffffffffffffffffffffffffffffffffff91828151168552826020820151166020860152015116910152565b019501939291016107d6565b6060810190811067ffffffffffffffff82111761085057604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6080810190811067ffffffffffffffff82111761085057604052565b60a0810190811067ffffffffffffffff82111761085057604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761085057604052565b6040519061090582610834565b60006040838281528260208201520152565b519073ffffffffffffffffffffffffffffffffffffffff8216820361093857565b600080fd5b6109456108f8565b5073ffffffffffffffffffffffffffffffffffffffff91827f000000000000000000000000000000000000000000000000000000000000000016915b83811660409182517f8da5cb5b000000000000000000000000000000000000000000000000000000009081815260209260049184818481895afa908115610ba55789918b91600091610b6b575b501614610a41575090829185518095819382525afa928315610a375750908592916000926109ff575b505016610981565b90809350813d8311610a30575b610a1681836108b7565b8101031261014d5750610a298491610917565b38806109f7565b503d610a0c565b513d6000823e3d90fd5b9450505050508392509290921660408051927fce9b79300000000000000000000000000000000000000000000000000000000084526020908185600481875afa948515610b6057600095610b29575b5082517f1e31d0530000000000000000000000000000000000000000000000000000000081528281600481885afa908115610b1e57600091610ae9575b5081845196610adb88610834565b168652169084015282015290565b908382813d8311610b17575b610aff81836108b7565b8101031261014d5750610b1190610917565b38610acd565b503d610af5565b84513d6000823e3d90fd5b90948282813d8311610b59575b610b4081836108b7565b8101031261014d5750610b5290610917565b9338610a90565b503d610b36565b83513d6000823e3d90fd5b925090508582813d8111610b9e575b610b8481836108b7565b8101031261014d575089610b988a92610917565b386109ce565b503d610b7a565b87513d6000823e3d90fd5b906005811015610bc15760051b0190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b67ffffffffffffffff81116108505760051b60200190565b90610c1282610bf0565b610c1f60405191826108b7565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0610c4d8294610bf0565b019060005b828110610c5e57505050565b602090610c696108f8565b82828501015201610c52565b805115610bc15760200190565b8051821015610bc15760209160051b010190565b90816020910312610938575173ffffffffffffffffffffffffffffffffffffffff811681036109385790565b604080517fd1ddf4ef000000000000000000000000000000000000000000000000000000008152929160049160009173ffffffffffffffffffffffffffffffffffffffff9081169083878681855afa968715610f99578497610eff575b50610d2a8751610c08565b96845b8151811015610ef65782610d418284610c82565b5116908551917f78b440ac000000000000000000000000000000000000000000000000000000008352888301526020918281602481895afa908115610eec579085918991610ecf575b50168651907fce9b793000000000000000000000000000000000000000000000000000000000825283828b81845afa918215610ec5578992610e8a575b508751937f1e31d05300000000000000000000000000000000000000000000000000000000855280858c81855afa8015610e805787908b90610e3d575b60019650818b5195610e1587610834565b168552169083015287820152610e2b828c610c82565b52610e36818b610c82565b5001610d2d565b5050909192938181813d8311610e79575b610e5881836108b7565b81010312610e75579086610e70600196959493610917565b610e04565b8980fd5b503d610e4e565b89513d8c823e3d90fd5b9091508381813d8311610ebe575b610ea281836108b7565b81010312610eba57610eb390610917565b9038610dc7565b8880fd5b503d610e98565b88513d8b823e3d90fd5b610ee69150843d861161065f5761065181836108b7565b38610d8a565b87513d8a823e3d90fd5b50505050505050565b9096503d8085833e610f1181836108b7565b81019060209081818403126103875780519067ffffffffffffffff821161042057019180601f84011215610387578251610f4a81610bf0565b93610f57875195866108b7565b818552838086019260051b8201019283116103db578301905b828210610f8257505050509538610d1f565b838091610f8e84610917565b815201910190610f70565b50505051903d90823e3d90fd5b60005b828110610fb557505050565b606082820152602001610fa9565b60405190610fd08261087f565b60006060838281528260208201528260408201520152565b90604090815192610ff88461089b565b60005b60a0811061138d575061100d9061093d565b82519183830183811067ffffffffffffffff821117610850578452600180845260005b6020808210156110525790602091611046610fc3565b90828801015201611030565b5050928552928291611079906110679061139b565b86519061107382610c75565b52610c75565b50815b6005811061115757506000825b611127575b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06110d3826110cb6110c26110dd95610bf0565b955195866108b7565b808552610bf0565b0160208301610fa6565b80946000925b6110ee575b50505050565b8151831015611122578383611104829584610bb0565b5161110f8286610c82565b5261111a8185610c82565b5001926110e3565b6110e8565b6005811080611143575b1561113e57820182611089565b61108e565b5061114e8187610bb0565b51511515611131565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81939293016111878188610bb0565b51516111ce61119582610bf0565b916111a2875193846108b7565b8083527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0918291610bf0565b01906111de602092838501610fa6565b8560009485915b611300575b505061120d6111f885610bf0565b94611205895196876108b7565b808652610bf0565b0160005b8181106112dc575050506000908482905b611249575b5050506112348288610bb0565b5261123f8187610bb0565b500190829161107c565b909192939482518210156112d4579291906000845b61126f575b50830183959493611222565b61127d828495969394610c82565b51518110156112ca57806112be6112a86112a2859461129c888a610c82565b51610c82565b5161139b565b96838101976112b7828b610c82565b5288610c82565b5001819493929161125e565b9392919093611263565b949392611227565b8293949596506112ed929192610fc3565b8282880101520190879594939291611211565b90959691929394888c6113138482610bb0565b51518910156113805773ffffffffffffffffffffffffffffffffffffffff8593926113458b61129c8861134e96610bb0565b51015116610cc2565b6113588989610c82565b526113638888610c82565b5061136e8888610c82565b515101960190829796959493926111e5565b50509493929196956111ea565b606085820152602001610ffb565b6113a3610fc3565b50602473ffffffffffffffffffffffffffffffffffffffff80835116926020808360408183860151169401511693604051958680927f70a082310000000000000000000000000000000000000000000000000000000082528760048301527f0000000000000000000000000000000000000000000000000000000000000000165afa93841561148357600094611454575b50604051946114428661087f565b85528401526040830152606082015290565b90938482813d831161147c575b61146b81836108b7565b8101031261014d5750519238611434565b503d611461565b6040513d6000823e3d90fdfea164736f6c634300080f000a0000000000000000000000001f9840a85d5af5bf1d1762f925bdaddc4201f984000000000000000000000000f754a7e347f81cfdc70af9fbcce9df3d826360fa",
"nonce": "0x1",
"accessList": []
}
}
],
"receipts": [
{
"transactionHash": "0x1a4f063278d24aacf05b39df3726fd1c1b4a959779ba667e7dbf986874fcff51",
"transactionIndex": "0x0",
"blockHash": "0x86cfb6698360dfbb3064123e23b0cccce518d5a1d910fe2631d2f3d2f85b5acd",
"blockNumber": "0x18c48d",
"from": "0x9f67f9af54513a1790ed159dd65e8be73fb531fd",
"to": null,
"cumulativeGasUsed": "0x2bd522",
"gasUsed": "0x2bd522",
"contractAddress": "0xf754a7e347f81cfdc70af9fbcce9df3d826360fa",
"logs": [
{
"address": "0x5bd57ff26b100303184339353b190fbafefa19ea",
"topics": [
"0x8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d76",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000"
],
"data": "0x",
"blockHash": "0x86cfb6698360dfbb3064123e23b0cccce518d5a1d910fe2631d2f3d2f85b5acd",
"blockNumber": "0x18c48d",
"transactionHash": "0x1a4f063278d24aacf05b39df3726fd1c1b4a959779ba667e7dbf986874fcff51",
"transactionIndex": "0x0",
"logIndex": "0x0",
"removed": false
}
],
"status": "0x1",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000800000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000008000000000000000000000000000000000000000000000000000000000020000000000000000000020000000000000000000002000000000000000000000000",
"type": "0x2",
"effectiveGasPrice": "0xb2d05e07"
},
{
"transactionHash": "0x186e37e88742310ec2389199ab6ae9fceed21e81ebdbb8e95e2a40dc1d0bf71c",
"transactionIndex": "0x1",
"blockHash": "0x86cfb6698360dfbb3064123e23b0cccce518d5a1d910fe2631d2f3d2f85b5acd",
"blockNumber": "0x18c48d",
"from": "0x9f67f9af54513a1790ed159dd65e8be73fb531fd",
"to": null,
"cumulativeGasUsed": "0x3e3557",
"gasUsed": "0x126035",
"contractAddress": "0x3e718c61a2849fbb0181eba83de4ee8363014106",
"logs": [],
"status": "0x1",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"type": "0x2",
"effectiveGasPrice": "0xb2d05e07"
}
],
"libraries": [],
"pending": [],
"path": "broadcast/Deploy.s.sol/11155111/run-latest.json",
"returns": {},
"timestamp": 1659450153
}
{
"transactions": [
{
"hash": "0x394991d84c8dd95751574b2d99a8d3e3db0ba9feb69d4acf5c59ce4e697539d1",
"type": "CREATE",
"contractAddress": "0xf754a7e347f81cfdc70af9fbcce9df3d826360fa",
"tx": {
"type": "0x02",
"from": "0x9f67f9af54513a1790ed159dd65e8be73fb531fd",
"gas": "0x38fb79",
"value": "0x0",
"data": "0x60c0346100e6576001600160401b0390601f61345a38819003918201601f1916830191848311848410176100d0578084926020946040528339810103126100e657516001600160a01b038116908181036100e6576080526040519161240f90818401908111848210176100d057602092849261104b843981520301906000f080156100c45760a052604051610f5f90816100ec823960805181818161058a0152818161094d0152610dae015260a0518181816106bd0152818161079f01526109ca0152f35b6040513d6000823e3d90fd5b634e487b7160e01b600052604160045260246000fd5b600080fdfe60806040526004361015610013575b600080fd5b60003560e01c806319f86ffd146100db5780635b145a75146100d25780637b1837de146100c95780638de804a7146100c0578063a1343f37146100b7578063b0340123146100ae578063b6a24b0e146100a5578063c61bdcd21461009c578063e95c4d36146100935763f5f2df861461008b57600080fd5b61000e61071c565b5061000e6106e1565b5061000e610671565b5061000e6105ae565b5061000e61053e565b5061000e6104cd565b5061000e610353565b5061000e61028a565b5061000e6101f2565b3461000e576100f26100ec36610112565b90610cbe565b005b73ffffffffffffffffffffffffffffffffffffffff81160361000e57565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc604091011261000e57600435610148816100f4565b90602435610155816100f4565b90565b9181601f8401121561000e5782359167ffffffffffffffff831161000e576020808501948460051b01011161000e57565b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc82011261000e5767ffffffffffffffff9160043583811161000e57826101d491600401610158565b9390939260243591821161000e576101ee91600401610158565b9091565b503461000e5761020136610189565b908183036102515760005b83811061021557005b8061024b6102266001938789610c7a565b35610230816100f4565b61023b838787610c7a565b3590610246826100f4565b610cbe565b0161020c565b506040517ffa5dbe0800000000000000000000000000000000000000000000000000000000815260048101929092526024820152604490fd5b503461000e5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5760206102d46004356102cb816100f4565b60243590610915565b73ffffffffffffffffffffffffffffffffffffffff60405191168152f35b6064359060ff8216820361000e57565b6020908160408183019282815285518094520193019160005b828110610329575050505090565b835173ffffffffffffffffffffffffffffffffffffffff168552938101939281019260010161031b565b503461000e5760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5767ffffffffffffffff60043581811161000e576103a4903690600401610158565b9160243590811161000e576103bd903690600401610158565b6103c56102f2565b818503610492576000805b86811061046d5750906103ee9160a435916084359160443590610d61565b6103f784610bfb565b9360005b81811061041457604051806104108882610302565b0390f35b8061046761044261043061042b600195878b610c7a565b610c92565b61043b84888a610c7a565b3590610915565b61044c838a610c9c565b9073ffffffffffffffffffffffffffffffffffffffff169052565b016103fb565b9061048761048d91610480848789610c7a565b3590610f46565b91610f0c565b6103d0565b506040517ffa5dbe08000000000000000000000000000000000000000000000000000000008152600481018590526024810191909152604490fd5b503461000e5760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5760206102d460043561050e816100f4565b6024359061052e61051d6102f2565b60a435906084359060443586610d61565b610915565b600091031261000e57565b503461000e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b503461000e576105bd36610189565b8083949303610637576105cf84610bfb565b9360005b8181106105e857604051806104108882610302565b8061060e6105f96001938589610c7a565b35610603816100f4565b61043b838789610c7a565b73ffffffffffffffffffffffffffffffffffffffff61062d838a610c9c565b91169052016105d3565b6040517ffa5dbe08000000000000000000000000000000000000000000000000000000008152600481018590526024810191909152604490fd5b503461000e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b503461000e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57602060405160088152f35b503461000e5760206102d461073036610112565b905b603761075573ffffffffffffffffffffffffffffffffffffffff9360559361089b565b604051907f3d602d80600a3d3981f3363d3d373d3d3d363d7300000000000000000000000082527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000060601b1660148301527f5af43d82803e903d91602b57fd5bf3ff0000000000000000000000000000000060288301523060601b6038830152604c820152818120606c82015201201690565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b67ffffffffffffffff811161084d57604052565b610855610809565b604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761084d57604052565b6040519060208201927fffffffffffffffffffffffffffffffffffffffff000000000000000000000000809260601b16845260601b166034820152602881526060810181811067ffffffffffffffff8211176108fb575b60405251902090565b610903610809565b6108f2565b506040513d6000823e3d90fd5b9190916109228133610732565b9273ffffffffffffffffffffffffffffffffffffffff80851690813b15610974575b610972935033907f000000000000000000000000000000000000000000000000000000000000000016610b1d565b565b8061097f853361089b565b60376040517f3d602d80600a3d3981f3363d3d373d3d3d363d7300000000000000000000000081527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000060601b1660148201527f5af43d82803e903d91602b57fd5bf3000000000000000000000000000000000060288201526000f51615610abf57813b1561000e576040517fc861c25000000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff949094166024850152600860448501526109729360008160648183875af18015610ab2575b610a99575b50610944565b80610aa6610aac92610839565b80610533565b38610a93565b610aba610908565b610a8e565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f455243313136373a2063726561746532206661696c65640000000000000000006044820152fd5b9060006064926020958295604051947f23b872dd0000000000000000000000000000000000000000000000000000000086526004860152602485015260448401525af13d15601f3d1160016000511416171615610b7657565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152fd5b60209067ffffffffffffffff8111610bee575b60051b0190565b610bf6610809565b610be7565b90610c0582610bd4565b610c12604051918261085a565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0610c408294610bd4565b0190602036910137565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b9190811015610c8a5760051b0190565b610bf6610c4a565b35610155816100f4565b6020918151811015610cb1575b60051b010190565b610cb9610c4a565b610ca9565b90610ce19173ffffffffffffffffffffffffffffffffffffffff92839133610732565b1691823b610cee57505050565b823b1561000e5760246000928360405195869485937fca4305190000000000000000000000000000000000000000000000000000000085521660048401525af18015610d45575b610d3c5750565b61097290610839565b610d4d610908565b610d35565b9081602091031261000e575190565b6040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523360048201523060248201529394909373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169291908590602081604481885afa908115610ecf575b600091610ea1575b5010610dfb575b505050505050565b823b1561000e576040517fd505accf0000000000000000000000000000000000000000000000000000000081523360048201523060248201526044810195909552606485019190915260ff92909216608484015260a483019390935260c482015290600090829060e490829084905af18015610e94575b610e81575b8080808080610df3565b80610aa6610e8e92610839565b38610e77565b610e9c610908565b610e72565b610ec2915060203d8111610ec8575b610eba818361085a565b810190610d52565b38610dec565b503d610eb0565b610ed7610908565b610de4565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6001907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114610f3a570190565b610f42610edc565b0190565b81198111610f3a57019056fea164736f6c634300080f000a60c03461010f57601f61240f38819003918201601f19168301916001600160401b038311848410176101145780849260209460405283398101031261010f57516001600160a01b038116810361010f57608052600080546001600160a01b031990811682556040519160019190807f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d768186a33060a05260025416176002556122e4908161012b823960805181818161050b01528181610b2d01528181610dea015281816110190152818161139101526117c5015260a051818181610465015281816105eb01528181610a0201528181610c41015281816117260152818161187001528181611d270152611f510152f35b600080fd5b634e487b7160e01b600052604160045260246000fdfe60806040526004361015610013575b600080fd5b60003560e01c806301ce3e9d1461016757806313af40351461015e5780631ba8d97c146101555780631e31d0531461014c5780635c2927781461014357806368afe2b71461013a57806378b440ac146101315780638da5cb5b14610128578063b03401231461011f578063c5546c3f14610116578063c61bdcd21461010d578063c861c25014610104578063ca430519146100fb578063ce9b7930146100f2578063d1ddf4ef146100e9578063e08eae5f146100e05763f2a41374146100d857600080fd5b61000e611239565b5061000e6111fe565b5061000e611111565b5061000e6110d7565b5061000e610f79565b5061000e610c65565b5061000e610bf5565b5061000e610b51565b5061000e610ae1565b5061000e610a8e565b5061000e61095b565b5061000e6107f7565b5061000e61039a565b5061000e610347565b5061000e610307565b5061000e610244565b5061000e61018e565b73ffffffffffffffffffffffffffffffffffffffff81160361000e57565b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e576004356101ca81610170565b73ffffffffffffffffffffffffffffffffffffffff600254168033036101f5576101f382611cbd565b005b6040517f2488453800000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff919091166024820152604490fd5b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5760007fffffffffffffffffffffffff00000000000000000000000000000000000000006004356102a381610170565b82549073ffffffffffffffffffffffffffffffffffffffff906102c982841633146114d5565b169182911617825560405190337f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d768484a3f35b600091031261000e57565b503461000e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57602060025460a01c604051908152f35b503461000e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57602073ffffffffffffffffffffffffffffffffffffffff60025416604051908152f35b503461000e576040807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e576004356103d781610170565b6002549173ffffffffffffffffffffffffffffffffffffffff80841680330361072757506104d3836055603773ffffffffffffffffffffffffffffffffffffffff92604051907f3d602d80600a3d3981f3363d3d373d3d3d363d7300000000000000000000000082527fffffffffffffffffffffffffffffffffffffffff00000000000000000000000090817f000000000000000000000000000000000000000000000000000000000000000060601b1660148401527f5af43d82803e903d91602b57fd5bf3ff0000000000000000000000000000000060288401523060601b603884015260601b16604c820152818120606c82015201201690565b92818116946104f66104f2876000526004602052604060002054151590565b1590565b61055a575b61055685856105316024358784167f00000000000000000000000000000000000000000000000000000000000000008916611ba1565b5173ffffffffffffffffffffffffffffffffffffffff90911681529081906020820190565b0390f35b6003549060a01c8091146106e7575061057a61057586611b0a565b611987565b838216803b156105bc575b505061053190610556947f3aadde169d4a8fa84eba250da3838e12911f6dffadeb18f092363cfbfa8a12c160008551a290386104fb565b9190946106107fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008760601b16837f0000000000000000000000000000000000000000000000000000000000000000166119bd565b5061063461062060025460a01c90565b60011c6b7fffffffffffffffffffffff1690565b92803b1561000e5784517ff2a4137400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90971660048801526bffffffffffffffffffffffff9093166024870152610556956105319360009082908183816044810103925af180156106da575b6106c1575b5094819250610585565b806106ce6106d49261156a565b806102fc565b386106b7565b6106e26115cc565b6106b2565b83517f1d7d1c310000000000000000000000000000000000000000000000000000000081526bffffffffffffffffffffffff919091166004820152602490fd5b82517f2488453800000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff919091166024820152604490fd5b9181601f8401121561000e5782359167ffffffffffffffff831161000e576020808501948460051b01011161000e57565b6020908160408183019282815285518094520193019160005b8281106107cd575050505090565b835173ffffffffffffffffffffffffffffffffffffffff16855293810193928101926001016107bf565b503461000e5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5767ffffffffffffffff60043581811161000e57610848903690600401610775565b9160243590811161000e57610861903690600401610775565b8084036109245761087184611c52565b9361087f604051958661158b565b8085527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06108ac82611c52565b0136602087013760005b8181106108cb576040518061055688826107a6565b8061091e6108f96108e76108e2600195878b611c79565b611c91565b6108f284888a611c79565b3590611698565b610903838a611c9b565b9073ffffffffffffffffffffffffffffffffffffffff169052565b016108b6565b83604491604051917ffa5dbe0800000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e576020610a7060043561099c81610170565b6055603773ffffffffffffffffffffffffffffffffffffffff92604051907f3d602d80600a3d3981f3363d3d373d3d3d363d7300000000000000000000000082527fffffffffffffffffffffffffffffffffffffffff00000000000000000000000090817f000000000000000000000000000000000000000000000000000000000000000060601b1660148401527f5af43d82803e903d91602b57fd5bf3ff0000000000000000000000000000000060288401523060601b603884015260601b16604c820152818120606c82015201201690565b73ffffffffffffffffffffffffffffffffffffffff60405191168152f35b503461000e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b503461000e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5760043567ffffffffffffffff811161000e57610ba1903690600401610775565b9073ffffffffffffffffffffffffffffffffffffffff600254168033036101f5575060005b828110610bcf57005b80610bef610be06001938686611c79565b35610bea81610170565b611cbd565b01610bc6565b503461000e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b503461000e5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57600435610ca181610170565b602435610cad81610170565b604435906bffffffffffffffffffffffff8216820361000e5773ffffffffffffffffffffffffffffffffffffffff90818116938415610f4f57610d21610d0860025473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1690565b610f2557600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001633179055828116610eda575b50610d9d8173ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff00000000000000000000000000000000000000006002541617600255565b610de78373ffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffff00000000000000000000000000000000000000006002549260a01b16911617600255565b817f000000000000000000000000000000000000000000000000000000000000000016803b1561000e576040517f5c19a95c00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9290921660048301526000908290602490829084905af18015610ecd575b610eba575b50610e786115d9565b6040516bffffffffffffffffffffffff939093168352169033907f8e063a11026e8eeb829fca77696415cbd6da2c4d6e73aaf6fc3e5c902c882a4d90602090a4005b806106ce610ec79261156a565b38610e6f565b610ed56115cc565b610e6a565b610f1f9073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff00000000000000000000000000000000000000006001541617600155565b38610d57565b60046040517f0dc149f0000000000000000000000000000000000000000000000000000000008152fd5b60046040517fcfb6286a000000000000000000000000000000000000000000000000000000008152fd5b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57600435610fb581610170565b73ffffffffffffffffffffffffffffffffffffffff90610fda826000541633146114d5565b600354805b61109557506040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526101f3929091907f000000000000000000000000000000000000000000000000000000000000000016602083602481845afa928315611088575b600093611058575b50611ba1565b61107a91935060203d8111611081575b611072818361158b565b810190612285565b9138611052565b503d611068565b6110906115cc565b61104a565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0191826110d06110cb610d08610d0884612294565b611ee7565b9092610fdf565b503461000e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e576020610a706115d9565b503461000e576000807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126111fb5760405180916003549081835260208093018092600383527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b90835b8181106111e7575050508461119391038561158b565b60405193838594850191818652518092526040850193925b8281106111ba57505050500390f35b835173ffffffffffffffffffffffffffffffffffffffff16855286955093810193928101926001016111ab565b82548452928601926001928301920161117d565b80fd5b503461000e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57602060405160028152f35b503461000e576040807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e576004359061127782610170565b6024356bffffffffffffffffffffffff8116810361000e5773ffffffffffffffffffffffffffffffffffffffff908184169182156114ac576112d1610d0860025473ffffffffffffffffffffffffffffffffffffffff1690565b61148357600080547fffffffffffffffffffffffff000000000000000000000000000000000000000016331790556113448573ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff00000000000000000000000000000000000000006002541617600255565b61138e8273ffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffff00000000000000000000000000000000000000006002549260a01b16911617600255565b807f00000000000000000000000000000000000000000000000000000000000000001694853b1561000e5784517f5c19a95c00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff91909116600482015260009586908290602490829084905af18015611476575b611463575b506114216115d9565b84516bffffffffffffffffffffffff939093168352169033907f8e063a11026e8eeb829fca77696415cbd6da2c4d6e73aaf6fc3e5c902c882a4d90602090a451f35b806106ce6114709261156a565b38611418565b61147e6115cc565b611413565b600484517f0dc149f0000000000000000000000000000000000000000000000000000000008152fd5b600484517fcfb6286a000000000000000000000000000000000000000000000000000000008152fd5b156114dc57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152fd5b507f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b67ffffffffffffffff811161157e57604052565b61158661153a565b604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761157e57604052565b506040513d6000823e3d90fd5b60015473ffffffffffffffffffffffffffffffffffffffff9081169081156115ff575090565b905060005416806116105750600090565b6020600491604051928380927f1e31d0530000000000000000000000000000000000000000000000000000000082525afa90811561168b575b600091611654575090565b906020823d8211611683575b8161166d6020938361158b565b810103126111fb57505161168081610170565b90565b3d9150611660565b6116936115cc565b611649565b6002549173ffffffffffffffffffffffffffffffffffffffff8084168033036101f55750611794836055603773ffffffffffffffffffffffffffffffffffffffff92604051907f3d602d80600a3d3981f3363d3d373d3d3d363d7300000000000000000000000082527fffffffffffffffffffffffffffffffffffffffff00000000000000000000000090817f000000000000000000000000000000000000000000000000000000000000000060601b1660148401527f5af43d82803e903d91602b57fd5bf3ff0000000000000000000000000000000060288401523060601b603884015260601b16604c820152818120606c82015201201690565b93818416906117b36104f2836000526004602052604060002054151590565b6117ea575b50506116809250808416907f000000000000000000000000000000000000000000000000000000000000000016611ba1565b6003549060a01c809114611946575061180561057582611b0a565b848216803b15611844575b5061168093507f3aadde169d4a8fa84eba250da3838e12911f6dffadeb18f092363cfbfa8a12c16000604051a238806117b8565b6118957fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008660601b16847f0000000000000000000000000000000000000000000000000000000000000000166119bd565b506118a561062060025460a01c90565b94813b1561000e576040517ff2a4137400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201526bffffffffffffffffffffffff9095166024860152611680949060009082908183816044810103925af18015611939575b1561181057806106ce6119339261156a565b38611810565b6119416115cc565b611921565b6040517f1d7d1c310000000000000000000000000000000000000000000000000000000081526bffffffffffffffffffffffff919091166004820152602490fd5b1561198e57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b603790604051907f3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000825260601b60148201527f5af43d82803e903d91602b57fd5bf3000000000000000000000000000000000060288201526000f59073ffffffffffffffffffffffffffffffffffffffff821615611a3757565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f455243313136373a2063726561746532206661696c65640000000000000000006044820152fd5b507f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600354811015611afd575b60036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0190600090565b611b05611a95565b611ad0565b80600052600460205260406000205415600014611b9b578060035468010000000000000000811015611b8e575b6001810180600355811015611b81575b7fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0155600354906000526004602052604060002055600190565b611b89611a95565b611b47565b611b9661153a565b611b37565b50600090565b60009182604492602095604051937fa9059cbb000000000000000000000000000000000000000000000000000000008552600485015260248401525af13d15601f3d1160016000511416171615611bf457565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152fd5b60209067ffffffffffffffff8111611c6c575b60051b0190565b611c7461153a565b611c65565b9190811015611c895760051b0190565b611c74611a95565b3561168081610170565b6020918151811015611cb0575b60051b010190565b611cb8611a95565b611ca8565b611d95816055603773ffffffffffffffffffffffffffffffffffffffff92604051907f3d602d80600a3d3981f3363d3d373d3d3d363d7300000000000000000000000082527fffffffffffffffffffffffffffffffffffffffff00000000000000000000000090817f000000000000000000000000000000000000000000000000000000000000000060601b1660148401527f5af43d82803e903d91602b57fd5bf3ff0000000000000000000000000000000060288401523060601b603884015260601b16604c820152818120606c82015201201690565b9073ffffffffffffffffffffffffffffffffffffffff80911691611dc6836000526004602052604060002054151590565b15611e7057611dd76105758461216c565b16803b1561000e576040517fca430519000000000000000000000000000000000000000000000000000000008152306004820152906000908290602490829084905af18015611e63575b611e50575b507f5b52cabd65626f7970ccfee89ce9d84764a149a8289e4d4356d32819e7b010876000604051a2565b806106ce611e5d9261156a565b38611e26565b611e6b6115cc565b611e21565b169050803b611e7d575b50565b803b1561000e576040517fca430519000000000000000000000000000000000000000000000000000000008152306004820152906000908290602490829084905af18015611eda575b15611e7a57806106ce611ed89261156a565b565b611ee26115cc565b611ec6565b611fbf816055603773ffffffffffffffffffffffffffffffffffffffff92604051907f3d602d80600a3d3981f3363d3d373d3d3d363d7300000000000000000000000082527fffffffffffffffffffffffffffffffffffffffff00000000000000000000000090817f000000000000000000000000000000000000000000000000000000000000000060601b1660148401527f5af43d82803e903d91602b57fd5bf3ff0000000000000000000000000000000060288401523060601b603884015260601b16604c820152818120606c82015201201690565b9073ffffffffffffffffffffffffffffffffffffffff80911691611fe56105758461216c565b16803b1561000e57600080916024604051809481937fca4305190000000000000000000000000000000000000000000000000000000083523060048401525af18015612063575b61205a57507f5b52cabd65626f7970ccfee89ce9d84764a149a8289e4d4356d32819e7b010876000604051a2565b611e5d9061156a565b61206b6115cc565b61202c565b6001811061209d577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600354801561213d5760007fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83019280841015612130575b600383520155600355565b612138611a95565b612125565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b600081815260046020526040812054909190801561228057600181106122535790817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6121e393016121bf600354612070565b908082036121e9575b5050506121d36120cc565b6000526004602052604060002090565b55600190565b6121d3612211916122096121ff61224a95611ac5565b90549060031b1c90565b928391611ac5565b90919082549060031b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811b9283911b16911916179055565b553880806121c8565b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b505090565b9081602091031261000e575190565b6003548110156122ca575b60036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b015490565b6122d2611a95565b61229f56fea164736f6c634300080f000a0000000000000000000000001f9840a85d5af5bf1d1762f925bdaddc4201f984",
"nonce": "0x0",
"accessList": []
}
},
{
"hash": "0xd7f4c8ad7f0c125ebc9c88ea3ffc2b5c278cbbd55aaeab9f59668152adb33152",
"type": "CREATE",
"contractAddress": "0x3e718c61a2849fbb0181eba83de4ee8363014106",
"tx": {
"type": "0x02",
"from": "0x9f67f9af54513a1790ed159dd65e8be73fb531fd",
"gas": "0x17e378",
"value": "0x0",
"data": "0x60c060409080825234620001f15781816200170c8038038091620000248285620001f6565b833981010312620001f15780516001600160a01b0391908281168103620001f15760208092015190838216808303620001f157600492849260805260a0528551928380926374ae269b60e11b82525afa9081156200017257600091620001cf575b506001600160601b03906200009f90821660081462000251565b6004828460a0511686519283809263630dee6960e11b82525afa8015620001c45783916000916200017d575b50600486518096819363e08eae5f60e01b8352165afa8015620001725762000103936002936000926200013e575b5050161462000251565b5161149c908162000270823960805181818161019f0152611403015260a05181818160b901528181610235015281816105ef015261095f0152f35b620001629250803d106200016a575b620001598183620001f6565b81019062000230565b3880620000f9565b503d6200014d565b84513d6000823e3d90fd5b909181813d8311620001bc575b620001968183620001f6565b81010312620001b85751908482168203620001b55750829038620000cb565b80fd5b5080fd5b503d6200018a565b85513d6000823e3d90fd5b620001ea9150823d84116200016a57620001598183620001f6565b3862000085565b600080fd5b601f909101601f19168101906001600160401b038211908210176200021a57604052565b634e487b7160e01b600052604160045260246000fd5b90816020910312620001f157516001600160601b0381168103620001f15790565b156200025957565b634e487b7160e01b600052600160045260246000fdfe6040608081526004908136101561001557600080fd5b600091823560e01c90816308ecd5c0146106765781631cad183c1461055457816371456623146104bb57816397f2c53d146101c3578163b034012314610154578163df5fc105146100e1575063f3dc336b1461007057600080fd5b346100dd57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100dd576020905173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b5080fd5b9050346101505760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261015057359173ffffffffffffffffffffffffffffffffffffffff8316830361014d575061013e61014992610cc2565b9051918291826107bd565b0390f35b80fd5b8280fd5b5050346100dd57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100dd576020905173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b90503461015057602092837ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261014d5773ffffffffffffffffffffffffffffffffffffffff938235858116810361015057845190836102248361089b565b83855b60a081106104a757505087807f000000000000000000000000000000000000000000000000000000000000000016925b16828114610435578751917fce9b793000000000000000000000000000000000000000000000000000000000835285838981855afa92831561042b5787936103f0575b5088517f1e31d05300000000000000000000000000000000000000000000000000000000815286818a81865afa9081156103e657918b8a94928c8a958c9261039c575b50908261031494939251986102f18a610834565b1688521684870152828c870152600181019561030d828a610bb0565b5287610bb0565b508951928380927f8da5cb5b0000000000000000000000000000000000000000000000000000000082525afa80156103925789908790610358575b81925016610257565b50508481813d831161038b575b61036f81836108b7565b8101031261038757886103828192610917565b61034f565b8580fd5b503d610365565b88513d88823e3d90fd5b9496505050509181813d83116103df575b6103b781836108b7565b810103126103db579161031487928c8c816103d28e98610917565b929394506102dd565b8780fd5b503d6103ad565b8a513d8a823e3d90fd5b9092508581813d8311610424575b61040881836108b7565b810103126104205761041990610917565b913861029a565b8680fd5b503d6103fe565b89513d89823e3d90fd5b8786858461044281610c08565b925b8181106104585784518061014986826107bd565b806104a061046860019386610bb0565b517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83860301906104998289610c82565b5286610c82565b5001610444565b6104af6108f8565b81860152018490610227565b9050346101505760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261015057359173ffffffffffffffffffffffffffffffffffffffff8316830361014d575061055261051a60609361093d565b915180926040908173ffffffffffffffffffffffffffffffffffffffff91828151168552826020820151166020860152015116910152565bf35b90503461015057817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101505780359073ffffffffffffffffffffffffffffffffffffffff91828116809103610672576024359280841680940361038757602092604491865195869485937ff5f2df8600000000000000000000000000000000000000000000000000000000855284015260248301527f0000000000000000000000000000000000000000000000000000000000000000165afa908115610666579061062d916101499491610638575b50610fe8565b9051918291826106d3565b610659915060203d811161065f575b61065181836108b7565b810190610c96565b38610627565b503d610647565b505051903d90823e3d90fd5b8480fd5b9050346101505760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261015057359173ffffffffffffffffffffffffffffffffffffffff8316830361014d575061062d61014992610fe8565b602080820190808352835180925260409283810182858560051b840101960194600080935b86851061070a57505050505050505090565b9091929394887fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08383999a9b0301865289519082808351928381520192019085905b80821061076d5750505090806001929a0195019501939695949291906106f8565b9193608060019294865173ffffffffffffffffffffffffffffffffffffffff808251168352808583015116858401528b820151168b8301526060809101519082015201940192018993929161074c565b6020908160408183019282815285518094520193019160005b8281106107e4575050505090565b909192938260608261082860019489516040908173ffffffffffffffffffffffffffffffffffffffff91828151168552826020820151166020860152015116910152565b019501939291016107d6565b6060810190811067ffffffffffffffff82111761085057604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6080810190811067ffffffffffffffff82111761085057604052565b60a0810190811067ffffffffffffffff82111761085057604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761085057604052565b6040519061090582610834565b60006040838281528260208201520152565b519073ffffffffffffffffffffffffffffffffffffffff8216820361093857565b600080fd5b6109456108f8565b5073ffffffffffffffffffffffffffffffffffffffff91827f000000000000000000000000000000000000000000000000000000000000000016915b83811660409182517f8da5cb5b000000000000000000000000000000000000000000000000000000009081815260209260049184818481895afa908115610ba55789918b91600091610b6b575b501614610a41575090829185518095819382525afa928315610a375750908592916000926109ff575b505016610981565b90809350813d8311610a30575b610a1681836108b7565b8101031261014d5750610a298491610917565b38806109f7565b503d610a0c565b513d6000823e3d90fd5b9450505050508392509290921660408051927fce9b79300000000000000000000000000000000000000000000000000000000084526020908185600481875afa948515610b6057600095610b29575b5082517f1e31d0530000000000000000000000000000000000000000000000000000000081528281600481885afa908115610b1e57600091610ae9575b5081845196610adb88610834565b168652169084015282015290565b908382813d8311610b17575b610aff81836108b7565b8101031261014d5750610b1190610917565b38610acd565b503d610af5565b84513d6000823e3d90fd5b90948282813d8311610b59575b610b4081836108b7565b8101031261014d5750610b5290610917565b9338610a90565b503d610b36565b83513d6000823e3d90fd5b925090508582813d8111610b9e575b610b8481836108b7565b8101031261014d575089610b988a92610917565b386109ce565b503d610b7a565b87513d6000823e3d90fd5b906005811015610bc15760051b0190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b67ffffffffffffffff81116108505760051b60200190565b90610c1282610bf0565b610c1f60405191826108b7565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0610c4d8294610bf0565b019060005b828110610c5e57505050565b602090610c696108f8565b82828501015201610c52565b805115610bc15760200190565b8051821015610bc15760209160051b010190565b90816020910312610938575173ffffffffffffffffffffffffffffffffffffffff811681036109385790565b604080517fd1ddf4ef000000000000000000000000000000000000000000000000000000008152929160049160009173ffffffffffffffffffffffffffffffffffffffff9081169083878681855afa968715610f99578497610eff575b50610d2a8751610c08565b96845b8151811015610ef65782610d418284610c82565b5116908551917f78b440ac000000000000000000000000000000000000000000000000000000008352888301526020918281602481895afa908115610eec579085918991610ecf575b50168651907fce9b793000000000000000000000000000000000000000000000000000000000825283828b81845afa918215610ec5578992610e8a575b508751937f1e31d05300000000000000000000000000000000000000000000000000000000855280858c81855afa8015610e805787908b90610e3d575b60019650818b5195610e1587610834565b168552169083015287820152610e2b828c610c82565b52610e36818b610c82565b5001610d2d565b5050909192938181813d8311610e79575b610e5881836108b7565b81010312610e75579086610e70600196959493610917565b610e04565b8980fd5b503d610e4e565b89513d8c823e3d90fd5b9091508381813d8311610ebe575b610ea281836108b7565b81010312610eba57610eb390610917565b9038610dc7565b8880fd5b503d610e98565b88513d8b823e3d90fd5b610ee69150843d861161065f5761065181836108b7565b38610d8a565b87513d8a823e3d90fd5b50505050505050565b9096503d8085833e610f1181836108b7565b81019060209081818403126103875780519067ffffffffffffffff821161042057019180601f84011215610387578251610f4a81610bf0565b93610f57875195866108b7565b818552838086019260051b8201019283116103db578301905b828210610f8257505050509538610d1f565b838091610f8e84610917565b815201910190610f70565b50505051903d90823e3d90fd5b60005b828110610fb557505050565b606082820152602001610fa9565b60405190610fd08261087f565b60006060838281528260208201528260408201520152565b90604090815192610ff88461089b565b60005b60a0811061138d575061100d9061093d565b82519183830183811067ffffffffffffffff821117610850578452600180845260005b6020808210156110525790602091611046610fc3565b90828801015201611030565b5050928552928291611079906110679061139b565b86519061107382610c75565b52610c75565b50815b6005811061115757506000825b611127575b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06110d3826110cb6110c26110dd95610bf0565b955195866108b7565b808552610bf0565b0160208301610fa6565b80946000925b6110ee575b50505050565b8151831015611122578383611104829584610bb0565b5161110f8286610c82565b5261111a8185610c82565b5001926110e3565b6110e8565b6005811080611143575b1561113e57820182611089565b61108e565b5061114e8187610bb0565b51511515611131565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81939293016111878188610bb0565b51516111ce61119582610bf0565b916111a2875193846108b7565b8083527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0918291610bf0565b01906111de602092838501610fa6565b8560009485915b611300575b505061120d6111f885610bf0565b94611205895196876108b7565b808652610bf0565b0160005b8181106112dc575050506000908482905b611249575b5050506112348288610bb0565b5261123f8187610bb0565b500190829161107c565b909192939482518210156112d4579291906000845b61126f575b50830183959493611222565b61127d828495969394610c82565b51518110156112ca57806112be6112a86112a2859461129c888a610c82565b51610c82565b5161139b565b96838101976112b7828b610c82565b5288610c82565b5001819493929161125e565b9392919093611263565b949392611227565b8293949596506112ed929192610fc3565b8282880101520190879594939291611211565b90959691929394888c6113138482610bb0565b51518910156113805773ffffffffffffffffffffffffffffffffffffffff8593926113458b61129c8861134e96610bb0565b51015116610cc2565b6113588989610c82565b526113638888610c82565b5061136e8888610c82565b515101960190829796959493926111e5565b50509493929196956111ea565b606085820152602001610ffb565b6113a3610fc3565b50602473ffffffffffffffffffffffffffffffffffffffff80835116926020808360408183860151169401511693604051958680927f70a082310000000000000000000000000000000000000000000000000000000082528760048301527f0000000000000000000000000000000000000000000000000000000000000000165afa93841561148357600094611454575b50604051946114428661087f565b85528401526040830152606082015290565b90938482813d831161147c575b61146b81836108b7565b8101031261014d5750519238611434565b503d611461565b6040513d6000823e3d90fdfea164736f6c634300080f000a0000000000000000000000001f9840a85d5af5bf1d1762f925bdaddc4201f984000000000000000000000000f754a7e347f81cfdc70af9fbcce9df3d826360fa",
"nonce": "0x1",
"accessList": []
}
}
],
"receipts": [
{
"transactionHash": "0x394991d84c8dd95751574b2d99a8d3e3db0ba9feb69d4acf5c59ce4e697539d1",
"transactionIndex": "0x4",
"blockHash": "0x5c2947c2ef3d33415b97fa65df81df3c5ff1b66c8c9e16a4faa67a6aaed9cbcc",
"blockNumber": "0x6fe9af",
"from": "0x9f67f9af54513a1790ed159dd65e8be73fb531fd",
"to": null,
"cumulativeGasUsed": "0x4c45b0",
"gasUsed": "0x2bd522",
"contractAddress": "0xf754a7e347f81cfdc70af9fbcce9df3d826360fa",
"logs": [
{
"address": "0x5bd57ff26b100303184339353b190fbafefa19ea",
"topics": [
"0x8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d76",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000"
],
"data": "0x",
"blockHash": "0x5c2947c2ef3d33415b97fa65df81df3c5ff1b66c8c9e16a4faa67a6aaed9cbcc",
"blockNumber": "0x6fe9af",
"transactionHash": "0x394991d84c8dd95751574b2d99a8d3e3db0ba9feb69d4acf5c59ce4e697539d1",
"transactionIndex": "0x4",
"logIndex": "0x2",
"removed": false
}
],
"status": "0x1",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000800000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000008000000000000000000000000000000000000000000000000000000000020000000000000000000020000000000000000000002000000000000000000000000",
"type": "0x2",
"effectiveGasPrice": "0xb2d05e10"
},
{
"transactionHash": "0xd7f4c8ad7f0c125ebc9c88ea3ffc2b5c278cbbd55aaeab9f59668152adb33152",
"transactionIndex": "0x5",
"blockHash": "0x5c2947c2ef3d33415b97fa65df81df3c5ff1b66c8c9e16a4faa67a6aaed9cbcc",
"blockNumber": "0x6fe9af",
"from": "0x9f67f9af54513a1790ed159dd65e8be73fb531fd",
"to": null,
"cumulativeGasUsed": "0x5ea5e5",
"gasUsed": "0x126035",
"contractAddress": "0x3e718c61a2849fbb0181eba83de4ee8363014106",
"logs": [],
"status": "0x1",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"type": "0x2",
"effectiveGasPrice": "0xb2d05e10"
}
],
"libraries": [],
"pending": [],
"path": "broadcast/Deploy.s.sol/5/run-latest.json",
"returns": {},
"timestamp": 1659449455
}
[profile.default]
src = 'src'
out = 'out'
libs = ['lib']
solc-version = "0.8.15"
optimizer = true
via_ir = true
optimizer_runs = 1000000
bytecode_hash = "none"
# See more config options https://github.com/foundry-rs/foundry/tree/master/config
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/AccessControl.sol)
pragma solidity ^0.8.0;
import "./IAccessControl.sol";
import "../utils/Context.sol";
import "../utils/Strings.sol";
import "../utils/introspection/ERC165.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it.
*/
abstract contract AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address => bool) members;
bytes32 adminRole;
}
mapping(bytes32 => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with a standardized message including the required role.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*
* _Available since v4.1._
*/
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
return _roles[role].members[account];
}
/**
* @dev Revert with a standard message if `_msgSender()` is missing `role`.
* Overriding this function changes the behavior of the {onlyRole} modifier.
*
* Format of the revert message is described in {_checkRole}.
*
* _Available since v4.6._
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Revert with a standard message if `account` is missing `role`.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert(
string(
abi.encodePacked(
"AccessControl: account ",
Strings.toHexString(account),
" is missing role ",
Strings.toHexString(uint256(role), 32)
)
)
);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
return _roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleGranted} event.
*/
function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleRevoked} event.
*/
function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address account) public virtual override {
require(account == _msgSender(), "AccessControl: can only renounce roles for self");
_revokeRole(role, account);
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event. Note that unlike {grantRole}, this function doesn't perform any
* checks on the calling account.
*
* May emit a {RoleGranted} event.
*
* [WARNING]
* ====
* This function should only be called from the constructor when setting
* up the initial roles for the system.
*
* Using this function in any other way is effectively circumventing the admin
* system imposed by {AccessControl}.
* ====
*
* NOTE: This function is deprecated in favor of {_grantRole}.
*/
function _setupRole(bytes32 role, address account) internal virtual {
_grantRole(role, account);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Grants `role` to `account`.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual {
if (!hasRole(role, account)) {
_roles[role].members[account] = true;
emit RoleGranted(role, account, _msgSender());
}
}
/**
* @dev Revokes `role` from `account`.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual {
if (hasRole(role, account)) {
_roles[role].members[account] = false;
emit RoleRevoked(role, account, _msgSender());
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (access/AccessControlCrossChain.sol)
pragma solidity ^0.8.4;
import "./AccessControl.sol";
import "../crosschain/CrossChainEnabled.sol";
/**
* @dev An extension to {AccessControl} with support for cross-chain access management.
* For each role, is extension implements an equivalent "aliased" role that is used for
* restricting calls originating from other chains.
*
* For example, if a function `myFunction` is protected by `onlyRole(SOME_ROLE)`, and
* if an address `x` has role `SOME_ROLE`, it would be able to call `myFunction` directly.
* A wallet or contract at the same address on another chain would however not be able
* to call this function. In order to do so, it would require to have the role
* `_crossChainRoleAlias(SOME_ROLE)`.
*
* This aliasing is required to protect against multiple contracts living at the same
* address on different chains but controlled by conflicting entities.
*
* _Available since v4.6._
*/
abstract contract AccessControlCrossChain is AccessControl, CrossChainEnabled {
bytes32 public constant CROSSCHAIN_ALIAS = keccak256("CROSSCHAIN_ALIAS");
/**
* @dev See {AccessControl-_checkRole}.
*/
function _checkRole(bytes32 role) internal view virtual override {
if (_isCrossChain()) {
_checkRole(_crossChainRoleAlias(role), _crossChainSender());
} else {
super._checkRole(role);
}
}
/**
* @dev Returns the aliased role corresponding to `role`.
*/
function _crossChainRoleAlias(bytes32 role) internal pure virtual returns (bytes32) {
return role ^ CROSSCHAIN_ALIAS;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol)
pragma solidity ^0.8.0;
import "./IAccessControlEnumerable.sol";
import "./AccessControl.sol";
import "../utils/structs/EnumerableSet.sol";
/**
* @dev Extension of {AccessControl} that allows enumerating the members of each role.
*/
abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl {
using EnumerableSet for EnumerableSet.AddressSet;
mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers;
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns one of the accounts that have `role`. `index` must be a
* value between 0 and {getRoleMemberCount}, non-inclusive.
*
* Role bearers are not sorted in any particular way, and their ordering may
* change at any point.
*
* WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
* you perform all queries on the same block. See the following
* https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
* for more information.
*/
function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) {
return _roleMembers[role].at(index);
}
/**
* @dev Returns the number of accounts that have `role`. Can be used
* together with {getRoleMember} to enumerate all bearers of a role.
*/
function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) {
return _roleMembers[role].length();
}
/**
* @dev Overload {_grantRole} to track enumerable memberships
*/
function _grantRole(bytes32 role, address account) internal virtual override {
super._grantRole(role, account);
_roleMembers[role].add(account);
}
/**
* @dev Overload {_revokeRole} to track enumerable memberships
*/
function _revokeRole(bytes32 role, address account) internal virtual override {
super._revokeRole(role, account);
_roleMembers[role].remove(account);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
pragma solidity ^0.8.0;
/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/
interface IAccessControl {
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*
* _Available since v3.1._
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, an admin role
* bearer except when using {AccessControl-_setupRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/
function renounceRole(bytes32 role, address account) external;
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol)
pragma solidity ^0.8.0;
import "./IAccessControl.sol";
/**
* @dev External interface of AccessControlEnumerable declared to support ERC165 detection.
*/
interface IAccessControlEnumerable is IAccessControl {
/**
* @dev Returns one of the accounts that have `role`. `index` must be a
* value between 0 and {getRoleMemberCount}, non-inclusive.
*
* Role bearers are not sorted in any particular way, and their ordering may
* change at any point.
*
* WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
* you perform all queries on the same block. See the following
* https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
* for more information.
*/
function getRoleMember(bytes32 role, uint256 index) external view returns (address);
/**
* @dev Returns the number of accounts that have `role`. Can be used
* together with {getRoleMember} to enumerate all bearers of a role.
*/
function getRoleMemberCount(bytes32 role) external view returns (uint256);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "./Ownable.sol";
/**
* @dev Contract module which provides access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership} and {acceptOwnership}.
*
* This module is used through inheritance. It will make available all functions
* from parent (Ownable).
*/
abstract contract Ownable2Step is Ownable {
address private _pendingOwner;
event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
/**
* @dev Returns the address of the pending owner.
*/
function pendingOwner() public view virtual returns (address) {
return _pendingOwner;
}
/**
* @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual override onlyOwner {
_pendingOwner = newOwner;
emit OwnershipTransferStarted(owner(), newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual override {
delete _pendingOwner;
super._transferOwnership(newOwner);
}
/**
* @dev The new owner accepts the ownership transfer.
*/
function acceptOwnership() external {
address sender = _msgSender();
require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner");
_transferOwnership(sender);
}
}

Access Control

Note
This document is better viewed at https://docs.openzeppelin.com/contracts/api/access

This directory provides ways to restrict who can access the functions of a contract or when they can do it.

  • {AccessControl} provides a general role based access control mechanism. Multiple hierarchical roles can be created and assigned each to multiple accounts.

  • {Ownable} is a simpler mechanism with a single owner "role" that can be assigned to a single account. This simpler mechanism can be useful for quick tests but projects with production concerns are likely to outgrow it.

Authorization

{{Ownable}}

{{IAccessControl}}

{{AccessControl}}

{{AccessControlCrossChain}}

{{IAccessControlEnumerable}}

{{AccessControlEnumerable}}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (crosschain/amb/CrossChainEnabledAMB.sol)
pragma solidity ^0.8.4;
import "../CrossChainEnabled.sol";
import "./LibAMB.sol";
/**
* @dev https://docs.tokenbridge.net/amb-bridge/about-amb-bridge[AMB]
* specialization or the {CrossChainEnabled} abstraction.
*
* As of february 2020, AMB bridges are available between the following chains:
*
* - https://docs.tokenbridge.net/eth-xdai-amb-bridge/about-the-eth-xdai-amb[ETH ⇌ xDai]
* - https://docs.tokenbridge.net/eth-qdai-bridge/about-the-eth-qdai-amb[ETH ⇌ qDai]
* - https://docs.tokenbridge.net/eth-etc-amb-bridge/about-the-eth-etc-amb[ETH ⇌ ETC]
* - https://docs.tokenbridge.net/eth-bsc-amb/about-the-eth-bsc-amb[ETH ⇌ BSC]
* - https://docs.tokenbridge.net/eth-poa-amb-bridge/about-the-eth-poa-amb[ETH ⇌ POA]
* - https://docs.tokenbridge.net/bsc-xdai-amb/about-the-bsc-xdai-amb[BSC ⇌ xDai]
* - https://docs.tokenbridge.net/poa-xdai-amb/about-the-poa-xdai-amb[POA ⇌ xDai]
* - https://docs.tokenbridge.net/rinkeby-xdai-amb-bridge/about-the-rinkeby-xdai-amb[Rinkeby ⇌ xDai]
* - https://docs.tokenbridge.net/kovan-sokol-amb-bridge/about-the-kovan-sokol-amb[Kovan ⇌ Sokol]
*
* _Available since v4.6._
*/
contract CrossChainEnabledAMB is CrossChainEnabled {
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
address private immutable _bridge;
/// @custom:oz-upgrades-unsafe-allow constructor
constructor(address bridge) {
_bridge = bridge;
}
/**
* @dev see {CrossChainEnabled-_isCrossChain}
*/
function _isCrossChain() internal view virtual override returns (bool) {
return LibAMB.isCrossChain(_bridge);
}
/**
* @dev see {CrossChainEnabled-_crossChainSender}
*/
function _crossChainSender() internal view virtual override onlyCrossChain returns (address) {
return LibAMB.crossChainSender(_bridge);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (crosschain/amb/LibAMB.sol)
pragma solidity ^0.8.4;
import {IAMB as AMB_Bridge} from "../../vendor/amb/IAMB.sol";
import "../errors.sol";
/**
* @dev Primitives for cross-chain aware contracts using the
* https://docs.tokenbridge.net/amb-bridge/about-amb-bridge[AMB]
* family of bridges.
*/
library LibAMB {
/**
* @dev Returns whether the current function call is the result of a
* cross-chain message relayed by `bridge`.
*/
function isCrossChain(address bridge) internal view returns (bool) {
return msg.sender == bridge;
}
/**
* @dev Returns the address of the sender that triggered the current
* cross-chain message through `bridge`.
*
* NOTE: {isCrossChain} should be checked before trying to recover the
* sender, as it will revert with `NotCrossChainCall` if the current
* function call is not the result of a cross-chain message.
*/
function crossChainSender(address bridge) internal view returns (address) {
if (!isCrossChain(bridge)) revert NotCrossChainCall();
return AMB_Bridge(bridge).messageSender();
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (crosschain/arbitrum/CrossChainEnabledArbitrumL1.sol)
pragma solidity ^0.8.4;
import "../CrossChainEnabled.sol";
import "./LibArbitrumL1.sol";
/**
* @dev https://arbitrum.io/[Arbitrum] specialization or the
* {CrossChainEnabled} abstraction the L1 side (mainnet).
*
* This version should only be deployed on L1 to process cross-chain messages
* originating from L2. For the other side, use {CrossChainEnabledArbitrumL2}.
*
* The bridge contract is provided and maintained by the arbitrum team. You can
* find the address of this contract on the rinkeby testnet in
* https://developer.offchainlabs.com/docs/useful_addresses[Arbitrum's developer documentation].
*
* _Available since v4.6._
*/
abstract contract CrossChainEnabledArbitrumL1 is CrossChainEnabled {
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
address private immutable _bridge;
/// @custom:oz-upgrades-unsafe-allow constructor
constructor(address bridge) {
_bridge = bridge;
}
/**
* @dev see {CrossChainEnabled-_isCrossChain}
*/
function _isCrossChain() internal view virtual override returns (bool) {
return LibArbitrumL1.isCrossChain(_bridge);
}
/**
* @dev see {CrossChainEnabled-_crossChainSender}
*/
function _crossChainSender() internal view virtual override onlyCrossChain returns (address) {
return LibArbitrumL1.crossChainSender(_bridge);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (crosschain/arbitrum/CrossChainEnabledArbitrumL2.sol)
pragma solidity ^0.8.4;
import "../CrossChainEnabled.sol";
import "./LibArbitrumL2.sol";
/**
* @dev https://arbitrum.io/[Arbitrum] specialization or the
* {CrossChainEnabled} abstraction the L2 side (arbitrum).
*
* This version should only be deployed on L2 to process cross-chain messages
* originating from L1. For the other side, use {CrossChainEnabledArbitrumL1}.
*
* Arbitrum L2 includes the `ArbSys` contract at a fixed address. Therefore,
* this specialization of {CrossChainEnabled} does not include a constructor.
*
* _Available since v4.6._
*
* WARNING: There is currently a bug in Arbitrum that causes this contract to
* fail to detect cross-chain calls when deployed behind a proxy. This will be
* fixed when the network is upgraded to Arbitrum Nitro, currently scheduled for
* August 31st 2022.
*/
abstract contract CrossChainEnabledArbitrumL2 is CrossChainEnabled {
/**
* @dev see {CrossChainEnabled-_isCrossChain}
*/
function _isCrossChain() internal view virtual override returns (bool) {
return LibArbitrumL2.isCrossChain(LibArbitrumL2.ARBSYS);
}
/**
* @dev see {CrossChainEnabled-_crossChainSender}
*/
function _crossChainSender() internal view virtual override onlyCrossChain returns (address) {
return LibArbitrumL2.crossChainSender(LibArbitrumL2.ARBSYS);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (crosschain/arbitrum/LibArbitrumL1.sol)
pragma solidity ^0.8.4;
import {IBridge as ArbitrumL1_Bridge} from "../../vendor/arbitrum/IBridge.sol";
import {IOutbox as ArbitrumL1_Outbox} from "../../vendor/arbitrum/IOutbox.sol";
import "../errors.sol";
/**
* @dev Primitives for cross-chain aware contracts for
* https://arbitrum.io/[Arbitrum].
*
* This version should only be used on L1 to process cross-chain messages
* originating from L2. For the other side, use {LibArbitrumL2}.
*/
library LibArbitrumL1 {
/**
* @dev Returns whether the current function call is the result of a
* cross-chain message relayed by the `bridge`.
*/
function isCrossChain(address bridge) internal view returns (bool) {
return msg.sender == bridge;
}
/**
* @dev Returns the address of the sender that triggered the current
* cross-chain message through the `bridge`.
*
* NOTE: {isCrossChain} should be checked before trying to recover the
* sender, as it will revert with `NotCrossChainCall` if the current
* function call is not the result of a cross-chain message.
*/
function crossChainSender(address bridge) internal view returns (address) {
if (!isCrossChain(bridge)) revert NotCrossChainCall();
address sender = ArbitrumL1_Outbox(ArbitrumL1_Bridge(bridge).activeOutbox()).l2ToL1Sender();
require(sender != address(0), "LibArbitrumL1: system messages without sender");
return sender;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (crosschain/arbitrum/LibArbitrumL2.sol)
pragma solidity ^0.8.4;
import {IArbSys as ArbitrumL2_Bridge} from "../../vendor/arbitrum/IArbSys.sol";
import "../errors.sol";
/**
* @dev Primitives for cross-chain aware contracts for
* https://arbitrum.io/[Arbitrum].
*
* This version should only be used on L2 to process cross-chain messages
* originating from L1. For the other side, use {LibArbitrumL1}.
*
* WARNING: There is currently a bug in Arbitrum that causes this contract to
* fail to detect cross-chain calls when deployed behind a proxy. This will be
* fixed when the network is upgraded to Arbitrum Nitro, currently scheduled for
* August 31st 2022.
*/
library LibArbitrumL2 {
/**
* @dev Returns whether the current function call is the result of a
* cross-chain message relayed by `arbsys`.
*/
address public constant ARBSYS = 0x0000000000000000000000000000000000000064;
function isCrossChain(address arbsys) internal view returns (bool) {
return ArbitrumL2_Bridge(arbsys).wasMyCallersAddressAliased();
}
/**
* @dev Returns the address of the sender that triggered the current
* cross-chain message through `arbsys`.
*
* NOTE: {isCrossChain} should be checked before trying to recover the
* sender, as it will revert with `NotCrossChainCall` if the current
* function call is not the result of a cross-chain message.
*/
function crossChainSender(address arbsys) internal view returns (address) {
if (!isCrossChain(arbsys)) revert NotCrossChainCall();
return ArbitrumL2_Bridge(arbsys).myCallersAddressWithoutAliasing();
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (crosschain/CrossChainEnabled.sol)
pragma solidity ^0.8.4;
import "./errors.sol";
/**
* @dev Provides information for building cross-chain aware contracts. This
* abstract contract provides accessors and modifiers to control the execution
* flow when receiving cross-chain messages.
*
* Actual implementations of cross-chain aware contracts, which are based on
* this abstraction, will have to inherit from a bridge-specific
* specialization. Such specializations are provided under
* `crosschain/<chain>/CrossChainEnabled<chain>.sol`.
*
* _Available since v4.6._
*/
abstract contract CrossChainEnabled {
/**
* @dev Throws if the current function call is not the result of a
* cross-chain execution.
*/
modifier onlyCrossChain() {
if (!_isCrossChain()) revert NotCrossChainCall();
_;
}
/**
* @dev Throws if the current function call is not the result of a
* cross-chain execution initiated by `account`.
*/
modifier onlyCrossChainSender(address expected) {
address actual = _crossChainSender();
if (expected != actual) revert InvalidCrossChainSender(actual, expected);
_;
}
/**
* @dev Returns whether the current function call is the result of a
* cross-chain message.
*/
function _isCrossChain() internal view virtual returns (bool);
/**
* @dev Returns the address of the sender of the cross-chain message that
* triggered the current function call.
*
* IMPORTANT: Should revert with `NotCrossChainCall` if the current function
* call is not the result of a cross-chain message.
*/
function _crossChainSender() internal view virtual returns (address);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (crosschain/errors.sol)
pragma solidity ^0.8.4;
error NotCrossChainCall();
error InvalidCrossChainSender(address actual, address expected);
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (crosschain/optimism/CrossChainEnabledOptimism.sol)
pragma solidity ^0.8.4;
import "../CrossChainEnabled.sol";
import "./LibOptimism.sol";
/**
* @dev https://www.optimism.io/[Optimism] specialization or the
* {CrossChainEnabled} abstraction.
*
* The messenger (`CrossDomainMessenger`) contract is provided and maintained by
* the optimism team. You can find the address of this contract on mainnet and
* kovan in the https://github.com/ethereum-optimism/optimism/tree/develop/packages/contracts/deployments[deployments section of Optimism monorepo].
*
* _Available since v4.6._
*/
abstract contract CrossChainEnabledOptimism is CrossChainEnabled {
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
address private immutable _messenger;
/// @custom:oz-upgrades-unsafe-allow constructor
constructor(address messenger) {
_messenger = messenger;
}
/**
* @dev see {CrossChainEnabled-_isCrossChain}
*/
function _isCrossChain() internal view virtual override returns (bool) {
return LibOptimism.isCrossChain(_messenger);
}
/**
* @dev see {CrossChainEnabled-_crossChainSender}
*/
function _crossChainSender() internal view virtual override onlyCrossChain returns (address) {
return LibOptimism.crossChainSender(_messenger);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (crosschain/optimism/LibOptimism.sol)
pragma solidity ^0.8.4;
import {ICrossDomainMessenger as Optimism_Bridge} from "../../vendor/optimism/ICrossDomainMessenger.sol";
import "../errors.sol";
/**
* @dev Primitives for cross-chain aware contracts for https://www.optimism.io/[Optimism].
* See the https://community.optimism.io/docs/developers/bridge/messaging/#accessing-msg-sender[documentation]
* for the functionality used here.
*/
library LibOptimism {
/**
* @dev Returns whether the current function call is the result of a
* cross-chain message relayed by `messenger`.
*/
function isCrossChain(address messenger) internal view returns (bool) {
return msg.sender == messenger;
}
/**
* @dev Returns the address of the sender that triggered the current
* cross-chain message through `messenger`.
*
* NOTE: {isCrossChain} should be checked before trying to recover the
* sender, as it will revert with `NotCrossChainCall` if the current
* function call is not the result of a cross-chain message.
*/
function crossChainSender(address messenger) internal view returns (address) {
if (!isCrossChain(messenger)) revert NotCrossChainCall();
return Optimism_Bridge(messenger).xDomainMessageSender();
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (crosschain/polygon/CrossChainEnabledPolygonChild.sol)
pragma solidity ^0.8.4;
import "../CrossChainEnabled.sol";
import "../../security/ReentrancyGuard.sol";
import "../../utils/Address.sol";
import "../../vendor/polygon/IFxMessageProcessor.sol";
address constant DEFAULT_SENDER = 0x000000000000000000000000000000000000dEaD;
/**
* @dev https://polygon.technology/[Polygon] specialization or the
* {CrossChainEnabled} abstraction the child side (polygon/mumbai).
*
* This version should only be deployed on child chain to process cross-chain
* messages originating from the parent chain.
*
* The fxChild contract is provided and maintained by the polygon team. You can
* find the address of this contract polygon and mumbai in
* https://docs.polygon.technology/docs/develop/l1-l2-communication/fx-portal/#contract-addresses[Polygon's Fx-Portal documentation].
*
* _Available since v4.6._
*/
abstract contract CrossChainEnabledPolygonChild is IFxMessageProcessor, CrossChainEnabled, ReentrancyGuard {
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
address private immutable _fxChild;
address private _sender = DEFAULT_SENDER;
/// @custom:oz-upgrades-unsafe-allow constructor
constructor(address fxChild) {
_fxChild = fxChild;
}
/**
* @dev see {CrossChainEnabled-_isCrossChain}
*/
function _isCrossChain() internal view virtual override returns (bool) {
return msg.sender == _fxChild;
}
/**
* @dev see {CrossChainEnabled-_crossChainSender}
*/
function _crossChainSender() internal view virtual override onlyCrossChain returns (address) {
return _sender;
}
/**
* @dev External entry point to receive and relay messages originating
* from the fxChild.
*
* Non-reentrancy is crucial to avoid a cross-chain call being able
* to impersonate anyone by just looping through this with user-defined
* arguments.
*
* Note: if _fxChild calls any other function that does a delegate-call,
* then security could be compromised.
*/
function processMessageFromRoot(
uint256, /* stateId */
address rootMessageSender,
bytes calldata data
) external override nonReentrant {
if (!_isCrossChain()) revert NotCrossChainCall();
_sender = rootMessageSender;
Address.functionDelegateCall(address(this), data, "cross-chain execution failed");
_sender = DEFAULT_SENDER;
}
}

Cross Chain Awareness

Note
This document is better viewed at https://docs.openzeppelin.com/contracts/api/crosschain

This directory provides building blocks to improve cross-chain awareness of smart contracts.

  • {CrossChainEnabled} is an abstraction that contains accessors and modifiers to control the execution flow when receiving cross-chain messages.

CrossChainEnabled specializations

The following specializations of {CrossChainEnabled} provide implementations of the {CrossChainEnabled} abstraction for specific bridges. This can be used to complex cross-chain aware components such as {AccessControlCrossChain}.

{{CrossChainEnabledAMB}}

{{CrossChainEnabledArbitrumL1}}

{{CrossChainEnabledArbitrumL2}}

{{CrossChainEnabledOptimism}}

{{CrossChainEnabledPolygonChild}}

Libraries for cross-chain

In addition to the {CrossChainEnabled} abstraction, cross-chain awareness is also available through libraries. These libraries can be used to build complex designs such as contracts with the ability to interact with multiple bridges.

{{LibAMB}}

{{LibArbitrumL1}}

{{LibArbitrumL2}}

{{LibOptimism}}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (finance/PaymentSplitter.sol)
pragma solidity ^0.8.0;
import "../token/ERC20/utils/SafeERC20.sol";
import "../utils/Address.sol";
import "../utils/Context.sol";
/**
* @title PaymentSplitter
* @dev This contract allows to split Ether payments among a group of accounts. The sender does not need to be aware
* that the Ether will be split in this way, since it is handled transparently by the contract.
*
* The split can be in equal parts or in any other arbitrary proportion. The way this is specified is by assigning each
* account to a number of shares. Of all the Ether that this contract receives, each account will then be able to claim
* an amount proportional to the percentage of total shares they were assigned. The distribution of shares is set at the
* time of contract deployment and can't be updated thereafter.
*
* `PaymentSplitter` follows a _pull payment_ model. This means that payments are not automatically forwarded to the
* accounts but kept in this contract, and the actual transfer is triggered as a separate step by calling the {release}
* function.
*
* NOTE: This contract assumes that ERC20 tokens will behave similarly to native tokens (Ether). Rebasing tokens, and
* tokens that apply fees during transfers, are likely to not be supported as expected. If in doubt, we encourage you
* to run tests before sending real value to this contract.
*/
contract PaymentSplitter is Context {
event PayeeAdded(address account, uint256 shares);
event PaymentReleased(address to, uint256 amount);
event ERC20PaymentReleased(IERC20 indexed token, address to, uint256 amount);
event PaymentReceived(address from, uint256 amount);
uint256 private _totalShares;
uint256 private _totalReleased;
mapping(address => uint256) private _shares;
mapping(address => uint256) private _released;
address[] private _payees;
mapping(IERC20 => uint256) private _erc20TotalReleased;
mapping(IERC20 => mapping(address => uint256)) private _erc20Released;
/**
* @dev Creates an instance of `PaymentSplitter` where each account in `payees` is assigned the number of shares at
* the matching position in the `shares` array.
*
* All addresses in `payees` must be non-zero. Both arrays must have the same non-zero length, and there must be no
* duplicates in `payees`.
*/
constructor(address[] memory payees, uint256[] memory shares_) payable {
require(payees.length == shares_.length, "PaymentSplitter: payees and shares length mismatch");
require(payees.length > 0, "PaymentSplitter: no payees");
for (uint256 i = 0; i < payees.length; i++) {
_addPayee(payees[i], shares_[i]);
}
}
/**
* @dev The Ether received will be logged with {PaymentReceived} events. Note that these events are not fully
* reliable: it's possible for a contract to receive Ether without triggering this function. This only affects the
* reliability of the events, and not the actual splitting of Ether.
*
* To learn more about this see the Solidity documentation for
* https://solidity.readthedocs.io/en/latest/contracts.html#fallback-function[fallback
* functions].
*/
receive() external payable virtual {
emit PaymentReceived(_msgSender(), msg.value);
}
/**
* @dev Getter for the total shares held by payees.
*/
function totalShares() public view returns (uint256) {
return _totalShares;
}
/**
* @dev Getter for the total amount of Ether already released.
*/
function totalReleased() public view returns (uint256) {
return _totalReleased;
}
/**
* @dev Getter for the total amount of `token` already released. `token` should be the address of an IERC20
* contract.
*/
function totalReleased(IERC20 token) public view returns (uint256) {
return _erc20TotalReleased[token];
}
/**
* @dev Getter for the amount of shares held by an account.
*/
function shares(address account) public view returns (uint256) {
return _shares[account];
}
/**
* @dev Getter for the amount of Ether already released to a payee.
*/
function released(address account) public view returns (uint256) {
return _released[account];
}
/**
* @dev Getter for the amount of `token` tokens already released to a payee. `token` should be the address of an
* IERC20 contract.
*/
function released(IERC20 token, address account) public view returns (uint256) {
return _erc20Released[token][account];
}
/**
* @dev Getter for the address of the payee number `index`.
*/
function payee(uint256 index) public view returns (address) {
return _payees[index];
}
/**
* @dev Getter for the amount of payee's releasable Ether.
*/
function releasable(address account) public view returns (uint256) {
uint256 totalReceived = address(this).balance + totalReleased();
return _pendingPayment(account, totalReceived, released(account));
}
/**
* @dev Getter for the amount of payee's releasable `token` tokens. `token` should be the address of an
* IERC20 contract.
*/
function releasable(IERC20 token, address account) public view returns (uint256) {
uint256 totalReceived = token.balanceOf(address(this)) + totalReleased(token);
return _pendingPayment(account, totalReceived, released(token, account));
}
/**
* @dev Triggers a transfer to `account` of the amount of Ether they are owed, according to their percentage of the
* total shares and their previous withdrawals.
*/
function release(address payable account) public virtual {
require(_shares[account] > 0, "PaymentSplitter: account has no shares");
uint256 payment = releasable(account);
require(payment != 0, "PaymentSplitter: account is not due payment");
// _totalReleased is the sum of all values in _released.
// If "_totalReleased += payment" does not overflow, then "_released[account] += payment" cannot overflow.
_totalReleased += payment;
unchecked {
_released[account] += payment;
}
Address.sendValue(account, payment);
emit PaymentReleased(account, payment);
}
/**
* @dev Triggers a transfer to `account` of the amount of `token` tokens they are owed, according to their
* percentage of the total shares and their previous withdrawals. `token` must be the address of an IERC20
* contract.
*/
function release(IERC20 token, address account) public virtual {
require(_shares[account] > 0, "PaymentSplitter: account has no shares");
uint256 payment = releasable(token, account);
require(payment != 0, "PaymentSplitter: account is not due payment");
// _erc20TotalReleased[token] is the sum of all values in _erc20Released[token].
// If "_erc20TotalReleased[token] += payment" does not overflow, then "_erc20Released[token][account] += payment"
// cannot overflow.
_erc20TotalReleased[token] += payment;
unchecked {
_erc20Released[token][account] += payment;
}
SafeERC20.safeTransfer(token, account, payment);
emit ERC20PaymentReleased(token, account, payment);
}
/**
* @dev internal logic for computing the pending payment of an `account` given the token historical balances and
* already released amounts.
*/
function _pendingPayment(
address account,
uint256 totalReceived,
uint256 alreadyReleased
) private view returns (uint256) {
return (totalReceived * _shares[account]) / _totalShares - alreadyReleased;
}
/**
* @dev Add a new payee to the contract.
* @param account The address of the payee to add.
* @param shares_ The number of shares owned by the payee.
*/
function _addPayee(address account, uint256 shares_) private {
require(account != address(0), "PaymentSplitter: account is the zero address");
require(shares_ > 0, "PaymentSplitter: shares are 0");
require(_shares[account] == 0, "PaymentSplitter: account already has shares");
_payees.push(account);
_shares[account] = shares_;
_totalShares = _totalShares + shares_;
emit PayeeAdded(account, shares_);
}
}

Finance

Note
This document is better viewed at https://docs.openzeppelin.com/contracts/api/finance

This directory includes primitives for financial systems:

  • {PaymentSplitter} allows to split Ether and ERC20 payments among a group of accounts. The sender does not need to be aware that the assets will be split in this way, since it is handled transparently by the contract. The split can be in equal parts or in any other arbitrary proportion.

  • {VestingWallet} handles the vesting of Ether and ERC20 tokens for a given beneficiary. Custody of multiple tokens can be given to this contract, which will release the token to the beneficiary following a given, customizable, vesting schedule.

Contracts

{{PaymentSplitter}}

{{VestingWallet}}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (finance/VestingWallet.sol)
pragma solidity ^0.8.0;
import "../token/ERC20/utils/SafeERC20.sol";
import "../utils/Address.sol";
import "../utils/Context.sol";
/**
* @title VestingWallet
* @dev This contract handles the vesting of Eth and ERC20 tokens for a given beneficiary. Custody of multiple tokens
* can be given to this contract, which will release the token to the beneficiary following a given vesting schedule.
* The vesting schedule is customizable through the {vestedAmount} function.
*
* Any token transferred to this contract will follow the vesting schedule as if they were locked from the beginning.
* Consequently, if the vesting has already started, any amount of tokens sent to this contract will (at least partly)
* be immediately releasable.
*/
contract VestingWallet is Context {
event EtherReleased(uint256 amount);
event ERC20Released(address indexed token, uint256 amount);
uint256 private _released;
mapping(address => uint256) private _erc20Released;
address private immutable _beneficiary;
uint64 private immutable _start;
uint64 private immutable _duration;
/**
* @dev Set the beneficiary, start timestamp and vesting duration of the vesting wallet.
*/
constructor(
address beneficiaryAddress,
uint64 startTimestamp,
uint64 durationSeconds
) payable {
require(beneficiaryAddress != address(0), "VestingWallet: beneficiary is zero address");
_beneficiary = beneficiaryAddress;
_start = startTimestamp;
_duration = durationSeconds;
}
/**
* @dev The contract should be able to receive Eth.
*/
receive() external payable virtual {}
/**
* @dev Getter for the beneficiary address.
*/
function beneficiary() public view virtual returns (address) {
return _beneficiary;
}
/**
* @dev Getter for the start timestamp.
*/
function start() public view virtual returns (uint256) {
return _start;
}
/**
* @dev Getter for the vesting duration.
*/
function duration() public view virtual returns (uint256) {
return _duration;
}
/**
* @dev Amount of eth already released
*/
function released() public view virtual returns (uint256) {
return _released;
}
/**
* @dev Amount of token already released
*/
function released(address token) public view virtual returns (uint256) {
return _erc20Released[token];
}
/**
* @dev Getter for the amount of releasable eth.
*/
function releasable() public view virtual returns (uint256) {
return vestedAmount(uint64(block.timestamp)) - released();
}
/**
* @dev Getter for the amount of releasable `token` tokens. `token` should be the address of an
* IERC20 contract.
*/
function releasable(address token) public view virtual returns (uint256) {
return vestedAmount(token, uint64(block.timestamp)) - released(token);
}
/**
* @dev Release the native token (ether) that have already vested.
*
* Emits a {EtherReleased} event.
*/
function release() public virtual {
uint256 amount = releasable();
_released += amount;
emit EtherReleased(amount);
Address.sendValue(payable(beneficiary()), amount);
}
/**
* @dev Release the tokens that have already vested.
*
* Emits a {ERC20Released} event.
*/
function release(address token) public virtual {
uint256 amount = releasable(token);
_erc20Released[token] += amount;
emit ERC20Released(token, amount);
SafeERC20.safeTransfer(IERC20(token), beneficiary(), amount);
}
/**
* @dev Calculates the amount of ether that has already vested. Default implementation is a linear vesting curve.
*/
function vestedAmount(uint64 timestamp) public view virtual returns (uint256) {
return _vestingSchedule(address(this).balance + released(), timestamp);
}
/**
* @dev Calculates the amount of tokens that has already vested. Default implementation is a linear vesting curve.
*/
function vestedAmount(address token, uint64 timestamp) public view virtual returns (uint256) {
return _vestingSchedule(IERC20(token).balanceOf(address(this)) + released(token), timestamp);
}
/**
* @dev Virtual implementation of the vesting formula. This returns the amount vested, as a function of time, for
* an asset given its total historical allocation.
*/
function _vestingSchedule(uint256 totalAllocation, uint64 timestamp) internal view virtual returns (uint256) {
if (timestamp < start()) {
return 0;
} else if (timestamp > start() + duration()) {
return totalAllocation;
} else {
return (totalAllocation * (timestamp - start())) / duration();
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (governance/compatibility/GovernorCompatibilityBravo.sol)
pragma solidity ^0.8.0;
import "../../utils/math/SafeCast.sol";
import "../extensions/IGovernorTimelock.sol";
import "../Governor.sol";
import "./IGovernorCompatibilityBravo.sol";
/**
* @dev Compatibility layer that implements GovernorBravo compatibility on top of {Governor}.
*
* This compatibility layer includes a voting system and requires a {IGovernorTimelock} compatible module to be added
* through inheritance. It does not include token bindings, nor does it include any variable upgrade patterns.
*
* NOTE: When using this module, you may need to enable the Solidity optimizer to avoid hitting the contract size limit.
*
* _Available since v4.3._
*/
abstract contract GovernorCompatibilityBravo is IGovernorTimelock, IGovernorCompatibilityBravo, Governor {
enum VoteType {
Against,
For,
Abstain
}
struct ProposalDetails {
address proposer;
address[] targets;
uint256[] values;
string[] signatures;
bytes[] calldatas;
uint256 forVotes;
uint256 againstVotes;
uint256 abstainVotes;
mapping(address => Receipt) receipts;
bytes32 descriptionHash;
}
mapping(uint256 => ProposalDetails) private _proposalDetails;
// solhint-disable-next-line func-name-mixedcase
function COUNTING_MODE() public pure virtual override returns (string memory) {
return "support=bravo&quorum=bravo";
}
// ============================================== Proposal lifecycle ==============================================
/**
* @dev See {IGovernor-propose}.
*/
function propose(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
string memory description
) public virtual override(IGovernor, Governor) returns (uint256) {
_storeProposal(_msgSender(), targets, values, new string[](calldatas.length), calldatas, description);
return super.propose(targets, values, calldatas, description);
}
/**
* @dev See {IGovernorCompatibilityBravo-propose}.
*/
function propose(
address[] memory targets,
uint256[] memory values,
string[] memory signatures,
bytes[] memory calldatas,
string memory description
) public virtual override returns (uint256) {
_storeProposal(_msgSender(), targets, values, signatures, calldatas, description);
return propose(targets, values, _encodeCalldata(signatures, calldatas), description);
}
/**
* @dev See {IGovernorCompatibilityBravo-queue}.
*/
function queue(uint256 proposalId) public virtual override {
ProposalDetails storage details = _proposalDetails[proposalId];
queue(
details.targets,
details.values,
_encodeCalldata(details.signatures, details.calldatas),
details.descriptionHash
);
}
/**
* @dev See {IGovernorCompatibilityBravo-execute}.
*/
function execute(uint256 proposalId) public payable virtual override {
ProposalDetails storage details = _proposalDetails[proposalId];
execute(
details.targets,
details.values,
_encodeCalldata(details.signatures, details.calldatas),
details.descriptionHash
);
}
function cancel(uint256 proposalId) public virtual override {
ProposalDetails storage details = _proposalDetails[proposalId];
require(
_msgSender() == details.proposer || getVotes(details.proposer, block.number - 1) < proposalThreshold(),
"GovernorBravo: proposer above threshold"
);
_cancel(
details.targets,
details.values,
_encodeCalldata(details.signatures, details.calldatas),
details.descriptionHash
);
}
/**
* @dev Encodes calldatas with optional function signature.
*/
function _encodeCalldata(string[] memory signatures, bytes[] memory calldatas)
private
pure
returns (bytes[] memory)
{
bytes[] memory fullcalldatas = new bytes[](calldatas.length);
for (uint256 i = 0; i < signatures.length; ++i) {
fullcalldatas[i] = bytes(signatures[i]).length == 0
? calldatas[i]
: abi.encodePacked(bytes4(keccak256(bytes(signatures[i]))), calldatas[i]);
}
return fullcalldatas;
}
/**
* @dev Store proposal metadata for later lookup
*/
function _storeProposal(
address proposer,
address[] memory targets,
uint256[] memory values,
string[] memory signatures,
bytes[] memory calldatas,
string memory description
) private {
bytes32 descriptionHash = keccak256(bytes(description));
uint256 proposalId = hashProposal(targets, values, _encodeCalldata(signatures, calldatas), descriptionHash);
ProposalDetails storage details = _proposalDetails[proposalId];
if (details.descriptionHash == bytes32(0)) {
details.proposer = proposer;
details.targets = targets;
details.values = values;
details.signatures = signatures;
details.calldatas = calldatas;
details.descriptionHash = descriptionHash;
}
}
// ==================================================== Views =====================================================
/**
* @dev See {IGovernorCompatibilityBravo-proposals}.
*/
function proposals(uint256 proposalId)
public
view
virtual
override
returns (
uint256 id,
address proposer,
uint256 eta,
uint256 startBlock,
uint256 endBlock,
uint256 forVotes,
uint256 againstVotes,
uint256 abstainVotes,
bool canceled,
bool executed
)
{
id = proposalId;
eta = proposalEta(proposalId);
startBlock = proposalSnapshot(proposalId);
endBlock = proposalDeadline(proposalId);
ProposalDetails storage details = _proposalDetails[proposalId];
proposer = details.proposer;
forVotes = details.forVotes;
againstVotes = details.againstVotes;
abstainVotes = details.abstainVotes;
ProposalState status = state(proposalId);
canceled = status == ProposalState.Canceled;
executed = status == ProposalState.Executed;
}
/**
* @dev See {IGovernorCompatibilityBravo-getActions}.
*/
function getActions(uint256 proposalId)
public
view
virtual
override
returns (
address[] memory targets,
uint256[] memory values,
string[] memory signatures,
bytes[] memory calldatas
)
{
ProposalDetails storage details = _proposalDetails[proposalId];
return (details.targets, details.values, details.signatures, details.calldatas);
}
/**
* @dev See {IGovernorCompatibilityBravo-getReceipt}.
*/
function getReceipt(uint256 proposalId, address voter) public view virtual override returns (Receipt memory) {
return _proposalDetails[proposalId].receipts[voter];
}
/**
* @dev See {IGovernorCompatibilityBravo-quorumVotes}.
*/
function quorumVotes() public view virtual override returns (uint256) {
return quorum(block.number - 1);
}
// ==================================================== Voting ====================================================
/**
* @dev See {IGovernor-hasVoted}.
*/
function hasVoted(uint256 proposalId, address account) public view virtual override returns (bool) {
return _proposalDetails[proposalId].receipts[account].hasVoted;
}
/**
* @dev See {Governor-_quorumReached}. In this module, only forVotes count toward the quorum.
*/
function _quorumReached(uint256 proposalId) internal view virtual override returns (bool) {
ProposalDetails storage details = _proposalDetails[proposalId];
return quorum(proposalSnapshot(proposalId)) <= details.forVotes;
}
/**
* @dev See {Governor-_voteSucceeded}. In this module, the forVotes must be strictly over the againstVotes.
*/
function _voteSucceeded(uint256 proposalId) internal view virtual override returns (bool) {
ProposalDetails storage details = _proposalDetails[proposalId];
return details.forVotes > details.againstVotes;
}
/**
* @dev See {Governor-_countVote}. In this module, the support follows Governor Bravo.
*/
function _countVote(
uint256 proposalId,
address account,
uint8 support,
uint256 weight,
bytes memory // params
) internal virtual override {
ProposalDetails storage details = _proposalDetails[proposalId];
Receipt storage receipt = details.receipts[account];
require(!receipt.hasVoted, "GovernorCompatibilityBravo: vote already cast");
receipt.hasVoted = true;
receipt.support = support;
receipt.votes = SafeCast.toUint96(weight);
if (support == uint8(VoteType.Against)) {
details.againstVotes += weight;
} else if (support == uint8(VoteType.For)) {
details.forVotes += weight;
} else if (support == uint8(VoteType.Abstain)) {
details.abstainVotes += weight;
} else {
revert("GovernorCompatibilityBravo: invalid vote type");
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (governance/compatibility/IGovernorCompatibilityBravo.sol)
pragma solidity ^0.8.0;
import "../IGovernor.sol";
/**
* @dev Interface extension that adds missing functions to the {Governor} core to provide `GovernorBravo` compatibility.
*
* _Available since v4.3._
*/
abstract contract IGovernorCompatibilityBravo is IGovernor {
/**
* @dev Proposal structure from Compound Governor Bravo. Not actually used by the compatibility layer, as
* {{proposal}} returns a very different structure.
*/
struct Proposal {
uint256 id;
address proposer;
uint256 eta;
address[] targets;
uint256[] values;
string[] signatures;
bytes[] calldatas;
uint256 startBlock;
uint256 endBlock;
uint256 forVotes;
uint256 againstVotes;
uint256 abstainVotes;
bool canceled;
bool executed;
mapping(address => Receipt) receipts;
}
/**
* @dev Receipt structure from Compound Governor Bravo
*/
struct Receipt {
bool hasVoted;
uint8 support;
uint96 votes;
}
/**
* @dev Part of the Governor Bravo's interface.
*/
function quorumVotes() public view virtual returns (uint256);
/**
* @dev Part of the Governor Bravo's interface: _"The official record of all proposals ever proposed"_.
*/
function proposals(uint256)
public
view
virtual
returns (
uint256 id,
address proposer,
uint256 eta,
uint256 startBlock,
uint256 endBlock,
uint256 forVotes,
uint256 againstVotes,
uint256 abstainVotes,
bool canceled,
bool executed
);
/**
* @dev Part of the Governor Bravo's interface: _"Function used to propose a new proposal"_.
*/
function propose(
address[] memory targets,
uint256[] memory values,
string[] memory signatures,
bytes[] memory calldatas,
string memory description
) public virtual returns (uint256);
/**
* @dev Part of the Governor Bravo's interface: _"Queues a proposal of state succeeded"_.
*/
function queue(uint256 proposalId) public virtual;
/**
* @dev Part of the Governor Bravo's interface: _"Executes a queued proposal if eta has passed"_.
*/
function execute(uint256 proposalId) public payable virtual;
/**
* @dev Cancels a proposal only if sender is the proposer, or proposer delegates dropped below proposal threshold.
*/
function cancel(uint256 proposalId) public virtual;
/**
* @dev Part of the Governor Bravo's interface: _"Gets actions of a proposal"_.
*/
function getActions(uint256 proposalId)
public
view
virtual
returns (
address[] memory targets,
uint256[] memory values,
string[] memory signatures,
bytes[] memory calldatas
);
/**
* @dev Part of the Governor Bravo's interface: _"Gets the receipt for a voter on a given proposal"_.
*/
function getReceipt(uint256 proposalId, address voter) public view virtual returns (Receipt memory);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (governance/extensions/GovernorCountingSimple.sol)
pragma solidity ^0.8.0;
import "../Governor.sol";
/**
* @dev Extension of {Governor} for simple, 3 options, vote counting.
*
* _Available since v4.3._
*/
abstract contract GovernorCountingSimple is Governor {
/**
* @dev Supported vote types. Matches Governor Bravo ordering.
*/
enum VoteType {
Against,
For,
Abstain
}
struct ProposalVote {
uint256 againstVotes;
uint256 forVotes;
uint256 abstainVotes;
mapping(address => bool) hasVoted;
}
mapping(uint256 => ProposalVote) private _proposalVotes;
/**
* @dev See {IGovernor-COUNTING_MODE}.
*/
// solhint-disable-next-line func-name-mixedcase
function COUNTING_MODE() public pure virtual override returns (string memory) {
return "support=bravo&quorum=for,abstain";
}
/**
* @dev See {IGovernor-hasVoted}.
*/
function hasVoted(uint256 proposalId, address account) public view virtual override returns (bool) {
return _proposalVotes[proposalId].hasVoted[account];
}
/**
* @dev Accessor to the internal vote counts.
*/
function proposalVotes(uint256 proposalId)
public
view
virtual
returns (
uint256 againstVotes,
uint256 forVotes,
uint256 abstainVotes
)
{
ProposalVote storage proposalVote = _proposalVotes[proposalId];
return (proposalVote.againstVotes, proposalVote.forVotes, proposalVote.abstainVotes);
}
/**
* @dev See {Governor-_quorumReached}.
*/
function _quorumReached(uint256 proposalId) internal view virtual override returns (bool) {
ProposalVote storage proposalVote = _proposalVotes[proposalId];
return quorum(proposalSnapshot(proposalId)) <= proposalVote.forVotes + proposalVote.abstainVotes;
}
/**
* @dev See {Governor-_voteSucceeded}. In this module, the forVotes must be strictly over the againstVotes.
*/
function _voteSucceeded(uint256 proposalId) internal view virtual override returns (bool) {
ProposalVote storage proposalVote = _proposalVotes[proposalId];
return proposalVote.forVotes > proposalVote.againstVotes;
}
/**
* @dev See {Governor-_countVote}. In this module, the support follows the `VoteType` enum (from Governor Bravo).
*/
function _countVote(
uint256 proposalId,
address account,
uint8 support,
uint256 weight,
bytes memory // params
) internal virtual override {
ProposalVote storage proposalVote = _proposalVotes[proposalId];
require(!proposalVote.hasVoted[account], "GovernorVotingSimple: vote already cast");
proposalVote.hasVoted[account] = true;
if (support == uint8(VoteType.Against)) {
proposalVote.againstVotes += weight;
} else if (support == uint8(VoteType.For)) {
proposalVote.forVotes += weight;
} else if (support == uint8(VoteType.Abstain)) {
proposalVote.abstainVotes += weight;
} else {
revert("GovernorVotingSimple: invalid value for enum VoteType");
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (governance/extensions/GovernorPreventLateQuorum.sol)
pragma solidity ^0.8.0;
import "../Governor.sol";
import "../../utils/math/Math.sol";
/**
* @dev A module that ensures there is a minimum voting period after quorum is reached. This prevents a large voter from
* swaying a vote and triggering quorum at the last minute, by ensuring there is always time for other voters to react
* and try to oppose the decision.
*
* If a vote causes quorum to be reached, the proposal's voting period may be extended so that it does not end before at
* least a given number of blocks have passed (the "vote extension" parameter). This parameter can be set by the
* governance executor (e.g. through a governance proposal).
*
* _Available since v4.5._
*/
abstract contract GovernorPreventLateQuorum is Governor {
using SafeCast for uint256;
using Timers for Timers.BlockNumber;
uint64 private _voteExtension;
mapping(uint256 => Timers.BlockNumber) private _extendedDeadlines;
/// @dev Emitted when a proposal deadline is pushed back due to reaching quorum late in its voting period.
event ProposalExtended(uint256 indexed proposalId, uint64 extendedDeadline);
/// @dev Emitted when the {lateQuorumVoteExtension} parameter is changed.
event LateQuorumVoteExtensionSet(uint64 oldVoteExtension, uint64 newVoteExtension);
/**
* @dev Initializes the vote extension parameter: the number of blocks that are required to pass since a proposal
* reaches quorum until its voting period ends. If necessary the voting period will be extended beyond the one set
* at proposal creation.
*/
constructor(uint64 initialVoteExtension) {
_setLateQuorumVoteExtension(initialVoteExtension);
}
/**
* @dev Returns the proposal deadline, which may have been extended beyond that set at proposal creation, if the
* proposal reached quorum late in the voting period. See {Governor-proposalDeadline}.
*/
function proposalDeadline(uint256 proposalId) public view virtual override returns (uint256) {
return Math.max(super.proposalDeadline(proposalId), _extendedDeadlines[proposalId].getDeadline());
}
/**
* @dev Casts a vote and detects if it caused quorum to be reached, potentially extending the voting period. See
* {Governor-_castVote}.
*
* May emit a {ProposalExtended} event.
*/
function _castVote(
uint256 proposalId,
address account,
uint8 support,
string memory reason,
bytes memory params
) internal virtual override returns (uint256) {
uint256 result = super._castVote(proposalId, account, support, reason, params);
Timers.BlockNumber storage extendedDeadline = _extendedDeadlines[proposalId];
if (extendedDeadline.isUnset() && _quorumReached(proposalId)) {
uint64 extendedDeadlineValue = block.number.toUint64() + lateQuorumVoteExtension();
if (extendedDeadlineValue > proposalDeadline(proposalId)) {
emit ProposalExtended(proposalId, extendedDeadlineValue);
}
extendedDeadline.setDeadline(extendedDeadlineValue);
}
return result;
}
/**
* @dev Returns the current value of the vote extension parameter: the number of blocks that are required to pass
* from the time a proposal reaches quorum until its voting period ends.
*/
function lateQuorumVoteExtension() public view virtual returns (uint64) {
return _voteExtension;
}
/**
* @dev Changes the {lateQuorumVoteExtension}. This operation can only be performed by the governance executor,
* generally through a governance proposal.
*
* Emits a {LateQuorumVoteExtensionSet} event.
*/
function setLateQuorumVoteExtension(uint64 newVoteExtension) public virtual onlyGovernance {
_setLateQuorumVoteExtension(newVoteExtension);
}
/**
* @dev Changes the {lateQuorumVoteExtension}. This is an internal function that can be exposed in a public function
* like {setLateQuorumVoteExtension} if another access control mechanism is needed.
*
* Emits a {LateQuorumVoteExtensionSet} event.
*/
function _setLateQuorumVoteExtension(uint64 newVoteExtension) internal virtual {
emit LateQuorumVoteExtensionSet(_voteExtension, newVoteExtension);
_voteExtension = newVoteExtension;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (governance/extensions/GovernorProposalThreshold.sol)
pragma solidity ^0.8.0;
import "../Governor.sol";
/**
* @dev Extension of {Governor} for proposal restriction to token holders with a minimum balance.
*
* _Available since v4.3._
* _Deprecated since v4.4._
*/
abstract contract GovernorProposalThreshold is Governor {
function propose(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
string memory description
) public virtual override returns (uint256) {
return super.propose(targets, values, calldatas, description);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (governance/extensions/GovernorSettings.sol)
pragma solidity ^0.8.0;
import "../Governor.sol";
/**
* @dev Extension of {Governor} for settings updatable through governance.
*
* _Available since v4.4._
*/
abstract contract GovernorSettings is Governor {
uint256 private _votingDelay;
uint256 private _votingPeriod;
uint256 private _proposalThreshold;
event VotingDelaySet(uint256 oldVotingDelay, uint256 newVotingDelay);
event VotingPeriodSet(uint256 oldVotingPeriod, uint256 newVotingPeriod);
event ProposalThresholdSet(uint256 oldProposalThreshold, uint256 newProposalThreshold);
/**
* @dev Initialize the governance parameters.
*/
constructor(
uint256 initialVotingDelay,
uint256 initialVotingPeriod,
uint256 initialProposalThreshold
) {
_setVotingDelay(initialVotingDelay);
_setVotingPeriod(initialVotingPeriod);
_setProposalThreshold(initialProposalThreshold);
}
/**
* @dev See {IGovernor-votingDelay}.
*/
function votingDelay() public view virtual override returns (uint256) {
return _votingDelay;
}
/**
* @dev See {IGovernor-votingPeriod}.
*/
function votingPeriod() public view virtual override returns (uint256) {
return _votingPeriod;
}
/**
* @dev See {Governor-proposalThreshold}.
*/
function proposalThreshold() public view virtual override returns (uint256) {
return _proposalThreshold;
}
/**
* @dev Update the voting delay. This operation can only be performed through a governance proposal.
*
* Emits a {VotingDelaySet} event.
*/
function setVotingDelay(uint256 newVotingDelay) public virtual onlyGovernance {
_setVotingDelay(newVotingDelay);
}
/**
* @dev Update the voting period. This operation can only be performed through a governance proposal.
*
* Emits a {VotingPeriodSet} event.
*/
function setVotingPeriod(uint256 newVotingPeriod) public virtual onlyGovernance {
_setVotingPeriod(newVotingPeriod);
}
/**
* @dev Update the proposal threshold. This operation can only be performed through a governance proposal.
*
* Emits a {ProposalThresholdSet} event.
*/
function setProposalThreshold(uint256 newProposalThreshold) public virtual onlyGovernance {
_setProposalThreshold(newProposalThreshold);
}
/**
* @dev Internal setter for the voting delay.
*
* Emits a {VotingDelaySet} event.
*/
function _setVotingDelay(uint256 newVotingDelay) internal virtual {
emit VotingDelaySet(_votingDelay, newVotingDelay);
_votingDelay = newVotingDelay;
}
/**
* @dev Internal setter for the voting period.
*
* Emits a {VotingPeriodSet} event.
*/
function _setVotingPeriod(uint256 newVotingPeriod) internal virtual {
// voting period must be at least one block long
require(newVotingPeriod > 0, "GovernorSettings: voting period too low");
emit VotingPeriodSet(_votingPeriod, newVotingPeriod);
_votingPeriod = newVotingPeriod;
}
/**
* @dev Internal setter for the proposal threshold.
*
* Emits a {ProposalThresholdSet} event.
*/
function _setProposalThreshold(uint256 newProposalThreshold) internal virtual {
emit ProposalThresholdSet(_proposalThreshold, newProposalThreshold);
_proposalThreshold = newProposalThreshold;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (governance/extensions/GovernorTimelockCompound.sol)
pragma solidity ^0.8.0;
import "./IGovernorTimelock.sol";
import "../Governor.sol";
import "../../utils/math/SafeCast.sol";
import "../../vendor/compound/ICompoundTimelock.sol";
/**
* @dev Extension of {Governor} that binds the execution process to a Compound Timelock. This adds a delay, enforced by
* the external timelock to all successful proposal (in addition to the voting duration). The {Governor} needs to be
* the admin of the timelock for any operation to be performed. A public, unrestricted,
* {GovernorTimelockCompound-__acceptAdmin} is available to accept ownership of the timelock.
*
* Using this model means the proposal will be operated by the {TimelockController} and not by the {Governor}. Thus,
* the assets and permissions must be attached to the {TimelockController}. Any asset sent to the {Governor} will be
* inaccessible.
*
* _Available since v4.3._
*/
abstract contract GovernorTimelockCompound is IGovernorTimelock, Governor {
using SafeCast for uint256;
using Timers for Timers.Timestamp;
struct ProposalTimelock {
Timers.Timestamp timer;
}
ICompoundTimelock private _timelock;
mapping(uint256 => ProposalTimelock) private _proposalTimelocks;
/**
* @dev Emitted when the timelock controller used for proposal execution is modified.
*/
event TimelockChange(address oldTimelock, address newTimelock);
/**
* @dev Set the timelock.
*/
constructor(ICompoundTimelock timelockAddress) {
_updateTimelock(timelockAddress);
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, Governor) returns (bool) {
return interfaceId == type(IGovernorTimelock).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Overridden version of the {Governor-state} function with added support for the `Queued` and `Expired` status.
*/
function state(uint256 proposalId) public view virtual override(IGovernor, Governor) returns (ProposalState) {
ProposalState status = super.state(proposalId);
if (status != ProposalState.Succeeded) {
return status;
}
uint256 eta = proposalEta(proposalId);
if (eta == 0) {
return status;
} else if (block.timestamp >= eta + _timelock.GRACE_PERIOD()) {
return ProposalState.Expired;
} else {
return ProposalState.Queued;
}
}
/**
* @dev Public accessor to check the address of the timelock
*/
function timelock() public view virtual override returns (address) {
return address(_timelock);
}
/**
* @dev Public accessor to check the eta of a queued proposal
*/
function proposalEta(uint256 proposalId) public view virtual override returns (uint256) {
return _proposalTimelocks[proposalId].timer.getDeadline();
}
/**
* @dev Function to queue a proposal to the timelock.
*/
function queue(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) public virtual override returns (uint256) {
uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash);
require(state(proposalId) == ProposalState.Succeeded, "Governor: proposal not successful");
uint256 eta = block.timestamp + _timelock.delay();
_proposalTimelocks[proposalId].timer.setDeadline(eta.toUint64());
for (uint256 i = 0; i < targets.length; ++i) {
require(
!_timelock.queuedTransactions(keccak256(abi.encode(targets[i], values[i], "", calldatas[i], eta))),
"GovernorTimelockCompound: identical proposal action already queued"
);
_timelock.queueTransaction(targets[i], values[i], "", calldatas[i], eta);
}
emit ProposalQueued(proposalId, eta);
return proposalId;
}
/**
* @dev Overridden execute function that run the already queued proposal through the timelock.
*/
function _execute(
uint256 proposalId,
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 /*descriptionHash*/
) internal virtual override {
uint256 eta = proposalEta(proposalId);
require(eta > 0, "GovernorTimelockCompound: proposal not yet queued");
Address.sendValue(payable(_timelock), msg.value);
for (uint256 i = 0; i < targets.length; ++i) {
_timelock.executeTransaction(targets[i], values[i], "", calldatas[i], eta);
}
}
/**
* @dev Overridden version of the {Governor-_cancel} function to cancel the timelocked proposal if it as already
* been queued.
*/
function _cancel(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) internal virtual override returns (uint256) {
uint256 proposalId = super._cancel(targets, values, calldatas, descriptionHash);
uint256 eta = proposalEta(proposalId);
if (eta > 0) {
for (uint256 i = 0; i < targets.length; ++i) {
_timelock.cancelTransaction(targets[i], values[i], "", calldatas[i], eta);
}
_proposalTimelocks[proposalId].timer.reset();
}
return proposalId;
}
/**
* @dev Address through which the governor executes action. In this case, the timelock.
*/
function _executor() internal view virtual override returns (address) {
return address(_timelock);
}
/**
* @dev Accept admin right over the timelock.
*/
// solhint-disable-next-line private-vars-leading-underscore
function __acceptAdmin() public {
_timelock.acceptAdmin();
}
/**
* @dev Public endpoint to update the underlying timelock instance. Restricted to the timelock itself, so updates
* must be proposed, scheduled, and executed through governance proposals.
*
* For security reasons, the timelock must be handed over to another admin before setting up a new one. The two
* operations (hand over the timelock) and do the update can be batched in a single proposal.
*
* Note that if the timelock admin has been handed over in a previous operation, we refuse updates made through the
* timelock if admin of the timelock has already been accepted and the operation is executed outside the scope of
* governance.
* CAUTION: It is not recommended to change the timelock while there are other queued governance proposals.
*/
function updateTimelock(ICompoundTimelock newTimelock) external virtual onlyGovernance {
_updateTimelock(newTimelock);
}
function _updateTimelock(ICompoundTimelock newTimelock) private {
emit TimelockChange(address(_timelock), address(newTimelock));
_timelock = newTimelock;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (governance/extensions/GovernorTimelockControl.sol)
pragma solidity ^0.8.0;
import "./IGovernorTimelock.sol";
import "../Governor.sol";
import "../TimelockController.sol";
/**
* @dev Extension of {Governor} that binds the execution process to an instance of {TimelockController}. This adds a
* delay, enforced by the {TimelockController} to all successful proposal (in addition to the voting duration). The
* {Governor} needs the proposer (and ideally the executor) roles for the {Governor} to work properly.
*
* Using this model means the proposal will be operated by the {TimelockController} and not by the {Governor}. Thus,
* the assets and permissions must be attached to the {TimelockController}. Any asset sent to the {Governor} will be
* inaccessible.
*
* WARNING: Setting up the TimelockController to have additional proposers besides the governor is very risky, as it
* grants them powers that they must be trusted or known not to use: 1) {onlyGovernance} functions like {relay} are
* available to them through the timelock, and 2) approved governance proposals can be blocked by them, effectively
* executing a Denial of Service attack. This risk will be mitigated in a future release.
*
* _Available since v4.3._
*/
abstract contract GovernorTimelockControl is IGovernorTimelock, Governor {
TimelockController private _timelock;
mapping(uint256 => bytes32) private _timelockIds;
/**
* @dev Emitted when the timelock controller used for proposal execution is modified.
*/
event TimelockChange(address oldTimelock, address newTimelock);
/**
* @dev Set the timelock.
*/
constructor(TimelockController timelockAddress) {
_updateTimelock(timelockAddress);
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, Governor) returns (bool) {
return interfaceId == type(IGovernorTimelock).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Overridden version of the {Governor-state} function with added support for the `Queued` status.
*/
function state(uint256 proposalId) public view virtual override(IGovernor, Governor) returns (ProposalState) {
ProposalState status = super.state(proposalId);
if (status != ProposalState.Succeeded) {
return status;
}
// core tracks execution, so we just have to check if successful proposal have been queued.
bytes32 queueid = _timelockIds[proposalId];
if (queueid == bytes32(0)) {
return status;
} else if (_timelock.isOperationDone(queueid)) {
return ProposalState.Executed;
} else if (_timelock.isOperationPending(queueid)) {
return ProposalState.Queued;
} else {
return ProposalState.Canceled;
}
}
/**
* @dev Public accessor to check the address of the timelock
*/
function timelock() public view virtual override returns (address) {
return address(_timelock);
}
/**
* @dev Public accessor to check the eta of a queued proposal
*/
function proposalEta(uint256 proposalId) public view virtual override returns (uint256) {
uint256 eta = _timelock.getTimestamp(_timelockIds[proposalId]);
return eta == 1 ? 0 : eta; // _DONE_TIMESTAMP (1) should be replaced with a 0 value
}
/**
* @dev Function to queue a proposal to the timelock.
*/
function queue(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) public virtual override returns (uint256) {
uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash);
require(state(proposalId) == ProposalState.Succeeded, "Governor: proposal not successful");
uint256 delay = _timelock.getMinDelay();
_timelockIds[proposalId] = _timelock.hashOperationBatch(targets, values, calldatas, 0, descriptionHash);
_timelock.scheduleBatch(targets, values, calldatas, 0, descriptionHash, delay);
emit ProposalQueued(proposalId, block.timestamp + delay);
return proposalId;
}
/**
* @dev Overridden execute function that run the already queued proposal through the timelock.
*/
function _execute(
uint256, /* proposalId */
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) internal virtual override {
_timelock.executeBatch{value: msg.value}(targets, values, calldatas, 0, descriptionHash);
}
/**
* @dev Overridden version of the {Governor-_cancel} function to cancel the timelocked proposal if it as already
* been queued.
*/
// This function can reenter through the external call to the timelock, but we assume the timelock is trusted and
// well behaved (according to TimelockController) and this will not happen.
// slither-disable-next-line reentrancy-no-eth
function _cancel(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) internal virtual override returns (uint256) {
uint256 proposalId = super._cancel(targets, values, calldatas, descriptionHash);
if (_timelockIds[proposalId] != 0) {
_timelock.cancel(_timelockIds[proposalId]);
delete _timelockIds[proposalId];
}
return proposalId;
}
/**
* @dev Address through which the governor executes action. In this case, the timelock.
*/
function _executor() internal view virtual override returns (address) {
return address(_timelock);
}
/**
* @dev Public endpoint to update the underlying timelock instance. Restricted to the timelock itself, so updates
* must be proposed, scheduled, and executed through governance proposals.
*
* CAUTION: It is not recommended to change the timelock while there are other queued governance proposals.
*/
function updateTimelock(TimelockController newTimelock) external virtual onlyGovernance {
_updateTimelock(newTimelock);
}
function _updateTimelock(TimelockController newTimelock) private {
emit TimelockChange(address(_timelock), address(newTimelock));
_timelock = newTimelock;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (governance/extensions/GovernorVotes.sol)
pragma solidity ^0.8.0;
import "../Governor.sol";
import "../utils/IVotes.sol";
/**
* @dev Extension of {Governor} for voting weight extraction from an {ERC20Votes} token, or since v4.5 an {ERC721Votes} token.
*
* _Available since v4.3._
*/
abstract contract GovernorVotes is Governor {
IVotes public immutable token;
constructor(IVotes tokenAddress) {
token = tokenAddress;
}
/**
* Read the voting weight from the token's built in snapshot mechanism (see {Governor-_getVotes}).
*/
function _getVotes(
address account,
uint256 blockNumber,
bytes memory /*params*/
) internal view virtual override returns (uint256) {
return token.getPastVotes(account, blockNumber);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (governance/extensions/GovernorVotesComp.sol)
pragma solidity ^0.8.0;
import "../Governor.sol";
import "../../token/ERC20/extensions/ERC20VotesComp.sol";
/**
* @dev Extension of {Governor} for voting weight extraction from a Comp token.
*
* _Available since v4.3._
*/
abstract contract GovernorVotesComp is Governor {
ERC20VotesComp public immutable token;
constructor(ERC20VotesComp token_) {
token = token_;
}
/**
* Read the voting weight from the token's built in snapshot mechanism (see {Governor-_getVotes}).
*/
function _getVotes(
address account,
uint256 blockNumber,
bytes memory /*params*/
) internal view virtual override returns (uint256) {
return token.getPriorVotes(account, blockNumber);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (governance/extensions/GovernorVotesQuorumFraction.sol)
pragma solidity ^0.8.0;
import "./GovernorVotes.sol";
import "../../utils/Checkpoints.sol";
import "../../utils/math/SafeCast.sol";
/**
* @dev Extension of {Governor} for voting weight extraction from an {ERC20Votes} token and a quorum expressed as a
* fraction of the total supply.
*
* _Available since v4.3._
*/
abstract contract GovernorVotesQuorumFraction is GovernorVotes {
using Checkpoints for Checkpoints.History;
uint256 private _quorumNumerator; // DEPRECATED
Checkpoints.History private _quorumNumeratorHistory;
event QuorumNumeratorUpdated(uint256 oldQuorumNumerator, uint256 newQuorumNumerator);
/**
* @dev Initialize quorum as a fraction of the token's total supply.
*
* The fraction is specified as `numerator / denominator`. By default the denominator is 100, so quorum is
* specified as a percent: a numerator of 10 corresponds to quorum being 10% of total supply. The denominator can be
* customized by overriding {quorumDenominator}.
*/
constructor(uint256 quorumNumeratorValue) {
_updateQuorumNumerator(quorumNumeratorValue);
}
/**
* @dev Returns the current quorum numerator. See {quorumDenominator}.
*/
function quorumNumerator() public view virtual returns (uint256) {
return _quorumNumeratorHistory._checkpoints.length == 0 ? _quorumNumerator : _quorumNumeratorHistory.latest();
}
/**
* @dev Returns the quorum numerator at a specific block number. See {quorumDenominator}.
*/
function quorumNumerator(uint256 blockNumber) public view virtual returns (uint256) {
// If history is empty, fallback to old storage
uint256 length = _quorumNumeratorHistory._checkpoints.length;
if (length == 0) {
return _quorumNumerator;
}
// Optimistic search, check the latest checkpoint
Checkpoints.Checkpoint memory latest = _quorumNumeratorHistory._checkpoints[length - 1];
if (latest._blockNumber <= blockNumber) {
return latest._value;
}
// Otherwise, do the binary search
return _quorumNumeratorHistory.getAtBlock(blockNumber);
}
/**
* @dev Returns the quorum denominator. Defaults to 100, but may be overridden.
*/
function quorumDenominator() public view virtual returns (uint256) {
return 100;
}
/**
* @dev Returns the quorum for a block number, in terms of number of votes: `supply * numerator / denominator`.
*/
function quorum(uint256 blockNumber) public view virtual override returns (uint256) {
return (token.getPastTotalSupply(blockNumber) * quorumNumerator(blockNumber)) / quorumDenominator();
}
/**
* @dev Changes the quorum numerator.
*
* Emits a {QuorumNumeratorUpdated} event.
*
* Requirements:
*
* - Must be called through a governance proposal.
* - New numerator must be smaller or equal to the denominator.
*/
function updateQuorumNumerator(uint256 newQuorumNumerator) external virtual onlyGovernance {
_updateQuorumNumerator(newQuorumNumerator);
}
/**
* @dev Changes the quorum numerator.
*
* Emits a {QuorumNumeratorUpdated} event.
*
* Requirements:
*
* - New numerator must be smaller or equal to the denominator.
*/
function _updateQuorumNumerator(uint256 newQuorumNumerator) internal virtual {
require(
newQuorumNumerator <= quorumDenominator(),
"GovernorVotesQuorumFraction: quorumNumerator over quorumDenominator"
);
uint256 oldQuorumNumerator = quorumNumerator();
// Make sure we keep track of the original numerator in contracts upgraded from a version without checkpoints.
if (oldQuorumNumerator != 0 && _quorumNumeratorHistory._checkpoints.length == 0) {
_quorumNumeratorHistory._checkpoints.push(
Checkpoints.Checkpoint({_blockNumber: 0, _value: SafeCast.toUint224(oldQuorumNumerator)})
);
}
// Set new quorum for future proposals
_quorumNumeratorHistory.push(newQuorumNumerator);
emit QuorumNumeratorUpdated(oldQuorumNumerator, newQuorumNumerator);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (governance/extensions/IGovernorTimelock.sol)
pragma solidity ^0.8.0;
import "../IGovernor.sol";
/**
* @dev Extension of the {IGovernor} for timelock supporting modules.
*
* _Available since v4.3._
*/
abstract contract IGovernorTimelock is IGovernor {
event ProposalQueued(uint256 proposalId, uint256 eta);
function timelock() public view virtual returns (address);
function proposalEta(uint256 proposalId) public view virtual returns (uint256);
function queue(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) public virtual returns (uint256 proposalId);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (governance/Governor.sol)
pragma solidity ^0.8.0;
import "../token/ERC721/IERC721Receiver.sol";
import "../token/ERC1155/IERC1155Receiver.sol";
import "../utils/cryptography/ECDSA.sol";
import "../utils/cryptography/EIP712.sol";
import "../utils/introspection/ERC165.sol";
import "../utils/math/SafeCast.sol";
import "../utils/structs/DoubleEndedQueue.sol";
import "../utils/Address.sol";
import "../utils/Context.sol";
import "../utils/Timers.sol";
import "./IGovernor.sol";
/**
* @dev Core of the governance system, designed to be extended though various modules.
*
* This contract is abstract and requires several function to be implemented in various modules:
*
* - A counting module must implement {quorum}, {_quorumReached}, {_voteSucceeded} and {_countVote}
* - A voting module must implement {_getVotes}
* - Additionally, the {votingPeriod} must also be implemented
*
* _Available since v4.3._
*/
abstract contract Governor is Context, ERC165, EIP712, IGovernor, IERC721Receiver, IERC1155Receiver {
using DoubleEndedQueue for DoubleEndedQueue.Bytes32Deque;
using SafeCast for uint256;
using Timers for Timers.BlockNumber;
bytes32 public constant BALLOT_TYPEHASH = keccak256("Ballot(uint256 proposalId,uint8 support)");
bytes32 public constant EXTENDED_BALLOT_TYPEHASH =
keccak256("ExtendedBallot(uint256 proposalId,uint8 support,string reason,bytes params)");
struct ProposalCore {
Timers.BlockNumber voteStart;
Timers.BlockNumber voteEnd;
bool executed;
bool canceled;
}
string private _name;
mapping(uint256 => ProposalCore) private _proposals;
// This queue keeps track of the governor operating on itself. Calls to functions protected by the
// {onlyGovernance} modifier needs to be whitelisted in this queue. Whitelisting is set in {_beforeExecute},
// consumed by the {onlyGovernance} modifier and eventually reset in {_afterExecute}. This ensures that the
// execution of {onlyGovernance} protected calls can only be achieved through successful proposals.
DoubleEndedQueue.Bytes32Deque private _governanceCall;
/**
* @dev Restricts a function so it can only be executed through governance proposals. For example, governance
* parameter setters in {GovernorSettings} are protected using this modifier.
*
* The governance executing address may be different from the Governor's own address, for example it could be a
* timelock. This can be customized by modules by overriding {_executor}. The executor is only able to invoke these
* functions during the execution of the governor's {execute} function, and not under any other circumstances. Thus,
* for example, additional timelock proposers are not able to change governance parameters without going through the
* governance protocol (since v4.6).
*/
modifier onlyGovernance() {
require(_msgSender() == _executor(), "Governor: onlyGovernance");
if (_executor() != address(this)) {
bytes32 msgDataHash = keccak256(_msgData());
// loop until popping the expected operation - throw if deque is empty (operation not authorized)
while (_governanceCall.popFront() != msgDataHash) {}
}
_;
}
/**
* @dev Sets the value for {name} and {version}
*/
constructor(string memory name_) EIP712(name_, version()) {
_name = name_;
}
/**
* @dev Function to receive ETH that will be handled by the governor (disabled if executor is a third party contract)
*/
receive() external payable virtual {
require(_executor() == address(this));
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165) returns (bool) {
// In addition to the current interfaceId, also support previous version of the interfaceId that did not
// include the castVoteWithReasonAndParams() function as standard
return
interfaceId ==
(type(IGovernor).interfaceId ^
this.castVoteWithReasonAndParams.selector ^
this.castVoteWithReasonAndParamsBySig.selector ^
this.getVotesWithParams.selector) ||
interfaceId == type(IGovernor).interfaceId ||
interfaceId == type(IERC1155Receiver).interfaceId ||
super.supportsInterface(interfaceId);
}
/**
* @dev See {IGovernor-name}.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev See {IGovernor-version}.
*/
function version() public view virtual override returns (string memory) {
return "1";
}
/**
* @dev See {IGovernor-hashProposal}.
*
* The proposal id is produced by hashing the ABI encoded `targets` array, the `values` array, the `calldatas` array
* and the descriptionHash (bytes32 which itself is the keccak256 hash of the description string). This proposal id
* can be produced from the proposal data which is part of the {ProposalCreated} event. It can even be computed in
* advance, before the proposal is submitted.
*
* Note that the chainId and the governor address are not part of the proposal id computation. Consequently, the
* same proposal (with same operation and same description) will have the same id if submitted on multiple governors
* across multiple networks. This also means that in order to execute the same operation twice (on the same
* governor) the proposer will have to change the description in order to avoid proposal id conflicts.
*/
function hashProposal(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) public pure virtual override returns (uint256) {
return uint256(keccak256(abi.encode(targets, values, calldatas, descriptionHash)));
}
/**
* @dev See {IGovernor-state}.
*/
function state(uint256 proposalId) public view virtual override returns (ProposalState) {
ProposalCore storage proposal = _proposals[proposalId];
if (proposal.executed) {
return ProposalState.Executed;
}
if (proposal.canceled) {
return ProposalState.Canceled;
}
uint256 snapshot = proposalSnapshot(proposalId);
if (snapshot == 0) {
revert("Governor: unknown proposal id");
}
if (snapshot >= block.number) {
return ProposalState.Pending;
}
uint256 deadline = proposalDeadline(proposalId);
if (deadline >= block.number) {
return ProposalState.Active;
}
if (_quorumReached(proposalId) && _voteSucceeded(proposalId)) {
return ProposalState.Succeeded;
} else {
return ProposalState.Defeated;
}
}
/**
* @dev See {IGovernor-proposalSnapshot}.
*/
function proposalSnapshot(uint256 proposalId) public view virtual override returns (uint256) {
return _proposals[proposalId].voteStart.getDeadline();
}
/**
* @dev See {IGovernor-proposalDeadline}.
*/
function proposalDeadline(uint256 proposalId) public view virtual override returns (uint256) {
return _proposals[proposalId].voteEnd.getDeadline();
}
/**
* @dev Part of the Governor Bravo's interface: _"The number of votes required in order for a voter to become a proposer"_.
*/
function proposalThreshold() public view virtual returns (uint256) {
return 0;
}
/**
* @dev Amount of votes already cast passes the threshold limit.
*/
function _quorumReached(uint256 proposalId) internal view virtual returns (bool);
/**
* @dev Is the proposal successful or not.
*/
function _voteSucceeded(uint256 proposalId) internal view virtual returns (bool);
/**
* @dev Get the voting weight of `account` at a specific `blockNumber`, for a vote as described by `params`.
*/
function _getVotes(
address account,
uint256 blockNumber,
bytes memory params
) internal view virtual returns (uint256);
/**
* @dev Register a vote for `proposalId` by `account` with a given `support`, voting `weight` and voting `params`.
*
* Note: Support is generic and can represent various things depending on the voting system used.
*/
function _countVote(
uint256 proposalId,
address account,
uint8 support,
uint256 weight,
bytes memory params
) internal virtual;
/**
* @dev Default additional encoded parameters used by castVote methods that don't include them
*
* Note: Should be overridden by specific implementations to use an appropriate value, the
* meaning of the additional params, in the context of that implementation
*/
function _defaultParams() internal view virtual returns (bytes memory) {
return "";
}
/**
* @dev See {IGovernor-propose}.
*/
function propose(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
string memory description
) public virtual override returns (uint256) {
require(
getVotes(_msgSender(), block.number - 1) >= proposalThreshold(),
"Governor: proposer votes below proposal threshold"
);
uint256 proposalId = hashProposal(targets, values, calldatas, keccak256(bytes(description)));
require(targets.length == values.length, "Governor: invalid proposal length");
require(targets.length == calldatas.length, "Governor: invalid proposal length");
require(targets.length > 0, "Governor: empty proposal");
ProposalCore storage proposal = _proposals[proposalId];
require(proposal.voteStart.isUnset(), "Governor: proposal already exists");
uint64 snapshot = block.number.toUint64() + votingDelay().toUint64();
uint64 deadline = snapshot + votingPeriod().toUint64();
proposal.voteStart.setDeadline(snapshot);
proposal.voteEnd.setDeadline(deadline);
emit ProposalCreated(
proposalId,
_msgSender(),
targets,
values,
new string[](targets.length),
calldatas,
snapshot,
deadline,
description
);
return proposalId;
}
/**
* @dev See {IGovernor-execute}.
*/
function execute(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) public payable virtual override returns (uint256) {
uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash);
ProposalState status = state(proposalId);
require(
status == ProposalState.Succeeded || status == ProposalState.Queued,
"Governor: proposal not successful"
);
_proposals[proposalId].executed = true;
emit ProposalExecuted(proposalId);
_beforeExecute(proposalId, targets, values, calldatas, descriptionHash);
_execute(proposalId, targets, values, calldatas, descriptionHash);
_afterExecute(proposalId, targets, values, calldatas, descriptionHash);
return proposalId;
}
/**
* @dev Internal execution mechanism. Can be overridden to implement different execution mechanism
*/
function _execute(
uint256, /* proposalId */
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 /*descriptionHash*/
) internal virtual {
string memory errorMessage = "Governor: call reverted without message";
for (uint256 i = 0; i < targets.length; ++i) {
(bool success, bytes memory returndata) = targets[i].call{value: values[i]}(calldatas[i]);
Address.verifyCallResult(success, returndata, errorMessage);
}
}
/**
* @dev Hook before execution is triggered.
*/
function _beforeExecute(
uint256, /* proposalId */
address[] memory targets,
uint256[] memory, /* values */
bytes[] memory calldatas,
bytes32 /*descriptionHash*/
) internal virtual {
if (_executor() != address(this)) {
for (uint256 i = 0; i < targets.length; ++i) {
if (targets[i] == address(this)) {
_governanceCall.pushBack(keccak256(calldatas[i]));
}
}
}
}
/**
* @dev Hook after execution is triggered.
*/
function _afterExecute(
uint256, /* proposalId */
address[] memory, /* targets */
uint256[] memory, /* values */
bytes[] memory, /* calldatas */
bytes32 /*descriptionHash*/
) internal virtual {
if (_executor() != address(this)) {
if (!_governanceCall.empty()) {
_governanceCall.clear();
}
}
}
/**
* @dev Internal cancel mechanism: locks up the proposal timer, preventing it from being re-submitted. Marks it as
* canceled to allow distinguishing it from executed proposals.
*
* Emits a {IGovernor-ProposalCanceled} event.
*/
function _cancel(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) internal virtual returns (uint256) {
uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash);
ProposalState status = state(proposalId);
require(
status != ProposalState.Canceled && status != ProposalState.Expired && status != ProposalState.Executed,
"Governor: proposal not active"
);
_proposals[proposalId].canceled = true;
emit ProposalCanceled(proposalId);
return proposalId;
}
/**
* @dev See {IGovernor-getVotes}.
*/
function getVotes(address account, uint256 blockNumber) public view virtual override returns (uint256) {
return _getVotes(account, blockNumber, _defaultParams());
}
/**
* @dev See {IGovernor-getVotesWithParams}.
*/
function getVotesWithParams(
address account,
uint256 blockNumber,
bytes memory params
) public view virtual override returns (uint256) {
return _getVotes(account, blockNumber, params);
}
/**
* @dev See {IGovernor-castVote}.
*/
function castVote(uint256 proposalId, uint8 support) public virtual override returns (uint256) {
address voter = _msgSender();
return _castVote(proposalId, voter, support, "");
}
/**
* @dev See {IGovernor-castVoteWithReason}.
*/
function castVoteWithReason(
uint256 proposalId,
uint8 support,
string calldata reason
) public virtual override returns (uint256) {
address voter = _msgSender();
return _castVote(proposalId, voter, support, reason);
}
/**
* @dev See {IGovernor-castVoteWithReasonAndParams}.
*/
function castVoteWithReasonAndParams(
uint256 proposalId,
uint8 support,
string calldata reason,
bytes memory params
) public virtual override returns (uint256) {
address voter = _msgSender();
return _castVote(proposalId, voter, support, reason, params);
}
/**
* @dev See {IGovernor-castVoteBySig}.
*/
function castVoteBySig(
uint256 proposalId,
uint8 support,
uint8 v,
bytes32 r,
bytes32 s
) public virtual override returns (uint256) {
address voter = ECDSA.recover(
_hashTypedDataV4(keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support))),
v,
r,
s
);
return _castVote(proposalId, voter, support, "");
}
/**
* @dev See {IGovernor-castVoteWithReasonAndParamsBySig}.
*/
function castVoteWithReasonAndParamsBySig(
uint256 proposalId,
uint8 support,
string calldata reason,
bytes memory params,
uint8 v,
bytes32 r,
bytes32 s
) public virtual override returns (uint256) {
address voter = ECDSA.recover(
_hashTypedDataV4(
keccak256(
abi.encode(
EXTENDED_BALLOT_TYPEHASH,
proposalId,
support,
keccak256(bytes(reason)),
keccak256(params)
)
)
),
v,
r,
s
);
return _castVote(proposalId, voter, support, reason, params);
}
/**
* @dev Internal vote casting mechanism: Check that the vote is pending, that it has not been cast yet, retrieve
* voting weight using {IGovernor-getVotes} and call the {_countVote} internal function. Uses the _defaultParams().
*
* Emits a {IGovernor-VoteCast} event.
*/
function _castVote(
uint256 proposalId,
address account,
uint8 support,
string memory reason
) internal virtual returns (uint256) {
return _castVote(proposalId, account, support, reason, _defaultParams());
}
/**
* @dev Internal vote casting mechanism: Check that the vote is pending, that it has not been cast yet, retrieve
* voting weight using {IGovernor-getVotes} and call the {_countVote} internal function.
*
* Emits a {IGovernor-VoteCast} event.
*/
function _castVote(
uint256 proposalId,
address account,
uint8 support,
string memory reason,
bytes memory params
) internal virtual returns (uint256) {
ProposalCore storage proposal = _proposals[proposalId];
require(state(proposalId) == ProposalState.Active, "Governor: vote not currently active");
uint256 weight = _getVotes(account, proposal.voteStart.getDeadline(), params);
_countVote(proposalId, account, support, weight, params);
if (params.length == 0) {
emit VoteCast(account, proposalId, support, weight, reason);
} else {
emit VoteCastWithParams(account, proposalId, support, weight, reason, params);
}
return weight;
}
/**
* @dev Relays a transaction or function call to an arbitrary target. In cases where the governance executor
* is some contract other than the governor itself, like when using a timelock, this function can be invoked
* in a governance proposal to recover tokens or Ether that was sent to the governor contract by mistake.
* Note that if the executor is simply the governor itself, use of `relay` is redundant.
*/
function relay(
address target,
uint256 value,
bytes calldata data
) external payable virtual onlyGovernance {
(bool success, bytes memory returndata) = target.call{value: value}(data);
Address.verifyCallResult(success, returndata, "Governor: relay reverted without message");
}
/**
* @dev Address through which the governor executes action. Will be overloaded by module that execute actions
* through another contract such as a timelock.
*/
function _executor() internal view virtual returns (address) {
return address(this);
}
/**
* @dev See {IERC721Receiver-onERC721Received}.
*/
function onERC721Received(
address,
address,
uint256,
bytes memory
) public virtual override returns (bytes4) {
return this.onERC721Received.selector;
}
/**
* @dev See {IERC1155Receiver-onERC1155Received}.
*/
function onERC1155Received(
address,
address,
uint256,
uint256,
bytes memory
) public virtual override returns (bytes4) {
return this.onERC1155Received.selector;
}
/**
* @dev See {IERC1155Receiver-onERC1155BatchReceived}.
*/
function onERC1155BatchReceived(
address,
address,
uint256[] memory,
uint256[] memory,
bytes memory
) public virtual override returns (bytes4) {
return this.onERC1155BatchReceived.selector;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (governance/IGovernor.sol)
pragma solidity ^0.8.0;
import "../utils/introspection/ERC165.sol";
/**
* @dev Interface of the {Governor} core.
*
* _Available since v4.3._
*/
abstract contract IGovernor is IERC165 {
enum ProposalState {
Pending,
Active,
Canceled,
Defeated,
Succeeded,
Queued,
Expired,
Executed
}
/**
* @dev Emitted when a proposal is created.
*/
event ProposalCreated(
uint256 proposalId,
address proposer,
address[] targets,
uint256[] values,
string[] signatures,
bytes[] calldatas,
uint256 startBlock,
uint256 endBlock,
string description
);
/**
* @dev Emitted when a proposal is canceled.
*/
event ProposalCanceled(uint256 proposalId);
/**
* @dev Emitted when a proposal is executed.
*/
event ProposalExecuted(uint256 proposalId);
/**
* @dev Emitted when a vote is cast without params.
*
* Note: `support` values should be seen as buckets. Their interpretation depends on the voting module used.
*/
event VoteCast(address indexed voter, uint256 proposalId, uint8 support, uint256 weight, string reason);
/**
* @dev Emitted when a vote is cast with params.
*
* Note: `support` values should be seen as buckets. Their interpretation depends on the voting module used.
* `params` are additional encoded parameters. Their intepepretation also depends on the voting module used.
*/
event VoteCastWithParams(
address indexed voter,
uint256 proposalId,
uint8 support,
uint256 weight,
string reason,
bytes params
);
/**
* @notice module:core
* @dev Name of the governor instance (used in building the ERC712 domain separator).
*/
function name() public view virtual returns (string memory);
/**
* @notice module:core
* @dev Version of the governor instance (used in building the ERC712 domain separator). Default: "1"
*/
function version() public view virtual returns (string memory);
/**
* @notice module:voting
* @dev A description of the possible `support` values for {castVote} and the way these votes are counted, meant to
* be consumed by UIs to show correct vote options and interpret the results. The string is a URL-encoded sequence of
* key-value pairs that each describe one aspect, for example `support=bravo&quorum=for,abstain`.
*
* There are 2 standard keys: `support` and `quorum`.
*
* - `support=bravo` refers to the vote options 0 = Against, 1 = For, 2 = Abstain, as in `GovernorBravo`.
* - `quorum=bravo` means that only For votes are counted towards quorum.
* - `quorum=for,abstain` means that both For and Abstain votes are counted towards quorum.
*
* If a counting module makes use of encoded `params`, it should include this under a `params` key with a unique
* name that describes the behavior. For example:
*
* - `params=fractional` might refer to a scheme where votes are divided fractionally between for/against/abstain.
* - `params=erc721` might refer to a scheme where specific NFTs are delegated to vote.
*
* NOTE: The string can be decoded by the standard
* https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams[`URLSearchParams`]
* JavaScript class.
*/
// solhint-disable-next-line func-name-mixedcase
function COUNTING_MODE() public pure virtual returns (string memory);
/**
* @notice module:core
* @dev Hashing function used to (re)build the proposal id from the proposal details..
*/
function hashProposal(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) public pure virtual returns (uint256);
/**
* @notice module:core
* @dev Current state of a proposal, following Compound's convention
*/
function state(uint256 proposalId) public view virtual returns (ProposalState);
/**
* @notice module:core
* @dev Block number used to retrieve user's votes and quorum. As per Compound's Comp and OpenZeppelin's
* ERC20Votes, the snapshot is performed at the end of this block. Hence, voting for this proposal starts at the
* beginning of the following block.
*/
function proposalSnapshot(uint256 proposalId) public view virtual returns (uint256);
/**
* @notice module:core
* @dev Block number at which votes close. Votes close at the end of this block, so it is possible to cast a vote
* during this block.
*/
function proposalDeadline(uint256 proposalId) public view virtual returns (uint256);
/**
* @notice module:user-config
* @dev Delay, in number of block, between the proposal is created and the vote starts. This can be increassed to
* leave time for users to buy voting power, or delegate it, before the voting of a proposal starts.
*/
function votingDelay() public view virtual returns (uint256);
/**
* @notice module:user-config
* @dev Delay, in number of blocks, between the vote start and vote ends.
*
* NOTE: The {votingDelay} can delay the start of the vote. This must be considered when setting the voting
* duration compared to the voting delay.
*/
function votingPeriod() public view virtual returns (uint256);
/**
* @notice module:user-config
* @dev Minimum number of cast voted required for a proposal to be successful.
*
* Note: The `blockNumber` parameter corresponds to the snapshot used for counting vote. This allows to scale the
* quorum depending on values such as the totalSupply of a token at this block (see {ERC20Votes}).
*/
function quorum(uint256 blockNumber) public view virtual returns (uint256);
/**
* @notice module:reputation
* @dev Voting power of an `account` at a specific `blockNumber`.
*
* Note: this can be implemented in a number of ways, for example by reading the delegated balance from one (or
* multiple), {ERC20Votes} tokens.
*/
function getVotes(address account, uint256 blockNumber) public view virtual returns (uint256);
/**
* @notice module:reputation
* @dev Voting power of an `account` at a specific `blockNumber` given additional encoded parameters.
*/
function getVotesWithParams(
address account,
uint256 blockNumber,
bytes memory params
) public view virtual returns (uint256);
/**
* @notice module:voting
* @dev Returns whether `account` has cast a vote on `proposalId`.
*/
function hasVoted(uint256 proposalId, address account) public view virtual returns (bool);
/**
* @dev Create a new proposal. Vote start {IGovernor-votingDelay} blocks after the proposal is created and ends
* {IGovernor-votingPeriod} blocks after the voting starts.
*
* Emits a {ProposalCreated} event.
*/
function propose(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
string memory description
) public virtual returns (uint256 proposalId);
/**
* @dev Execute a successful proposal. This requires the quorum to be reached, the vote to be successful, and the
* deadline to be reached.
*
* Emits a {ProposalExecuted} event.
*
* Note: some module can modify the requirements for execution, for example by adding an additional timelock.
*/
function execute(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) public payable virtual returns (uint256 proposalId);
/**
* @dev Cast a vote
*
* Emits a {VoteCast} event.
*/
function castVote(uint256 proposalId, uint8 support) public virtual returns (uint256 balance);
/**
* @dev Cast a vote with a reason
*
* Emits a {VoteCast} event.
*/
function castVoteWithReason(
uint256 proposalId,
uint8 support,
string calldata reason
) public virtual returns (uint256 balance);
/**
* @dev Cast a vote with a reason and additional encoded parameters
*
* Emits a {VoteCast} or {VoteCastWithParams} event depending on the length of params.
*/
function castVoteWithReasonAndParams(
uint256 proposalId,
uint8 support,
string calldata reason,
bytes memory params
) public virtual returns (uint256 balance);
/**
* @dev Cast a vote using the user's cryptographic signature.
*
* Emits a {VoteCast} event.
*/
function castVoteBySig(
uint256 proposalId,
uint8 support,
uint8 v,
bytes32 r,
bytes32 s
) public virtual returns (uint256 balance);
/**
* @dev Cast a vote with a reason and additional encoded parameters using the user's cryptographic signature.
*
* Emits a {VoteCast} or {VoteCastWithParams} event depending on the length of params.
*/
function castVoteWithReasonAndParamsBySig(
uint256 proposalId,
uint8 support,
string calldata reason,
bytes memory params,
uint8 v,
bytes32 r,
bytes32 s
) public virtual returns (uint256 balance);
}

Governance

Note
This document is better viewed at https://docs.openzeppelin.com/contracts/api/governance

This directory includes primitives for on-chain governance.

Governor

This modular system of Governor contracts allows the deployment on-chain voting protocols similar to Compound’s Governor Alpha & Bravo and beyond, through the ability to easily customize multiple aspects of the protocol.

Tip

For a guided experience, set up your Governor contract using Contracts Wizard.

For a written walkthrough, check out our guide on How to set up on-chain governance.

  • {Governor}: The core contract that contains all the logic and primitives. It is abstract and requires choosing one of each of the modules below, or custom ones.

Votes modules determine the source of voting power, and sometimes quorum number.

  • {GovernorVotes}: Extracts voting weight from an {ERC20Votes} token.

  • {GovernorVotesComp}: Extracts voting weight from a COMP-like or {ERC20VotesComp} token.

  • {GovernorVotesQuorumFraction}: Combines with GovernorVotes to set the quorum as a fraction of the total token supply.

Counting modules determine valid voting options.

  • {GovernorCountingSimple}: Simple voting mechanism with 3 voting options: Against, For and Abstain.

Timelock extensions add a delay for governance decisions to be executed. The workflow is extended to require a queue step before execution. With these modules, proposals are executed by the external timelock contract, thus it is the timelock that has to hold the assets that are being governed.

  • {GovernorTimelockControl}: Connects with an instance of {TimelockController}. Allows multiple proposers and executors, in addition to the Governor itself.

  • {GovernorTimelockCompound}: Connects with an instance of Compound’s Timelock contract.

Other extensions can customize the behavior or interface in multiple ways.

  • {GovernorCompatibilityBravo}: Extends the interface to be fully GovernorBravo-compatible. Note that events are compatible regardless of whether this extension is included or not.

  • {GovernorSettings}: Manages some of the settings (voting delay, voting period duration, and proposal threshold) in a way that can be updated through a governance proposal, without requiring an upgrade.

  • {GovernorPreventLateQuorum}: Ensures there is a minimum voting period after quorum is reached as a security protection against large voters.

In addition to modules and extensions, the core contract requires a few virtual functions to be implemented to your particular specifications:

  • votingDelay(): Delay (in number of blocks) since the proposal is submitted until voting power is fixed and voting starts. This can be used to enforce a delay after a proposal is published for users to buy tokens, or delegate their votes.

  • votingPeriod(): Delay (in number of blocks) since the proposal starts until voting ends.

  • quorum(uint256 blockNumber): Quorum required for a proposal to be successful. This function includes a blockNumber argument so the quorum can adapt through time, for example, to follow a token’s totalSupply.

Note
Functions of the Governor contract do not include access control. If you want to restrict access, you should add these checks by overloading the particular functions. Among these, {Governor-_cancel} is internal by default, and you will have to expose it (with the right access control mechanism) yourself if this function is needed.

Core

{{IGovernor}}

{{Governor}}

Modules

{{GovernorCountingSimple}}

{{GovernorVotes}}

{{GovernorVotesQuorumFraction}}

{{GovernorVotesComp}}

Extensions

{{GovernorTimelockControl}}

{{GovernorTimelockCompound}}

{{GovernorSettings}}

{{GovernorPreventLateQuorum}}

{{GovernorCompatibilityBravo}}

Deprecated

{{GovernorProposalThreshold}}

Utils

{{Votes}}

Timelock

In a governance system, the {TimelockController} contract is in charge of introducing a delay between a proposal and its execution. It can be used with or without a {Governor}.

{{TimelockController}}

Terminology

  • Operation: A transaction (or a set of transactions) that is the subject of the timelock. It has to be scheduled by a proposer and executed by an executor. The timelock enforces a minimum delay between the proposition and the execution (see operation lifecycle). If the operation contains multiple transactions (batch mode), they are executed atomically. Operations are identified by the hash of their content.

  • Operation status:

    • Unset: An operation that is not part of the timelock mechanism.

    • Pending: An operation that has been scheduled, before the timer expires.

    • Ready: An operation that has been scheduled, after the timer expires.

    • Done: An operation that has been executed.

  • Predecessor: An (optional) dependency between operations. An operation can depend on another operation (its predecessor), forcing the execution order of these two operations.

  • Role:

    • Admin: An address (smart contract or EOA) that is in charge of granting the roles of Proposer and Executor.

    • Proposer: An address (smart contract or EOA) that is in charge of scheduling (and cancelling) operations.

    • Executor: An address (smart contract or EOA) that is in charge of executing operations once the timelock has expired. This role can be given to the zero address to allow anyone to execute operations.

Operation structure

Operation executed by the TimelockController can contain one or multiple subsequent calls. Depending on whether you need to multiple calls to be executed atomically, you can either use simple or batched operations.

Both operations contain:

  • Target, the address of the smart contract that the timelock should operate on.

  • Value, in wei, that should be sent with the transaction. Most of the time this will be 0. Ether can be deposited before-end or passed along when executing the transaction.

  • Data, containing the encoded function selector and parameters of the call. This can be produced using a number of tools. For example, a maintenance operation granting role ROLE to ACCOUNT can be encoded using web3js as follows:

const data = timelock.contract.methods.grantRole(ROLE, ACCOUNT).encodeABI()
  • Predecessor, that specifies a dependency between operations. This dependency is optional. Use bytes32(0) if the operation does not have any dependency.

  • Salt, used to disambiguate two otherwise identical operations. This can be any random value.

In the case of batched operations, target, value and data are specified as arrays, which must be of the same length.

Operation lifecycle

Timelocked operations are identified by a unique id (their hash) and follow a specific lifecycle:

UnsetPendingPending + ReadyDone

  • By calling schedule (or scheduleBatch), a proposer moves the operation from the Unset to the Pending state. This starts a timer that must be longer than the minimum delay. The timer expires at a timestamp accessible through the getTimestamp method.

  • Once the timer expires, the operation automatically gets the Ready state. At this point, it can be executed.

  • By calling execute (or executeBatch), an executor triggers the operation’s underlying transactions and moves it to the Done state. If the operation has a predecessor, it has to be in the Done state for this transition to succeed.

  • cancel allows proposers to cancel any Pending operation. This resets the operation to the Unset state. It is thus possible for a proposer to re-schedule an operation that has been cancelled. In this case, the timer restarts when the operation is re-scheduled.

Operations status can be queried using the functions:

  • isOperationPending(bytes32)

  • isOperationReady(bytes32)

  • isOperationDone(bytes32)

Roles

Admin

The admins are in charge of managing proposers and executors. For the timelock to be self-governed, this role should only be given to the timelock itself. Upon deployment, the admin role can be granted to any address (in addition to the timelock itself). After further configuration and testing, this optional admin should renounce its role such that all further maintenance operations have to go through the timelock process.

This role is identified by the TIMELOCK_ADMIN_ROLE value: 0x5f58e3a2316349923ce3780f8d587db2d72378aed66a8261c916544fa6846ca5

Proposer

The proposers are in charge of scheduling (and cancelling) operations. This is a critical role, that should be given to governing entities. This could be an EOA, a multisig, or a DAO.

Warning
Proposer fight: Having multiple proposers, while providing redundancy in case one becomes unavailable, can be dangerous. As proposer have their say on all operations, they could cancel operations they disagree with, including operations to remove them for the proposers.

This role is identified by the PROPOSER_ROLE value: 0xb09aa5aeb3702cfd50b6b62bc4532604938f21248a27a1d5ca736082b6819cc1

Executor

The executors are in charge of executing the operations scheduled by the proposers once the timelock expires. Logic dictates that multisig or DAO that are proposers should also be executors in order to guarantee operations that have been scheduled will eventually be executed. However, having additional executors can reduce the cost (the executing transaction does not require validation by the multisig or DAO that proposed it), while ensuring whoever is in charge of execution cannot trigger actions that have not been scheduled by the proposers. Alternatively, it is possible to allow any address to execute a proposal once the timelock has expired by granting the executor role to the zero address.

This role is identified by the EXECUTOR_ROLE value: 0xd8aa0f3194971a2a116679f7c2090f6939c8d4e01a2a8d7e41d55e5351469e63

Warning
A live contract without at least one proposer and one executor is locked. Make sure these roles are filled by reliable entities before the deployer renounces its administrative rights in favour of the timelock contract itself. See the {AccessControl} documentation to learn more about role management.
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (governance/TimelockController.sol)
pragma solidity ^0.8.0;
import "../access/AccessControl.sol";
import "../token/ERC721/IERC721Receiver.sol";
import "../token/ERC1155/IERC1155Receiver.sol";
import "../utils/Address.sol";
/**
* @dev Contract module which acts as a timelocked controller. When set as the
* owner of an `Ownable` smart contract, it enforces a timelock on all
* `onlyOwner` maintenance operations. This gives time for users of the
* controlled contract to exit before a potentially dangerous maintenance
* operation is applied.
*
* By default, this contract is self administered, meaning administration tasks
* have to go through the timelock process. The proposer (resp executor) role
* is in charge of proposing (resp executing) operations. A common use case is
* to position this {TimelockController} as the owner of a smart contract, with
* a multisig or a DAO as the sole proposer.
*
* _Available since v3.3._
*/
contract TimelockController is AccessControl, IERC721Receiver, IERC1155Receiver {
bytes32 public constant TIMELOCK_ADMIN_ROLE = keccak256("TIMELOCK_ADMIN_ROLE");
bytes32 public constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE");
bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE");
bytes32 public constant CANCELLER_ROLE = keccak256("CANCELLER_ROLE");
uint256 internal constant _DONE_TIMESTAMP = uint256(1);
mapping(bytes32 => uint256) private _timestamps;
uint256 private _minDelay;
/**
* @dev Emitted when a call is scheduled as part of operation `id`.
*/
event CallScheduled(
bytes32 indexed id,
uint256 indexed index,
address target,
uint256 value,
bytes data,
bytes32 predecessor,
uint256 delay
);
/**
* @dev Emitted when a call is performed as part of operation `id`.
*/
event CallExecuted(bytes32 indexed id, uint256 indexed index, address target, uint256 value, bytes data);
/**
* @dev Emitted when operation `id` is cancelled.
*/
event Cancelled(bytes32 indexed id);
/**
* @dev Emitted when the minimum delay for future operations is modified.
*/
event MinDelayChange(uint256 oldDuration, uint256 newDuration);
/**
* @dev Initializes the contract with the following parameters:
*
* - `minDelay`: initial minimum delay for operations
* - `proposers`: accounts to be granted proposer and canceller roles
* - `executors`: accounts to be granted executor role
* - `admin`: optional account to be granted admin role; disable with zero address
*
* IMPORTANT: The optional admin can aid with initial configuration of roles after deployment
* without being subject to delay, but this role should be subsequently renounced in favor of
* administration through timelocked proposals. Previous versions of this contract would assign
* this admin to the deployer automatically and should be renounced as well.
*/
constructor(
uint256 minDelay,
address[] memory proposers,
address[] memory executors,
address admin
) {
_setRoleAdmin(TIMELOCK_ADMIN_ROLE, TIMELOCK_ADMIN_ROLE);
_setRoleAdmin(PROPOSER_ROLE, TIMELOCK_ADMIN_ROLE);
_setRoleAdmin(EXECUTOR_ROLE, TIMELOCK_ADMIN_ROLE);
_setRoleAdmin(CANCELLER_ROLE, TIMELOCK_ADMIN_ROLE);
// self administration
_setupRole(TIMELOCK_ADMIN_ROLE, address(this));
// optional admin
if (admin != address(0)) {
_setupRole(TIMELOCK_ADMIN_ROLE, admin);
}
// register proposers and cancellers
for (uint256 i = 0; i < proposers.length; ++i) {
_setupRole(PROPOSER_ROLE, proposers[i]);
_setupRole(CANCELLER_ROLE, proposers[i]);
}
// register executors
for (uint256 i = 0; i < executors.length; ++i) {
_setupRole(EXECUTOR_ROLE, executors[i]);
}
_minDelay = minDelay;
emit MinDelayChange(0, minDelay);
}
/**
* @dev Modifier to make a function callable only by a certain role. In
* addition to checking the sender's role, `address(0)` 's role is also
* considered. Granting a role to `address(0)` is equivalent to enabling
* this role for everyone.
*/
modifier onlyRoleOrOpenRole(bytes32 role) {
if (!hasRole(role, address(0))) {
_checkRole(role, _msgSender());
}
_;
}
/**
* @dev Contract might receive/hold ETH as part of the maintenance process.
*/
receive() external payable {}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, AccessControl) returns (bool) {
return interfaceId == type(IERC1155Receiver).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns whether an id correspond to a registered operation. This
* includes both Pending, Ready and Done operations.
*/
function isOperation(bytes32 id) public view virtual returns (bool registered) {
return getTimestamp(id) > 0;
}
/**
* @dev Returns whether an operation is pending or not.
*/
function isOperationPending(bytes32 id) public view virtual returns (bool pending) {
return getTimestamp(id) > _DONE_TIMESTAMP;
}
/**
* @dev Returns whether an operation is ready or not.
*/
function isOperationReady(bytes32 id) public view virtual returns (bool ready) {
uint256 timestamp = getTimestamp(id);
return timestamp > _DONE_TIMESTAMP && timestamp <= block.timestamp;
}
/**
* @dev Returns whether an operation is done or not.
*/
function isOperationDone(bytes32 id) public view virtual returns (bool done) {
return getTimestamp(id) == _DONE_TIMESTAMP;
}
/**
* @dev Returns the timestamp at which an operation becomes ready (0 for
* unset operations, 1 for done operations).
*/
function getTimestamp(bytes32 id) public view virtual returns (uint256 timestamp) {
return _timestamps[id];
}
/**
* @dev Returns the minimum delay for an operation to become valid.
*
* This value can be changed by executing an operation that calls `updateDelay`.
*/
function getMinDelay() public view virtual returns (uint256 duration) {
return _minDelay;
}
/**
* @dev Returns the identifier of an operation containing a single
* transaction.
*/
function hashOperation(
address target,
uint256 value,
bytes calldata data,
bytes32 predecessor,
bytes32 salt
) public pure virtual returns (bytes32 hash) {
return keccak256(abi.encode(target, value, data, predecessor, salt));
}
/**
* @dev Returns the identifier of an operation containing a batch of
* transactions.
*/
function hashOperationBatch(
address[] calldata targets,
uint256[] calldata values,
bytes[] calldata payloads,
bytes32 predecessor,
bytes32 salt
) public pure virtual returns (bytes32 hash) {
return keccak256(abi.encode(targets, values, payloads, predecessor, salt));
}
/**
* @dev Schedule an operation containing a single transaction.
*
* Emits a {CallScheduled} event.
*
* Requirements:
*
* - the caller must have the 'proposer' role.
*/
function schedule(
address target,
uint256 value,
bytes calldata data,
bytes32 predecessor,
bytes32 salt,
uint256 delay
) public virtual onlyRole(PROPOSER_ROLE) {
bytes32 id = hashOperation(target, value, data, predecessor, salt);
_schedule(id, delay);
emit CallScheduled(id, 0, target, value, data, predecessor, delay);
}
/**
* @dev Schedule an operation containing a batch of transactions.
*
* Emits one {CallScheduled} event per transaction in the batch.
*
* Requirements:
*
* - the caller must have the 'proposer' role.
*/
function scheduleBatch(
address[] calldata targets,
uint256[] calldata values,
bytes[] calldata payloads,
bytes32 predecessor,
bytes32 salt,
uint256 delay
) public virtual onlyRole(PROPOSER_ROLE) {
require(targets.length == values.length, "TimelockController: length mismatch");
require(targets.length == payloads.length, "TimelockController: length mismatch");
bytes32 id = hashOperationBatch(targets, values, payloads, predecessor, salt);
_schedule(id, delay);
for (uint256 i = 0; i < targets.length; ++i) {
emit CallScheduled(id, i, targets[i], values[i], payloads[i], predecessor, delay);
}
}
/**
* @dev Schedule an operation that is to become valid after a given delay.
*/
function _schedule(bytes32 id, uint256 delay) private {
require(!isOperation(id), "TimelockController: operation already scheduled");
require(delay >= getMinDelay(), "TimelockController: insufficient delay");
_timestamps[id] = block.timestamp + delay;
}
/**
* @dev Cancel an operation.
*
* Requirements:
*
* - the caller must have the 'canceller' role.
*/
function cancel(bytes32 id) public virtual onlyRole(CANCELLER_ROLE) {
require(isOperationPending(id), "TimelockController: operation cannot be cancelled");
delete _timestamps[id];
emit Cancelled(id);
}
/**
* @dev Execute an (ready) operation containing a single transaction.
*
* Emits a {CallExecuted} event.
*
* Requirements:
*
* - the caller must have the 'executor' role.
*/
// This function can reenter, but it doesn't pose a risk because _afterCall checks that the proposal is pending,
// thus any modifications to the operation during reentrancy should be caught.
// slither-disable-next-line reentrancy-eth
function execute(
address target,
uint256 value,
bytes calldata payload,
bytes32 predecessor,
bytes32 salt
) public payable virtual onlyRoleOrOpenRole(EXECUTOR_ROLE) {
bytes32 id = hashOperation(target, value, payload, predecessor, salt);
_beforeCall(id, predecessor);
_execute(target, value, payload);
emit CallExecuted(id, 0, target, value, payload);
_afterCall(id);
}
/**
* @dev Execute an (ready) operation containing a batch of transactions.
*
* Emits one {CallExecuted} event per transaction in the batch.
*
* Requirements:
*
* - the caller must have the 'executor' role.
*/
function executeBatch(
address[] calldata targets,
uint256[] calldata values,
bytes[] calldata payloads,
bytes32 predecessor,
bytes32 salt
) public payable virtual onlyRoleOrOpenRole(EXECUTOR_ROLE) {
require(targets.length == values.length, "TimelockController: length mismatch");
require(targets.length == payloads.length, "TimelockController: length mismatch");
bytes32 id = hashOperationBatch(targets, values, payloads, predecessor, salt);
_beforeCall(id, predecessor);
for (uint256 i = 0; i < targets.length; ++i) {
address target = targets[i];
uint256 value = values[i];
bytes calldata payload = payloads[i];
_execute(target, value, payload);
emit CallExecuted(id, i, target, value, payload);
}
_afterCall(id);
}
/**
* @dev Execute an operation's call.
*/
function _execute(
address target,
uint256 value,
bytes calldata data
) internal virtual {
(bool success, ) = target.call{value: value}(data);
require(success, "TimelockController: underlying transaction reverted");
}
/**
* @dev Checks before execution of an operation's calls.
*/
function _beforeCall(bytes32 id, bytes32 predecessor) private view {
require(isOperationReady(id), "TimelockController: operation is not ready");
require(predecessor == bytes32(0) || isOperationDone(predecessor), "TimelockController: missing dependency");
}
/**
* @dev Checks after execution of an operation's calls.
*/
function _afterCall(bytes32 id) private {
require(isOperationReady(id), "TimelockController: operation is not ready");
_timestamps[id] = _DONE_TIMESTAMP;
}
/**
* @dev Changes the minimum timelock duration for future operations.
*
* Emits a {MinDelayChange} event.
*
* Requirements:
*
* - the caller must be the timelock itself. This can only be achieved by scheduling and later executing
* an operation where the timelock is the target and the data is the ABI-encoded call to this function.
*/
function updateDelay(uint256 newDelay) external virtual {
require(msg.sender == address(this), "TimelockController: caller must be timelock");
emit MinDelayChange(_minDelay, newDelay);
_minDelay = newDelay;
}
/**
* @dev See {IERC721Receiver-onERC721Received}.
*/
function onERC721Received(
address,
address,
uint256,
bytes memory
) public virtual override returns (bytes4) {
return this.onERC721Received.selector;
}
/**
* @dev See {IERC1155Receiver-onERC1155Received}.
*/
function onERC1155Received(
address,
address,
uint256,
uint256,
bytes memory
) public virtual override returns (bytes4) {
return this.onERC1155Received.selector;
}
/**
* @dev See {IERC1155Receiver-onERC1155BatchReceived}.
*/
function onERC1155BatchReceived(
address,
address,
uint256[] memory,
uint256[] memory,
bytes memory
) public virtual override returns (bytes4) {
return this.onERC1155BatchReceived.selector;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (governance/utils/IVotes.sol)
pragma solidity ^0.8.0;
/**
* @dev Common interface for {ERC20Votes}, {ERC721Votes}, and other {Votes}-enabled contracts.
*
* _Available since v4.5._
*/
interface IVotes {
/**
* @dev Emitted when an account changes their delegate.
*/
event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);
/**
* @dev Emitted when a token transfer or delegate change results in changes to a delegate's number of votes.
*/
event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance);
/**
* @dev Returns the current amount of votes that `account` has.
*/
function getVotes(address account) external view returns (uint256);
/**
* @dev Returns the amount of votes that `account` had at the end of a past block (`blockNumber`).
*/
function getPastVotes(address account, uint256 blockNumber) external view returns (uint256);
/**
* @dev Returns the total supply of votes available at the end of a past block (`blockNumber`).
*
* NOTE: This value is the sum of all available votes, which is not necessarily the sum of all delegated votes.
* Votes that have not been delegated are still part of total supply, even though they would not participate in a
* vote.
*/
function getPastTotalSupply(uint256 blockNumber) external view returns (uint256);
/**
* @dev Returns the delegate that `account` has chosen.
*/
function delegates(address account) external view returns (address);
/**
* @dev Delegates votes from the sender to `delegatee`.
*/
function delegate(address delegatee) external;
/**
* @dev Delegates votes from signer to `delegatee`.
*/
function delegateBySig(
address delegatee,
uint256 nonce,
uint256 expiry,
uint8 v,
bytes32 r,
bytes32 s
) external;
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (governance/utils/Votes.sol)
pragma solidity ^0.8.0;
import "../../utils/Context.sol";
import "../../utils/Counters.sol";
import "../../utils/Checkpoints.sol";
import "../../utils/cryptography/EIP712.sol";
import "./IVotes.sol";
/**
* @dev This is a base abstract contract that tracks voting units, which are a measure of voting power that can be
* transferred, and provides a system of vote delegation, where an account can delegate its voting units to a sort of
* "representative" that will pool delegated voting units from different accounts and can then use it to vote in
* decisions. In fact, voting units _must_ be delegated in order to count as actual votes, and an account has to
* delegate those votes to itself if it wishes to participate in decisions and does not have a trusted representative.
*
* This contract is often combined with a token contract such that voting units correspond to token units. For an
* example, see {ERC721Votes}.
*
* The full history of delegate votes is tracked on-chain so that governance protocols can consider votes as distributed
* at a particular block number to protect against flash loans and double voting. The opt-in delegate system makes the
* cost of this history tracking optional.
*
* When using this module the derived contract must implement {_getVotingUnits} (for example, make it return
* {ERC721-balanceOf}), and can use {_transferVotingUnits} to track a change in the distribution of those units (in the
* previous example, it would be included in {ERC721-_beforeTokenTransfer}).
*
* _Available since v4.5._
*/
abstract contract Votes is IVotes, Context, EIP712 {
using Checkpoints for Checkpoints.History;
using Counters for Counters.Counter;
bytes32 private constant _DELEGATION_TYPEHASH =
keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)");
mapping(address => address) private _delegation;
mapping(address => Checkpoints.History) private _delegateCheckpoints;
Checkpoints.History private _totalCheckpoints;
mapping(address => Counters.Counter) private _nonces;
/**
* @dev Returns the current amount of votes that `account` has.
*/
function getVotes(address account) public view virtual override returns (uint256) {
return _delegateCheckpoints[account].latest();
}
/**
* @dev Returns the amount of votes that `account` had at the end of a past block (`blockNumber`).
*
* Requirements:
*
* - `blockNumber` must have been already mined
*/
function getPastVotes(address account, uint256 blockNumber) public view virtual override returns (uint256) {
return _delegateCheckpoints[account].getAtProbablyRecentBlock(blockNumber);
}
/**
* @dev Returns the total supply of votes available at the end of a past block (`blockNumber`).
*
* NOTE: This value is the sum of all available votes, which is not necessarily the sum of all delegated votes.
* Votes that have not been delegated are still part of total supply, even though they would not participate in a
* vote.
*
* Requirements:
*
* - `blockNumber` must have been already mined
*/
function getPastTotalSupply(uint256 blockNumber) public view virtual override returns (uint256) {
require(blockNumber < block.number, "Votes: block not yet mined");
return _totalCheckpoints.getAtProbablyRecentBlock(blockNumber);
}
/**
* @dev Returns the current total supply of votes.
*/
function _getTotalSupply() internal view virtual returns (uint256) {
return _totalCheckpoints.latest();
}
/**
* @dev Returns the delegate that `account` has chosen.
*/
function delegates(address account) public view virtual override returns (address) {
return _delegation[account];
}
/**
* @dev Delegates votes from the sender to `delegatee`.
*/
function delegate(address delegatee) public virtual override {
address account = _msgSender();
_delegate(account, delegatee);
}
/**
* @dev Delegates votes from signer to `delegatee`.
*/
function delegateBySig(
address delegatee,
uint256 nonce,
uint256 expiry,
uint8 v,
bytes32 r,
bytes32 s
) public virtual override {
require(block.timestamp <= expiry, "Votes: signature expired");
address signer = ECDSA.recover(
_hashTypedDataV4(keccak256(abi.encode(_DELEGATION_TYPEHASH, delegatee, nonce, expiry))),
v,
r,
s
);
require(nonce == _useNonce(signer), "Votes: invalid nonce");
_delegate(signer, delegatee);
}
/**
* @dev Delegate all of `account`'s voting units to `delegatee`.
*
* Emits events {IVotes-DelegateChanged} and {IVotes-DelegateVotesChanged}.
*/
function _delegate(address account, address delegatee) internal virtual {
address oldDelegate = delegates(account);
_delegation[account] = delegatee;
emit DelegateChanged(account, oldDelegate, delegatee);
_moveDelegateVotes(oldDelegate, delegatee, _getVotingUnits(account));
}
/**
* @dev Transfers, mints, or burns voting units. To register a mint, `from` should be zero. To register a burn, `to`
* should be zero. Total supply of voting units will be adjusted with mints and burns.
*/
function _transferVotingUnits(
address from,
address to,
uint256 amount
) internal virtual {
if (from == address(0)) {
_totalCheckpoints.push(_add, amount);
}
if (to == address(0)) {
_totalCheckpoints.push(_subtract, amount);
}
_moveDelegateVotes(delegates(from), delegates(to), amount);
}
/**
* @dev Moves delegated votes from one delegate to another.
*/
function _moveDelegateVotes(
address from,
address to,
uint256 amount
) private {
if (from != to && amount > 0) {
if (from != address(0)) {
(uint256 oldValue, uint256 newValue) = _delegateCheckpoints[from].push(_subtract, amount);
emit DelegateVotesChanged(from, oldValue, newValue);
}
if (to != address(0)) {
(uint256 oldValue, uint256 newValue) = _delegateCheckpoints[to].push(_add, amount);
emit DelegateVotesChanged(to, oldValue, newValue);
}
}
}
function _add(uint256 a, uint256 b) private pure returns (uint256) {
return a + b;
}
function _subtract(uint256 a, uint256 b) private pure returns (uint256) {
return a - b;
}
/**
* @dev Consumes a nonce.
*
* Returns the current value and increments nonce.
*/
function _useNonce(address owner) internal virtual returns (uint256 current) {
Counters.Counter storage nonce = _nonces[owner];
current = nonce.current();
nonce.increment();
}
/**
* @dev Returns an address nonce.
*/
function nonces(address owner) public view virtual returns (uint256) {
return _nonces[owner].current();
}
/**
* @dev Returns the contract's {EIP712} domain separator.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32) {
return _domainSeparatorV4();
}
/**
* @dev Must return the voting units held by an account.
*/
function _getVotingUnits(address) internal view virtual returns (uint256);
}
{
"id": "07712e59aba8b03244641aac2eb625b3",
"_format": "hh-sol-build-info-1",
"solcVersion": "0.8.15",
"solcLongVersion": "0.8.15+commit.e14f2714",
"input": {
"language": "Solidity",
"sources": {
"lib/openzepelin-contract/Contract/interfaces/draft-IERC1822.sol": {
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified\n * proxy whose upgrades are fully controlled by the current implementation.\n */\ninterface IERC1822Proxiable {\n /**\n * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation\n * address.\n *\n * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks\n * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this\n * function revert if invoked through a proxy.\n */\n function proxiableUUID() external view returns (bytes32);\n}\n"
}
},
"settings": {
"optimizer": {
"enabled": false,
"runs": 200
},
"outputSelection": {
"*": {
"": [
"ast"
],
"*": [
"abi",
"metadata",
"devdoc",
"userdoc",
"storageLayout",
"evm.legacyAssembly",
"evm.bytecode",
"evm.deployedBytecode",
"evm.methodIdentifiers",
"evm.gasEstimates",
"evm.assembly"
]
}
}
}
},
"output": {
"contracts": {
"lib/openzepelin-contract/Contract/interfaces/draft-IERC1822.sol": {
"IERC1822Proxiable": {
"abi": [
{
"inputs": [],
"name": "proxiableUUID",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
}
],
"devdoc": {
"details": "ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified proxy whose upgrades are fully controlled by the current implementation.",
"kind": "dev",
"methods": {
"proxiableUUID()": {
"details": "Returns the storage slot that the proxiable contract assumes is being used to store the implementation address. IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this function revert if invoked through a proxy."
}
},
"version": 1
},
"evm": {
"assembly": "",
"bytecode": {
"functionDebugData": {},
"generatedSources": [],
"linkReferences": {},
"object": "",
"opcodes": "",
"sourceMap": ""
},
"deployedBytecode": {
"functionDebugData": {},
"generatedSources": [],
"immutableReferences": {},
"linkReferences": {},
"object": "",
"opcodes": "",
"sourceMap": ""
},
"gasEstimates": null,
"legacyAssembly": null,
"methodIdentifiers": {
"proxiableUUID()": "52d1902d"
}
},
"metadata": "{\"compiler\":{\"version\":\"0.8.15+commit.e14f2714\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"proxiableUUID\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"details\":\"ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified proxy whose upgrades are fully controlled by the current implementation.\",\"kind\":\"dev\",\"methods\":{\"proxiableUUID()\":{\"details\":\"Returns the storage slot that the proxiable contract assumes is being used to store the implementation address. IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this function revert if invoked through a proxy.\"}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"lib/openzepelin-contract/Contract/interfaces/draft-IERC1822.sol\":\"IERC1822Proxiable\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":false,\"runs\":200},\"remappings\":[]},\"sources\":{\"lib/openzepelin-contract/Contract/interfaces/draft-IERC1822.sol\":{\"keccak256\":\"0x1d4afe6cb24200cc4545eed814ecf5847277dfe5d613a1707aad5fceecebcfff\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://383fb7b8181016ac5ccf07bc9cdb7c1b5045ea36e2cc4df52bcbf20396fc7688\",\"dweb:/ipfs/QmYJ7Cg4WmE3rR8KGQxjUCXFfTH6TcwZ2Z1f6tPrq7jHFr\"]}},\"version\":1}",
"storageLayout": {
"storage": [],
"types": null
},
"userdoc": {
"kind": "user",
"methods": {},
"version": 1
}
}
}
},
"sources": {
"lib/openzepelin-contract/Contract/interfaces/draft-IERC1822.sol": {
"ast": {
"absolutePath": "lib/openzepelin-contract/Contract/interfaces/draft-IERC1822.sol",
"exportedSymbols": {
"IERC1822Proxiable": [
9
]
},
"id": 10,
"license": "MIT",
"nodeType": "SourceUnit",
"nodes": [
{
"id": 1,
"literals": [
"solidity",
"^",
"0.8",
".0"
],
"nodeType": "PragmaDirective",
"src": "113:23:0"
},
{
"abstract": false,
"baseContracts": [],
"canonicalName": "IERC1822Proxiable",
"contractDependencies": [],
"contractKind": "interface",
"documentation": {
"id": 2,
"nodeType": "StructuredDocumentation",
"src": "138:203:0",
"text": " @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified\n proxy whose upgrades are fully controlled by the current implementation."
},
"fullyImplemented": false,
"id": 9,
"linearizedBaseContracts": [
9
],
"name": "IERC1822Proxiable",
"nameLocation": "352:17:0",
"nodeType": "ContractDefinition",
"nodes": [
{
"documentation": {
"id": 3,
"nodeType": "StructuredDocumentation",
"src": "376:438:0",
"text": " @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation\n address.\n IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks\n bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this\n function revert if invoked through a proxy."
},
"functionSelector": "52d1902d",
"id": 8,
"implemented": false,
"kind": "function",
"modifiers": [],
"name": "proxiableUUID",
"nameLocation": "828:13:0",
"nodeType": "FunctionDefinition",
"parameters": {
"id": 4,
"nodeType": "ParameterList",
"parameters": [],
"src": "841:2:0"
},
"returnParameters": {
"id": 7,
"nodeType": "ParameterList",
"parameters": [
{
"constant": false,
"id": 6,
"mutability": "mutable",
"name": "",
"nameLocation": "-1:-1:-1",
"nodeType": "VariableDeclaration",
"scope": 8,
"src": "867:7:0",
"stateVariable": false,
"storageLocation": "default",
"typeDescriptions": {
"typeIdentifier": "t_bytes32",
"typeString": "bytes32"
},
"typeName": {
"id": 5,
"name": "bytes32",
"nodeType": "ElementaryTypeName",
"src": "867:7:0",
"typeDescriptions": {
"typeIdentifier": "t_bytes32",
"typeString": "bytes32"
}
},
"visibility": "internal"
}
],
"src": "866:9:0"
},
"scope": 9,
"src": "819:57:0",
"stateMutability": "view",
"virtual": false,
"visibility": "external"
}
],
"scope": 10,
"src": "342:536:0",
"usedErrors": []
}
],
"src": "113:766:0"
},
"id": 0
}
}
}
}
{
"deploy": {
"VM:-": {
"linkReferences": {},
"autoDeployLib": true
},
"main:1": {
"linkReferences": {},
"autoDeployLib": true
},
"ropsten:3": {
"linkReferences": {},
"autoDeployLib": true
},
"rinkeby:4": {
"linkReferences": {},
"autoDeployLib": true
},
"kovan:42": {
"linkReferences": {},
"autoDeployLib": true
},
"goerli:5": {
"linkReferences": {},
"autoDeployLib": true
},
"Custom": {
"linkReferences": {},
"autoDeployLib": true
}
},
"data": {
"bytecode": {
"functionDebugData": {},
"generatedSources": [],
"linkReferences": {},
"object": "",
"opcodes": "",
"sourceMap": ""
},
"deployedBytecode": {
"functionDebugData": {},
"generatedSources": [],
"immutableReferences": {},
"linkReferences": {},
"object": "",
"opcodes": "",
"sourceMap": ""
},
"gasEstimates": null,
"methodIdentifiers": {
"proxiableUUID()": "52d1902d"
}
},
"abi": [
{
"inputs": [],
"name": "proxiableUUID",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
}
]
}
{
"compiler": {
"version": "0.8.15+commit.e14f2714"
},
"language": "Solidity",
"output": {
"abi": [
{
"inputs": [],
"name": "proxiableUUID",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
}
],
"devdoc": {
"details": "ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified proxy whose upgrades are fully controlled by the current implementation.",
"kind": "dev",
"methods": {
"proxiableUUID()": {
"details": "Returns the storage slot that the proxiable contract assumes is being used to store the implementation address. IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this function revert if invoked through a proxy."
}
},
"version": 1
},
"userdoc": {
"kind": "user",
"methods": {},
"version": 1
}
},
"settings": {
"compilationTarget": {
"lib/openzepelin-contract/Contract/interfaces/draft-IERC1822.sol": "IERC1822Proxiable"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
},
"sources": {
"lib/openzepelin-contract/Contract/interfaces/draft-IERC1822.sol": {
"keccak256": "0x1d4afe6cb24200cc4545eed814ecf5847277dfe5d613a1707aad5fceecebcfff",
"license": "MIT",
"urls": [
"bzz-raw://383fb7b8181016ac5ccf07bc9cdb7c1b5045ea36e2cc4df52bcbf20396fc7688",
"dweb:/ipfs/QmYJ7Cg4WmE3rR8KGQxjUCXFfTH6TcwZ2Z1f6tPrq7jHFr"
]
}
},
"version": 1
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)
pragma solidity ^0.8.0;
/**
* @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
* proxy whose upgrades are fully controlled by the current implementation.
*/
interface IERC1822Proxiable {
/**
* @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
* address.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy.
*/
function proxiableUUID() external view returns (bytes32);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// EIP-2612 is Final as of 2022-11-01. This file is deprecated.
import "./IERC2612.sol";
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1155.sol)
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC1155 compliant contract, as defined in the
* https://eips.ethereum.org/EIPS/eip-1155[EIP].
*
* _Available since v3.1._
*/
interface IERC1155 is IERC165 {
/**
* @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
*/
event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
/**
* @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
* transfers.
*/
event TransferBatch(address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values);
/**
* @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
* `approved`.
*/
event ApprovalForAll(address indexed account, address indexed operator, bool approved);
/**
* @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
*
* If an {URI} event was emitted for `id`, the standard
* https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
* returned by {IERC1155MetadataURI-uri}.
*/
event URI(string value, uint256 indexed id);
/**
* @dev Returns the amount of tokens of token type `id` owned by `account`.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function balanceOf(address account, uint256 id) external view returns (uint256);
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
*
* Requirements:
*
* - `accounts` and `ids` must have the same length.
*/
function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) external view returns (uint256[] memory);
/**
* @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
*
* Emits an {ApprovalForAll} event.
*
* Requirements:
*
* - `operator` cannot be the caller.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
*
* See {setApprovalForAll}.
*/
function isApprovedForAll(address account, address operator) external view returns (bool);
/**
* @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - If the caller is not `from`, it must be have been approved to spend ``from``'s tokens via {setApprovalForAll}.
* - `from` must have a balance of tokens of type `id` of at least `amount`.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
* acceptance magic value.
*/
function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external;
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
*
* Emits a {TransferBatch} event.
*
* Requirements:
*
* - `ids` and `amounts` must have the same length.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
* acceptance magic value.
*/
function safeBatchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data) external;
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1155MetadataURI.sol)
pragma solidity ^0.8.0;
import "../IERC1155.sol";
/**
* @dev Interface of the optional ERC1155MetadataExtension interface, as defined
* in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP].
*
* _Available since v3.1._
*/
interface IERC1155MetadataURI is IERC1155 {
/**
* @dev Returns the URI for token type `id`.
*
* If the `\{id\}` substring is present in the URI, it must be replaced by
* clients with the actual token type ID.
*/
function uri(uint256 id) external view returns (string memory);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1155Receiver.sol)
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165.sol";
/**
* @dev _Available since v3.1._
*/
interface IERC1155Receiver is IERC165 {
/**
@dev Handles the receipt of a single ERC1155 token type. This function is
called at the end of a `safeTransferFrom` after the balance has been updated.
To accept the transfer, this must return
`bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
(i.e. 0xf23a6e61, or its own function selector).
@param operator The address which initiated the transfer (i.e. msg.sender)
@param from The address which previously owned the token
@param id The ID of the token being transferred
@param value The amount of tokens being transferred
@param data Additional data with no specified format
@return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
*/
function onERC1155Received(
address operator,
address from,
uint256 id,
uint256 value,
bytes calldata data
)
external
returns(bytes4);
/**
@dev Handles the receipt of a multiple ERC1155 token types. This function
is called at the end of a `safeBatchTransferFrom` after the balances have
been updated. To accept the transfer(s), this must return
`bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
(i.e. 0xbc197c81, or its own function selector).
@param operator The address which initiated the batch transfer (i.e. msg.sender)
@param from The address which previously owned the token
@param ids An array containing ids of each token being transferred (order and length must match values array)
@param values An array containing amounts of each token being transferred (order and length must match ids array)
@param data Additional data with no specified format
@return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
*/
function onERC1155BatchReceived(
address operator,
address from,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
)
external
returns(bytes4);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1271.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC1271 standard signature validation method for
* contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
*
* _Available since v4.1._
*/
interface IERC1271 {
/**
* @dev Should return whether the signature provided is valid for the provided data
* @param hash Hash of the data to be signed
* @param signature Signature byte array associated with _data
*/
function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1363.sol)
pragma solidity ^0.8.0;
import "./IERC20.sol";
import "./IERC165.sol";
interface IERC1363 is IERC165, IERC20 {
/*
* Note: the ERC-165 identifier for this interface is 0x4bbee2df.
* 0x4bbee2df ===
* bytes4(keccak256('transferAndCall(address,uint256)')) ^
* bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)'))
*/
/*
* Note: the ERC-165 identifier for this interface is 0xfb9ec8ce.
* 0xfb9ec8ce ===
* bytes4(keccak256('approveAndCall(address,uint256)')) ^
* bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
*/
/**
* @dev Transfer tokens from `msg.sender` to another address and then call `onTransferReceived` on receiver
* @param to address The address which you want to transfer to
* @param value uint256 The amount of tokens to be transferred
* @return true unless throwing
*/
function transferAndCall(address to, uint256 value) external returns (bool);
/**
* @dev Transfer tokens from `msg.sender` to another address and then call `onTransferReceived` on receiver
* @param to address The address which you want to transfer to
* @param value uint256 The amount of tokens to be transferred
* @param data bytes Additional data with no specified format, sent in call to `to`
* @return true unless throwing
*/
function transferAndCall(
address to,
uint256 value,
bytes memory data
) external returns (bool);
/**
* @dev Transfer tokens from one address to another and then call `onTransferReceived` on receiver
* @param from address The address which you want to send tokens from
* @param to address The address which you want to transfer to
* @param value uint256 The amount of tokens to be transferred
* @return true unless throwing
*/
function transferFromAndCall(
address from,
address to,
uint256 value
) external returns (bool);
/**
* @dev Transfer tokens from one address to another and then call `onTransferReceived` on receiver
* @param from address The address which you want to send tokens from
* @param to address The address which you want to transfer to
* @param value uint256 The amount of tokens to be transferred
* @param data bytes Additional data with no specified format, sent in call to `to`
* @return true unless throwing
*/
function transferFromAndCall(
address from,
address to,
uint256 value,
bytes memory data
) external returns (bool);
/**
* @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender
* and then call `onApprovalReceived` on spender.
* @param spender address The address which will spend the funds
* @param value uint256 The amount of tokens to be spent
*/
function approveAndCall(address spender, uint256 value) external returns (bool);
/**
* @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender
* and then call `onApprovalReceived` on spender.
* @param spender address The address which will spend the funds
* @param value uint256 The amount of tokens to be spent
* @param data bytes Additional data with no specified format, sent in call to `spender`
*/
function approveAndCall(
address spender,
uint256 value,
bytes memory data
) external returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1363Receiver.sol)
pragma solidity ^0.8.0;
interface IERC1363Receiver {
/*
* Note: the ERC-165 identifier for this interface is 0x88a7ca5c.
* 0x88a7ca5c === bytes4(keccak256("onTransferReceived(address,address,uint256,bytes)"))
*/
/**
* @notice Handle the receipt of ERC1363 tokens
* @dev Any ERC1363 smart contract calls this function on the recipient
* after a `transfer` or a `transferFrom`. This function MAY throw to revert and reject the
* transfer. Return of other than the magic value MUST result in the
* transaction being reverted.
* Note: the token contract address is always the message sender.
* @param operator address The address which called `transferAndCall` or `transferFromAndCall` function
* @param from address The address which are token transferred from
* @param value uint256 The amount of tokens transferred
* @param data bytes Additional data with no specified format
* @return `bytes4(keccak256("onTransferReceived(address,address,uint256,bytes)"))`
* unless throwing
*/
function onTransferReceived(
address operator,
address from,
uint256 value,
bytes memory data
) external returns (bytes4);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1363Spender.sol)
pragma solidity ^0.8.0;
interface IERC1363Spender {
/*
* Note: the ERC-165 identifier for this interface is 0x7b04a2d0.
* 0x7b04a2d0 === bytes4(keccak256("onApprovalReceived(address,uint256,bytes)"))
*/
/**
* @notice Handle the approval of ERC1363 tokens
* @dev Any ERC1363 smart contract calls this function on the recipient
* after an `approve`. This function MAY throw to revert and reject the
* approval. Return of other than the magic value MUST result in the
* transaction being reverted.
* Note: the token contract address is always the message sender.
* @param owner address The address which called `approveAndCall` function
* @param value uint256 The amount of tokens to be spent
* @param data bytes Additional data with no specified format
* @return `bytes4(keccak256("onApprovalReceived(address,uint256,bytes)"))`
* unless throwing
*/
function onApprovalReceived(
address owner,
uint256 value,
bytes memory data
) external returns (bytes4);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1820Implementer.sol)
pragma solidity ^0.8.0;
import "../utils/introspection/IERC1820Implementer.sol";
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1820Registry.sol)
pragma solidity ^0.8.0;
import "../utils/introspection/IERC1820Registry.sol";
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC20Metadata.sol)
pragma solidity ^0.8.0;
import "../token/ERC20/extensions/IERC20Metadata.sol";
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev ERC-2309: ERC-721 Consecutive Transfer Extension.
*
* _Available since v4.8._
*/
interface IERC2309 {
/**
* @dev Emitted when the tokens from `fromTokenId` to `toTokenId` are transferred from `fromAddress` to `toAddress`.
*/
event ConsecutiveTransfer(
uint256 indexed fromTokenId,
uint256 toTokenId,
address indexed fromAddress,
address indexed toAddress
);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC2612.sol)
pragma solidity ^0.8.0;
import "../token/ERC20/extensions/IERC20Permit.sol";
interface IERC2612 is IERC20Permit {}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (interfaces/IERC2981.sol)
pragma solidity ^0.8.0;
import "../utils/introspection/IERC165.sol";
/**
* @dev Interface for the NFT Royalty Standard.
*
* A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
* support for royalty payments across all NFT marketplaces and ecosystem participants.
*
* _Available since v4.5._
*/
interface IERC2981 is IERC165 {
/**
* @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
* exchange. The royalty amount is denominated and should be paid in that same unit of exchange.
*/
function royaltyInfo(uint256 tokenId, uint256 salePrice)
external
view
returns (address receiver, uint256 royaltyAmount);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC3156 FlashBorrower, as defined in
* https://eips.ethereum.org/EIPS/eip-3156[ERC-3156].
*
* _Available since v4.1._
*/
interface IERC3156FlashBorrower {
/**
* @dev Receive a flash loan.
* @param initiator The initiator of the loan.
* @param token The loan currency.
* @param amount The amount of tokens lent.
* @param fee The additional amount of tokens to repay.
* @param data Arbitrary data structure, intended to contain user-defined parameters.
* @return The keccak256 hash of "ERC3156FlashBorrower.onFlashLoan"
*/
function onFlashLoan(
address initiator,
address token,
uint256 amount,
uint256 fee,
bytes calldata data
) external returns (bytes32);
}
/**
* @dev Interface of the ERC3156 FlashLender, as defined in
* https://eips.ethereum.org/EIPS/eip-3156[ERC-3156].
*/
interface IERC3156FlashLender {
/**
* @dev The amount of currency available to be lended.
* @param token The loan currency.
* @return The amount of `token` that can be borrowed.
*/
function maxFlashLoan(address token) external view returns (uint256);
/**
* @dev The fee to be charged for a given loan.
* @param token The loan currency.
* @param amount The amount of tokens lent.
* @return The amount of `token` to be charged for the loan, on top of the returned principal.
*/
function flashFee(address token, uint256 amount) external view returns (uint256);
/**
* @dev Initiate a flash loan.
* @param receiver The receiver of the tokens in the loan, and the receiver of the callback.
* @param token The loan currency.
* @param amount The amount of tokens lent.
* @param data Arbitrary data structure, intended to contain user-defined parameters.
*/
function flashLoan(
IERC3156FlashBorrower receiver,
address token,
uint256 amount,
bytes calldata data
) external returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (interfaces/IERC3156FlashBorrower.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC3156 FlashBorrower, as defined in
* https://eips.ethereum.org/EIPS/eip-3156[ERC-3156].
*
* _Available since v4.1._
*/
interface IERC3156FlashBorrower {
/**
* @dev Receive a flash loan.
* @param initiator The initiator of the loan.
* @param token The loan currency.
* @param amount The amount of tokens lent.
* @param fee The additional amount of tokens to repay.
* @param data Arbitrary data structure, intended to contain user-defined parameters.
* @return The keccak256 hash of "IERC3156FlashBorrower.onFlashLoan"
*/
function onFlashLoan(
address initiator,
address token,
uint256 amount,
uint256 fee,
bytes calldata data
) external returns (bytes32);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC3156FlashLender.sol)
pragma solidity ^0.8.0;
import "./IERC3156FlashBorrower.sol";
/**
* @dev Interface of the ERC3156 FlashLender, as defined in
* https://eips.ethereum.org/EIPS/eip-3156[ERC-3156].
*
* _Available since v4.1._
*/
interface IERC3156FlashLender {
/**
* @dev The amount of currency available to be lended.
* @param token The loan currency.
* @return The amount of `token` that can be borrowed.
*/
function maxFlashLoan(address token) external view returns (uint256);
/**
* @dev The fee to be charged for a given loan.
* @param token The loan currency.
* @param amount The amount of tokens lent.
* @return The amount of `token` to be charged for the loan, on top of the returned principal.
*/
function flashFee(address token, uint256 amount) external view returns (uint256);
/**
* @dev Initiate a flash loan.
* @param receiver The receiver of the tokens in the loan, and the receiver of the callback.
* @param token The loan currency.
* @param amount The amount of tokens lent.
* @param data Arbitrary data structure, intended to contain user-defined parameters.
*/
function flashLoan(
IERC3156FlashBorrower receiver,
address token,
uint256 amount,
bytes calldata data
) external returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (interfaces/IERC4626.sol)
pragma solidity ^0.8.0;
import "../token/ERC20/IERC20.sol";
import "../token/ERC20/extensions/IERC20Metadata.sol";
/**
* @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in
* https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].
*
* _Available since v4.7._
*/
interface IERC4626 is IERC20, IERC20Metadata {
event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);
event Withdraw(
address indexed sender,
address indexed receiver,
address indexed owner,
uint256 assets,
uint256 shares
);
/**
* @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
*
* - MUST be an ERC-20 token contract.
* - MUST NOT revert.
*/
function asset() external view returns (address assetTokenAddress);
/**
* @dev Returns the total amount of the underlying asset that is “managed” by Vault.
*
* - SHOULD include any compounding that occurs from yield.
* - MUST be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT revert.
*/
function totalAssets() external view returns (uint256 totalManagedAssets);
/**
* @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal
* scenario where all the conditions are met.
*
* - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT show any variations depending on the caller.
* - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
* - MUST NOT revert.
*
* NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
* “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
* from.
*/
function convertToShares(uint256 assets) external view returns (uint256 shares);
/**
* @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal
* scenario where all the conditions are met.
*
* - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT show any variations depending on the caller.
* - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
* - MUST NOT revert.
*
* NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
* “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
* from.
*/
function convertToAssets(uint256 shares) external view returns (uint256 assets);
/**
* @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
* through a deposit call.
*
* - MUST return a limited value if receiver is subject to some deposit limit.
* - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
* - MUST NOT revert.
*/
function maxDeposit(address receiver) external view returns (uint256 maxAssets);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
* current on-chain conditions.
*
* - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit
* call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called
* in the same transaction.
* - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the
* deposit would be accepted, regardless if the user has enough tokens approved, etc.
* - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by depositing.
*/
function previewDeposit(uint256 assets) external view returns (uint256 shares);
/**
* @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
*
* - MUST emit the Deposit event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* deposit execution, and are accounted for during deposit.
* - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not
* approving enough underlying tokens to the Vault contract, etc).
*
* NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
*/
function deposit(uint256 assets, address receiver) external returns (uint256 shares);
/**
* @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
* - MUST return a limited value if receiver is subject to some mint limit.
* - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
* - MUST NOT revert.
*/
function maxMint(address receiver) external view returns (uint256 maxShares);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
* current on-chain conditions.
*
* - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call
* in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the
* same transaction.
* - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint
* would be accepted, regardless if the user has enough tokens approved, etc.
* - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by minting.
*/
function previewMint(uint256 shares) external view returns (uint256 assets);
/**
* @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
*
* - MUST emit the Deposit event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint
* execution, and are accounted for during mint.
* - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not
* approving enough underlying tokens to the Vault contract, etc).
*
* NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
*/
function mint(uint256 shares, address receiver) external returns (uint256 assets);
/**
* @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
* Vault, through a withdraw call.
*
* - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
* - MUST NOT revert.
*/
function maxWithdraw(address owner) external view returns (uint256 maxAssets);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
* given current on-chain conditions.
*
* - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw
* call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if
* called
* in the same transaction.
* - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though
* the withdrawal would be accepted, regardless if the user has enough shares, etc.
* - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by depositing.
*/
function previewWithdraw(uint256 assets) external view returns (uint256 shares);
/**
* @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.
*
* - MUST emit the Withdraw event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* withdraw execution, and are accounted for during withdraw.
* - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
* not having enough shares, etc).
*
* Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
* Those methods should be performed separately.
*/
function withdraw(
uint256 assets,
address receiver,
address owner
) external returns (uint256 shares);
/**
* @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
* through a redeem call.
*
* - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
* - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
* - MUST NOT revert.
*/
function maxRedeem(address owner) external view returns (uint256 maxShares);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,
* given current on-chain conditions.
*
* - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call
* in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the
* same transaction.
* - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the
* redemption would be accepted, regardless if the user has enough shares, etc.
* - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by redeeming.
*/
function previewRedeem(uint256 shares) external view returns (uint256 assets);
/**
* @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.
*
* - MUST emit the Withdraw event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* redeem execution, and are accounted for during redeem.
* - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner
* not having enough shares, etc).
*
* NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
* Those methods should be performed separately.
*/
function redeem(
uint256 shares,
address receiver,
address owner
) external returns (uint256 assets);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC721.sol)
pragma solidity ^0.8.0;
import "../token/ERC721/IERC721.sol";
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC721Enumerable.sol)
pragma solidity ^0.8.0;
import "../token/ERC721/extensions/IERC721Enumerable.sol";
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC721Metadata.sol)
pragma solidity ^0.8.0;
import "../token/ERC721/extensions/IERC721Metadata.sol";
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC721Receiver.sol)
pragma solidity ^0.8.0;
import "../token/ERC721/IERC721Receiver.sol";
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC777.sol)
pragma solidity ^0.8.0;
import "../token/ERC777/IERC777.sol";
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC777Recipient.sol)
pragma solidity ^0.8.0;
import "../token/ERC777/IERC777Recipient.sol";
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC777Sender.sol)
pragma solidity ^0.8.0;
import "../token/ERC777/IERC777Sender.sol";

Interfaces

Note
This document is better viewed at https://docs.openzeppelin.com/contracts/api/interfaces

List of standardized interfaces

These interfaces are available as .sol files, and also as compiler .json ABI files (through the npm package). These are useful to interact with third party contracts that implement them.

  • {IERC20}

  • {IERC20Metadata}

  • {IERC165}

  • {IERC721}

  • {IERC721Receiver}

  • {IERC721Enumerable}

  • {IERC721Metadata}

  • {IERC777}

  • {IERC777Recipient}

  • {IERC777Sender}

  • {IERC1155}

  • {IERC1155Receiver}

  • {IERC1155MetadataURI}

  • {IERC1271}

  • {IERC1363}

  • {IERC1820Implementer}

  • {IERC1820Registry}

  • {IERC1822Proxiable}

  • {IERC2612}

  • {IERC2981}

  • {IERC3156FlashLender}

  • {IERC3156FlashBorrower}

  • {IERC4626}

Detailed ABI

{{IERC1271}}

{{IERC1363}}

{{IERC1363Receiver}}

{{IERC1820Implementer}}

{{IERC1820Registry}}

{{IERC1822Proxiable}}

{{IERC2612}}

{{IERC2981}}

{{IERC3156FlashLender}}

{{IERC3156FlashBorrower}}

{{IERC4626}}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (metatx/ERC2771Context.sol)
pragma solidity ^0.8.9;
import "../utils/Context.sol";
/**
* @dev Context variant with ERC2771 support.
*/
abstract contract ERC2771Context is Context {
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
address private immutable _trustedForwarder;
/// @custom:oz-upgrades-unsafe-allow constructor
constructor(address trustedForwarder) {
_trustedForwarder = trustedForwarder;
}
function isTrustedForwarder(address forwarder) public view virtual returns (bool) {
return forwarder == _trustedForwarder;
}
function _msgSender() internal view virtual override returns (address sender) {
if (isTrustedForwarder(msg.sender)) {
// The assembly code is more direct than the Solidity version using `abi.decode`.
/// @solidity memory-safe-assembly
assembly {
sender := shr(96, calldataload(sub(calldatasize(), 20)))
}
} else {
return super._msgSender();
}
}
function _msgData() internal view virtual override returns (bytes calldata) {
if (isTrustedForwarder(msg.sender)) {
return msg.data[:msg.data.length - 20];
} else {
return super._msgData();
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (metatx/MinimalForwarder.sol)
pragma solidity ^0.8.0;
import "../utils/cryptography/ECDSA.sol";
import "../utils/cryptography/EIP712.sol";
/**
* @dev Simple minimal forwarder to be used together with an ERC2771 compatible contract. See {ERC2771Context}.
*
* MinimalForwarder is mainly meant for testing, as it is missing features to be a good production-ready forwarder. This
* contract does not intend to have all the properties that are needed for a sound forwarding system. A fully
* functioning forwarding system with good properties requires more complexity. We suggest you look at other projects
* such as the GSN which do have the goal of building a system like that.
*/
contract MinimalForwarder is EIP712 {
using ECDSA for bytes32;
struct ForwardRequest {
address from;
address to;
uint256 value;
uint256 gas;
uint256 nonce;
bytes data;
}
bytes32 private constant _TYPEHASH =
keccak256("ForwardRequest(address from,address to,uint256 value,uint256 gas,uint256 nonce,bytes data)");
mapping(address => uint256) private _nonces;
constructor() EIP712("MinimalForwarder", "0.0.1") {}
function getNonce(address from) public view returns (uint256) {
return _nonces[from];
}
function verify(ForwardRequest calldata req, bytes calldata signature) public view returns (bool) {
address signer = _hashTypedDataV4(
keccak256(abi.encode(_TYPEHASH, req.from, req.to, req.value, req.gas, req.nonce, keccak256(req.data)))
).recover(signature);
return _nonces[req.from] == req.nonce && signer == req.from;
}
function execute(ForwardRequest calldata req, bytes calldata signature)
public
payable
returns (bool, bytes memory)
{
require(verify(req, signature), "MinimalForwarder: signature does not match request");
_nonces[req.from] = req.nonce + 1;
(bool success, bytes memory returndata) = req.to.call{gas: req.gas, value: req.value}(
abi.encodePacked(req.data, req.from)
);
// Validate that the relayer has sent enough gas for the call.
// See https://ronan.eth.limo/blog/ethereum-gas-dangers/
if (gasleft() <= req.gas / 63) {
// We explicitly trigger invalid opcode to consume all gas and bubble-up the effects, since
// neither revert or assert consume all gas since Solidity 0.8.0
// https://docs.soliditylang.org/en/v0.8.0/control-structures.html#panic-via-assert-and-error-via-require
/// @solidity memory-safe-assembly
assembly {
invalid()
}
}
return (success, returndata);
}
}
{
"name": "@openzeppelin/contracts",
"description": "Secure Smart Contract library for Solidity",
"version": "4.7.0",
"files": [
"**/*.sol",
"/build/contracts/*.json",
"!/mocks/**/*"
],
"scripts": {
"prepare": "bash ../scripts/prepare-contracts-package.sh",
"prepare-docs": "cd ..; npm run prepare-docs"
},
"repository": {
"type": "git",
"url": "https://github.com/OpenZeppelin/openzeppelin-contracts.git"
},
"keywords": [
"solidity",
"ethereum",
"smart",
"contracts",
"security",
"zeppelin"
],
"author": "OpenZeppelin Community <maintainers@openzeppelin.org>",
"license": "MIT",
"bugs": {
"url": "https://github.com/OpenZeppelin/openzeppelin-contracts/issues"
},
"homepage": "https://openzeppelin.com/contracts/"
}
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.
forge-std/=lib/forge-std/src/
openzeppelin-contracts/=lib/openzeppelin-contracts/
solmate/=lib/solmate/src/
{
"id": "48ed3268cc30294f0a21e101ea2b543e",
"_format": "hh-sol-build-info-1",
"solcVersion": "0.8.15",
"solcLongVersion": "0.8.15+commit.e14f2714",
"input": {
"language": "Solidity",
"sources": {
"scripts/github.sol": {
"content": ""
}
},
"settings": {
"optimizer": {
"enabled": false,
"runs": 200
},
"outputSelection": {
"*": {
"": [
"ast"
],
"*": [
"abi",
"metadata",
"devdoc",
"userdoc",
"storageLayout",
"evm.legacyAssembly",
"evm.bytecode",
"evm.deployedBytecode",
"evm.methodIdentifiers",
"evm.gasEstimates",
"evm.assembly"
]
}
}
}
},
"output": {
"errors": [
{
"component": "general",
"errorCode": "1878",
"formattedMessage": "Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing \"SPDX-License-Identifier: <SPDX-License>\" to each source file. Use \"SPDX-License-Identifier: UNLICENSED\" for non-open-source code. Please see https://spdx.org for more information.\n--> scripts/github.sol\n\n",
"message": "SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing \"SPDX-License-Identifier: <SPDX-License>\" to each source file. Use \"SPDX-License-Identifier: UNLICENSED\" for non-open-source code. Please see https://spdx.org for more information.",
"severity": "warning",
"sourceLocation": {
"end": -1,
"file": "scripts/github.sol",
"start": -1
},
"type": "Warning"
},
{
"component": "general",
"errorCode": "3420",
"formattedMessage": "Warning: Source file does not specify required compiler version! Consider adding \"pragma solidity ^0.8.15;\"\n--> scripts/github.sol\n\n",
"message": "Source file does not specify required compiler version! Consider adding \"pragma solidity ^0.8.15;\"",
"severity": "warning",
"sourceLocation": {
"end": -1,
"file": "scripts/github.sol",
"start": -1
},
"type": "Warning"
}
],
"sources": {
"scripts/github.sol": {
"ast": {
"absolutePath": "scripts/github.sol",
"exportedSymbols": {},
"id": 1,
"nodeType": "SourceUnit",
"nodes": [],
"src": "0:0:0"
},
"id": 0
}
}
}
}
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.15;
import {Script} from "forge-std/Script.sol";
import {IVotingToken} from "../src/interfaces/IVotingToken.sol";
import {IVotingToken} from "../src/FranchiserFactory.sol";
import {FranchiserFactory} from "../src/FranchiserFactory.sol";
import {FranchiserLens} from "../src/FranchiserLens.sol";
contract Deploy is Script {
IVotingToken private constant UNI =
IVotingToken(0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984);
function run() public {
vm.startBroadcast();
FranchiserFactory franchiserFactory = new FranchiserFactory(UNI);
new FranchiserLens(UNI, franchiserFactory);
vm.stopBroadcast();
}
}
// This script can be used to deploy the "Storage" contract using ethers.js library.
// Please make sure to compile "./contracts/1_Storage.sol" file before running this script.
// And use Right click -> "Run" from context menu of the file to run the script. Shortcut: Ctrl+Shift+S
import { deploy } from './ethers-lib'
(async () => {
try {
const result = await deploy('Storage', [])
console.log(`address: ${result.address}`)
} catch (e) {
console.log(e.message)
}
})()
// This script can be used to deploy the "Storage" contract using Web3 library.
// Please make sure to compile "./contracts/1_Storage.sol" file before running this script.
// And use Right click -> "Run" from context menu of the file to run the script. Shortcut: Ctrl+Shift+S
import { deploy } from './web3-lib'
(async () => {
try {
const result = await deploy('Storage', [])
console.log(`address: ${result.address}`)
} catch (e) {
console.log(e.message)
}
})()
import { ethers } from 'ethers'
/**
* Deploy the given contract
* @param {string} contractName name of the contract to deploy
* @param {Array<any>} args list of constructor' parameters
* @param {Number} accountIndex account index from the exposed account
* @return {Contract} deployed contract
*/
export const deploy = async (contractName: string, args: Array<any>, accountIndex?: number): Promise<ethers.Contract> => {
console.log(`deploying ${contractName}`)
// Note that the script needs the ABI which is generated from the compilation artifact.
// Make sure contract is compiled and artifacts are generated
const artifactsPath = `browser/contracts/artifacts/${contractName}.json` // Change this for different path
const metadata = JSON.parse(await remix.call('fileManager', 'getFile', artifactsPath))
// 'web3Provider' is a remix global variable object
const signer = (new ethers.providers.Web3Provider(web3Provider)).getSigner(accountIndex)
const factory = new ethers.ContractFactory(metadata.abi, metadata.data.bytecode.object, signer)
const contract = await factory.deploy(...args)
// The contract is NOT deployed yet; we must wait until it is mined
await contract.deployed()
return contract
}
import Web3 from 'web3'
import { Contract, ContractSendMethod, Options } from 'web3-eth-contract'
/**
* Deploy the given contract
* @param {string} contractName name of the contract to deploy
* @param {Array<any>} args list of constructor' parameters
* @param {string} from account used to send the transaction
* @param {number} gas gas limit
* @return {Options} deployed contract
*/
export const deploy = async (contractName: string, args: Array<any>, from?: string, gas?: number): Promise<Options> => {
const web3 = new Web3(web3Provider)
console.log(`deploying ${contractName}`)
// Note that the script needs the ABI which is generated from the compilation artifact.
// Make sure contract is compiled and artifacts are generated
const artifactsPath = `browser/contracts/artifacts/${contractName}.json`
const metadata = JSON.parse(await remix.call('fileManager', 'getFile', artifactsPath))
const accounts = await web3.eth.getAccounts()
const contract: Contract = new web3.eth.Contract(metadata.abi)
const contractSend: ContractSendMethod = contract.deploy({
data: metadata.data.bytecode.object,
arguments: args
})
const newContractInstance = await contractSend.send({
from: from || accounts[0],
gas: gas || 1500000
})
return newContractInstance.options
}
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8;
import {IFranchiserImmutableState} from "../interfaces/IFranchiserImmutableState.sol";
import {IVotingToken} from "../interfaces/IVotingToken.sol";
abstract contract FranchiserImmutableState is IFranchiserImmutableState {
/// @inheritdoc IFranchiserImmutableState
IVotingToken public immutable votingToken;
constructor(IVotingToken votingToken_) {
votingToken = votingToken_;
}
}
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.15;
import {IFranchiser} from "./interfaces/Franchiser/IFranchiser.sol";
import {FranchiserImmutableState} from "./base/FranchiserImmutableState.sol";
import {Owned} from "solmate/auth/Owned.sol";
import {EnumerableSet} from "openzeppelin-contracts/contracts/utils/structs/EnumerableSet.sol";
import {Address} from "openzeppelin-contracts/contracts/utils/Address.sol";
import {Clones} from "openzeppelin-contracts/contracts/proxy/Clones.sol";
import {SafeTransferLib, ERC20} from "solmate/utils/SafeTransferLib.sol";
import {IVotingToken} from "./interfaces/IVotingToken.sol";
contract Franchiser is IFranchiser, FranchiserImmutableState, Owned {
using EnumerableSet for EnumerableSet.AddressSet;
using Address for address;
using Clones for address;
using SafeTransferLib for ERC20;
/// @inheritdoc IFranchiser
uint96 public constant DECAY_FACTOR = 2;
/// @inheritdoc IFranchiser
Franchiser public immutable franchiserImplementation;
address private _delegator;
/// @inheritdoc IFranchiser
address public delegatee;
/// @inheritdoc IFranchiser
uint96 public maximumSubDelegatees;
EnumerableSet.AddressSet private _subDelegatees;
/// @inheritdoc IFranchiser
function delegator() public view returns (address) {
// if a delegator has explicitly been set, return it
if (_delegator != address(0)) return _delegator;
// otherwise, look it up from the owner
else if (owner != address(0)) return Franchiser(owner).delegatee();
// return 0 in the implementation contract
return address(0);
}
/// @inheritdoc IFranchiser
function subDelegatees() external view returns (address[] memory) {
return _subDelegatees.values();
}
/// @dev Reverts if called by any account other than the `delegatee`.
modifier onlyDelegatee() {
if (msg.sender != delegatee) revert NotDelegatee(msg.sender, delegatee);
_;
}
constructor(IVotingToken votingToken_)
FranchiserImmutableState(votingToken_)
Owned(address(0))
{
franchiserImplementation = Franchiser(address(this));
// this borks the implementation contract as desired,
// new instances should be cloned.
delegatee = address(1);
}
/// @inheritdoc IFranchiser
function initialize(
address delegator_,
address delegatee_,
uint96 maximumSubDelegatees_
) public {
// the following two conditions, along with the fact
// that delegatee is only set below (outside of the constructor),
// ensures that initialize can only be called once in clones
if (delegatee_ == address(0)) revert NoDelegatee();
if (delegatee != address(0)) revert AlreadyInitialized();
owner = msg.sender;
// only store the delegator if necessary
if (delegator_ != address(0)) _delegator = delegator_;
delegatee = delegatee_;
maximumSubDelegatees = maximumSubDelegatees_;
votingToken.delegate(delegatee_);
emit Initialized(
msg.sender,
// ensure that we return the delegator consistently
delegator(),
delegatee_,
maximumSubDelegatees_
);
}
/// @inheritdoc IFranchiser
function initialize(address delegatee_, uint96 maximumSubDelegatees_)
external
{
initialize(address(0), delegatee_, maximumSubDelegatees_);
}
function getSalt(address subDelegatee) private pure returns (bytes32) {
return bytes20(subDelegatee);
}
/// @inheritdoc IFranchiser
function getFranchiser(address subDelegatee)
public
view
returns (Franchiser)
{
return
Franchiser(
address(franchiserImplementation).predictDeterministicAddress(
getSalt(subDelegatee),
address(this)
)
);
}
/// @inheritdoc IFranchiser
function subDelegate(address subDelegatee, uint256 amount)
public
onlyDelegatee
returns (Franchiser franchiser)
{
franchiser = getFranchiser(subDelegatee);
if (!_subDelegatees.contains(subDelegatee)) {
if (_subDelegatees.length() == maximumSubDelegatees)
revert CannotExceedMaximumSubDelegatees(maximumSubDelegatees);
assert(_subDelegatees.add(subDelegatee));
if (!address(franchiser).isContract()) {
// deploy a new contract if necessary
address(franchiserImplementation).cloneDeterministic(
getSalt(subDelegatee)
);
franchiser.initialize(
subDelegatee,
maximumSubDelegatees / DECAY_FACTOR
);
}
emit SubDelegateeActivated(subDelegatee);
}
ERC20(address(votingToken)).safeTransfer(address(franchiser), amount);
}
/// @inheritdoc IFranchiser
function subDelegateMany(
address[] calldata subDelegatees_,
uint256[] calldata amounts
) external returns (Franchiser[] memory franchisers) {
if (subDelegatees_.length != amounts.length)
revert ArrayLengthMismatch(subDelegatees_.length, amounts.length);
franchisers = new Franchiser[](subDelegatees_.length);
unchecked {
for (uint256 i = 0; i < subDelegatees_.length; i++)
franchisers[i] = subDelegate(subDelegatees_[i], amounts[i]);
}
}
/// @inheritdoc IFranchiser
function unSubDelegate(address subDelegatee) external onlyDelegatee {
_unSubDelegate(subDelegatee, false);
}
/// @dev Must only set assumeExistence to true when the subDelegatee exists
/// and is already a subDelegatee. This saves gas in recall.
function _unSubDelegate(address subDelegatee, bool assumeExistence)
private
{
Franchiser franchiser = getFranchiser(subDelegatee);
if (assumeExistence || _subDelegatees.contains(subDelegatee)) {
assert(_subDelegatees.remove(subDelegatee));
franchiser.recall(address(this));
emit SubDelegateeDeactivated(subDelegatee);
}
// this condition can only be reached if unSubDelegate is called with a subDelegatee
// that has a franchiser contract but isn't currently active - when this is the case,
// calling recall is a no-op if the franchiser doesn't have tokens, so it's fine,
// but in the very odd case that the franchiser has received voting tokens out of
// band, this will retrieve them silently, which is also fine
else if (address(franchiser).isContract())
franchiser.recall(address(this));
}
/// @inheritdoc IFranchiser
function unSubDelegateMany(address[] calldata subDelegatees_)
external
onlyDelegatee
{
unchecked {
for (uint256 i = 0; i < subDelegatees_.length; i++)
_unSubDelegate(subDelegatees_[i], false);
}
}
/// @inheritdoc IFranchiser
function recall(address to) external onlyOwner {
uint256 numberOfSubDelegatees = _subDelegatees.length();
while (numberOfSubDelegatees != 0) {
unchecked {
_unSubDelegate(
// ordering isn't consistent across removals, but this works
_subDelegatees.at(--numberOfSubDelegatees),
true
);
}
}
ERC20(address(votingToken)).safeTransfer(
to,
votingToken.balanceOf(address(this))
);
}
}
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.15;
import {IFranchiserFactory} from "./interfaces/FranchiserFactory/IFranchiserFactory.sol";
import {FranchiserImmutableState} from "./base/FranchiserImmutableState.sol";
import {Address} from "openzeppelin-contracts/contracts/utils/Address.sol";
import {Clones} from "openzeppelin-contracts/contracts/proxy/Clones.sol";
import {SafeTransferLib, ERC20} from "solmate/utils/SafeTransferLib.sol";
import {IVotingToken} from "./interfaces/IVotingToken.sol";
import {Franchiser} from "./Franchiser.sol";
contract FranchiserFactory is IFranchiserFactory, FranchiserImmutableState {
using Address for address;
using Clones for address;
using SafeTransferLib for ERC20;
/// @inheritdoc IFranchiserFactory
uint96 public constant INITIAL_MAXIMUM_SUBDELEGATEES = 2**3; // 8
/// @inheritdoc IFranchiserFactory
Franchiser public immutable franchiserImplementation;
constructor(IVotingToken votingToken_)
FranchiserImmutableState(votingToken_)
{
franchiserImplementation = new Franchiser(votingToken_);
}
function getSalt(address owner, address delegatee)
private
pure
returns (bytes32)
{
return keccak256(abi.encodePacked(owner, delegatee));
}
/// @inheritdoc IFranchiserFactory
function getFranchiser(address owner, address delegatee)
public
view
returns (Franchiser)
{
return
Franchiser(
address(franchiserImplementation).predictDeterministicAddress(
getSalt(owner, delegatee),
address(this)
)
);
}
/// @inheritdoc IFranchiserFactory
function fund(address delegatee, uint256 amount)
public
returns (Franchiser franchiser)
{
franchiser = getFranchiser(msg.sender, delegatee);
if (!address(franchiser).isContract()) {
// deploy a new contract if necessary
address(franchiserImplementation).cloneDeterministic(
getSalt(msg.sender, delegatee)
);
franchiser.initialize(
msg.sender,
delegatee,
INITIAL_MAXIMUM_SUBDELEGATEES
);
}
ERC20(address(votingToken)).safeTransferFrom(
msg.sender,
address(franchiser),
amount
);
}
/// @inheritdoc IFranchiserFactory
function fundMany(address[] calldata delegatees, uint256[] calldata amounts)
external
returns (Franchiser[] memory franchisers)
{
if (delegatees.length != amounts.length)
revert ArrayLengthMismatch(delegatees.length, amounts.length);
franchisers = new Franchiser[](delegatees.length);
unchecked {
for (uint256 i = 0; i < delegatees.length; i++)
franchisers[i] = fund(delegatees[i], amounts[i]);
}
}
/// @inheritdoc IFranchiserFactory
function recall(address delegatee, address to) public {
Franchiser franchiser = getFranchiser(msg.sender, delegatee);
if (address(franchiser).isContract()) franchiser.recall(to);
}
/// @inheritdoc IFranchiserFactory
function recallMany(address[] calldata delegatees, address[] calldata tos)
external
{
if (delegatees.length != tos.length)
revert ArrayLengthMismatch(delegatees.length, tos.length);
unchecked {
for (uint256 i = 0; i < delegatees.length; i++)
recall(delegatees[i], tos[i]);
}
}
function permit(
uint256 amount,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) private {
// this check ensures that if the permit is front-run,
// the call does not fail
if (votingToken.allowance(msg.sender, address(this)) < amount)
votingToken.permit(
msg.sender,
address(this),
amount,
deadline,
v,
r,
s
);
}
/// @inheritdoc IFranchiserFactory
function permitAndFund(
address delegatee,
uint256 amount,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external returns (Franchiser) {
permit(amount, deadline, v, r, s);
return fund(delegatee, amount);
}
/// @inheritdoc IFranchiserFactory
function permitAndFundMany(
address[] calldata delegatees,
uint256[] calldata amounts,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external returns (Franchiser[] memory franchisers) {
if (delegatees.length != amounts.length)
revert ArrayLengthMismatch(delegatees.length, amounts.length);
uint256 amount = 0;
for (uint256 i = 0; i < delegatees.length; i++) amount += amounts[i];
permit(amount, deadline, v, r, s);
franchisers = new Franchiser[](delegatees.length);
unchecked {
for (uint256 i = 0; i < delegatees.length; i++)
franchisers[i] = fund(delegatees[i], amounts[i]);
}
}
}
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.15;
import {IFranchiserLens} from "./interfaces/IFranchiserLens.sol";
import {FranchiserImmutableState} from "./base/FranchiserImmutableState.sol";
import {FranchiserFactory} from "./FranchiserFactory.sol";
import {IVotingToken} from "./interfaces/IVotingToken.sol";
import {Franchiser} from "./Franchiser.sol";
contract FranchiserLens is IFranchiserLens, FranchiserImmutableState {
/// @dev The asserts in the constructor ensure that this is safe to encode as a constant.
uint256 private constant MAXIMUM_NESTING_DEPTH = 5; // log2(8) + 2
/// @inheritdoc IFranchiserLens
FranchiserFactory public immutable franchiserFactory;
constructor(IVotingToken votingToken_, FranchiserFactory franchiserFactory_)
FranchiserImmutableState(votingToken_)
{
franchiserFactory = franchiserFactory_;
assert(franchiserFactory.INITIAL_MAXIMUM_SUBDELEGATEES() == 8);
assert(
franchiserFactory.franchiserImplementation().DECAY_FACTOR() == 2
);
}
/// @inheritdoc IFranchiserLens
function getRootDelegation(Franchiser franchiser)
public
view
returns (Delegation memory delegation)
{
while (franchiser.owner() != address(franchiserFactory))
franchiser = Franchiser(franchiser.owner());
return
Delegation({
delegator: franchiser.delegator(),
delegatee: franchiser.delegatee(),
franchiser: franchiser
});
}
/// @inheritdoc IFranchiserLens
function getVerticalDelegations(Franchiser franchiser)
external
view
returns (Delegation[] memory delegations)
{
uint256 delegatorsSeen = 0;
Delegation[MAXIMUM_NESTING_DEPTH] memory delegatorsTemporary;
unchecked {
while (address(franchiser) != address(franchiserFactory)) {
delegatorsTemporary[delegatorsSeen++] = Delegation({
delegator: franchiser.delegator(),
delegatee: franchiser.delegatee(),
franchiser: franchiser
});
franchiser = Franchiser(franchiser.owner());
}
delegations = new Delegation[](delegatorsSeen);
for (uint256 i = 0; i < delegatorsSeen; i++)
delegations[delegatorsSeen - i - 1] = delegatorsTemporary[i];
}
}
/// @inheritdoc IFranchiserLens
function getHorizontalDelegations(Franchiser franchiser)
public
view
returns (Delegation[] memory delegations)
{
address[] memory subDelegatees = franchiser.subDelegatees();
delegations = new Delegation[](subDelegatees.length);
unchecked {
for (uint256 i = 0; i < subDelegatees.length; i++) {
Franchiser subDelegateeFranchiser = franchiser.getFranchiser(
subDelegatees[i]
);
delegations[i] = Delegation({
delegator: subDelegateeFranchiser.delegator(),
delegatee: subDelegateeFranchiser.delegatee(),
franchiser: subDelegateeFranchiser
});
}
}
}
function getVotes(Delegation memory delegation)
private
view
returns (DelegationWithVotes memory)
{
return
DelegationWithVotes({
delegator: delegation.delegator,
delegatee: delegation.delegatee,
franchiser: delegation.franchiser,
votes: votingToken.balanceOf(address(delegation.franchiser))
});
}
/// @inheritdoc IFranchiserLens
function getAllDelegations(Franchiser franchiser)
public
view
returns (DelegationWithVotes[][] memory delegationsWithVotes)
{
DelegationWithVotes[][MAXIMUM_NESTING_DEPTH]
memory delegationsWithVotesTemporary;
Delegation memory rootDelegation = getRootDelegation(franchiser);
delegationsWithVotesTemporary[0] = new DelegationWithVotes[](1);
delegationsWithVotesTemporary[0][0] = getVotes(rootDelegation);
unchecked {
for (uint256 i = 1; i < MAXIMUM_NESTING_DEPTH; i++) {
Delegation[][] memory descendantsNested = new Delegation[][](
delegationsWithVotesTemporary[i - 1].length
);
uint256 totalDescendants;
for (
uint256 j = 0;
j < delegationsWithVotesTemporary[i - 1].length;
j++
) {
descendantsNested[j] = getHorizontalDelegations(
delegationsWithVotesTemporary[i - 1][j].franchiser
);
totalDescendants += descendantsNested[j].length;
}
DelegationWithVotes[]
memory descendantsWithVotes = new DelegationWithVotes[](
totalDescendants
);
uint256 descendantsIndex;
for (uint256 j = 0; j < descendantsNested.length; j++)
for (uint256 k = 0; k < descendantsNested[j].length; k++)
descendantsWithVotes[descendantsIndex++] = getVotes(
descendantsNested[j][k]
);
delegationsWithVotesTemporary[i] = descendantsWithVotes;
}
uint256 delegationsWithVotesIndex = 0;
while (
delegationsWithVotesIndex < MAXIMUM_NESTING_DEPTH &&
delegationsWithVotesTemporary[delegationsWithVotesIndex]
.length !=
0
) delegationsWithVotesIndex++;
delegationsWithVotes = new DelegationWithVotes[][](
delegationsWithVotesIndex
);
for (uint256 i = 0; i < delegationsWithVotes.length; i++)
delegationsWithVotes[i] = delegationsWithVotesTemporary[i];
}
}
/// @inheritdoc IFranchiserLens
function getAllDelegations(address owner, address delegatee)
external
view
returns (DelegationWithVotes[][] memory)
{
return
getAllDelegations(
franchiserFactory.getFranchiser(owner, delegatee)
);
}
}
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8;
import {IFranchiserErrors} from "./IFranchiserErrors.sol";
import {IFranchiserEvents} from "./IFranchiserEvents.sol";
import {Franchiser} from "../../Franchiser.sol";
/// @title Interface for the Franchiser contract.
interface IFranchiser is IFranchiserErrors, IFranchiserEvents {
/// @notice The value resonsible for decaying `maximumSubDelegatees`.
/// @dev At each nesting level, `maximumSubDelegatees` is divided by this factor.
/// @return The `DECAY_FACTOR`.
function DECAY_FACTOR() external view returns (uint96);
/// @notice The implementation contract used to clone Franchiser contracts.
/// @dev Used as part of an EIP-1167 proxy minimal proxy setup.
/// @return The Franchiser implementation contract.
function franchiserImplementation() external view returns (Franchiser);
/// @notice The address that delegated tokens to this address.
/// @dev Is derived from the `delegatee` of the `owner`, except for
/// direct descendants of the FranchiserFactory.
/// Never changes after being set via initialize.
/// @return The `delegator`.
function delegator() external view returns (address);
/// @notice The `delegatee` of the contract.
/// @dev Never changes after being set via initialize.
/// Packed with `maximumSubDelegatees`.
/// @return The `delegatee`.
function delegatee() external returns (address);
/// @notice The maximum number of `subDelegatee` addresses that the contract
/// can have at any one time.
/// @dev Never changes after being set via initialize.
/// Packed with `delegatee`.
/// @return The maximum number of `subDelegatee` addresses.
function maximumSubDelegatees() external returns (uint96);
/// @notice The list of current `subDelegatee` addresses.
/// @return The current `subDelegatee` addresses.
function subDelegatees() external returns (address[] memory);
/// @notice Calls initialize with `delegator` set to address(0).
/// @dev Used for all Franchiser initialization beyond the first level of nesting.
/// @param delegatee The `delegatee`.
/// @param maximumSubDelegatees The maximum number of `subDelegatee` addresses.
function initialize(address delegatee, uint96 maximumSubDelegatees)
external;
/// @notice Can be called once to set the contract's `delegator`, `owner`,
/// `delegatee`, and `maximumSubDelegatees`.
/// @dev The `owner` is always the sender of the call.
/// @param delegator The `delegator`.
/// @param delegatee The `delegatee`.
/// @param maximumSubDelegatees The maximum number of `subDelegatee` addresses.
function initialize(
address delegator,
address delegatee,
uint96 maximumSubDelegatees
) external;
/// @notice Looks up the Franchiser associated with the `subDelegatee`.
/// @dev Returns the address of the Franchiser even it it does not yet exist,
/// thanks to CREATE2.
/// @param subDelegatee The target `subDelegatee`.
/// @return franchiser The Franchiser contract, whether or not it exists yet.
function getFranchiser(address subDelegatee)
external
view
returns (Franchiser franchiser);
/// @notice Calls subDelegate many times.
/// @param subDelegatees The addresses that will receive voting power.
/// @param amounts The amounts of voting power.
/// @return franchisers The Franchiser contracts.
function subDelegateMany(
address[] calldata subDelegatees,
uint256[] calldata amounts
) external returns (Franchiser[] memory franchisers);
/// @notice Delegates `amount` of `votingToken` to `subDelegatee`.
/// @dev Can only be called by the `delegatee`. The Franchiser associated
/// with the `subDelegatee` must not already be active.
/// @param subDelegatee The address that will receive voting power.
/// @param amount The amount of voting power.
/// @return franchiser The Franchiser contract.
function subDelegate(address subDelegatee, uint256 amount)
external
returns (Franchiser franchiser);
/// @notice Undelegates to `subDelegatee`.
/// @dev Can only be called by the `delegatee`. No-op if the Franchiser associated
/// with the `subDelegatee` does not exist, or the address is not a `subDelegatee`.
/// @param subDelegatee The address that voting power will be removed from.
function unSubDelegate(address subDelegatee) external;
/// @notice Calls unSubDelegate many times.
/// @param subDelegatees The addresses that voting power will be removed from.
function unSubDelegateMany(address[] calldata subDelegatees) external;
/// @notice Transfers the contract's balance of `votingToken`, as well as the balance
/// of all nested Franchiser contracts associated with each `subDelegatee`, to `to`.
/// @dev Can only be called by the `owner`.
/// @param to The address that will receive tokens.
function recall(address to) external;
}
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8;
/// @title Errors thrown by the Franchiser contract.
interface IFranchiserErrors {
/// @notice Thrown when an address other than the `delegatee` attempts to call a
/// function restricted to the `delegatee`.
/// @param caller The address that attempted the call.
/// @param delegatee The `delegatee`.
error NotDelegatee(address caller, address delegatee);
/// @notice Thrown when attempting to initialize an OwnedDelegator contract with
/// a `delegatee` address of 0.
error NoDelegatee();
/// @notice Thrown when attempting to set the `delegatee` more than once.
error AlreadyInitialized();
/// @notice Thrown when attempting to add too many `subDelegatees`.
/// @param maximumSubDelegatees The maximum (and current) number of `subDelegatees`.
error CannotExceedMaximumSubDelegatees(uint256 maximumSubDelegatees);
/// @notice Emitted when two array arguments have different cardinalities.
/// @param length0 The length of the first array argument.
/// @param length1 The length of the second array argument.
error ArrayLengthMismatch(uint256 length0, uint256 length1);
}
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8;
import {Franchiser} from "../../Franchiser.sol";
/// @title Events for the Franchiser contract.
interface IFranchiserEvents {
/// @notice Emitted once per Franchiser.
/// @param owner The `owner`.
/// @param delegator The `delegator`.
/// @param delegatee The `delegatee`.
/// @param maximumSubDelegatees The `maximumSubDelegatees`.
event Initialized(
address indexed owner,
address indexed delegator,
address indexed delegatee,
uint96 maximumSubDelegatees
);
/// @notice Emitted when a `subDelegatee` is activated.
/// @param subDelegatee The `subDelegatee`.
event SubDelegateeActivated(address indexed subDelegatee);
/// @notice Emitted when a `subDelegatee` is deactivated.
/// @param subDelegatee The `subDelegatee`.
event SubDelegateeDeactivated(address indexed subDelegatee);
}
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8;
import {IFranchiserFactoryErrors} from "./IFranchiserFactoryErrors.sol";
import {IFranchiserImmutableState} from "../IFranchiserImmutableState.sol";
import {Franchiser} from "../../Franchiser.sol";
/// @title Interface for the FranchiserFactory contract.
interface IFranchiserFactory is
IFranchiserFactoryErrors,
IFranchiserImmutableState
{
/// @notice The initial value for the maximum number of `subDelegatee` addresses that a Franchiser
/// contract can have at any one time.
/// @dev Decreases by half every level of nesting.
/// Of type uint96 for storage packing in Franchiser contracts.
/// @return The intial maximum number of `subDelegatee` addresses.
function INITIAL_MAXIMUM_SUBDELEGATEES() external returns (uint96);
/// @notice The implementation contract used to clone Franchiser contracts.
/// @dev Used as part of an EIP-1167 proxy minimal proxy setup.
/// @return The Franchiser implementation contract.
function franchiserImplementation() external view returns (Franchiser);
/// @notice Looks up the Franchiser associated with the `owner` and `delegatee`.
/// @dev Returns the address of the Franchiser even it it does not yet exist,
/// thanks to CREATE2.
/// @param owner The target `owner`.
/// @param delegatee The target `delegatee`.
/// @return franchiser The Franchiser contract, whether or not it exists yet.
function getFranchiser(address owner, address delegatee)
external
view
returns (Franchiser franchiser);
/// @notice Funds the Franchiser contract associated with the `delegatee`
/// from the sender of the call.
/// @dev Requires the sender of the call to have approved this contract for `amount`.
/// If a Franchiser does not yet exist, one is created.
/// @param delegatee The target `delegatee`.
/// @param amount The amount of `votingToken` to allocate.
/// @return franchiser The Franchiser contract.
function fund(address delegatee, uint256 amount)
external
returns (Franchiser franchiser);
/// @notice Calls fund many times.
/// @dev Requires the sender of the call to have approved this contract for sum of `amounts`.
/// @param delegatees The target `delegatees`.
/// @param amounts The amounts of `votingToken` to allocate.
/// @return franchisers The Franchiser contracts.
function fundMany(address[] calldata delegatees, uint256[] calldata amounts)
external
returns (Franchiser[] memory franchisers);
/// @notice Recalls funds in the Franchiser contract associated with the `delegatee`.
/// @dev No-op if a Franchiser does not exist.
/// @param delegatee The target `delegatee`.
/// @param to The `votingToken` recipient.
function recall(address delegatee, address to) external;
/// @notice Calls recall many times.
/// @param delegatees The target `delegatees`.
/// @param tos The `votingToken` recipients.
function recallMany(address[] calldata delegatees, address[] calldata tos)
external;
/// @notice Funds the Franchiser contract associated with the `delegatee`
/// using a signature.
/// @dev The signature must have been produced by the sender of the call.
/// If a Franchiser does not yet exist, one is created.
/// @param delegatee The target `delegatee`.
/// @param amount The amount of `votingToken` to allocate.
/// @param deadline A timestamp which the current timestamp must be less than or equal to.
/// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`.
/// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`.
/// @param s Must produce valid secp256k1 signature from the holder along with `v` and `r`.
/// @return franchiser The Franchiser contract.
function permitAndFund(
address delegatee,
uint256 amount,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external returns (Franchiser franchiser);
/// @notice Calls permitAndFund many times.
/// @dev The permit must be for the sum of `amounts`.
/// @param delegatees The target `delegatees`.
/// @param amounts The amounts of `votingToken` to allocate.
/// @param deadline A timestamp which the current timestamp must be less than or equal to.
/// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`.
/// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`.
/// @param s Must produce valid secp256k1 signature from the holder along with `v` and `r`.
/// @return franchisers The Franchiser contracts.
function permitAndFundMany(
address[] calldata delegatees,
uint256[] calldata amounts,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external returns (Franchiser[] memory franchisers);
}
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8;
/// @title Errors thrown by the FranchiserFactory contract.
interface IFranchiserFactoryErrors {
/// @notice Emitted when two array arguments have different cardinalities.
/// @param length0 The length of the first array argument.
/// @param length1 The length of the second array argument.
error ArrayLengthMismatch(uint256 length0, uint256 length1);
}
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8;
import {IVotingToken} from "./IVotingToken.sol";
/// @title Interface for immutable state shared across Franchiser-related contracts.
interface IFranchiserImmutableState {
/// @notice The `votingToken` of the contract.
/// @return The `votingToken`.
function votingToken() external returns (IVotingToken);
}
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8;
import {IFranchiserImmutableState} from "./IFranchiserImmutableState.sol";
import {FranchiserFactory} from "../FranchiserFactory.sol";
import {Franchiser} from "../Franchiser.sol";
// import {IVotingToken} from "./IVotingToken.sol";
/// @title Interface for the FranchiserLens contract.
interface IFranchiserLens is IFranchiserImmutableState {
/// @param delegator The `delegator`.
/// @param delegatee The `delegatee`.
/// @param franchiser The `franchiser`.
struct Delegation {
address delegator;
address delegatee;
Franchiser franchiser;
}
/// @param delegator The `delegator`.
/// @param delegatee The `delegatee`.
/// @param franchiser The `franchiser`.
/// @param votes The voting power currently held in the `franchiser`.
struct DelegationWithVotes {
address delegator;
address delegatee;
Franchiser franchiser;
uint256 votes;
}
/// @notice The deployed `franchiserFactory`.
/// @return The `franchiserFactory`.
function franchiserFactory() external returns (FranchiserFactory);
/// @notice Gets the root delegation for any nested franchiser.
/// @param franchiser The `franchiser`.
/// @return delegation The root `delegation`.
function getRootDelegation(Franchiser franchiser)
external
view
returns (Delegation memory delegation);
/// @notice Gets all vertical delegations starting from `franchiser`.
/// @param franchiser The `franchiser`.
/// @return delegations The chained `delegations`, starting from `franchiser`
/// and ending at the root.
function getVerticalDelegations(Franchiser franchiser)
external
view
returns (Delegation[] memory delegations);
/// @notice Gets all horizontal delegations of `franchiser`.
/// @param franchiser The `franchiser`.
/// @return delegations The descendant `delegations`.
function getHorizontalDelegations(Franchiser franchiser)
external
view
returns (Delegation[] memory delegations);
/// @notice Gets the entire delegation tree containing the `franchiser`.
/// @param franchiser The `franchiser`.
/// @return delegationsWithVotes The `delegationsWithVotes`.
function getAllDelegations(Franchiser franchiser)
external
view
returns (DelegationWithVotes[][] memory delegationsWithVotes);
/// @notice Calls getAllDelegations with the franchiser associated with `owner` and `delegatee`.
/// @param owner The `owner`.
/// @param delegatee The `delegatee`.
/// @return delegationsWithVotes The `delegationsWithVotes`.
function getAllDelegations(address owner, address delegatee)
external
view
returns (DelegationWithVotes[][] memory delegationsWithVotes);
}
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8;
import {IERC20, IERC20Permit, IVotes} from "openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Votes.sol";
/// @title Interface for voting tokens (see https://docs.openzeppelin.com/contracts/4.x/api/token/erc20#ERC20Votes).
interface IVotingToken is IERC20, IERC20Permit, IVotes {
}
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.15;
import {Test} from "forge-std/Test.sol";
import {IFranchiserErrors} from "../src/interfaces/Franchiser/IFranchiserErrors.sol";
import {IFranchiserEvents} from "../src/interfaces/Franchiser/IFranchiserEvents.sol";
import {VotingTokenConcrete} from "./VotingTokenConcrete.sol";
import {Franchiser} from "../src/Franchiser.sol";
import {IVotingToken} from "../src/interfaces/IVotingToken.sol";
import {Clones} from "openzeppelin-contracts/contracts/proxy/Clones.sol";
import {Utils} from "./Utils.sol";
contract FranchiserTest is Test, IFranchiserErrors, IFranchiserEvents {
using Clones for address;
VotingTokenConcrete private votingToken;
Franchiser private franchiserImplementation;
Franchiser private franchiser;
function setUp() public {
votingToken = new VotingTokenConcrete();
franchiserImplementation = new Franchiser(
IVotingToken(address(votingToken))
);
// we need to set this up as a clone to work
franchiser = Franchiser(address(franchiserImplementation).clone());
}
function testSetUp() public {
assertEq(franchiserImplementation.DECAY_FACTOR(), 2);
assertEq(
address(franchiserImplementation.franchiserImplementation()),
address(franchiserImplementation)
);
assertEq(franchiserImplementation.owner(), address(0));
assertEq(franchiserImplementation.delegator(), address(0));
assertEq(franchiserImplementation.delegatee(), address(1));
assertEq(franchiserImplementation.maximumSubDelegatees(), 0);
assertEq(franchiserImplementation.subDelegatees(), new address[](0));
assertEq(franchiser.DECAY_FACTOR(), 2);
assertEq(
address(franchiser.franchiserImplementation()),
address(franchiserImplementation)
);
assertEq(franchiser.owner(), address(0));
assertEq(franchiser.delegator(), address(0));
assertEq(franchiser.delegatee(), address(0));
assertEq(franchiser.maximumSubDelegatees(), 0);
assertEq(franchiser.subDelegatees(), new address[](0));
}
function testInitializeRevertsNoDelegatee() public {
vm.expectRevert(NoDelegatee.selector);
franchiser.initialize(address(0), 0);
vm.expectRevert(NoDelegatee.selector);
franchiser.initialize(Utils.alice, address(0), 0);
}
function testInitialize() public {
vm.expectEmit(true, true, false, true, address(franchiser));
emit Initialized(address(1), Utils.alice, Utils.bob, 1);
vm.prank(address(1));
franchiser.initialize(Utils.alice, Utils.bob, 1);
assertEq(franchiser.owner(), address(1));
assertEq(franchiser.delegator(), Utils.alice);
assertEq(franchiser.delegatee(), Utils.bob);
assertEq(franchiser.maximumSubDelegatees(), 1);
assertEq(votingToken.delegates(address(franchiser)), Utils.bob);
}
function testInitializeNoDelegator() public {
vm.expectEmit(true, true, false, true, address(franchiser));
emit Initialized(address(1), address(0), Utils.bob, 1);
vm.mockCall(
address(1),
abi.encodeWithSignature("delegatee()"),
abi.encode(address(0))
);
vm.prank(address(1));
franchiser.initialize(Utils.bob, 1);
assertEq(franchiser.owner(), address(1));
assertEq(franchiser.delegator(), address(0));
assertEq(franchiser.delegatee(), Utils.bob);
assertEq(franchiser.maximumSubDelegatees(), 1);
assertEq(votingToken.delegates(address(franchiser)), Utils.bob);
}
function testInitializeRevertsAlreadyInitialized() public {
franchiser.initialize(Utils.alice, Utils.bob, 0);
vm.expectRevert(AlreadyInitialized.selector);
franchiser.initialize(Utils.alice, Utils.bob, 0);
vm.expectRevert(AlreadyInitialized.selector);
franchiser.initialize(Utils.bob, 0);
}
function testInitializeRevertsAlreadyInitializedNoDelegator() public {
vm.mockCall(
address(1),
abi.encodeWithSignature("delegatee()"),
abi.encode(address(0))
);
vm.prank(address(1));
franchiser.initialize(Utils.bob, 0);
vm.expectRevert(AlreadyInitialized.selector);
franchiser.initialize(Utils.bob, 0);
vm.expectRevert(AlreadyInitialized.selector);
franchiser.initialize(Utils.alice, Utils.bob, 0);
}
function testSubDelegateRevertsNotDelegatee() public {
franchiser.initialize(Utils.alice, Utils.bob, 0);
vm.expectRevert(
abi.encodeWithSelector(
NotDelegatee.selector,
Utils.alice,
Utils.bob
)
);
vm.prank(Utils.alice);
franchiser.subDelegate(address(1), 0);
}
function testSubDelegateRevertsCannotExceedMaximumSubDelegatees() public {
franchiser.initialize(Utils.alice, Utils.bob, 0);
vm.expectRevert(
abi.encodeWithSelector(CannotExceedMaximumSubDelegatees.selector, 0)
);
vm.prank(Utils.bob);
franchiser.subDelegate(address(1), 0);
}
function testSubDelegateZero() public {
franchiser.initialize(Utils.alice, Utils.bob, 1);
Franchiser expectedFranchiser = franchiser.getFranchiser(Utils.carol);
vm.expectEmit(true, true, false, true, address(expectedFranchiser));
emit Initialized(address(franchiser), Utils.bob, Utils.carol, 0);
vm.expectEmit(true, false, false, true, address(franchiser));
emit SubDelegateeActivated(Utils.carol);
vm.prank(Utils.bob);
Franchiser returnedFranchiser = franchiser.subDelegate(Utils.carol, 0);
assertEq(address(expectedFranchiser), address(returnedFranchiser));
address[] memory expectedSubDelegatees = new address[](1);
expectedSubDelegatees[0] = Utils.carol;
assertEq(franchiser.subDelegatees(), expectedSubDelegatees);
assertEq(returnedFranchiser.owner(), address(franchiser));
assertEq(returnedFranchiser.delegator(), Utils.bob);
assertEq(returnedFranchiser.delegatee(), Utils.carol);
assertEq(returnedFranchiser.maximumSubDelegatees(), 0);
assertEq(returnedFranchiser.subDelegatees(), new address[](0));
assertEq(
votingToken.delegates(address(returnedFranchiser)),
Utils.carol
);
}
function testSubDelegateZeroNested() public {
franchiser.initialize(Utils.alice, Utils.bob, 2);
vm.prank(Utils.bob);
Franchiser carolFranchiser = franchiser.subDelegate(Utils.carol, 0);
vm.prank(Utils.carol);
Franchiser daveFranchiser = carolFranchiser.subDelegate(Utils.dave, 0);
assertEq(carolFranchiser.maximumSubDelegatees(), 1);
assertEq(daveFranchiser.maximumSubDelegatees(), 0);
}
function testSubDelegateCanCallTwice() public {
franchiser.initialize(Utils.alice, Utils.bob, 2);
vm.startPrank(Utils.bob);
franchiser.subDelegate(Utils.carol, 0);
franchiser.subDelegate(Utils.carol, 0);
vm.stopPrank();
}
function testSubDelegateNonZeroFull() public {
franchiser.initialize(Utils.alice, Utils.bob, 1);
votingToken.mint(address(franchiser), 100);
vm.prank(Utils.bob);
Franchiser returnedFranchiser = franchiser.subDelegate(
Utils.carol,
100
);
assertEq(votingToken.balanceOf(address(returnedFranchiser)), 100);
}
function testSubDelegateNonZeroPartial() public {
franchiser.initialize(Utils.alice, Utils.bob, 1);
votingToken.mint(address(franchiser), 100);
vm.prank(Utils.bob);
Franchiser returnedFranchiser = franchiser.subDelegate(Utils.carol, 50);
assertEq(votingToken.balanceOf(address(franchiser)), 50);
assertEq(votingToken.balanceOf(address(returnedFranchiser)), 50);
}
function testSubDelegateManyRevertsArrayLengthMismatch() public {
franchiser.initialize(Utils.alice, Utils.bob, 2);
address[] memory subDelegatees = new address[](0);
uint256[] memory amounts = new uint256[](1);
vm.expectRevert(
abi.encodeWithSelector(ArrayLengthMismatch.selector, 0, 1)
);
vm.prank(Utils.bob);
franchiser.subDelegateMany(subDelegatees, amounts);
}
function testSubDelegateMany() public {
franchiser.initialize(Utils.alice, Utils.bob, 2);
address[] memory subDelegatees = new address[](2);
subDelegatees[0] = Utils.carol;
subDelegatees[1] = Utils.dave;
uint256[] memory amounts = new uint256[](2);
vm.prank(Utils.bob);
Franchiser[] memory franchisers = franchiser.subDelegateMany(
subDelegatees,
amounts
);
assertEq(franchisers.length, 2);
}
function testUnSubDelegateRevertsNotDelegatee() public {
franchiser.initialize(Utils.alice, Utils.bob, 1);
vm.prank(Utils.bob);
franchiser.subDelegate(Utils.carol, 0);
vm.expectRevert(
abi.encodeWithSelector(
NotDelegatee.selector,
Utils.alice,
Utils.bob
)
);
vm.prank(Utils.alice);
franchiser.unSubDelegate(Utils.carol);
}
function testUnSubDelegateZero() public {
franchiser.initialize(Utils.alice, Utils.bob, 1);
vm.startPrank(Utils.bob);
franchiser.subDelegate(Utils.carol, 0);
vm.expectEmit(true, false, false, true, address(franchiser));
emit SubDelegateeDeactivated(Utils.carol);
franchiser.unSubDelegate(Utils.carol);
vm.stopPrank();
assertEq(franchiser.subDelegatees(), new address[](0));
}
function testUnSubDelegateCanCallTwice() public {
franchiser.initialize(Utils.alice, Utils.bob, 1);
vm.startPrank(Utils.bob);
franchiser.subDelegate(Utils.carol, 0);
franchiser.unSubDelegate(Utils.carol);
franchiser.unSubDelegate(Utils.carol);
vm.stopPrank();
assertEq(franchiser.subDelegatees(), new address[](0));
}
function testUnSubDelegateNonZero() public {
franchiser.initialize(Utils.alice, Utils.bob, 1);
votingToken.mint(address(franchiser), 100);
vm.startPrank(Utils.bob);
franchiser.subDelegate(Utils.carol, 100);
franchiser.unSubDelegate(Utils.carol);
vm.stopPrank();
assertEq(franchiser.subDelegatees(), new address[](0));
assertEq(votingToken.balanceOf(address(franchiser)), 100);
}
function testUnSubDelegateMany() public {
franchiser.initialize(Utils.alice, Utils.bob, 2);
address[] memory subDelegatees = new address[](2);
subDelegatees[0] = Utils.carol;
subDelegatees[1] = Utils.dave;
vm.startPrank(Utils.bob);
franchiser.subDelegate(Utils.carol, 0);
franchiser.subDelegate(Utils.dave, 0);
franchiser.unSubDelegateMany(subDelegatees);
vm.stopPrank();
}
function testUnSubDelegateManyRevertsNotDelegatee() public {
franchiser.initialize(Utils.alice, Utils.bob, 2);
address[] memory subDelegatees = new address[](2);
subDelegatees[0] = Utils.carol;
subDelegatees[1] = Utils.dave;
vm.startPrank(Utils.bob);
franchiser.subDelegate(Utils.carol, 0);
franchiser.subDelegate(Utils.dave, 0);
vm.stopPrank();
vm.expectRevert(
abi.encodeWithSelector(
NotDelegatee.selector,
Utils.alice,
Utils.bob
)
);
vm.prank(Utils.alice);
franchiser.unSubDelegateMany(subDelegatees);
}
function testRecallRevertsUNAUTHORIZED() public {
vm.expectRevert(bytes("UNAUTHORIZED"));
vm.prank(address(1));
franchiser.recall(address(0));
}
function testRecallZeroNoSubDelegatees() public {
vm.startPrank(Utils.alice);
franchiser.initialize(Utils.alice, Utils.bob, 0);
franchiser.recall(Utils.alice);
vm.stopPrank();
}
function testRecallNonZeroNoSubDelegatees() public {
votingToken.mint(address(franchiser), 100);
vm.startPrank(Utils.alice);
franchiser.initialize(Utils.alice, Utils.bob, 0);
franchiser.recall(Utils.alice);
vm.stopPrank();
assertEq(votingToken.balanceOf(address(Utils.alice)), 100);
}
function testRecallNonZeroOneSubDelegatee() public {
votingToken.mint(address(franchiser), 100);
vm.prank(Utils.alice);
franchiser.initialize(Utils.alice, Utils.bob, 1);
vm.prank(Utils.bob);
franchiser.subDelegate(Utils.carol, 50);
vm.prank(Utils.alice);
franchiser.recall(Utils.alice);
assertEq(votingToken.balanceOf(address(Utils.alice)), 100);
}
function testRecallNonZeroTwoSubDelegatees() public {
votingToken.mint(address(franchiser), 100);
vm.prank(Utils.alice);
franchiser.initialize(Utils.alice, Utils.bob, 2);
vm.startPrank(Utils.bob);
franchiser.subDelegate(Utils.carol, 25);
franchiser.subDelegate(Utils.dave, 25);
vm.stopPrank();
vm.prank(Utils.alice);
franchiser.recall(Utils.alice);
assertEq(votingToken.balanceOf(address(Utils.alice)), 100);
}
function testRecallNonZeroNestedSubDelegatees() public {
votingToken.mint(address(franchiser), 100);
vm.prank(Utils.alice);
franchiser.initialize(Utils.alice, Utils.bob, 2);
vm.prank(Utils.bob);
Franchiser carolFranchiser = franchiser.subDelegate(Utils.carol, 25);
vm.prank(Utils.carol);
carolFranchiser.subDelegate(Utils.dave, 25);
vm.prank(Utils.alice);
franchiser.recall(Utils.alice);
assertEq(votingToken.balanceOf(address(Utils.alice)), 100);
}
}
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.15;
import {Test, console2} from "forge-std/Test.sol";
import {IFranchiserFactoryErrors} from "../src/interfaces/FranchiserFactory/IFranchiserFactoryErrors.sol";
import {IFranchiserEvents} from "../src/interfaces/Franchiser/IFranchiserEvents.sol";
import {VotingTokenConcrete} from "./VotingTokenConcrete.sol";
import {IVotingToken} from "../src/interfaces/IVotingToken.sol";
import {FranchiserFactory} from "../src/FranchiserFactory.sol";
import {Franchiser} from "../src/Franchiser.sol";
import {Utils} from "./Utils.sol";
contract FranchiserFactoryTest is
Test,
IFranchiserFactoryErrors,
IFranchiserEvents
{
VotingTokenConcrete private votingToken;
FranchiserFactory private franchiserFactory;
function setUp() public {
votingToken = new VotingTokenConcrete();
franchiserFactory = new FranchiserFactory(
IVotingToken(address(votingToken))
);
}
function testSetUp() public {
assertEq(franchiserFactory.INITIAL_MAXIMUM_SUBDELEGATEES(), 8);
assertEq(
address(franchiserFactory.franchiserImplementation()),
address(
franchiserFactory
.franchiserImplementation()
.franchiserImplementation()
)
);
assertEq(
franchiserFactory.franchiserImplementation().owner(),
address(0)
);
assertEq(
franchiserFactory.franchiserImplementation().delegator(),
address(0)
);
assertEq(
franchiserFactory.franchiserImplementation().delegatee(),
address(1)
);
assertEq(
franchiserFactory.franchiserImplementation().maximumSubDelegatees(),
0
);
}
function testFundZero() public {
Franchiser expectedFranchiser = franchiserFactory.getFranchiser(
Utils.alice,
Utils.bob
);
vm.expectEmit(true, true, true, true, address(expectedFranchiser));
emit Initialized(address(franchiserFactory), Utils.alice, Utils.bob, 8);
vm.prank(Utils.alice);
Franchiser franchiser = franchiserFactory.fund(Utils.bob, 0);
assertEq(address(expectedFranchiser), address(franchiser));
assertEq(franchiser.owner(), address(franchiserFactory));
assertEq(franchiser.delegatee(), Utils.bob);
assertEq(votingToken.delegates(address(franchiser)), Utils.bob);
}
function testFundCanCallTwice() public {
vm.startPrank(Utils.alice);
franchiserFactory.fund(Utils.bob, 0);
franchiserFactory.fund(Utils.bob, 0);
vm.stopPrank();
}
function testFundNonZeroRevertsTRANSFER_FROM_FAILED() public {
vm.expectRevert(bytes("TRANSFER_FROM_FAILED"));
franchiserFactory.fund(Utils.bob, 100);
}
function testFundNonZero() public {
votingToken.mint(Utils.alice, 100);
vm.startPrank(Utils.alice);
votingToken.approve(address(franchiserFactory), 100);
Franchiser franchiser = franchiserFactory.fund(Utils.bob, 100);
vm.stopPrank();
assertEq(votingToken.balanceOf(address(franchiser)), 100);
assertEq(votingToken.getVotes(Utils.bob), 100);
}
function testFundManyRevertsArrayLengthMismatch() public {
vm.expectRevert(
abi.encodeWithSelector(ArrayLengthMismatch.selector, 0, 1)
);
franchiserFactory.fundMany(new address[](0), new uint256[](1));
vm.expectRevert(
abi.encodeWithSelector(ArrayLengthMismatch.selector, 1, 0)
);
franchiserFactory.fundMany(new address[](1), new uint256[](0));
}
function testFundMany() public {
votingToken.mint(Utils.alice, 100);
address[] memory delegatees = new address[](2);
delegatees[0] = Utils.bob;
delegatees[1] = Utils.carol;
uint256[] memory amounts = new uint256[](2);
amounts[0] = 50;
amounts[1] = 50;
vm.startPrank(Utils.alice);
votingToken.approve(address(franchiserFactory), 100);
Franchiser[] memory franchisers = franchiserFactory.fundMany(
delegatees,
amounts
);
vm.stopPrank();
assertEq(votingToken.balanceOf(address(franchisers[0])), 50);
assertEq(votingToken.balanceOf(address(franchisers[1])), 50);
}
function testRecallZero() public {
franchiserFactory.recall(Utils.bob, Utils.alice);
}
function testRecallNonZero() public {
votingToken.mint(Utils.alice, 100);
vm.startPrank(Utils.alice);
votingToken.approve(address(franchiserFactory), 100);
Franchiser franchiser = franchiserFactory.fund(Utils.bob, 100);
franchiserFactory.recall(Utils.bob, Utils.alice);
vm.stopPrank();
assertEq(votingToken.balanceOf(address(franchiser)), 0);
assertEq(votingToken.balanceOf(Utils.alice), 100);
assertEq(votingToken.getVotes(Utils.bob), 0);
}
function testRecallManyRevertsArrayLengthMismatch() public {
vm.expectRevert(
abi.encodeWithSelector(ArrayLengthMismatch.selector, 0, 1)
);
franchiserFactory.recallMany(new address[](0), new address[](1));
vm.expectRevert(
abi.encodeWithSelector(ArrayLengthMismatch.selector, 1, 0)
);
franchiserFactory.recallMany(new address[](1), new address[](0));
}
function testRecallMany() public {
votingToken.mint(Utils.alice, 100);
address[] memory delegatees = new address[](2);
delegatees[0] = Utils.bob;
delegatees[1] = Utils.carol;
address[] memory tos = new address[](2);
tos[0] = Utils.alice;
tos[1] = Utils.alice;
vm.startPrank(Utils.alice);
votingToken.approve(address(franchiserFactory), 100);
franchiserFactory.fund(Utils.bob, 50);
franchiserFactory.fund(Utils.carol, 50);
franchiserFactory.recallMany(delegatees, tos);
vm.stopPrank();
assertEq(votingToken.balanceOf(Utils.alice), 100);
}
function testRecallGasWorstCase() public {
Utils.nestMaximum(vm, votingToken, franchiserFactory);
vm.prank(address(1));
uint256 gasBefore = gasleft();
franchiserFactory.recall(address(2), address(1));
uint256 gasUsed = gasBefore - gasleft();
unchecked {
assertGt(gasUsed, 2 * 1e6);
assertLt(gasUsed, 5 * 1e6);
console2.log(gasUsed);
}
assertEq(votingToken.balanceOf(address(1)), 64);
}
function testPermitAndFund() public {
(
address owner,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) = votingToken.getPermitSignature(
vm,
0xa11ce,
address(franchiserFactory),
100
);
votingToken.mint(owner, 100);
vm.prank(owner);
Franchiser franchiser = franchiserFactory.permitAndFund(
Utils.bob,
100,
deadline,
v,
r,
s
);
assertEq(votingToken.balanceOf(address(franchiser)), 100);
assertEq(votingToken.getVotes(Utils.bob), 100);
}
function testPermitAndFundManyRevertsArrayLengthMismatch() public {
vm.expectRevert(
abi.encodeWithSelector(ArrayLengthMismatch.selector, 0, 1)
);
franchiserFactory.permitAndFundMany(
new address[](0),
new uint256[](1),
0,
0,
0,
0
);
vm.expectRevert(
abi.encodeWithSelector(ArrayLengthMismatch.selector, 1, 0)
);
franchiserFactory.permitAndFundMany(
new address[](1),
new uint256[](0),
0,
0,
0,
0
);
}
// fails because of overflow
function testFailPermitAndFundMany() public {
uint256[] memory amounts = new uint256[](2);
amounts[0] = type(uint256).max;
amounts[1] = 1;
franchiserFactory.permitAndFundMany(
new address[](2),
amounts,
0,
0,
0,
0
);
}
function testPermitAndFundMany() public {
(
address owner,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) = votingToken.getPermitSignature(
vm,
0xa11ce,
address(franchiserFactory),
100
);
votingToken.mint(owner, 100);
address[] memory delegatees = new address[](2);
delegatees[0] = Utils.bob;
delegatees[1] = Utils.carol;
uint256[] memory amounts = new uint256[](2);
amounts[0] = 50;
amounts[1] = 50;
vm.prank(owner);
Franchiser[] memory franchisers = franchiserFactory.permitAndFundMany(
delegatees,
amounts,
deadline,
v,
r,
s
);
assertEq(votingToken.balanceOf(address(franchisers[0])), 50);
assertEq(votingToken.balanceOf(address(franchisers[1])), 50);
}
}
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.15;
import {Test} from "forge-std/Test.sol";
import {FranchiserImmutableState} from "../src/base/FranchiserImmutableState.sol";
import {IVotingToken} from "../src/interfaces/IVotingToken.sol";
contract FranchiserImmutableStateConcrete is FranchiserImmutableState {
constructor(IVotingToken votingToken)
FranchiserImmutableState(votingToken)
{}
}
contract FranchiserImmutableStateTest is Test {
FranchiserImmutableStateConcrete private franchiserImmutableState;
function setUp() public {
franchiserImmutableState = new FranchiserImmutableStateConcrete(
IVotingToken(address(1))
);
}
function testSetUp() public {
assertEq(address(franchiserImmutableState.votingToken()), address(1));
}
}
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.15;
import {Test} from "forge-std/Test.sol";
import {VotingTokenConcrete} from "./VotingTokenConcrete.sol";
import {FranchiserFactory} from "../src/FranchiserFactory.sol";
import {FranchiserLens} from "../src/FranchiserLens.sol";
import {IVotingToken} from "../src/interfaces/IVotingToken.sol";
import {Franchiser} from "../src/Franchiser.sol";
import {IFranchiserLens} from "../src/interfaces/IFranchiserLens.sol";
import {Utils} from "./Utils.sol";
contract FranchiserLensTest is Test {
VotingTokenConcrete private votingToken;
FranchiserFactory private franchiserFactory;
FranchiserLens private franchiserLens;
function setUp() public {
votingToken = new VotingTokenConcrete();
franchiserFactory = new FranchiserFactory(
IVotingToken(address(votingToken))
);
franchiserLens = new FranchiserLens(
IVotingToken(address(votingToken)),
franchiserFactory
);
}
function testSetUp() public {
assertEq(
address(franchiserLens.franchiserFactory()),
address(franchiserFactory)
);
}
function testNesting1() public {
Franchiser[5] memory franchisers = Utils.nestVertical(
1,
vm,
votingToken,
franchiserFactory
);
IFranchiserLens.Delegation memory rootDelegation = franchiserLens
.getRootDelegation(franchisers[0]);
assertEq(rootDelegation.delegator, Utils.alice);
assertEq(rootDelegation.delegatee, Utils.bob);
assertEq(address(rootDelegation.franchiser), address(franchisers[0]));
IFranchiserLens.Delegation[] memory verticalDelegations = franchiserLens
.getVerticalDelegations(franchisers[0]);
assertEq(verticalDelegations.length, 1);
assertEq(
keccak256(abi.encode(verticalDelegations[0])),
keccak256(abi.encode(rootDelegation))
);
IFranchiserLens.Delegation[]
memory horizontalDelegations = franchiserLens
.getHorizontalDelegations(franchisers[0]);
assertEq(horizontalDelegations.length, 0);
}
function testNesting2() public {
Franchiser[5] memory franchisers = Utils.nestVertical(
2,
vm,
votingToken,
franchiserFactory
);
IFranchiserLens.Delegation memory rootDelegation = franchiserLens
.getRootDelegation(franchisers[0]);
assertEq(rootDelegation.delegator, Utils.alice);
assertEq(rootDelegation.delegatee, Utils.bob);
assertEq(address(rootDelegation.franchiser), address(franchisers[0]));
IFranchiserLens.Delegation[] memory verticalDelegations = franchiserLens
.getVerticalDelegations(franchisers[1]);
assertEq(verticalDelegations.length, 2);
assertEq(verticalDelegations[1].delegator, Utils.bob);
assertEq(verticalDelegations[1].delegatee, Utils.carol);
assertEq(
address(verticalDelegations[1].franchiser),
address(franchisers[1])
);
assertEq(
keccak256(abi.encode(verticalDelegations[0])),
keccak256(abi.encode(rootDelegation))
);
IFranchiserLens.Delegation[]
memory horizontalDelegations = franchiserLens
.getHorizontalDelegations(franchisers[0]);
assertEq(horizontalDelegations.length, 1);
assertEq(horizontalDelegations[0].delegator, Utils.bob);
assertEq(horizontalDelegations[0].delegatee, Utils.carol);
assertEq(
address(horizontalDelegations[0].franchiser),
address(franchisers[1])
);
}
function testNesting5() public {
Franchiser[5] memory franchisers = Utils.nestVertical(
5,
vm,
votingToken,
franchiserFactory
);
IFranchiserLens.Delegation memory rootDelegation = franchiserLens
.getRootDelegation(franchisers[0]);
assertEq(rootDelegation.delegator, Utils.alice);
assertEq(rootDelegation.delegatee, Utils.bob);
assertEq(address(rootDelegation.franchiser), address(franchisers[0]));
IFranchiserLens.Delegation[]
memory verticalDelegations = new IFranchiserLens.Delegation[](5);
verticalDelegations[0] = IFranchiserLens.Delegation({
delegator: Utils.alice,
delegatee: Utils.bob,
franchiser: franchisers[0]
});
verticalDelegations[1] = IFranchiserLens.Delegation({
delegator: Utils.bob,
delegatee: Utils.carol,
franchiser: franchisers[1]
});
verticalDelegations[2] = IFranchiserLens.Delegation({
delegator: Utils.carol,
delegatee: Utils.dave,
franchiser: franchisers[2]
});
verticalDelegations[3] = IFranchiserLens.Delegation({
delegator: Utils.dave,
delegatee: Utils.erin,
franchiser: franchisers[3]
});
verticalDelegations[4] = IFranchiserLens.Delegation({
delegator: Utils.erin,
delegatee: Utils.frank,
franchiser: franchisers[4]
});
assertEq(
keccak256(
abi.encode(
franchiserLens.getVerticalDelegations(
franchisers[franchisers.length - 1]
)
)
),
keccak256(abi.encode(verticalDelegations))
);
unchecked {
for (uint256 i; i < 4; i++) {
IFranchiserLens.Delegation[]
memory horizontalDelegations = franchiserLens
.getHorizontalDelegations(franchisers[i]);
assertEq(horizontalDelegations.length, 1);
assertEq(
address(horizontalDelegations[0].franchiser),
address(franchisers[i + 1])
);
}
}
}
function testGetAllDelegations() public {
Franchiser[][5] memory franchisers = Utils.nestMaximum(
vm,
votingToken,
franchiserFactory
);
IFranchiserLens.DelegationWithVotes[][]
memory delegationsWithVotes = franchiserLens.getAllDelegations(
franchisers[0][0]
);
assertEq(delegationsWithVotes.length, 5);
assertEq(delegationsWithVotes[0].length, 1);
assertEq(delegationsWithVotes[1].length, 8);
assertEq(delegationsWithVotes[2].length, 32);
assertEq(delegationsWithVotes[3].length, 64);
assertEq(delegationsWithVotes[4].length, 64);
assertEq(delegationsWithVotes[0][0].delegator, address(1));
assertEq(delegationsWithVotes[0][0].delegatee, address(2));
unchecked {
for (uint256 i; i < delegationsWithVotes.length; i++)
for (uint256 j; j < delegationsWithVotes[i].length; j++) {
assertEq(
address(delegationsWithVotes[i][j].franchiser),
address(franchisers[i][j])
);
assertEq(delegationsWithVotes[i][j].votes, i == 4 ? 1 : 0);
}
}
}
}
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.15;
import {Test} from "forge-std/Test.sol";
import {IVotingToken} from "../src/interfaces/IVotingToken.sol";
import {FranchiserFactory} from "../src/FranchiserFactory.sol";
import {Utils} from "./Utils.sol";
import {ERC20VotesComp} from "openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20VotesComp.sol";
import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
interface ITimelock {
function delay() external view returns (uint256);
}
interface IGovernorBravo {
function quorumVotes() external view returns (uint256);
function votingDelay() external view returns (uint256);
function votingPeriod() external view returns (uint256);
function propose(
address[] memory targets,
uint256[] memory values,
string[] memory signatures,
bytes[] memory calldatas,
string memory description
) external returns (uint256);
function castVote(uint256 proposalId, uint8 support) external;
function queue(uint256 proposalId) external;
function execute(uint256 proposalId) external;
}
contract IntegrationTest is Test {
IVotingToken private constant UNI =
IVotingToken(0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984);
ITimelock private constant TIMELOCK =
ITimelock(0x1a9C8182C09F50C8318d769245beA52c32BE35BC);
IGovernorBravo private constant GOVERNOR_BRAVO =
IGovernorBravo(0x408ED6354d4973f66138C91495F2f2FCbd8724C3);
FranchiserFactory private franchiserFactory;
function setUp() public {
vm.startPrank(address(0));
franchiserFactory = new FranchiserFactory(UNI);
// fund the timelock with 1 ETH to send txs
(bool success, ) = address(TIMELOCK).call{value: 1e18}("");
assert(success);
vm.stopPrank();
}
function testEndToEnd() public {
uint256 quorumVotes = GOVERNOR_BRAVO.quorumVotes();
vm.startPrank(address(TIMELOCK));
UNI.approve(address(franchiserFactory), quorumVotes);
franchiserFactory.fund(Utils.alice, quorumVotes);
vm.stopPrank();
// encode a call to send 1 wei of UNI to alice
address[] memory targets = new address[](1);
uint256[] memory values = new uint256[](1);
string[] memory signatures = new string[](1);
bytes[] memory calldatas = new bytes[](1);
targets[0] = address(UNI);
calldatas[0] = abi.encodeCall(IERC20.transfer, (Utils.alice, 1));
uint256 aliceBalance = UNI.balanceOf(Utils.alice);
// advance the block number so that alice's votes are locked in
vm.roll(block.number + 1);
vm.prank(Utils.alice);
uint256 proposalId = GOVERNOR_BRAVO.propose(
targets,
values,
signatures,
calldatas,
""
);
// advance the block number until voting starts
vm.roll(block.number + GOVERNOR_BRAVO.votingDelay() + 1);
vm.prank(Utils.alice);
GOVERNOR_BRAVO.castVote(proposalId, 1);
// advance the block number until voting ends
vm.roll(block.number + GOVERNOR_BRAVO.votingPeriod());
GOVERNOR_BRAVO.queue(proposalId);
// advance the block timestamp until the proposal can be executed
vm.warp(block.timestamp + TIMELOCK.delay());
GOVERNOR_BRAVO.execute(proposalId);
assertEq(UNI.balanceOf(Utils.alice), aliceBalance + 1);
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.15;
/// @notice lightly modified from https://github.com/kulkarohan/deposit/blob/7904c779e5074f68fc709f5f080e923a36155f01/test/utils/SigUtils.sol
library SigUtils {
bytes32 private constant PERMIT_TYPEHASH =
0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
function getTypedDataHash(
bytes32 DOMAIN_SEPARATOR,
address owner,
address spender,
uint256 value,
uint256 nonce,
uint256 deadline
) internal pure returns (bytes32) {
bytes32 structHash = keccak256(
abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonce, deadline)
);
return
keccak256(
abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, structHash)
);
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.15;
import {Vm} from "forge-std/Vm.sol";
import {VotingTokenConcrete} from "./VotingTokenConcrete.sol";
import {FranchiserFactory} from "../src/FranchiserFactory.sol";
import {Franchiser} from "../src/Franchiser.sol";
library Utils {
address internal constant alice =
0xaAaAaAaaAaAaAaaAaAAAAAAAAaaaAaAaAaaAaaAa;
address internal constant bob = 0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB;
address internal constant carol =
0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC;
address internal constant dave = 0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd;
address internal constant erin = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
address internal constant frank =
0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF;
function nestVertical(
uint256 levels,
Vm vm,
VotingTokenConcrete votingToken,
FranchiserFactory franchiserFactory
) internal returns (Franchiser[5] memory franchisers) {
assert(levels != 0);
assert(levels <= 5);
assert(franchiserFactory.INITIAL_MAXIMUM_SUBDELEGATEES() == 8);
assert(
franchiserFactory.franchiserImplementation().DECAY_FACTOR() == 2
);
address[5] memory delegatees = [bob, carol, dave, erin, frank];
votingToken.mint(alice, 1);
vm.startPrank(alice);
votingToken.approve(address(franchiserFactory), 1);
franchisers[0] = franchiserFactory.fund(delegatees[0], 1);
vm.stopPrank();
unchecked {
for (uint256 i = 1; i < levels; i++) {
vm.prank(delegatees[i - 1]);
franchisers[i] = franchisers[i - 1].subDelegate(
delegatees[i],
1
);
}
}
}
function incrementNextDelegatee(address nextDelegatee)
private
pure
returns (address)
{
return address(uint160(nextDelegatee) + 1);
}
function nestMaximum(
Vm vm,
VotingTokenConcrete votingToken,
FranchiserFactory franchiserFactory
) internal returns (Franchiser[][5] memory franchisers) {
assert(franchiserFactory.INITIAL_MAXIMUM_SUBDELEGATEES() == 8);
assert(
franchiserFactory.franchiserImplementation().DECAY_FACTOR() == 2
);
franchisers[0] = new Franchiser[](1);
franchisers[1] = new Franchiser[](8);
franchisers[2] = new Franchiser[](32);
franchisers[3] = new Franchiser[](64);
franchisers[4] = new Franchiser[](64);
address nextDelegatee = address(2);
votingToken.mint(address(1), 64);
vm.startPrank(address(1));
votingToken.approve(address(franchiserFactory), 64);
franchisers[0][0] = franchiserFactory.fund(nextDelegatee, 64);
vm.stopPrank();
nextDelegatee = incrementNextDelegatee(nextDelegatee);
unchecked {
for (uint256 i; i < 8; i++) {
address delegator = franchisers[0][0].delegatee();
vm.prank(delegator);
franchisers[1][i] = franchisers[0][0].subDelegate(
nextDelegatee,
8
);
nextDelegatee = address(uint160(nextDelegatee) + 1);
}
for (uint256 i; i < 32; i++) {
uint256 j = i / 4;
address delegator = franchisers[1][j].delegatee();
vm.prank(delegator);
franchisers[2][i] = franchisers[1][j].subDelegate(
nextDelegatee,
2
);
nextDelegatee = address(uint160(nextDelegatee) + 1);
}
for (uint256 i; i < 64; i++) {
uint256 j = i / 2;
address delegator = franchisers[2][j].delegatee();
vm.prank(delegator);
franchisers[3][i] = franchisers[2][j].subDelegate(
nextDelegatee,
1
);
nextDelegatee = address(uint160(nextDelegatee) + 1);
}
for (uint256 i; i < 64; i++) {
address delegator = franchisers[3][i].delegatee();
vm.prank(delegator);
franchisers[4][i] = franchisers[3][i].subDelegate(
nextDelegatee,
1
);
nextDelegatee = address(uint160(nextDelegatee) + 1);
}
}
assert(uint160(nextDelegatee) == 171);
}
}
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.15;
import {Vm} from "forge-std/Vm.sol";
import {ERC20, ERC20Permit, ERC20Votes} from "openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Votes.sol";
import {SigUtils} from "./SigUtils.sol";
contract VotingTokenConcrete is ERC20Votes {
constructor() ERC20("Test", "TEST") ERC20Permit("Test") {}
function mint(address to, uint256 amount) external {
_mint(to, amount);
}
function getPermitSignature(
Vm vm,
uint256 ownerPrivateKey,
address spender,
uint256 value
)
external
returns (
address owner,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
)
{
bytes32 digest = SigUtils.getTypedDataHash(
this.DOMAIN_SEPARATOR(),
owner = vm.addr(ownerPrivateKey),
spender,
value,
0,
deadline = type(uint256).max
);
(v, r, s) = vm.sign(ownerPrivateKey, digest);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment