├── .gitattributes
├── .gitignore
├── LICENSE.md
├── README.md
├── erc1155
├── .gitignore
├── README.md
├── contracts
│ ├── Migrations.sol
│ ├── erc1155.vy
│ └── erc1155_Metadata.vy
├── migrations
│ ├── 1_initial_migration.js
│ └── 2_deploy_contracts.js
├── test
│ ├── README.md
│ └── erc1155.js
└── truffle.js
├── erc20
├── .gitignore
├── README.md
├── contracts
│ ├── Migrations.sol
│ └── erc20.vy
├── migrations
│ ├── 1_initial_migration.js
│ └── 2_deploy_contracts.js
├── test
│ ├── README.md
│ ├── erc20-consensys.js
│ └── helpers
│ │ └── assertRevert.js
└── truffle.js
├── erc721
├── .gitignore
├── README.md
├── contracts
│ ├── Migrations.sol
│ └── erc721.vy
├── migrations
│ ├── 1_initial_migration.js
│ └── 2_deploy_contracts.js
├── test
│ └── erc721.js
└── truffle.js
├── erc777
├── .gitignore
├── README.md
├── contracts
│ ├── Migrations.sol
│ ├── erc777.vy
│ ├── erc777TokenReceiver.vy
│ ├── erc777TokenSender.vy
│ └── helpers
│ │ └── ERC1820Registry.sol
├── migrations
│ ├── 1_initial_migration.js
│ ├── 2_deploy_contracts.js
│ └── helpers
│ │ └── checkForERC182Registry.js
├── test
│ ├── README.md
│ └── erc777.js
└── truffle.js
├── linear_optimization_problem_bounty
├── .gitignore
├── README.md
├── contracts
│ ├── Migrations.sol
│ └── linear_optimization_problem_bounty.vy
├── migrations
│ ├── 1_initial_migration.js
│ └── 2_deploy_contracts.js
├── test
│ └── linear_optimization_problem_bounty.js
└── truffle.js
├── vyperStorage
├── .gitignore
├── README.md
├── contracts
│ ├── Migrations.sol
│ └── vyperStorage.vy
├── migrations
│ ├── 1_initial_migration.js
│ └── 2_deploy_contracts.js
├── test
│ └── vyperStorage.js
└── truffle.js
└── wallet
├── .gitignore
├── README.md
├── contracts
├── Migrations.sol
├── helpers
│ ├── README.md
│ ├── erc20.vy
│ ├── erc721.vy
│ ├── erc777.vy
│ └── erc777TokenReceiver.vy
└── wallet.vy
├── migrations
├── 1_initial_migration.js
├── 2_deploy_contracts.js
└── helpers
│ └── checkForERC182Registry.js
├── test
└── wallet.js
└── truffle.js
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.vy linguist-language=Python
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | .DS_Store
3 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | CC0 1.0 Universal
2 |
3 | Statement of Purpose
4 |
5 | The laws of most jurisdictions throughout the world automatically confer
6 | exclusive Copyright and Related Rights (defined below) upon the creator and
7 | subsequent owner(s) (each and all, an "owner") of an original work of
8 | authorship and/or a database (each, a "Work").
9 |
10 | Certain owners wish to permanently relinquish those rights to a Work for the
11 | purpose of contributing to a commons of creative, cultural and scientific
12 | works ("Commons") that the public can reliably and without fear of later
13 | claims of infringement build upon, modify, incorporate in other works, reuse
14 | and redistribute as freely as possible in any form whatsoever and for any
15 | purposes, including without limitation commercial purposes. These owners may
16 | contribute to the Commons to promote the ideal of a free culture and the
17 | further production of creative, cultural and scientific works, or to gain
18 | reputation or greater distribution for their Work in part through the use and
19 | efforts of others.
20 |
21 | For these and/or other purposes and motivations, and without any expectation
22 | of additional consideration or compensation, the person associating CC0 with a
23 | Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
25 | and publicly distribute the Work under its terms, with knowledge of his or her
26 | Copyright and Related Rights in the Work and the meaning and intended legal
27 | effect of CC0 on those rights.
28 |
29 | 1. Copyright and Related Rights. A Work made available under CC0 may be
30 | protected by copyright and related or neighboring rights ("Copyright and
31 | Related Rights"). Copyright and Related Rights include, but are not limited
32 | to, the following:
33 |
34 | i. the right to reproduce, adapt, distribute, perform, display, communicate,
35 | and translate a Work;
36 |
37 | ii. moral rights retained by the original author(s) and/or performer(s);
38 |
39 | iii. publicity and privacy rights pertaining to a person's image or likeness
40 | depicted in a Work;
41 |
42 | iv. rights protecting against unfair competition in regards to a Work,
43 | subject to the limitations in paragraph 4(a), below;
44 |
45 | v. rights protecting the extraction, dissemination, use and reuse of data in
46 | a Work;
47 |
48 | vi. database rights (such as those arising under Directive 96/9/EC of the
49 | European Parliament and of the Council of 11 March 1996 on the legal
50 | protection of databases, and under any national implementation thereof,
51 | including any amended or successor version of such directive); and
52 |
53 | vii. other similar, equivalent or corresponding rights throughout the world
54 | based on applicable law or treaty, and any national implementations thereof.
55 |
56 | 2. Waiver. To the greatest extent permitted by, but not in contravention of,
57 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
58 | unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
59 | and Related Rights and associated claims and causes of action, whether now
60 | known or unknown (including existing as well as future claims and causes of
61 | action), in the Work (i) in all territories worldwide, (ii) for the maximum
62 | duration provided by applicable law or treaty (including future time
63 | extensions), (iii) in any current or future medium and for any number of
64 | copies, and (iv) for any purpose whatsoever, including without limitation
65 | commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
66 | the Waiver for the benefit of each member of the public at large and to the
67 | detriment of Affirmer's heirs and successors, fully intending that such Waiver
68 | shall not be subject to revocation, rescission, cancellation, termination, or
69 | any other legal or equitable action to disrupt the quiet enjoyment of the Work
70 | by the public as contemplated by Affirmer's express Statement of Purpose.
71 |
72 | 3. Public License Fallback. Should any part of the Waiver for any reason be
73 | judged legally invalid or ineffective under applicable law, then the Waiver
74 | shall be preserved to the maximum extent permitted taking into account
75 | Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
76 | is so judged Affirmer hereby grants to each affected person a royalty-free,
77 | non transferable, non sublicensable, non exclusive, irrevocable and
78 | unconditional license to exercise Affirmer's Copyright and Related Rights in
79 | the Work (i) in all territories worldwide, (ii) for the maximum duration
80 | provided by applicable law or treaty (including future time extensions), (iii)
81 | in any current or future medium and for any number of copies, and (iv) for any
82 | purpose whatsoever, including without limitation commercial, advertising or
83 | promotional purposes (the "License"). The License shall be deemed effective as
84 | of the date CC0 was applied by Affirmer to the Work. Should any part of the
85 | License for any reason be judged legally invalid or ineffective under
86 | applicable law, such partial invalidity or ineffectiveness shall not
87 | invalidate the remainder of the License, and in such case Affirmer hereby
88 | affirms that he or she will not (i) exercise any of his or her remaining
89 | Copyright and Related Rights in the Work or (ii) assert any associated claims
90 | and causes of action with respect to the Work, in either case contrary to
91 | Affirmer's express Statement of Purpose.
92 |
93 | 4. Limitations and Disclaimers.
94 |
95 | a. No trademark or patent rights held by Affirmer are waived, abandoned,
96 | surrendered, licensed or otherwise affected by this document.
97 |
98 | b. Affirmer offers the Work as-is and makes no representations or warranties
99 | of any kind concerning the Work, express, implied, statutory or otherwise,
100 | including without limitation warranties of title, merchantability, fitness
101 | for a particular purpose, non infringement, or the absence of latent or
102 | other defects, accuracy, or the present or absence of errors, whether or not
103 | discoverable, all to the greatest extent permissible under applicable law.
104 |
105 | c. Affirmer disclaims responsibility for clearing rights of other persons
106 | that may apply to the Work or any use thereof, including without limitation
107 | any person's Copyright and Related Rights in the Work. Further, Affirmer
108 | disclaims responsibility for obtaining any necessary consents, permissions
109 | or other rights required for any use of the Work.
110 |
111 | d. Affirmer understands and acknowledges that Creative Commons is not a
112 | party to this document and has no duty or obligation with respect to this
113 | CC0 or use of the Work.
114 |
115 | For more information, please see
116 |
117 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Vyper Contracts
2 | All smart contracts listed at [vyperhub.io](https://contracts.vyperhub.io/)
3 |
4 | Each contract in this repository is wrapped inside its own [truffle](https://truffleframework.com/) project.
5 | This makes it easy to quickly compile and test them locally.
6 |
7 | ## Overview
8 |
9 | | Project | | |
10 | | - | - | - |
11 | | [ERC20](erc20/) | [Contract](erc20/contracts/erc20.vy) | [Tests](erc20/test/) |
12 | | [ERC721](erc721/) | [Contract](erc721/contracts/erc721.vy) | [Tests](erc721/test/erc721.js) |
13 | | [ERC777](erc777/) | [Contract](erc777/contracts/erc777.vy) | [Tests](erc777/test) |
14 | | [Linear optimization problem bounty](linear_optimization_problem_bounty/) | [Contract](linear_optimization_problem_bounty/contracts/linear_optimization_problem_bounty.vy) | [Tests](linear_optimization_problem_bounty/test/linear_optimization_problem_bounty.js) |
15 | | [VyperStorage](vyperStorage/) | [Contract](vyperStorage/contracts/vyperStorage.vy) | [Tests](vyperStorage/test/vyperStorage.js) |
16 | | [Wallet](wallet/) | [Contract](wallet/contracts/wallet.vy) | [Tests](wallet/test/wallet.js) |
17 |
18 | **Disclaimer:** All contracts in this repository as well as contracts hosted at [vyperhub.io](https://contracts.vyperhub.io/) are in beta and should not be used in production! We are not responsible for any losses that occure when using these or related contracts.
19 |
--------------------------------------------------------------------------------
/erc1155/.gitignore:
--------------------------------------------------------------------------------
1 | ./build
2 |
--------------------------------------------------------------------------------
/erc1155/README.md:
--------------------------------------------------------------------------------
1 | # ERC1155
2 | https://eips.ethereum.org/EIPS/eip-1155
3 |
4 | ## Run tests
5 | ```bash
6 | $ truffle test --network ganache
7 | ```
8 |
--------------------------------------------------------------------------------
/erc1155/contracts/Migrations.sol:
--------------------------------------------------------------------------------
1 | pragma solidity >=0.4.21 <0.6.0;
2 |
3 | contract Migrations {
4 | address public owner;
5 | uint public last_completed_migration;
6 |
7 | constructor() public {
8 | owner = msg.sender;
9 | }
10 |
11 | modifier restricted() {
12 | if (msg.sender == owner) _;
13 | }
14 |
15 | function setCompleted(uint completed) public restricted {
16 | last_completed_migration = completed;
17 | }
18 |
19 | function upgrade(address new_address) public restricted {
20 | Migrations upgraded = Migrations(new_address);
21 | upgraded.setCompleted(last_completed_migration);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/erc1155/contracts/erc1155.vy:
--------------------------------------------------------------------------------
1 | # Author: Sören Steiger, github.com/ssteiger
2 | # License: MIT
3 |
4 | # ERC1155 Token Standard
5 | # https://eips.ethereum.org/EIPS/eip-1155
6 |
7 |
8 | contract ERC1155TokenReceiver:
9 | def onERC1155Received(
10 | _operator: address,
11 | _from: address,
12 | _id: uint256,
13 | _value: uint256,
14 | _data: bytes[256]
15 | ) -> bytes32: modifying # TODO: should return bytes4
16 | def onERC1155BatchReceived(
17 | _operator: address,
18 | _from: address,
19 | _ids: uint256[BATCH_SIZE],
20 | _values: uint256[BATCH_SIZE],
21 | _data: bytes[256]
22 | ) -> bytes32: modifying # TODO: should return bytes4
23 |
24 |
25 | # @dev Either `TransferSingle` or `TransferBatch` MUST emit when tokens are transferred, including zero value transfers as well as minting or burning (see "Safe Transfer Rules" section of the standard).
26 | # The `_operator` argument MUST be the address of an account/contract that is approved to make the transfer (SHOULD be msg.sender).
27 | # The `_from` argument MUST be the address of the holder whose balance is decreased.
28 | # The `_to` argument MUST be the address of the recipient whose balance is increased.
29 | # The `_id` argument MUST be the token type being transferred.
30 | # The `_value` argument MUST be the number of tokens the holder balance is decreased by and match what the recipient balance is increased by.
31 | # When minting/creating tokens, the `_from` argument MUST be set to `0x0` (i.e. zero address).
32 | # When burning/destroying tokens, the `_to` argument MUST be set to `0x0` (i.e. zero address).
33 | # event TransferSingle(address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value);
34 | TransferSingle: event({
35 | _operator: indexed(address),
36 | _from: indexed(address),
37 | _to: indexed(address),
38 | _id: uint256,
39 | _value: uint256
40 | })
41 |
42 | # @dev Either `TransferSingle` or `TransferBatch` MUST emit when tokens are transferred, including zero value transfers as well as minting or burning (see "Safe Transfer Rules" section of the standard).
43 | # The `_operator` argument MUST be the address of an account/contract that is approved to make the transfer (SHOULD be msg.sender).
44 | # The `_from` argument MUST be the address of the holder whose balance is decreased.
45 | # The `_to` argument MUST be the address of the recipient whose balance is increased.
46 | # The `_ids` argument MUST be the list of tokens being transferred.
47 | # The `_values` argument MUST be the list of number of tokens (matching the list and order of tokens specified in _ids) the holder balance is decreased by and match what the recipient balance is increased by.
48 | # When minting/creating tokens, the `_from` argument MUST be set to `0x0` (i.e. zero address).
49 | # When burning/destroying tokens, the `_to` argument MUST be set to `0x0` (i.e. zero address).
50 | # event TransferBatch(address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values);
51 | TransferBatch: event({
52 | _operator: indexed(address),
53 | _from: indexed(address),
54 | _to: indexed(address),
55 | _ids: uint256[BATCH_SIZE],
56 | _value: uint256[BATCH_SIZE]
57 | })
58 |
59 | # @dev MUST emit when approval for a second party/operator address to manage all tokens for an owner address is enabled or disabled (absence of an event assumes disabled).
60 | # event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
61 | ApprovalForAll: event({
62 | _owner: indexed(address),
63 | _operator: indexed(address),
64 | _approved: bool
65 | })
66 |
67 |
68 |
69 | supportedInterfaces: map(bytes32, bool)
70 |
71 | # https://eips.ethereum.org/EIPS/eip-165
72 | ERC165_INTERFACE_ID: constant(bytes32) = 0x0000000000000000000000000000000000000000000000000000000001ffc9a7
73 | ERC1155_INTERFACE_ID: constant(bytes32) = 0x00000000000000000000000000000000000000000000000000000000d9b67a26
74 |
75 | tokensIdCount: uint256
76 |
77 | _balanceOf: map(address, map(uint256, uint256))
78 |
79 | operators: map(address, map(address, bool))
80 |
81 | # TODO: decide which batch size to use
82 | BATCH_SIZE: constant(uint256) = 5
83 |
84 |
85 |
86 | @public
87 | def __init__():
88 | self.tokensIdCount = 0
89 | self.supportedInterfaces[ERC165_INTERFACE_ID] = True
90 | self.supportedInterfaces[ERC1155_INTERFACE_ID] = True
91 |
92 |
93 | @public
94 | @constant
95 | def supportsInterface(_interfaceID: bytes32) -> bool:
96 | return self.supportedInterfaces[_interfaceID]
97 |
98 |
99 | # @notice Transfers `_value` amount of an `_id` from the `_from` address to the `_to` address specified (with safety call).
100 | # @dev Caller must be approved to manage the tokens being transferred out of the `_from` account (see "Approval" section of the standard).
101 | # MUST revert if `_to` is the zero address.
102 | # MUST revert if balance of holder for token `_id` is lower than the `_value` sent.
103 | # MUST revert on any other error.
104 | # MUST emit the `TransferSingle` event to reflect the balance change (see "Safe Transfer Rules" section of the standard).
105 | # After the above conditions are met, this function MUST check if `_to` is a smart contract (e.g. code size > 0). If so, it MUST call `onERC1155Received` on `_to` and act appropriately (see "Safe Transfer Rules" section of the standard).
106 | # @param _from Source address
107 | # @param _to Target address
108 | # @param _id ID of the token type
109 | # @param _value Transfer amount
110 | # @param _data Additional data with no specified format, MUST be sent unaltered in call to `onERC1155Received` on `_to`
111 | # function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external;
112 | @public
113 | def safeTransferFrom(
114 | _from: address,
115 | _to: address,
116 | _id: uint256,
117 | _value: uint256,
118 | _data: bytes[256]
119 | ):
120 | assert _from == msg.sender or (self.operators[_from])[msg.sender]
121 | assert _to != ZERO_ADDRESS
122 | assert self._balanceOf[_from][_id] >= _value
123 |
124 | if _to.is_contract:
125 | returnValue: bytes32 = ERC1155TokenReceiver(_to).onERC1155Received(msg.sender, _from, _id, _value, _data)
126 | assert returnValue == method_id("onERC1155Received(address,address,uint256,uint256,bytes)", bytes32)
127 |
128 | self._balanceOf[_from][_id] -= _value
129 | self._balanceOf[_to][_id] += _value
130 | log.TransferSingle(msg.sender, _from, _to, _id, _value)
131 |
132 |
133 | # @notice Transfers `_values` amount(s) of `_ids` from the `_from` address to the `_to` address specified (with safety call).
134 | # @dev Caller must be approved to manage the tokens being transferred out of the `_from` account (see "Approval" section of the standard).
135 | # MUST revert if `_to` is the zero address.
136 | # MUST revert if length of `_ids` is not the same as length of `_values`.
137 | # MUST revert if any of the balance(s) of the holder(s) for token(s) in `_ids` is lower than the respective amount(s) in `_values` sent to the recipient.
138 | # MUST revert on any other error.
139 | # MUST emit `TransferSingle` or `TransferBatch` event(s) such that all the balance changes are reflected (see "Safe Transfer Rules" section of the standard).
140 | # Balance changes and events MUST follow the ordering of the arrays (_ids[0]/_values[0] before _ids[1]/_values[1], etc).
141 | # After the above conditions for the transfer(s) in the batch are met, this function MUST check if `_to` is a smart contract (e.g. code size > 0). If so, it MUST call the relevant `ERC1155TokenReceiver` hook(s) on `_to` and act appropriately (see "Safe Transfer Rules" section of the standard).
142 | # @param _from Source address
143 | # @param _to Target address
144 | # @param _ids IDs of each token type (order and length must match _values array)
145 | # @param _values Transfer amounts per token type (order and length must match _ids array)
146 | # @param _data Additional data with no specified format, MUST be sent unaltered in call to the `ERC1155TokenReceiver` hook(s) on `_to`
147 | # function safeBatchTransferFrom(address _from, address _to, uint256[] calldata _ids, uint256[] calldata _values, bytes calldata _data) external;
148 | @public
149 | def safeBatchTransferFrom(
150 | _from: address,
151 | _to: address,
152 | _ids: uint256[BATCH_SIZE],
153 | _values: uint256[BATCH_SIZE],
154 | _data: bytes[256]
155 | ):
156 | assert _from == msg.sender or (self.operators[_from])[msg.sender]
157 | assert _to != ZERO_ADDRESS
158 | #assert len(_ids) == len(_values)
159 |
160 | for i in range(BATCH_SIZE):
161 | assert self._balanceOf[_from][_ids[i]] >= _values[i]
162 | self._balanceOf[_from][_ids[i]] -= _values[i]
163 | self._balanceOf[_to][_ids[i]] += _values[i]
164 | if _to.is_contract:
165 | returnValue: bytes32 = ERC1155TokenReceiver(_to).onERC1155Received(msg.sender, _from, _ids[i], _values[i], _data)
166 | assert returnValue == method_id("onERC1155Received(address,address,uint256,uint256,bytes)", bytes32)
167 |
168 | log.TransferBatch(msg.sender, _from, _to, _ids, _values)
169 |
170 |
171 | # @notice Get the balance of an account's tokens.
172 | # @param _owner The address of the token holder
173 | # @param _id ID of the token
174 | # @return The _owner's balance of the token type requested
175 | # function balanceOf(address _owner, uint256 _id) external view returns (uint256);
176 | @public
177 | @constant
178 | def balanceOf(
179 | _owner: address,
180 | _id: uint256
181 | ) -> uint256:
182 | assert _owner != ZERO_ADDRESS
183 | return self._balanceOf[_owner][_id]
184 |
185 |
186 | # @notice Get the balance of multiple account/token pairs
187 | # @param _owners The addresses of the token holders
188 | # @param _ids ID of the tokens
189 | # @return The _owner's balance of the token types requested (i.e. balance for each (owner, id) pair)
190 | # function balanceOfBatch(address[] calldata _owners, uint256[] calldata _ids) external view returns (uint256[] memory);
191 | @public
192 | @constant
193 | def balanceOfBatch(
194 | _owner: address[BATCH_SIZE],
195 | _ids: uint256[BATCH_SIZE]
196 | ) -> uint256[BATCH_SIZE]:
197 | returnValues: uint256[BATCH_SIZE]
198 | for i in range(BATCH_SIZE):
199 | returnValues[i] = self._balanceOf[_owner[i]][_ids[i]]
200 | return returnValues
201 |
202 |
203 | # @notice Enable or disable approval for a third party ("operator") to manage all of the caller's tokens.
204 | # @dev MUST emit the ApprovalForAll event on success.
205 | # @param _operator Address to add to the set of authorized operators
206 | # @param _approved True if the operator is approved, false to revoke approval
207 | # function setApprovalForAll(address _operator, bool _approved) external;
208 | @public
209 | def setApprovalForAll(
210 | _operator: address,
211 | _approved: bool
212 | ):
213 | (self.operators[msg.sender])[_operator] = _approved
214 | log.ApprovalForAll(msg.sender, _operator, _approved)
215 |
216 |
217 | # @notice Queries the approval status of an operator for a given owner.
218 | # @param _owner The owner of the tokens
219 | # @param _operator Address of authorized operator
220 | # @return True if the operator is approved, false if not
221 | # function isApprovedForAll(address _owner, address _operator) external view returns (bool);
222 | @public
223 | @constant
224 | def isApprovedForAll(
225 | _owner: address,
226 | _operator: address
227 | ) -> bool:
228 | return (self.operators[_owner])[_operator]
229 |
230 |
231 | # NOTE: This is not part of the standard
232 | # TODO: Right now anyone can mint
233 | @public
234 | def mint(
235 | _to: address,
236 | _supply: uint256,
237 | _data: bytes[256]=""
238 | ) -> uint256:
239 | assert _to != ZERO_ADDRESS
240 | self._balanceOf[msg.sender][self.tokensIdCount] = _supply
241 | self.tokensIdCount += 1
242 | log.TransferSingle(msg.sender, ZERO_ADDRESS, _to, self.tokensIdCount, _supply)
243 | return self.tokensIdCount
244 |
245 |
246 | # NOTE: This is not part of the standard
247 | # TODO: Right now anyone can mint
248 | @public
249 | def mintBatch(
250 | _to: address,
251 | _supplys: uint256[BATCH_SIZE],
252 | _data: bytes[256]=""
253 | ) -> uint256[BATCH_SIZE]:
254 | assert _to != ZERO_ADDRESS
255 | ids: uint256[BATCH_SIZE]
256 | for i in range(BATCH_SIZE):
257 | self._balanceOf[msg.sender][self.tokensIdCount] = _supplys[i]
258 | self.tokensIdCount += 1
259 | id: uint256 = self.tokensIdCount
260 | ids[i] = id
261 |
262 | log.TransferBatch(msg.sender, ZERO_ADDRESS, _to, ids, _supplys)
263 | return ids
264 |
265 |
266 | # TODO: specify a burn()/burnBatch() function
267 |
--------------------------------------------------------------------------------
/erc1155/contracts/erc1155_Metadata.vy:
--------------------------------------------------------------------------------
1 | # Author: Sören Steiger, github.com/ssteiger
2 | # License: MIT
3 |
4 | # ERC1155 Token Standard
5 | # https://eips.ethereum.org/EIPS/eip-1155
6 |
7 |
8 | contract ERC1155TokenReceiver:
9 | def onERC1155Received(
10 | _operator: address,
11 | _from: address,
12 | _id: uint256,
13 | _value: uint256,
14 | _data: bytes[256]
15 | ) -> bytes32: modifying # TODO: should return bytes4
16 | def onERC1155BatchReceived(
17 | _operator: address,
18 | _from: address,
19 | _ids: uint256[BATCH_SIZE],
20 | _values: uint256[BATCH_SIZE],
21 | _data: bytes[256]
22 | ) -> bytes32: modifying # TODO: should return bytes4
23 |
24 |
25 | # @dev Either `TransferSingle` or `TransferBatch` MUST emit when tokens are transferred, including zero value transfers as well as minting or burning (see "Safe Transfer Rules" section of the standard).
26 | # The `_operator` argument MUST be the address of an account/contract that is approved to make the transfer (SHOULD be msg.sender).
27 | # The `_from` argument MUST be the address of the holder whose balance is decreased.
28 | # The `_to` argument MUST be the address of the recipient whose balance is increased.
29 | # The `_id` argument MUST be the token type being transferred.
30 | # The `_value` argument MUST be the number of tokens the holder balance is decreased by and match what the recipient balance is increased by.
31 | # When minting/creating tokens, the `_from` argument MUST be set to `0x0` (i.e. zero address).
32 | # When burning/destroying tokens, the `_to` argument MUST be set to `0x0` (i.e. zero address).
33 | # event TransferSingle(address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value);
34 | TransferSingle: event({
35 | _operator: indexed(address),
36 | _from: indexed(address),
37 | _to: indexed(address),
38 | _id: uint256,
39 | _value: uint256
40 | })
41 |
42 | # @dev Either `TransferSingle` or `TransferBatch` MUST emit when tokens are transferred, including zero value transfers as well as minting or burning (see "Safe Transfer Rules" section of the standard).
43 | # The `_operator` argument MUST be the address of an account/contract that is approved to make the transfer (SHOULD be msg.sender).
44 | # The `_from` argument MUST be the address of the holder whose balance is decreased.
45 | # The `_to` argument MUST be the address of the recipient whose balance is increased.
46 | # The `_ids` argument MUST be the list of tokens being transferred.
47 | # The `_values` argument MUST be the list of number of tokens (matching the list and order of tokens specified in _ids) the holder balance is decreased by and match what the recipient balance is increased by.
48 | # When minting/creating tokens, the `_from` argument MUST be set to `0x0` (i.e. zero address).
49 | # When burning/destroying tokens, the `_to` argument MUST be set to `0x0` (i.e. zero address).
50 | # event TransferBatch(address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values);
51 | TransferBatch: event({
52 | _operator: indexed(address),
53 | _from: indexed(address),
54 | _to: indexed(address),
55 | _ids: uint256[BATCH_SIZE],
56 | _value: uint256[BATCH_SIZE]
57 | })
58 |
59 | # @dev MUST emit when approval for a second party/operator address to manage all tokens for an owner address is enabled or disabled (absence of an event assumes disabled).
60 | # event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
61 | ApprovalForAll: event({
62 | _owner: indexed(address),
63 | _operator: indexed(address),
64 | _approved: bool
65 | })
66 |
67 | # @dev MUST emit when the URI is updated for a token ID.
68 | # URIs are defined in RFC 3986.
69 | # The URI MUST point to a JSON file that conforms to the "ERC-1155 Metadata URI JSON Schema".
70 | # event URI(string _value, uint256 indexed _id);
71 | URI: event({
72 | # https://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers
73 | _value: string[MAX_URI_SIZE],
74 | _id: indexed(uint256)
75 | })
76 |
77 |
78 | # TODO: decide which batch size to use
79 | BATCH_SIZE: constant(uint256) = 5
80 | MAX_URI_SIZE: constant(uint256) = 1024
81 |
82 | # https://eips.ethereum.org/EIPS/eip-165
83 | INTERFACE_ID_ERC165: constant(bytes32) = 0x0000000000000000000000000000000000000000000000000000000001ffc9a7
84 | INTERFACE_ID_ERC1155: constant(bytes32) = 0x00000000000000000000000000000000000000000000000000000000d9b67a26
85 | INTERFACE_ID_ERC1155_METADATA: constant(bytes32) = 0x000000000000000000000000000000000000000000000000000000000e89341c
86 |
87 | supportedInterfaces: map(bytes32, bool)
88 |
89 | tokensIdCount: uint256
90 |
91 | _balanceOf: map(address, map(uint256, uint256))
92 |
93 | _uri: map(uint256, string[MAX_URI_SIZE])
94 |
95 | operators: map(address, map(address, bool))
96 |
97 |
98 |
99 | @public
100 | def __init__():
101 | self.tokensIdCount = 0
102 | self.supportedInterfaces[INTERFACE_ID_ERC165] = True
103 | self.supportedInterfaces[INTERFACE_ID_ERC1155] = True
104 | self.supportedInterfaces[INTERFACE_ID_ERC1155_METADATA] = True
105 |
106 |
107 | @public
108 | @constant
109 | def supportsInterface(_interfaceID: bytes32) -> bool:
110 | return self.supportedInterfaces[_interfaceID]
111 |
112 |
113 | # @notice Transfers `_value` amount of an `_id` from the `_from` address to the `_to` address specified (with safety call).
114 | # @dev Caller must be approved to manage the tokens being transferred out of the `_from` account (see "Approval" section of the standard).
115 | # MUST revert if `_to` is the zero address.
116 | # MUST revert if balance of holder for token `_id` is lower than the `_value` sent.
117 | # MUST revert on any other error.
118 | # MUST emit the `TransferSingle` event to reflect the balance change (see "Safe Transfer Rules" section of the standard).
119 | # After the above conditions are met, this function MUST check if `_to` is a smart contract (e.g. code size > 0). If so, it MUST call `onERC1155Received` on `_to` and act appropriately (see "Safe Transfer Rules" section of the standard).
120 | # @param _from Source address
121 | # @param _to Target address
122 | # @param _id ID of the token type
123 | # @param _value Transfer amount
124 | # @param _data Additional data with no specified format, MUST be sent unaltered in call to `onERC1155Received` on `_to`
125 | # function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external;
126 | @public
127 | def safeTransferFrom(
128 | _from: address,
129 | _to: address,
130 | _id: uint256,
131 | _value: uint256,
132 | _data: bytes[256]
133 | ):
134 | assert _to != ZERO_ADDRESS
135 | assert self._balanceOf[_from][_id] >= _value
136 |
137 | if _to.is_contract:
138 | returnValue: bytes32 = ERC1155TokenReceiver(_to).onERC1155Received(msg.sender, _from, _id, _value, _data)
139 | assert returnValue == method_id("onERC1155Received(address,address,uint256,uint256,bytes)", bytes32)
140 |
141 | self._balanceOf[_from][_id] -= _value
142 | self._balanceOf[_to][_id] += _value
143 | log.TransferSingle(msg.sender, _from, _to, _id, _value)
144 |
145 |
146 | # @notice Transfers `_values` amount(s) of `_ids` from the `_from` address to the `_to` address specified (with safety call).
147 | # @dev Caller must be approved to manage the tokens being transferred out of the `_from` account (see "Approval" section of the standard).
148 | # MUST revert if `_to` is the zero address.
149 | # MUST revert if length of `_ids` is not the same as length of `_values`.
150 | # MUST revert if any of the balance(s) of the holder(s) for token(s) in `_ids` is lower than the respective amount(s) in `_values` sent to the recipient.
151 | # MUST revert on any other error.
152 | # MUST emit `TransferSingle` or `TransferBatch` event(s) such that all the balance changes are reflected (see "Safe Transfer Rules" section of the standard).
153 | # Balance changes and events MUST follow the ordering of the arrays (_ids[0]/_values[0] before _ids[1]/_values[1], etc).
154 | # After the above conditions for the transfer(s) in the batch are met, this function MUST check if `_to` is a smart contract (e.g. code size > 0). If so, it MUST call the relevant `ERC1155TokenReceiver` hook(s) on `_to` and act appropriately (see "Safe Transfer Rules" section of the standard).
155 | # @param _from Source address
156 | # @param _to Target address
157 | # @param _ids IDs of each token type (order and length must match _values array)
158 | # @param _values Transfer amounts per token type (order and length must match _ids array)
159 | # @param _data Additional data with no specified format, MUST be sent unaltered in call to the `ERC1155TokenReceiver` hook(s) on `_to`
160 | # function safeBatchTransferFrom(address _from, address _to, uint256[] calldata _ids, uint256[] calldata _values, bytes calldata _data) external;
161 | @public
162 | def safeBatchTransferFrom(
163 | _from: address,
164 | _to: address,
165 | _ids: uint256[BATCH_SIZE],
166 | _values: uint256[BATCH_SIZE],
167 | _data: bytes[256]
168 | ):
169 | assert _to != ZERO_ADDRESS
170 | #assert len(_ids) == len(_values)
171 |
172 | for i in range(BATCH_SIZE):
173 | assert self._balanceOf[_from][_ids[i]] >= _values[i]
174 | self._balanceOf[_from][_ids[i]] -= _values[i]
175 | self._balanceOf[_to][_ids[i]] += _values[i]
176 | if _to.is_contract:
177 | returnValue: bytes32 = ERC1155TokenReceiver(_to).onERC1155Received(msg.sender, _from, _ids[i], _values[i], _data)
178 | assert returnValue == method_id("onERC1155Received(address,address,uint256,uint256,bytes)", bytes32)
179 |
180 | log.TransferBatch(msg.sender, _from, _to, _ids, _values)
181 |
182 |
183 | # @notice Get the balance of an account's tokens.
184 | # @param _owner The address of the token holder
185 | # @param _id ID of the token
186 | # @return The _owner's balance of the token type requested
187 | # function balanceOf(address _owner, uint256 _id) external view returns (uint256);
188 | @public
189 | @constant
190 | def balanceOf(
191 | _owner: address,
192 | _id: uint256
193 | ) -> uint256:
194 | assert _owner != ZERO_ADDRESS
195 | return self._balanceOf[_owner][_id]
196 |
197 |
198 | # NOTE: This is not part of the standard
199 | # TODO: Right now everyone can set/change an uri
200 | # https://www.ietf.org/rfc/rfc3986.txt
201 | @public
202 | def setUri(_id: uint256, _newUri: string[MAX_URI_SIZE]):
203 | self._uri[_id] = _newUri
204 | log.URI(_newUri, _id)
205 |
206 |
207 | # @notice A distinct Uniform Resource Identifier (URI) for a given token.
208 | # @dev URIs are defined in RFC 3986. (https://www.ietf.org/rfc/rfc3986.txt)
209 | # The URI MUST point to a JSON file that conforms to the "ERC-1155 Metadata URI JSON Schema".
210 | # @return URI string
211 | # function uri(uint256 _id) external view returns (string memory);
212 | @public
213 | @constant
214 | def uri(_id: uint256) -> string[MAX_URI_SIZE]:
215 | return self._uri[_id]
216 |
217 |
218 | # @notice Get the balance of multiple account/token pairs
219 | # @param _owners The addresses of the token holders
220 | # @param _ids ID of the tokens
221 | # @return The _owner's balance of the token types requested (i.e. balance for each (owner, id) pair)
222 | # function balanceOfBatch(address[] calldata _owners, uint256[] calldata _ids) external view returns (uint256[] memory);
223 | @public
224 | @constant
225 | def balanceOfBatch(
226 | _owner: address[BATCH_SIZE],
227 | _ids: uint256[BATCH_SIZE]
228 | ) -> uint256[BATCH_SIZE]:
229 | returnValues: uint256[BATCH_SIZE]
230 | for i in range(BATCH_SIZE):
231 | returnValues[i] = self._balanceOf[_owner[i]][_ids[i]]
232 | return returnValues
233 |
234 |
235 | # @notice Enable or disable approval for a third party ("operator") to manage all of the caller's tokens.
236 | # @dev MUST emit the ApprovalForAll event on success.
237 | # @param _operator Address to add to the set of authorized operators
238 | # @param _approved True if the operator is approved, false to revoke approval
239 | # function setApprovalForAll(address _operator, bool _approved) external;
240 | @public
241 | def setApprovalForAll(
242 | _operator: address,
243 | _approved: bool
244 | ):
245 | (self.operators[msg.sender])[_operator] = _approved
246 | log.ApprovalForAll(msg.sender, _operator, _approved)
247 |
248 |
249 | # @notice Queries the approval status of an operator for a given owner.
250 | # @param _owner The owner of the tokens
251 | # @param _operator Address of authorized operator
252 | # @return True if the operator is approved, false if not
253 | # function isApprovedForAll(address _owner, address _operator) external view returns (bool);
254 | @public
255 | @constant
256 | def isApprovedForAll(
257 | _owner: address,
258 | _operator: address
259 | ) -> bool:
260 | return (self.operators[_owner])[_operator]
261 |
262 |
263 | # NOTE: This is not part of the standard
264 | # TODO: Right now everyone can mint
265 | @public
266 | def mint(
267 | _to: address,
268 | _supply: uint256,
269 | _data: bytes[256]=""
270 | ) -> uint256:
271 | self._balanceOf[msg.sender][self.tokensIdCount] = _supply
272 | self.tokensIdCount += 1
273 | log.TransferSingle(msg.sender, ZERO_ADDRESS, _to, self.tokensIdCount, _supply)
274 | return self.tokensIdCount
275 |
276 |
277 | # NOTE: This is not part of the standard
278 | # TODO: Right now everyone can mint
279 | @public
280 | def mintBatch(
281 | _to: address,
282 | _supplys: uint256[BATCH_SIZE],
283 | _data: bytes[256]=""
284 | ) -> uint256[BATCH_SIZE]:
285 | assert _to != ZERO_ADDRESS
286 | ids: uint256[BATCH_SIZE]
287 | for i in range(BATCH_SIZE):
288 | self._balanceOf[msg.sender][self.tokensIdCount] = _supplys[i]
289 | self.tokensIdCount += 1
290 | id: uint256 = self.tokensIdCount
291 | ids[i] = id
292 |
293 | log.TransferBatch(msg.sender, ZERO_ADDRESS, _to, ids, _supplys)
294 | return ids
295 |
296 |
297 | # TODO: specify a burn()/burnBatch() function
298 |
--------------------------------------------------------------------------------
/erc1155/migrations/1_initial_migration.js:
--------------------------------------------------------------------------------
1 | var Migrations = artifacts.require("./Migrations.sol");
2 |
3 | module.exports = function(deployer) {
4 | deployer.deploy(Migrations);
5 | };
6 |
--------------------------------------------------------------------------------
/erc1155/migrations/2_deploy_contracts.js:
--------------------------------------------------------------------------------
1 | var ERC1155 = artifacts.require("erc1155");
2 |
3 | module.exports = function(deployer) {
4 | //deployer.deploy(ERC1155, name, symbol, decimals, totalSupply);
5 | };
6 |
--------------------------------------------------------------------------------
/erc1155/test/README.md:
--------------------------------------------------------------------------------
1 | ## Run tests
2 | ```bash
3 | $ truffle test --network ganache
4 | ```
5 |
--------------------------------------------------------------------------------
/erc1155/test/erc1155.js:
--------------------------------------------------------------------------------
1 | const ERC1155Abstraction = artifacts.require('erc1155');
2 |
3 | contract('ERC1155', (accounts) => {
4 | /*
5 | beforeEach(async () => {
6 | ERC1155 = await ERC1155Abstraction.new(
7 |
8 | { from: accounts[0] }
9 | );
10 | });
11 | */
12 | });
13 |
--------------------------------------------------------------------------------
/erc1155/truffle.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Use this file to configure your truffle project. It's seeded with some
3 | * common settings for different networks and features like migrations,
4 | * compilation and testing. Uncomment the ones you need or modify
5 | * them to suit your project as necessary.
6 | *
7 | * More information about configuration can be found at:
8 | *
9 | * truffleframework.com/docs/advanced/configuration
10 | *
11 | * To deploy via Infura you'll need a wallet provider (like truffle-hdwallet-provider)
12 | * to sign your transactions before they're sent to a remote public node. Infura API
13 | * keys are available for free at: infura.io/register
14 | *
15 | * You'll also need a mnemonic - the twelve word phrase the wallet uses to generate
16 | * public/private key pairs. If you're publishing your code to GitHub make sure you load this
17 | * phrase from a file you've .gitignored so it doesn't accidentally become public.
18 | *
19 | */
20 |
21 | module.exports = {
22 | networks: {
23 | ganache: {
24 | host: '127.0.0.1',
25 | port: 7545,
26 | network_id: '*', // match any network id
27 | from: '0x954e72fdc51Cf919203067406fB337Ed4bDC8CdA', // account address from which to deploy
28 | gas: 4000000,
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/erc20/.gitignore:
--------------------------------------------------------------------------------
1 | ./build
2 |
--------------------------------------------------------------------------------
/erc20/README.md:
--------------------------------------------------------------------------------
1 | # ERC20
2 | https://eips.ethereum.org/EIPS/eip-20
3 |
4 | ## Run tests
5 | ```bash
6 | $ truffle test --network ganache
7 | ```
8 |
--------------------------------------------------------------------------------
/erc20/contracts/Migrations.sol:
--------------------------------------------------------------------------------
1 | pragma solidity >=0.4.21 <0.6.0;
2 |
3 | contract Migrations {
4 | address public owner;
5 | uint public last_completed_migration;
6 |
7 | constructor() public {
8 | owner = msg.sender;
9 | }
10 |
11 | modifier restricted() {
12 | if (msg.sender == owner) _;
13 | }
14 |
15 | function setCompleted(uint completed) public restricted {
16 | last_completed_migration = completed;
17 | }
18 |
19 | function upgrade(address new_address) public restricted {
20 | Migrations upgraded = Migrations(new_address);
21 | upgraded.setCompleted(last_completed_migration);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/erc20/contracts/erc20.vy:
--------------------------------------------------------------------------------
1 | # Author: Sören Steiger, github.com/ssteiger
2 | # License: MIT
3 |
4 | # ERC20 Token Standard
5 | # https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
6 |
7 |
8 | from vyper.interfaces import ERC20
9 |
10 | implements: ERC20
11 |
12 | # EVENTS:
13 |
14 | # ----- Transfer -----
15 | # MUST trigger when tokens are transferred, including zero value transfers.
16 | # A token contract which creates new tokens SHOULD trigger a Transfer event
17 | # with the _from address set to 0x0 when tokens are created.
18 | Transfer: event({_from: indexed(address), _to: indexed(address), _value: uint256})
19 |
20 | # ----- Approval -----
21 | # MUST trigger on any successful call to approve(address _spender, uint256 _value).
22 | Approval: event({_owner: indexed(address), _spender: indexed(address), _value: uint256})
23 |
24 |
25 | # STATE VARIABLES:
26 | # values which are permanently stored in contract storage
27 |
28 | # ----- name -----
29 | # Returns the name of the token - e.g. "MyToken".
30 | # OPTIONAL - This method can be used to improve usability, but interfaces and
31 | # other contracts MUST NOT expect these values to be present.
32 | name: public(string[64]) # TODO: is this an acceptable size?
33 |
34 | # ----- symbol -----
35 | # Returns the symbol of the token. E.g. "HIX".
36 | # OPTIONAL - This method can be used to improve usability, but interfaces and
37 | # other contracts MUST NOT expect these values to be present.
38 | symbol: public(string[32]) # TODO: is this an acceptable size?
39 |
40 | # ----- decimals -----
41 | # Returns the number of decimals the token uses - e.g. 8, means to divide
42 | # the token amount by 100000000 to get its user representation.
43 | # OPTIONAL - This method can be used to improve usability, but interfaces and
44 | # other contracts MUST NOT expect these values to be present.
45 | decimals: public(uint256)
46 |
47 | # ----- totalSupply -----
48 | # Returns the total token supply.
49 | totalSupply: public(uint256)
50 |
51 | # mappings
52 | balanceOf: public(map(address, uint256))
53 | approvedFunds: map(address, map(address, uint256))
54 |
55 |
56 | @public
57 | def __init__(_name: string[64], _symbol: string[32], _decimals: uint256, _totalSupply: uint256):
58 | self.name = _name
59 | self.symbol = _symbol
60 | self.decimals = _decimals
61 | #self.totalSupply = _totalSupply * 10 ** _decimals
62 | self.totalSupply = _totalSupply
63 | # mint all tokens to the contract creator
64 | self.balanceOf[msg.sender] = self.totalSupply
65 | # fire transfer event
66 | log.Transfer(ZERO_ADDRESS, msg.sender, self.totalSupply)
67 |
68 |
69 | # METHODS:
70 |
71 | # NOTES:
72 | # Callers MUST handle false from returns (bool success).
73 | # Callers MUST NOT assume that false is never returned!
74 |
75 |
76 | # ----- balanceOf -----
77 | # Returns the account balance of another account with address _owner.
78 | # See: https://github.com/ethereum/vyper/issues/1241
79 | # And: https://vyper.readthedocs.io/en/v0.1.0-beta.8/types.html?highlight=getter#mappings
80 |
81 |
82 | # ----- transfer -----
83 | # Transfers _value amount of tokens to address _to, and MUST fire the Transfer
84 | # event. The function SHOULD throw if the _from account balance does not have
85 | # enough tokens to spend.
86 |
87 | # NOTE: Transfers of 0 values MUST be treated as normal transfers and fire the
88 | # Transfer event.
89 | @public
90 | def transfer(_to: address, _value: uint256) -> bool:
91 | # NOTE: vyper does not allow unterflows
92 | # so checks for sufficient funds are done implicitly
93 | # see https://github.com/ethereum/vyper/issues/1237#issuecomment-461957413
94 | # substract balance from sender
95 | self.balanceOf[msg.sender] -= _value
96 | # add balance to recipient
97 | self.balanceOf[_to] += _value
98 | # fire transfer event
99 | log.Transfer(msg.sender, _to, _value)
100 | return True
101 |
102 |
103 | # ----- transferFrom -----
104 | # Transfers _value amount of tokens from address _from to address _to,
105 | # and MUST fire the Transfer event.
106 |
107 | # The transferFrom method is used for a withdraw workflow, allowing contracts
108 | # to transfer tokens on your behalf. This can be used for example to allow a
109 | # contract to transfer tokens on your behalf and/or to charge fees in
110 | # sub-currencies. The function SHOULD throw unless the _from account has
111 | # deliberately authorized the sender of the message via some mechanism.
112 |
113 | # NOTE: Transfers of 0 values MUST be treated as normal transfers and fire the
114 | # Transfer event.
115 | @public
116 | def transferFrom(_from: address, _to: address, _value: uint256) -> bool:
117 | # NOTE: vyper does not allow unterflows
118 | # so checks for sufficient funds are done implicitly
119 | # see https://github.com/ethereum/vyper/issues/1237#issuecomment-461957413
120 | # update approved funds
121 | self.approvedFunds[_from][msg.sender] -= _value
122 | # update sender balance
123 | self.balanceOf[_from] -= _value
124 | # update recipient balance
125 | self.balanceOf[_to] += _value
126 | # fire transfer event
127 | log.Transfer(_from, _to, _value)
128 | return True
129 |
130 |
131 | # ----- approve -----
132 | # Allows _spender to withdraw from your account multiple times, up to the _value
133 | # amount. If this function is called again it overwrites the current allowance
134 | # with _value.
135 |
136 | # NOTE: To prevent attack vectors like the one described here and discussed here,
137 | # clients SHOULD make sure to create user interfaces in such a way that they set
138 | # the allowance first to 0 before setting it to another value for the same
139 | # spender. THOUGH The contract itself shouldn't enforce it, to allow backwards
140 | # compatibility with contracts deployed before.
141 | @public
142 | def approve(_spender: address, _value: uint256) -> bool:
143 | # overwrites the current allowance
144 | self.approvedFunds[msg.sender][_spender] = _value
145 | # fire approval event
146 | log.Approval(msg.sender, _spender, _value)
147 | return True
148 |
149 |
150 | # ----- allowance -----
151 | # Returns the amount which _spender is still allowed to withdraw from _owner.
152 | @public
153 | @constant
154 | def allowance(_owner: address, _spender: address) -> uint256:
155 | return self.approvedFunds[_owner][_spender]
156 |
--------------------------------------------------------------------------------
/erc20/migrations/1_initial_migration.js:
--------------------------------------------------------------------------------
1 | var Migrations = artifacts.require("./Migrations.sol");
2 |
3 | module.exports = function(deployer) {
4 | deployer.deploy(Migrations);
5 | };
6 |
--------------------------------------------------------------------------------
/erc20/migrations/2_deploy_contracts.js:
--------------------------------------------------------------------------------
1 | var ERC20 = artifacts.require("erc20");
2 |
3 | module.exports = function(deployer) {
4 | const name = "MyToken";
5 | const symbol = "MT";
6 | const decimals = 18;
7 | const totalSupply = 100000000;
8 | deployer.deploy(ERC20, name, symbol, decimals, totalSupply);
9 | };
10 |
--------------------------------------------------------------------------------
/erc20/test/README.md:
--------------------------------------------------------------------------------
1 | Test in this folder are taken from [ConsenSys/Tokens](https://github.com/ConsenSys/Tokens/blob/master/test/eip20/eip20.js).
2 |
--------------------------------------------------------------------------------
/erc20/test/erc20-consensys.js:
--------------------------------------------------------------------------------
1 | // NOTE: The following tests are taken from the consensys token repository
2 | // https://github.com/ConsenSys/Tokens/blob/master/test/eip20/eip20.js
3 |
4 | const { assertRevert } = require('./helpers/assertRevert');
5 |
6 | const EIP20Abstraction = artifacts.require('erc20');
7 |
8 | const args = {
9 | name: "Simon Bucks",
10 | symbol: "SBX",
11 | decimals: 1,
12 | totalSupply: 10000,
13 | }
14 |
15 | let HST;
16 |
17 | contract('ERC20', (accounts) => {
18 | beforeEach(async () => {
19 | HST = await EIP20Abstraction.new(
20 | args.name,
21 | args.symbol,
22 | args.decimals,
23 | args.totalSupply,
24 | { from: accounts[0] }
25 | );
26 | });
27 |
28 | it('creation: should create an initial balance of 10000 for the creator', async () => {
29 | const balance = await HST.balanceOf.call(accounts[0]);
30 | assert.strictEqual(balance.toNumber(), 10000);
31 | });
32 |
33 | it('creation: test correct setting of vanity information', async () => {
34 | const name = await HST.name.call();
35 | assert.strictEqual(name, 'Simon Bucks');
36 |
37 | const decimals = await HST.decimals.call();
38 | assert.strictEqual(decimals.toNumber(), 1);
39 |
40 | const symbol = await HST.symbol.call();
41 | assert.strictEqual(symbol, 'SBX');
42 | });
43 |
44 | it('creation: should succeed in creating over 2^256 - 1 (max) tokens', async () => {
45 | // 2^256 - 1
46 | const maxTotalSupply = '115792089237316195423570985008687907853269984665640564039457584007913129639935'
47 | const HST2 = await EIP20Abstraction.new(
48 | args.name,
49 | args.symbol,
50 | args.decimals,
51 | maxTotalSupply,
52 | { from: accounts[0] }
53 | );
54 | let totalSupply = await HST2.totalSupply();
55 | totalSupply = totalSupply.toString();
56 | assert.strictEqual(totalSupply, maxTotalSupply);
57 | //const match = totalSupply.equals('1.15792089237316195423570985008687907853269984665640564039457584007913129639935e+77');
58 | //assert(match, 'result is not correct');
59 | });
60 |
61 | // TRANSFERS
62 | // normal transfers without approvals
63 | it('transfers: ether transfer should be reversed.', async () => {
64 | const balanceBefore = await HST.balanceOf.call(accounts[0]);
65 | assert.strictEqual(balanceBefore.toNumber(), 10000);
66 |
67 | await assertRevert(new Promise((resolve, reject) => {
68 | web3.eth.sendTransaction({ from: accounts[0], to: HST.address, value: web3.utils.toWei('10', 'Ether') }, (err, res) => {
69 | if (err) { reject(err); }
70 | resolve(res);
71 | });
72 | }));
73 |
74 | const balanceAfter = await HST.balanceOf.call(accounts[0]);
75 | assert.strictEqual(balanceAfter.toNumber(), 10000);
76 | });
77 |
78 | it('transfers: should transfer 10000 to accounts[1] with accounts[0] having 10000', async () => {
79 | await HST.transfer(accounts[1], 10000, { from: accounts[0] });
80 | const balance = await HST.balanceOf.call(accounts[1]);
81 | assert.strictEqual(balance.toNumber(), 10000);
82 | });
83 |
84 | it('transfers: should fail when trying to transfer 10001 to accounts[1] with accounts[0] having 10000', async () => {
85 | await assertRevert(HST.transfer.call(accounts[1], 10001, { from: accounts[0] }));
86 | });
87 |
88 | it('transfers: should handle zero-transfers normally', async () => {
89 | assert(await HST.transfer.call(accounts[1], 0, { from: accounts[0] }), 'zero-transfer has failed');
90 | });
91 |
92 | // NOTE: testing uint256 wrapping is impossible since you can't supply > 2^256 -1
93 | // todo: transfer max amounts
94 |
95 | // APPROVALS
96 | it('approvals: msg.sender should approve 100 to accounts[1]', async () => {
97 | await HST.approve(accounts[1], 100, { from: accounts[0] });
98 | const allowance = await HST.allowance.call(accounts[0], accounts[1]);
99 | assert.strictEqual(allowance.toNumber(), 100);
100 | });
101 |
102 | // bit overkill. But is for testing a bug
103 | it('approvals: msg.sender approves accounts[1] of 100 & withdraws 20 once.', async () => {
104 | const balance0 = await HST.balanceOf.call(accounts[0]);
105 | assert.strictEqual(balance0.toNumber(), 10000);
106 |
107 | await HST.approve(accounts[1], 100, { from: accounts[0] }); // 100
108 | const balance2 = await HST.balanceOf.call(accounts[2]);
109 | assert.strictEqual(balance2.toNumber(), 0, 'balance2 not correct');
110 |
111 | await HST.transferFrom.call(accounts[0], accounts[2], 20, { from: accounts[1] });
112 | await HST.allowance.call(accounts[0], accounts[1]);
113 | await HST.transferFrom(accounts[0], accounts[2], 20, { from: accounts[1] }); // -20
114 | const allowance01 = await HST.allowance.call(accounts[0], accounts[1]);
115 | assert.strictEqual(allowance01.toNumber(), 80); // =80
116 |
117 | const balance22 = await HST.balanceOf.call(accounts[2]);
118 | assert.strictEqual(balance22.toNumber(), 20);
119 |
120 | const balance02 = await HST.balanceOf.call(accounts[0]);
121 | assert.strictEqual(balance02.toNumber(), 9980);
122 | });
123 |
124 | // should approve 100 of msg.sender & withdraw 50, twice. (should succeed)
125 | it('approvals: msg.sender approves accounts[1] of 100 & withdraws 20 twice.', async () => {
126 | await HST.approve(accounts[1], 100, { from: accounts[0] });
127 | const allowance01 = await HST.allowance.call(accounts[0], accounts[1]);
128 | assert.strictEqual(allowance01.toNumber(), 100);
129 |
130 | await HST.transferFrom(accounts[0], accounts[2], 20, { from: accounts[1] });
131 | const allowance012 = await HST.allowance.call(accounts[0], accounts[1]);
132 | assert.strictEqual(allowance012.toNumber(), 80);
133 |
134 | const balance2 = await HST.balanceOf.call(accounts[2]);
135 | assert.strictEqual(balance2.toNumber(), 20);
136 |
137 | const balance0 = await HST.balanceOf.call(accounts[0]);
138 | assert.strictEqual(balance0.toNumber(), 9980);
139 |
140 | // FIRST tx done.
141 | // onto next.
142 | await HST.transferFrom(accounts[0], accounts[2], 20, { from: accounts[1] });
143 | const allowance013 = await HST.allowance.call(accounts[0], accounts[1]);
144 | assert.strictEqual(allowance013.toNumber(), 60);
145 |
146 | const balance22 = await HST.balanceOf.call(accounts[2]);
147 | assert.strictEqual(balance22.toNumber(), 40);
148 |
149 | const balance02 = await HST.balanceOf.call(accounts[0]);
150 | assert.strictEqual(balance02.toNumber(), 9960);
151 | });
152 |
153 | // should approve 100 of msg.sender & withdraw 50 & 60 (should fail).
154 | it('approvals: msg.sender approves accounts[1] of 100 & withdraws 50 & 60 (2nd tx should fail)', async () => {
155 | await HST.approve(accounts[1], 100, { from: accounts[0] });
156 | const allowance01 = await HST.allowance.call(accounts[0], accounts[1]);
157 | assert.strictEqual(allowance01.toNumber(), 100);
158 |
159 | await HST.transferFrom(accounts[0], accounts[2], 50, { from: accounts[1] });
160 | const allowance012 = await HST.allowance.call(accounts[0], accounts[1]);
161 | assert.strictEqual(allowance012.toNumber(), 50);
162 |
163 | const balance2 = await HST.balanceOf.call(accounts[2]);
164 | assert.strictEqual(balance2.toNumber(), 50);
165 |
166 | const balance0 = await HST.balanceOf.call(accounts[0]);
167 | assert.strictEqual(balance0.toNumber(), 9950);
168 |
169 | // FIRST tx done.
170 | // onto next.
171 | await assertRevert(HST.transferFrom.call(accounts[0], accounts[2], 60, { from: accounts[1] }));
172 | });
173 |
174 | it('approvals: attempt withdrawal from account with no allowance (should fail)', async () => {
175 | await assertRevert(HST.transferFrom.call(accounts[0], accounts[2], 60, { from: accounts[1] }));
176 | });
177 |
178 | it('approvals: allow accounts[1] 100 to withdraw from accounts[0]. Withdraw 60 and then approve 0 & attempt transfer.', async () => {
179 | await HST.approve(accounts[1], 100, { from: accounts[0] });
180 | await HST.transferFrom(accounts[0], accounts[2], 60, { from: accounts[1] });
181 | await HST.approve(accounts[1], 0, { from: accounts[0] });
182 | await assertRevert(HST.transferFrom.call(accounts[0], accounts[2], 10, { from: accounts[1] }));
183 | });
184 |
185 | it('approvals: approve max (2^256 - 1)', async () => {
186 | // 2^256 - 1
187 | const maxAllowance = '115792089237316195423570985008687907853269984665640564039457584007913129639935'
188 | await HST.approve(accounts[1], maxAllowance, { from: accounts[0] });
189 | let allowance = await HST.allowance(accounts[0], accounts[1]);
190 | allowance = allowance.toString();
191 | assert.strictEqual(allowance, maxAllowance);
192 | //assert(allowance.equals('1.15792089237316195423570985008687907853269984665640564039457584007913129639935e+77'));
193 | });
194 |
195 | // should approve max of msg.sender & withdraw 20 without changing allowance (should succeed).
196 | it('approvals: msg.sender approves accounts[1] of max (2^256 - 1) & withdraws 20', async () => {
197 | const balance0 = await HST.balanceOf.call(accounts[0]);
198 | assert.strictEqual(balance0.toNumber(), 10000);
199 |
200 | const maxAllowance = '115792089237316195423570985008687907853269984665640564039457584007913129639935';
201 | await HST.approve(accounts[1], maxAllowance, { from: accounts[0] });
202 | const balance2 = await HST.balanceOf.call(accounts[2]);
203 | assert.strictEqual(balance2.toNumber(), 0, 'balance2 not correct');
204 |
205 | await HST.transferFrom(accounts[0], accounts[2], 20, { from: accounts[1] });
206 | let allowance01 = await HST.allowance.call(accounts[0], accounts[1]);
207 | //allowance01 = allowance01.toNumber();
208 | assert.strictEqual(allowance01.toString(), '115792089237316195423570985008687907853269984665640564039457584007913129639915');
209 |
210 | const balance22 = await HST.balanceOf.call(accounts[2]);
211 | assert.strictEqual(balance22.toNumber(), 20);
212 |
213 | const balance02 = await HST.balanceOf.call(accounts[0]);
214 | assert.strictEqual(balance02.toNumber(), 9980);
215 | });
216 |
217 | /* eslint-disable no-underscore-dangle */
218 | it('events: should fire Transfer event properly', async () => {
219 | const res = await HST.transfer(accounts[1], '2666', { from: accounts[0] });
220 | const transferLog = res.logs.find(element => element.event.match('Transfer'));
221 | assert.strictEqual(transferLog.args._from, accounts[0]);
222 | assert.strictEqual(transferLog.args._to, accounts[1]);
223 | assert.strictEqual(transferLog.args._value.toString(), '2666');
224 | });
225 |
226 | it('events: should fire Transfer event normally on a zero transfer', async () => {
227 | const res = await HST.transfer(accounts[1], '0', { from: accounts[0] });
228 | const transferLog = res.logs.find(element => element.event.match('Transfer'));
229 | assert.strictEqual(transferLog.args._from, accounts[0]);
230 | assert.strictEqual(transferLog.args._to, accounts[1]);
231 | assert.strictEqual(transferLog.args._value.toString(), '0');
232 | });
233 |
234 | it('events: should fire Approval event properly', async () => {
235 | const res = await HST.approve(accounts[1], '2666', { from: accounts[0] });
236 | const approvalLog = res.logs.find(element => element.event.match('Approval'));
237 | assert.strictEqual(approvalLog.args._owner, accounts[0]);
238 | assert.strictEqual(approvalLog.args._spender, accounts[1]);
239 | assert.strictEqual(approvalLog.args._value.toString(), '2666');
240 | });
241 | });
242 |
--------------------------------------------------------------------------------
/erc20/test/helpers/assertRevert.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | assertRevert: async (promise) => {
3 | try {
4 | await promise;
5 | } catch (error) {
6 | const revertFound = error.message.search('revert') >= 0;
7 | assert(revertFound, `Expected "revert", got ${error} instead`);
8 | return;
9 | }
10 | assert.fail('Expected revert not received');
11 | },
12 | };
13 |
--------------------------------------------------------------------------------
/erc20/truffle.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Use this file to configure your truffle project. It's seeded with some
3 | * common settings for different networks and features like migrations,
4 | * compilation and testing. Uncomment the ones you need or modify
5 | * them to suit your project as necessary.
6 | *
7 | * More information about configuration can be found at:
8 | *
9 | * truffleframework.com/docs/advanced/configuration
10 | *
11 | * To deploy via Infura you'll need a wallet provider (like truffle-hdwallet-provider)
12 | * to sign your transactions before they're sent to a remote public node. Infura API
13 | * keys are available for free at: infura.io/register
14 | *
15 | * You'll also need a mnemonic - the twelve word phrase the wallet uses to generate
16 | * public/private key pairs. If you're publishing your code to GitHub make sure you load this
17 | * phrase from a file you've .gitignored so it doesn't accidentally become public.
18 | *
19 | */
20 |
21 | module.exports = {
22 | networks: {
23 | ganache: {
24 | host: '127.0.0.1',
25 | port: 7545,
26 | network_id: '*', // match any network id
27 | from: '0x954e72fdc51Cf919203067406fB337Ed4bDC8CdA', // account address from which to deploy
28 | gas: 4000000,
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/erc721/.gitignore:
--------------------------------------------------------------------------------
1 | ./build
2 |
--------------------------------------------------------------------------------
/erc721/README.md:
--------------------------------------------------------------------------------
1 | # ERC721
2 |
3 | This contract implements the ERC721 standard.
4 | It allowes for the creation of so called non fungible tokens, or NFT's.
5 | The difference of NFT's to other token standards like ERC20 or ERC777 is that each token is unique and not interchangable.
6 |
7 |
8 | ## Getting started
9 |
10 | ### Run tests
11 |
12 | ```bash
13 | $ truffle test --network ganache
14 | ```
15 |
16 | | Further resources |
17 | | - |
18 | | [EIP](https://eips.ethereum.org/EIPS/eip-721) |
19 | | [What are ERC721 standard tokens?](https://hackernoon.com/what-are-erc721-standard-tokens-3624adcc3e54) |
20 | | [The Anatomy of ERC721](https://medium.com/crypto-currently/the-anatomy-of-erc721-e9db77abfc24) |
21 | | [Noobs Guide to Understanding ERC-20 vs ERC-721 Tokens](https://medium.com/@brenn.a.hill/noobs-guide-to-understanding-erc-20-vs-erc-721-tokens-d7f5657a4ee7) |
22 | | [Marketplace for trading ERC721 tokens](https://opensea.io/) |
23 |
24 | | Implementations |
25 | | - |
26 | | [CryptoKitties](https://www.cryptokitties.co/) |
27 | | [Decentraland](https://market.decentraland.org/) |
28 | | [Ethermon](https://www.etheremon.com/) |
29 |
--------------------------------------------------------------------------------
/erc721/contracts/Migrations.sol:
--------------------------------------------------------------------------------
1 | pragma solidity >=0.4.21 <0.6.0;
2 |
3 | contract Migrations {
4 | address public owner;
5 | uint public last_completed_migration;
6 |
7 | constructor() public {
8 | owner = msg.sender;
9 | }
10 |
11 | modifier restricted() {
12 | if (msg.sender == owner) _;
13 | }
14 |
15 | function setCompleted(uint completed) public restricted {
16 | last_completed_migration = completed;
17 | }
18 |
19 | function upgrade(address new_address) public restricted {
20 | Migrations upgraded = Migrations(new_address);
21 | upgraded.setCompleted(last_completed_migration);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/erc721/contracts/erc721.vy:
--------------------------------------------------------------------------------
1 | # Author: Sören Steiger, github.com/ssteiger
2 | # License: MIT
3 |
4 | # ERC721 Token Standard
5 | # https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
6 |
7 | # @dev Note: the ERC-165 identifier for this interface is 0x150b7a02.
8 | # @notice Handle the receipt of an NFT
9 | # @dev The ERC721 smart contract calls this function on the recipient
10 | # after a `transfer`.
11 | # This function MAY throw to revert and reject the transfer.
12 | # Return of other than the magic value MUST result in the transaction
13 | # being reverted.
14 | # Note: the contract address is always the message sender.
15 | # @param _operator The address which called `safeTransferFrom` function
16 | # @param _from The address which previously owned the token
17 | # @param _tokenId The NFT identifier which is being transferred
18 | # @param _data Additional data with no specified format
19 | # @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
20 | # unless throwing
21 | # function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes _data) external returns(bytes4);
22 | contract ERC721TokenReceiver:
23 | def onERC721Received(
24 | _operator: address,
25 | _from: address,
26 | _tokenId: uint256,
27 | _data: bytes[256]
28 | ) -> bytes32: constant
29 |
30 |
31 | # EVENTS:
32 |
33 | # @dev This emits when ownership of any NFT changes by any mechanism.
34 | # This event emits when NFTs are created (`from` == 0) and destroyed
35 | # (`to` == 0).
36 | # Exception: during contract creation, any number of NFTs may be created
37 | # and assigned without emitting Transfer.
38 | # At the time of any transfer, the approved address for that NFT (if any)
39 | # is reset to none.
40 | Transfer: event({
41 | _from: indexed(address),
42 | _to: indexed(address),
43 | _tokenId: indexed(uint256)
44 | })
45 |
46 |
47 | # NOTE: This is not part of the standard
48 | Mint: event({
49 | _to: indexed(address),
50 | _tokenId: indexed(uint256)
51 | })
52 |
53 |
54 | # @dev This emits when the approved address for an NFT is changed or reaffirmed.
55 | # The zero address indicates there is no approved address.
56 | # When a Transfer event emits, this also indicates that the approved
57 | # address for that NFT (if any) is reset to none.
58 | Approval: event({
59 | _owner: indexed(address),
60 | _approved: indexed(address),
61 | _tokenId: indexed(uint256)
62 | })
63 |
64 |
65 | # @dev This emits when an operator is enabled or disabled for an owner.
66 | # The operator can manage all NFTs of the owner.
67 | ApprovalForAll: event({
68 | _owner: indexed(address),
69 | _operator: indexed(address),
70 | _approved: bool
71 | })
72 |
73 |
74 | # STATE VARIABLES:
75 |
76 | # NOTE: This is not part of the standard
77 | contractOwner: public(address)
78 | # Used for token id's
79 | nftSupply: uint256
80 |
81 | # Used to keep track of the number of tokens an address holds
82 | nftCount: public(map(address, uint256))
83 | ownerOfNFT: public(map(uint256, address))
84 |
85 | operatorFor: public(map(uint256, address))
86 | approvedForAll: public(map(address, map(address, bool)))
87 |
88 | # Interface detection as specified in ERC165
89 | # https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md
90 | supportedInterfaces: public(map(bytes32, bool))
91 | # ERC165 interface ID's
92 | ERC165_INTERFACE_ID: constant(bytes32) = 0x0000000000000000000000000000000000000000000000000000000001ffc9a7
93 | ERC721_INTERFACE_ID: constant(bytes32) = 0x0000000000000000000000000000000000000000000000000000000080ac58cd
94 |
95 |
96 | # METHODS:
97 | @public
98 | def __init__():
99 | # set initial supply (used for token id's)
100 | self.nftSupply = 0
101 | # set supported interfaces
102 | self.supportedInterfaces[ERC165_INTERFACE_ID] = True
103 | self.supportedInterfaces[ERC721_INTERFACE_ID] = True
104 | # set contract owner
105 | # NOTE: This is not part of the standard
106 | # only contractOwner can call mint()
107 | self.contractOwner = msg.sender
108 |
109 |
110 | @private
111 | def _checkIfIsOwnerOrOperatorOrApprovedForAll(_msgSender: address, _from: address, _tokenId: uint256):
112 | # Throws unless `msg.sender` is
113 | # the current owner
114 | isOwner: bool = self.ownerOfNFT[_tokenId] == _msgSender
115 | # an authorized operator
116 | isOperator: bool = self.operatorFor[_tokenId] == _msgSender
117 | # or the approved address for this NFT
118 | isApprovedForAll: bool = (self.approvedForAll[_from])[_msgSender]
119 | assert (isOwner or isOperator or isApprovedForAll)
120 |
121 |
122 | @private
123 | def _setNewOwner(_currentOwner: address, _newOwner: address, _tokenId: uint256):
124 | # set new owner
125 | self.ownerOfNFT[_tokenId] = _newOwner
126 | # updated balances
127 | self.nftCount[_currentOwner] -= 1
128 | self.nftCount[_newOwner] += 1
129 | # reset operator
130 | self.operatorFor[_tokenId] = ZERO_ADDRESS
131 |
132 |
133 | @private
134 | def _transfer(_from: address, _to: address, _tokenId: uint256):
135 | # Throws if `_from` is not the current owner.
136 | assert self.ownerOfNFT[_tokenId] == _from
137 | # Throws if `_to` is the zero address.
138 | assert _to != ZERO_ADDRESS
139 | # Throws if `_tokenId` is not a valid NFT.
140 | assert self.ownerOfNFT[_tokenId] != ZERO_ADDRESS
141 | # transfer to new owner
142 | self._setNewOwner(_from, _to, _tokenId)
143 | # log transfer
144 | log.Transfer(_from, _to, _tokenId)
145 |
146 |
147 | @public
148 | @constant
149 | def supportsInterface(_interfaceID: bytes32) -> bool:
150 | # Interface detection as specified in ERC165
151 | # https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md
152 | return self.supportedInterfaces[_interfaceID]
153 |
154 |
155 | # @notice Count all NFTs assigned to an owner
156 | # @dev NFTs assigned to the zero address are considered invalid, and this
157 | # function throws for queries about the zero address.
158 | # @param _owner An address for whom to query the balance
159 | # @return The number of NFTs owned by `_owner`, possibly zero
160 | # function balanceOf(address _owner) external view returns (uint256);
161 | @public
162 | @constant
163 | def balanceOf(_owner: address) -> uint256:
164 | # NFTs assigned to the zero address are considered invalid, and this
165 | # function throws for queries about the zero address.
166 | assert _owner != ZERO_ADDRESS
167 | return self.nftCount[_owner]
168 |
169 |
170 | # @notice Find the owner of an NFT
171 | # @dev NFTs assigned to zero address are considered invalid, and queries
172 | # about them do throw.
173 | # @param _tokenId The identifier for an NFT
174 | # @return The address of the owner of the NFT
175 | # function ownerOf(uint256 _tokenId) external view returns (address);
176 | @public
177 | @constant
178 | def ownerOf(_tokenId: uint256) -> address:
179 | # NFTs assigned to the zero address are considered invalid, and this
180 | # function throws for queries about the zero address.
181 | owner: address = self.ownerOfNFT[_tokenId]
182 | assert owner != ZERO_ADDRESS
183 | return owner
184 |
185 |
186 | # @notice Transfers the ownership of an NFT from one address to another address
187 | # @dev Throws unless `msg.sender` is
188 | # the current owner,
189 | # an authorized operator,
190 | # or the approved address for this NFT.
191 | # Throws if `_from` is not the current owner.
192 | # Throws if `_to` is the zero address.
193 | # Throws if `_tokenId` is not a valid NFT.
194 | # When transfer is complete, this function checks if `_to` is
195 | # a smart contract (code size > 0). If so, it calls
196 | # `onERC721Received` on `_to` and throws if the return value is not
197 | # `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
198 | # @param _from The current owner of the NFT
199 | # @param _to The new owner
200 | # @param _tokenId The NFT to transfer
201 | # @param data Additional data with no specified format, sent in call to `_to`
202 | # function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;
203 | @public
204 | @payable
205 | def safeTransferFrom(_from: address, _to: address, _tokenId: uint256, _data: bytes[256]=""):
206 | # Throws unless `msg.sender` is
207 | # the current owner,
208 | # an authorized operator,
209 | # or the approved address for this NFT.
210 | self._checkIfIsOwnerOrOperatorOrApprovedForAll(msg.sender, _from, _tokenId)
211 | # transfer
212 | self._transfer(_from, _to, _tokenId)
213 | # When transfer is complete,
214 | # this function checks if `_to` is a smart contract (code size > 0)
215 | if _to.is_contract:
216 | # If so, it calls `onERC721Received` on `_to` and throws if the return value is not
217 | # `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
218 | returnValue: bytes32 = ERC721TokenReceiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data)
219 | assert returnValue == method_id("onERC721Received(address,address,uint256,bytes)", bytes32)
220 |
221 |
222 | # @notice Transfers the ownership of an NFT from one address to another address
223 | # @dev This works identically to the other function with an extra data
224 | # parameter, except this function just sets data to "".
225 | # @param _from The current owner of the NFT
226 | # @param _to The new owner
227 | # @param _tokenId The NFT to transfer
228 | # function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;
229 |
230 |
231 | # @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
232 | # TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE
233 | # THEY MAY BE PERMANENTLY LOST
234 | # @dev Throws unless
235 | # `msg.sender` is the current owner,
236 | # an authorized operator,
237 | # or the approved address for this NFT.
238 | # Throws if `_from` is not the current owner.
239 | # Throws if `_to` is the zero address.
240 | # Throws if `_tokenId` is not a valid NFT.
241 | # @param _from The current owner of the NFT
242 | # @param _to The new owner
243 | # @param _tokenId The NFT to transfer
244 | # function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
245 | @public
246 | @payable
247 | def transferFrom(_from: address, _to: address, _tokenId: uint256):
248 | # Throws unless `msg.sender` is
249 | # the current owner,
250 | # an authorized operator,
251 | # or the approved address for this NFT.
252 | self._checkIfIsOwnerOrOperatorOrApprovedForAll(msg.sender, _from, _tokenId)
253 | # do transfer
254 | self._transfer(_from, _to, _tokenId)
255 |
256 |
257 | # @notice Change or reaffirm the approved address for an NFT
258 | # @dev The zero address indicates there is no approved address.
259 | # Throws unless `msg.sender` is
260 | # the current NFT owner,
261 | # or an authorized operator of the current owner.
262 | # @param _approved The new approved NFT controller
263 | # @param _tokenId The NFT to approve
264 | # function approve(address _approved, uint256 _tokenId) external payable;
265 | @public
266 | @payable
267 | def approve(_approved: address, _tokenId: uint256):
268 | # Throws if _tokenId is not owned / a valid NFT
269 | assert self.ownerOfNFT[_tokenId] != ZERO_ADDRESS
270 | # Throws unless `msg.sender` is the current NFT owner
271 | isOwner: bool = self.ownerOfNFT[_tokenId] == msg.sender
272 | # or an authorized operator of the current owner.
273 | isOperator: bool = self.operatorFor[_tokenId] == msg.sender
274 | # TODO: does the this include approvedForAll?
275 | assert (isOwner or isOperator)
276 | # set new approved address
277 | self.operatorFor[_tokenId] = _approved
278 | # log change
279 | log.Approval(msg.sender, _approved, _tokenId)
280 |
281 |
282 | # @notice Enable or disable approval for a third party ("operator") to manage
283 | # all of `msg.sender`'s assets
284 | # @dev Emits the ApprovalForAll event.
285 | # The contract MUST allow multiple operators per owner.
286 | # @param _operator Address to add to the set of authorized operators
287 | # @param _approved True if the operator is approved, false to revoke approval
288 | # function setApprovalForAll(address _operator, bool _approved) external;
289 | @public
290 | def setApprovalForAll(_operator: address, _approved: bool):
291 | # The contract MUST allow multiple operators per owner.
292 | self.approvedForAll[msg.sender][_operator] = _approved
293 | # log change
294 | log.ApprovalForAll(msg.sender, _operator, _approved)
295 |
296 |
297 | # @notice Get the approved address for a single NFT
298 | # @dev Throws if `_tokenId` is not a valid NFT.
299 | # @param _tokenId The NFT to find the approved address for
300 | # @return The approved address for this NFT, or the zero address if
301 | # there is none
302 | # function getApproved(uint256 _tokenId) external view returns (address);
303 | @public
304 | @constant
305 | def getApproved(_tokenId: uint256) -> address:
306 | # Throws if `_tokenId` is not a valid NFT.
307 | assert self.ownerOfNFT[_tokenId] != ZERO_ADDRESS
308 | return self.operatorFor[_tokenId]
309 |
310 |
311 | # @notice Query if an address is an authorized operator for another address
312 | # @param _owner The address that owns the NFTs
313 | # @param _operator The address that acts on behalf of the owner
314 | # @return True if `_operator` is an approved operator for `_owner`,
315 | # false otherwise
316 | # function isApprovedForAll(address _owner, address _operator) external view returns (bool);
317 | @public
318 | @constant
319 | def isApprovedForAll(_owner: address, _operator: address) -> bool:
320 | return (self.approvedForAll[_owner])[_operator]
321 |
322 |
323 | # NOTE: This is not part of the standard
324 | @public
325 | def mint() -> uint256:
326 | # only contractOwner is allowed to mint
327 | assert msg.sender == self.contractOwner
328 | # update supply
329 | tokenId: uint256 = self.nftSupply
330 | self.nftSupply += 1
331 | # update ownership
332 | self.ownerOfNFT[tokenId] = msg.sender
333 | self.nftCount[msg.sender] += 1
334 | self.operatorFor[tokenId] = ZERO_ADDRESS
335 | # log mint
336 | log.Mint(msg.sender, tokenId)
337 | return tokenId
338 |
--------------------------------------------------------------------------------
/erc721/migrations/1_initial_migration.js:
--------------------------------------------------------------------------------
1 | var Migrations = artifacts.require("./Migrations.sol");
2 |
3 | module.exports = function(deployer) {
4 | deployer.deploy(Migrations);
5 | };
6 |
--------------------------------------------------------------------------------
/erc721/migrations/2_deploy_contracts.js:
--------------------------------------------------------------------------------
1 | var ERC721 = artifacts.require("erc721");
2 |
3 | module.exports = function(deployer) {
4 | deployer.deploy(ERC721);
5 | };
6 |
--------------------------------------------------------------------------------
/erc721/test/erc721.js:
--------------------------------------------------------------------------------
1 | const erc721 = artifacts.require("erc721");
2 |
3 | let owner,
4 | receiver,
5 | operator,
6 | erc721Token;
7 |
8 | const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
9 | const ERC165_INTERFACE_ID = '0x0000000000000000000000000000000000000000000000000000000001ffc9a7';
10 | const ERC721_INTERFACE_ID = '0x0000000000000000000000000000000000000000000000000000000080ac58cd';
11 |
12 | contract("ERC721", async accounts => {
13 | beforeEach(async () => {
14 | owner = accounts[0];
15 | receiver = accounts[1];
16 | operator = accounts[2];
17 | erc721Token = await erc721.new({ from: owner });
18 | });
19 |
20 | it("...should register ERC165 interface.", async () => {
21 | const has_erc165_interface = await erc721Token.supportsInterface.call(ERC165_INTERFACE_ID);
22 | assert.ok(has_erc165_interface, "The ERC165 interface was not correctly registered.");
23 | });
24 |
25 | it("...should register ERC721 interface.", async () => {
26 | const has_erc721_interface = await erc721Token.supportsInterface.call(ERC721_INTERFACE_ID);
27 | assert.ok(has_erc721_interface, "The ERC721 interface was not correctly registered.");
28 | });
29 |
30 | it("...should mint token to owner.", async () => {
31 | let balance = await erc721Token.balanceOf.call(owner);
32 | const minter_starting_balance = balance.toString();
33 |
34 | // TODO: weird -> we are getting the token id from the mint event parameters
35 | // how to get the return value of mint()??
36 | let returnData = await erc721Token.mint({ from: owner });
37 | const tokenId_1 = returnData.logs[0].args['1'].toString();
38 | returnData = await erc721Token.mint({ from: owner });
39 | const tokenId_2 = returnData.logs[0].args['1'].toString();
40 |
41 | const ownerOfToken_1 = await erc721Token.ownerOf.call(tokenId_1);
42 | const ownerOfToken_2 = await erc721Token.ownerOf.call(tokenId_2);
43 |
44 | balance = await erc721Token.balanceOf.call(owner);
45 | const minter_ending_balance = balance.toString();
46 |
47 | assert.equal(
48 | tokenId_1,
49 | 0,
50 | "Token ID wasn't correctly set."
51 | );
52 | assert.equal(
53 | tokenId_2,
54 | 1,
55 | "Token ID wasn't correctly set."
56 | );
57 | assert.equal(
58 | minter_starting_balance,
59 | minter_ending_balance - 2,
60 | "Token wasn't correctly minted to owner."
61 | );
62 | assert.equal(
63 | ownerOfToken_1,
64 | owner,
65 | "Owner of first minted token wasn't correctly set."
66 | );
67 | assert.equal(
68 | ownerOfToken_2,
69 | owner,
70 | "Owner of second minted token wasn't correctly set."
71 | );
72 | });
73 |
74 | // TODO:
75 | it("...mint should revert if msg.sender is not owner.", async () => {
76 | try {
77 | await erc721Token.mint({ from: receiver });
78 | } catch (e) {
79 | revert = e;
80 | }
81 |
82 | assert.instanceOf(
83 | revert,
84 | Error,
85 | 'Mint by non owner did not revert.'
86 | )
87 | });
88 |
89 | it("...should safeTransferFrom.", async () => {
90 | // TODO: weird -> we are getting the token id from the mint event parameters
91 | // how to get the return value of mint()??
92 | const returnData = await erc721Token.mint({ from: owner });
93 | const tokenId = returnData.logs[0].args['1'].toString();
94 |
95 | const ownerBalanceBefore = await erc721Token.balanceOf.call(owner);
96 | const receiverBalanceBefore = await erc721Token.balanceOf.call(receiver);
97 | const ownerOfTokenBefore = await erc721Token.ownerOf.call(tokenId);
98 |
99 | await erc721Token.safeTransferFrom(owner, receiver, tokenId);
100 |
101 | const ownerBalanceAfter = await erc721Token.balanceOf.call(owner);
102 | const receiverBalanceAfter = await erc721Token.balanceOf.call(receiver);
103 | const ownerOfTokenAfter = await erc721Token.ownerOf.call(tokenId);
104 |
105 | assert.equal(
106 | ownerBalanceBefore - 1,
107 | ownerBalanceAfter.toString(),
108 | "Balance of owner wasn't correctly updated."
109 | );
110 |
111 | assert.equal(
112 | receiverBalanceBefore.toString(),
113 | receiverBalanceAfter - 1,
114 | "Balance of reveiver wasn't correctly updated."
115 | );
116 |
117 | assert.equal(
118 | ownerOfTokenAfter,
119 | receiver,
120 | "Owner of token wasn't correctly updated."
121 | );
122 | });
123 |
124 | it("...should transferFrom.", async () => {
125 | // TODO: weird -> we are getting the token id from the mint event parameters
126 | // how to get the return value of mint()??
127 | const returnData = await erc721Token.mint({ from: owner });
128 | const tokenId = returnData.logs[0].args['1'].toString();
129 |
130 | const ownerBalanceBefore = await erc721Token.balanceOf.call(owner);
131 | const receiverBalanceBefore = await erc721Token.balanceOf.call(receiver);
132 | const ownerOfTokenBefore = await erc721Token.ownerOf.call(tokenId);
133 |
134 | await erc721Token.transferFrom(owner, receiver, tokenId);
135 |
136 | const ownerBalanceAfter = await erc721Token.balanceOf.call(owner);
137 | const receiverBalanceAfter = await erc721Token.balanceOf.call(receiver);
138 | const ownerOfTokenAfter = await erc721Token.ownerOf.call(tokenId);
139 |
140 | assert.equal(
141 | ownerBalanceBefore - 1,
142 | ownerBalanceAfter.toString(),
143 | "Balance of owner wasn't correctly updated."
144 | );
145 |
146 | assert.equal(
147 | receiverBalanceBefore.toString(),
148 | receiverBalanceAfter - 1,
149 | "Balance of reveiver wasn't correctly updated."
150 | );
151 |
152 | assert.equal(
153 | ownerOfTokenAfter,
154 | receiver,
155 | "Owner of token wasn't correctly updated."
156 | );
157 | });
158 |
159 | // TODO:
160 | it("...safeTransferFrom() to contract without onERC721Received should revert.", async () => {
161 |
162 | });
163 |
164 | it("...safeTransferFrom() to ZERO_ADDRESS should revert.", async () => {
165 | const returnData = await erc721Token.mint({ from: owner });
166 | const tokenId = returnData.logs[0].args['1'].toString();
167 |
168 | let revert = false;
169 | try {
170 | const result = await erc721Token.safeTransferFrom(owner, ZERO_ADDRESS, tokenId);
171 | } catch (e) {
172 | revert = e;
173 | }
174 | assert.instanceOf(
175 | revert,
176 | Error,
177 | "SafeTransferFrom to ZERO_ADDRESS did not revert."
178 | )
179 | });
180 |
181 | it("...transferFrom() to ZERO_ADDRESS should revert.", async () => {
182 | const returnData = await erc721Token.mint({ from: owner });
183 | const tokenId = returnData.logs[0].args['1'].toString();
184 |
185 | let revert = false;
186 | try {
187 | const result = await erc721Token.transferFrom(owner, ZERO_ADDRESS, tokenId);
188 | } catch (e) {
189 | revert = e;
190 | }
191 | assert.instanceOf(
192 | revert,
193 | Error,
194 | "TransferFrom to ZERO_ADDRESS did not revert."
195 | )
196 | });
197 |
198 | it("...safeTransferFrom() should revert if 'tokenId' is not a valid NFT.", async () => {
199 | const tokenId = -1
200 |
201 | let revert = false;
202 | try {
203 | await erc721Token.safeTransferFrom(owner, receiver, tokenId);
204 | } catch (e) {
205 | revert = e;
206 | }
207 |
208 | assert.instanceOf(
209 | revert,
210 | Error,
211 | "SafeTransferFrom of non valid NFT did not revert."
212 | )
213 | });
214 |
215 | it("...transferFrom() should revert if 'tokenId' is not a valid NFT.", async () => {
216 | const tokenId = -1
217 |
218 | let revert = false;
219 | try {
220 | await erc721Token.transferFrom(owner, receiver, tokenId);
221 | } catch (e) {
222 | revert = e;
223 | }
224 |
225 | assert.instanceOf(
226 | revert,
227 | Error,
228 | "TransferFrom of non valid NFT did not revert."
229 | )
230 | });
231 |
232 | it("...safeTransferFrom() should revert if 'from' is not the owner.", async () => {
233 | const returnData = await erc721Token.mint({ from: owner });
234 | const tokenId = returnData.logs[0].args['1'].toString();
235 |
236 | let revert = false;
237 | try {
238 | await erc721Token.safeTransferFrom(receiver, owner, tokenId);
239 | } catch (e) {
240 | revert = e;
241 | }
242 |
243 | assert.instanceOf(
244 | revert,
245 | Error,
246 | "SafeTransferFrom by non owner did not revert."
247 | )
248 | });
249 |
250 | it("...transferFrom() should revert if 'from' is not the owner.", async () => {
251 | const returnData = await erc721Token.mint({ from: owner });
252 | const tokenId = returnData.logs[0].args['1'].toString();
253 |
254 | let revert = false;
255 | try {
256 | await erc721Token.transferFrom(receiver, owner, tokenId);
257 | } catch (e) {
258 | revert = e;
259 | }
260 |
261 | assert.instanceOf(
262 | revert,
263 | Error,
264 | "SafeTransferFrom by non owner did not revert."
265 | )
266 | });
267 |
268 | it("...balanceOf() should revert for queries about the ZERO_ADDRESS.", async () => {
269 | let revert = false;
270 | try {
271 | await erc721Token.balanceOf(ZERO_ADDRESS);
272 | } catch (e) {
273 | revert = e;
274 | }
275 |
276 | assert.instanceOf(
277 | revert,
278 | Error,
279 | "BalanceOf query about ZERO_ADDRESS did not revert."
280 | )
281 | });
282 |
283 | it("...should approve() if is owner.", async () => {
284 | const returnData = await erc721Token.mint({ from: owner });
285 | const tokenId = returnData.logs[0].args['1'].toString();
286 |
287 | let revert = false;
288 | try {
289 | await erc721Token.approve(operator, tokenId, { from: owner });
290 | } catch (e) {
291 | revert = e;
292 | }
293 |
294 | const isApproved = await erc721Token.getApproved.call(tokenId);
295 |
296 | assert.isOk(
297 | isApproved,
298 | "Operator was not correctly approved."
299 | );
300 | assert.isNotOk(
301 | revert,
302 | "Operator was not correctly approved."
303 | );
304 | });
305 |
306 | it("...approve() should revert if 'tokenId' is not owned by 'msg.sender'.", async () => {
307 | const returnData = await erc721Token.mint({ from: owner });
308 | const tokenId = returnData.logs[0].args['1'].toString();
309 |
310 | let revert = false;
311 | try {
312 | await erc721Token.approve(owner, tokenId, { from: operator });
313 | } catch (e) {
314 | revert = e;
315 | }
316 |
317 | assert.instanceOf(
318 | revert,
319 | Error,
320 | "Approve from non owner did not revert."
321 | )
322 | });
323 |
324 | it("...approve() should revert if 'tokenId' is not a valid NFT.", async () => {
325 | let revert = false;
326 | try {
327 | await erc721Token.approve(owner, -1, { from: operator });
328 | } catch (e) {
329 | revert = e;
330 | }
331 |
332 | assert.instanceOf(
333 | revert,
334 | Error,
335 | "Approve of non valid NFT did not revert."
336 | )
337 | });
338 |
339 | it("...should approve if is an authorized operator.", async () => {
340 | const returnData = await erc721Token.mint({ from: owner });
341 | const tokenId = returnData.logs[0].args['1'].toString();
342 |
343 | await erc721Token.approve(operator, tokenId, { from: owner });
344 |
345 | await erc721Token.approve(accounts[3], tokenId, { from: operator });
346 |
347 | const isApproved = await erc721Token.getApproved.call(tokenId);
348 |
349 | assert.equal(
350 | isApproved,
351 | accounts[3],
352 | "Authorized operator did not correctly approve."
353 | );
354 | });
355 |
356 | it("...approve should revert if 'msg.sender' is not an authorized operator.", async () => {
357 | const returnData = await erc721Token.mint({ from: owner });
358 | const tokenId = returnData.logs[0].args['1'].toString();
359 |
360 | let revert = false;
361 | try {
362 | await erc721Token.approve(owner, tokenId, { from: account[3] });
363 | } catch (e) {
364 | revert = e;
365 | }
366 |
367 | assert.instanceOf(
368 | revert,
369 | Error,
370 | "Approve call from non authorized operator did not revert."
371 | )
372 | });
373 |
374 | it("...should getApproved.", async () => {
375 | const returnData = await erc721Token.mint({ from: owner });
376 | const tokenId = returnData.logs[0].args['1'].toString();
377 |
378 | await erc721Token.approve(operator, tokenId, { from: owner });
379 |
380 | const isApproved = await erc721Token.getApproved.call(tokenId);
381 |
382 | assert.equal(
383 | isApproved,
384 | operator,
385 | "Did not correctly getApproved."
386 | );
387 | });
388 |
389 | it("...should setApprovalForAll.", async () => {
390 | await erc721Token.setApprovalForAll(operator, true, { from: owner });
391 |
392 | const isApprovedForAll = await erc721Token.isApprovedForAll.call(owner, operator);
393 |
394 | assert.isOk(
395 | isApprovedForAll,
396 | "Did not setApprovalForAll."
397 | );
398 | });
399 |
400 | it("...isApprovedForAll should return 'False' if address is not approvedForAll.", async () => {
401 | const isApprovedForAll = await erc721Token.isApprovedForAll.call(owner, accounts[3]);
402 |
403 | assert.isNotOk(
404 | isApprovedForAll,
405 | "Did not return 'False' for address that is not approvedForAll."
406 | );
407 | });
408 |
409 | it("...getApproved should revert if 'tokenId' is not a valid NFT.", async () => {
410 | let revert = false;
411 | try {
412 | await erc721Token.getApproved.call(-1);
413 | } catch (e) {
414 | revert = e;
415 | }
416 |
417 | assert.instanceOf(
418 | revert,
419 | Error,
420 | "GetApproved of non valid NFT did not revert."
421 | )
422 | });
423 |
424 | // Test event logging
425 | it("...should log 'Minted' on mint().", async () => {
426 | const res = await erc721Token.mint({ from: owner });
427 | const log = res.logs.find(element => element.event.match('Mint'));
428 |
429 | assert.strictEqual(log.args._to, owner);
430 | assert.strictEqual(log.args._tokenId.toString(), "0");
431 | })
432 |
433 | it("...should log 'Transfer' on safeTransferFrom().", async () => {
434 | const returnData = await erc721Token.mint({ from: owner });
435 | const tokenId = returnData.logs[0].args['1'].toString();
436 |
437 | const res = await erc721Token.safeTransferFrom(owner, receiver, tokenId);
438 | const log = res.logs.find(element => element.event.match('Transfer'));
439 |
440 | assert.strictEqual(log.args._from, owner);
441 | assert.strictEqual(log.args._to, receiver);
442 | assert.strictEqual(log.args._tokenId.toString(), tokenId);
443 | })
444 |
445 | it("...should log 'Transfer' on transferFrom().", async () => {
446 | const returnData = await erc721Token.mint({ from: owner });
447 | const tokenId = returnData.logs[0].args['1'].toString();
448 |
449 | const res = await erc721Token.transferFrom(owner, receiver, tokenId);
450 | const log = res.logs.find(element => element.event.match('Transfer'));
451 |
452 | assert.strictEqual(log.args._from, owner);
453 | assert.strictEqual(log.args._to, receiver);
454 | assert.strictEqual(log.args._tokenId.toString(), tokenId);
455 | })
456 |
457 | it("...should log 'Approval' on approve().", async () => {
458 | const returnData = await erc721Token.mint({ from: owner });
459 | const tokenId = returnData.logs[0].args['1'].toString();
460 |
461 | const res = await erc721Token.approve(operator, tokenId, { from: owner });
462 | const log = res.logs.find(element => element.event.match('Approval'));
463 |
464 | assert.strictEqual(log.args._owner, owner);
465 | assert.strictEqual(log.args._approved, operator);
466 | assert.strictEqual(log.args._tokenId.toString(), tokenId);
467 | })
468 |
469 | it("...should log 'ApprovalForAll' on setApprovalForAll().", async () => {
470 | const res = await erc721Token.setApprovalForAll(operator, true, { from: owner });
471 | const log = res.logs.find(element => element.event.match('ApprovalForAll'));
472 |
473 | assert.strictEqual(log.args._owner, owner);
474 | assert.strictEqual(log.args._operator, operator);
475 | assert.strictEqual(log.args._approved, true);
476 | })
477 | });
478 |
--------------------------------------------------------------------------------
/erc721/truffle.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Use this file to configure your truffle project. It's seeded with some
3 | * common settings for different networks and features like migrations,
4 | * compilation and testing. Uncomment the ones you need or modify
5 | * them to suit your project as necessary.
6 | *
7 | * More information about configuration can be found at:
8 | *
9 | * truffleframework.com/docs/advanced/configuration
10 | *
11 | * To deploy via Infura you'll need a wallet provider (like truffle-hdwallet-provider)
12 | * to sign your transactions before they're sent to a remote public node. Infura API
13 | * keys are available for free at: infura.io/register
14 | *
15 | * You'll also need a mnemonic - the twelve word phrase the wallet uses to generate
16 | * public/private key pairs. If you're publishing your code to GitHub make sure you load this
17 | * phrase from a file you've .gitignored so it doesn't accidentally become public.
18 | *
19 | */
20 |
21 | module.exports = {
22 | networks: {
23 | ganache: {
24 | host: '127.0.0.1',
25 | port: 7545,
26 | network_id: '*', // match any network id
27 | from: '0x954e72fdc51Cf919203067406fB337Ed4bDC8CdA', // account address from which to deploy
28 | gas: 4000000,
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/erc777/.gitignore:
--------------------------------------------------------------------------------
1 | ./build
2 |
--------------------------------------------------------------------------------
/erc777/README.md:
--------------------------------------------------------------------------------
1 | # ERC777
2 |
3 | The [ERC777 Token Standard](https://eips.ethereum.org/EIPS/eip-777) improves upon the popular [ERC20](https://contracts.vyperhub.io/contracts/erc20) standard.
4 |
5 | Its most defining feature is the use of the new [ERC1820](http://eips.ethereum.org/EIPS/eip-1820) interface standard which it uses in such a way, that each time tokens are sent the ERC777 contract does two things:
6 | 1. It checks whether the sender of the transaction is a contract and whether that contract implements a `tokensToSend(_operator, _from, _to, _amount, _data, _operatorData)` function.
7 | 2. It checks whether the receiver of the transaction is a contract and whether that contract implements a `tokensToSend(_operator, _from, _to, _amount, _data, _operatorData)` function.
8 |
9 | If the functions exist, then the code inside of both functions is executed.
10 | The exciting part is, that there are no restrictions on what the code inside of the two functions looks like or what it does.
11 |
12 | ## Tests
13 |
14 | ### Run local tests
15 |
16 | ```bash
17 | $ truffle test --network ganache
18 | ```
19 | ### More tests
20 |
21 | Further tests for this implementation may be found [here](https://github.com/0xjac/ERC777/tree/master/test).
22 |
--------------------------------------------------------------------------------
/erc777/contracts/Migrations.sol:
--------------------------------------------------------------------------------
1 | pragma solidity >=0.4.21 <0.6.0;
2 |
3 | contract Migrations {
4 | address public owner;
5 | uint public last_completed_migration;
6 |
7 | constructor() public {
8 | owner = msg.sender;
9 | }
10 |
11 | modifier restricted() {
12 | if (msg.sender == owner) _;
13 | }
14 |
15 | function setCompleted(uint completed) public restricted {
16 | last_completed_migration = completed;
17 | }
18 |
19 | function upgrade(address new_address) public restricted {
20 | Migrations upgraded = Migrations(new_address);
21 | upgraded.setCompleted(last_completed_migration);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/erc777/contracts/erc777.vy:
--------------------------------------------------------------------------------
1 | # Author: Sören Steiger, github.com/ssteiger
2 | # License: MIT
3 |
4 | # ERC777 Token Standard
5 | # https://eips.ethereum.org/EIPS/eip-777
6 |
7 |
8 | # Interface for ERC1820 registry contract
9 | # https://eips.ethereum.org/EIPS/eip-1820
10 | contract ERC1820Registry:
11 | def setInterfaceImplementer(
12 | _addr: address,
13 | _interfaceHash: bytes32,
14 | _implementer: address,
15 | ): modifying
16 | def getInterfaceImplementer(
17 | _addr: address,
18 | _interfaceHash: bytes32,
19 | ) -> address: modifying
20 |
21 | # Interface for ERC777Tokens sender contracts
22 | contract ERC777TokensSender:
23 | def tokensToSend(
24 | _operator: address,
25 | _from: address,
26 | _to: address,
27 | _amount: uint256,
28 | _data: bytes[256],
29 | _operatorData: bytes[256]
30 | ): modifying
31 |
32 | # Interface for ERC777Tokens recipient contracts
33 | contract ERC777TokensRecipient:
34 | def tokensReceived(
35 | _operator: address,
36 | _from: address,
37 | _to: address,
38 | _amount: uint256,
39 | _data: bytes[256],
40 | _operatorData: bytes[256]
41 | ): modifying
42 |
43 |
44 | Sent: event({
45 | _operator: indexed(address), # Address which triggered the send.
46 | _from: indexed(address), # Token holder.
47 | _to: indexed(address), # Token recipient.
48 | _amount: uint256, # Number of tokens to send.
49 | _data: bytes[256], # Information provided by the token holder.
50 | _operatorData: bytes[256] # Information provided by the operator.
51 | })
52 |
53 | Minted: event({
54 | _operator: indexed(address), # Address which triggered the mint.
55 | _to: indexed(address), # Recipient of the tokens.
56 | _amount: uint256, # Number of tokens minted.
57 | _data: bytes[256], # Information provided for the recipient.
58 | _operatorData: bytes[256] # Information provided by the operator.
59 | })
60 |
61 | Burned: event({
62 | _operator: indexed(address), # Address which triggered the burn.
63 | _from: indexed(address), # Token holder whose tokens are burned.
64 | _amount: uint256, # Token holder whose tokens are burned.
65 | _data: bytes[256], # Information provided by the token holder.
66 | _operatorData: bytes[256] # Information provided by the operator.
67 | })
68 |
69 | AuthorizedOperator: event({
70 | _operator: indexed(address), # Address which became an operator of tokenHolder.
71 | _holder: indexed(address) # Address of a token holder which authorized the operator address as an operator.
72 | })
73 |
74 | RevokedOperator: event({
75 | _operator: indexed(address), # Address which was revoked as an operator of tokenHolder.
76 | _holder: indexed(address) # Address of a token holder which revoked the operator address as an operator.
77 | })
78 |
79 |
80 | erc1820Registry: ERC1820Registry
81 | erc1820RegistryAddress: constant(address) = 0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24
82 |
83 | # TODO: decide which size to use
84 | NO_OF_DEFAULT_OPERATORS: constant(uint256) = 5
85 | MAX_LENGTH_NAME: constant(uint256) = 64
86 | MAX_LENGTH_SYMBOL: constant(uint256) = 32
87 |
88 | name: public(string[MAX_LENGTH_NAME])
89 | symbol: public(string[MAX_LENGTH_SYMBOL])
90 |
91 | totalSupply: public(uint256)
92 | granularity: public(uint256)
93 |
94 | balanceOf: public(map(address, uint256))
95 |
96 | defaultOperatorsList: address[NO_OF_DEFAULT_OPERATORS]
97 | defaultOperatorsMap: map(address, bool)
98 |
99 | operators: map(address, map(address, bool))
100 |
101 |
102 | @public
103 | def __init__(
104 | _name: string[MAX_LENGTH_NAME],
105 | _symbol: string[MAX_LENGTH_SYMBOL],
106 | _totalSupply: uint256,
107 | _granularity: uint256,
108 | _defaultOperators: address[NO_OF_DEFAULT_OPERATORS]
109 | ):
110 | self.name = _name
111 | self.symbol = _symbol
112 | self.totalSupply = _totalSupply
113 | # The granularity value MUST be greater than or equal to 1
114 | assert _granularity >= 1
115 | self.granularity = _granularity
116 | self.defaultOperatorsList = _defaultOperators
117 | for i in range(NO_OF_DEFAULT_OPERATORS):
118 | assert _defaultOperators[i] != ZERO_ADDRESS
119 | self.defaultOperatorsMap[_defaultOperators[i]] = True
120 | self.erc1820Registry = ERC1820Registry(erc1820RegistryAddress)
121 | self.erc1820Registry.setInterfaceImplementer(self, keccak256("ERC777Token"), self)
122 |
123 |
124 | @private
125 | def _checkForERC777TokensInterface_Sender(
126 | _operator: address,
127 | _from: address,
128 | _to: address,
129 | _amount: uint256,
130 | _data: bytes[256]="",
131 | _operatorData: bytes[256]=""
132 | ):
133 | implementer: address = self.erc1820Registry.getInterfaceImplementer(_from, keccak256("ERC777TokensSender"))
134 | if implementer != ZERO_ADDRESS:
135 | ERC777TokensSender(_from).tokensToSend(_operator, _from, _to, _amount, _data, _operatorData)
136 |
137 |
138 | @private
139 | def _checkForERC777TokensInterface_Recipient(
140 | _operator: address,
141 | _from: address,
142 | _to: address,
143 | _amount: uint256,
144 | _data: bytes[256]="",
145 | _operatorData: bytes[256]=""
146 | ):
147 | implementer: address = self.erc1820Registry.getInterfaceImplementer(_to, keccak256("ERC777TokensRecipient"))
148 | if implementer != ZERO_ADDRESS:
149 | ERC777TokensRecipient(_to).tokensReceived(_operator, _from, _to, _amount, _data, _operatorData)
150 |
151 |
152 | @private
153 | def _transferFunds(
154 | _operator: address,
155 | _from: address,
156 | _to: address,
157 | _amount: uint256,
158 | _data: bytes[256]="",
159 | _operatorData: bytes[256]=""
160 | ):
161 | # any minting, sending or burning of tokens MUST be a multiple of the granularity value.
162 | assert _amount % self.granularity == 0
163 |
164 | # check for 'tokensToSend' hook
165 | if _from.is_contract:
166 | self._checkForERC777TokensInterface_Sender(_operator, _from, _to, _amount, _data, _operatorData)
167 |
168 | self.balanceOf[_from] -= _amount
169 | self.balanceOf[_to] += _amount
170 |
171 | # check for 'tokensReceived' hook
172 | # but only if transfer is not a burn
173 | if _to != ZERO_ADDRESS:
174 | if _to.is_contract:
175 | self._checkForERC777TokensInterface_Recipient(_operator, _from, _to, _amount, _data, _operatorData)
176 |
177 |
178 | @public
179 | @constant
180 | def defaultOperators() -> address[NO_OF_DEFAULT_OPERATORS]:
181 | return self.defaultOperatorsList
182 |
183 |
184 | @public
185 | @constant
186 | def isOperatorFor(_operator: address, _holder: address) -> bool:
187 | return (self.operators[_holder])[_operator] or self.defaultOperatorsMap[_operator] or _operator == _holder
188 |
189 |
190 | @public
191 | def authorizeOperator(_operator: address):
192 | (self.operators[msg.sender])[_operator] = True
193 | log.AuthorizedOperator(_operator, msg.sender)
194 |
195 |
196 | @public
197 | def revokeOperator(_operator: address):
198 | # MUST revert if it is called to revoke the holder as an operator for itself
199 | assert _operator != msg.sender
200 | (self.operators[msg.sender])[_operator] = False
201 | log.RevokedOperator(_operator, msg.sender)
202 |
203 |
204 | @public
205 | def send(_to: address, _amount: uint256, _data: bytes[256]=""):
206 | assert _to != ZERO_ADDRESS
207 | operatorData: bytes[256]=""
208 | self._transferFunds(msg.sender, msg.sender, _to, _amount, _data, operatorData)
209 | log.Sent(msg.sender, msg.sender, _to, _amount, _data, operatorData)
210 |
211 |
212 | @public
213 | def operatorSend(
214 | _from: address,
215 | _to: address,
216 | _amount: uint256,
217 | _data: bytes[256]="",
218 | _operatorData: bytes[256]=""
219 | ):
220 | assert _to != ZERO_ADDRESS
221 | assert self.isOperatorFor(msg.sender, _from)
222 | self._transferFunds(msg.sender, _from, _to, _amount, _data, _operatorData)
223 | log.Sent(msg.sender, _from, _to, _amount, _data, _operatorData)
224 |
225 |
226 | @public
227 | def burn(_amount: uint256, _data: bytes[256]=""):
228 | operatorData: bytes[256]=""
229 | self._transferFunds(msg.sender, msg.sender, ZERO_ADDRESS, _amount, _data, operatorData)
230 | self.totalSupply -= _amount
231 | log.Burned(msg.sender, msg.sender, _amount, _data, operatorData)
232 |
233 |
234 | @public
235 | def operatorBurn(
236 | _from: address,
237 | _amount: uint256,
238 | _data: bytes[256]="",
239 | _operatorData: bytes[256]=""
240 | ):
241 | # _from: Token holder whose tokens will be burned (or 0x0 to set from to msg.sender).
242 | fromAddress: address
243 | if _from == ZERO_ADDRESS:
244 | fromAddress = msg.sender
245 | else:
246 | fromAddress = _from
247 | assert self.isOperatorFor(msg.sender, fromAddress)
248 | self._transferFunds(msg.sender, fromAddress, ZERO_ADDRESS, _amount, _data, _operatorData)
249 | self.totalSupply -= _amount
250 | log.Burned(msg.sender, fromAddress, _amount, _data, _operatorData)
251 |
252 |
253 | # NOTE: ERC777 intentionally does not define specific functions to mint tokens.
254 | @public
255 | def mint(
256 | _to: address,
257 | _amount: uint256,
258 | _operatorData: bytes[256]=""
259 | ):
260 | assert _to != ZERO_ADDRESS
261 | # any minting, sending or burning of tokens MUST be a multiple of the granularity value.
262 | assert _amount % self.granularity == 0
263 | # only operators are allowed to mint
264 | assert self.defaultOperatorsMap[msg.sender]
265 | self.balanceOf[_to] += _amount
266 | self.totalSupply += _amount
267 | data: bytes[256]=""
268 | if _to.is_contract:
269 | self._checkForERC777TokensInterface_Recipient(msg.sender, ZERO_ADDRESS, _to, _amount, data, _operatorData)
270 | log.Minted(msg.sender, _to, _amount, data, _operatorData)
271 |
--------------------------------------------------------------------------------
/erc777/contracts/erc777TokenReceiver.vy:
--------------------------------------------------------------------------------
1 | # Author: Sören Steiger, github.com/ssteiger
2 | # License: MIT
3 |
4 | # ERC777 Token Receiver
5 | # https://eips.ethereum.org/EIPS/eip-777
6 |
7 | # Interface for ERC1820 registry contract
8 | # https://eips.ethereum.org/EIPS/eip-1820
9 | contract ERC1820Registry:
10 | def setInterfaceImplementer(
11 | _addr: address,
12 | _interfaceHash: bytes32,
13 | _implementer: address,
14 | ): modifying
15 |
16 |
17 | TokensReceived: event({
18 | _operator: indexed(address),
19 | _from: indexed(address),
20 | _to: indexed(address),
21 | _amount: uint256,
22 | _data: bytes[256],
23 | _operatorData: bytes[256]
24 | })
25 |
26 |
27 | erc1820Registry: ERC1820Registry
28 | erc1820RegistryAddress: constant(address) = 0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24
29 |
30 |
31 | @public
32 | def __init__():
33 | self.erc1820Registry = ERC1820Registry(erc1820RegistryAddress)
34 | self.erc1820Registry.setInterfaceImplementer(self, keccak256("ERC777TokensRecipient"), self)
35 |
36 |
37 | @public
38 | def tokensReceived(
39 | _operator: address,
40 | _from: address,
41 | _to: address,
42 | _amount: uint256,
43 | _data: bytes[256],
44 | _operatorData: bytes[256]
45 | ):
46 | log.TokensReceived(_operator, _from, _to, _amount, _data, _operatorData)
47 |
--------------------------------------------------------------------------------
/erc777/contracts/erc777TokenSender.vy:
--------------------------------------------------------------------------------
1 | # Author: Sören Steiger, github.com/ssteiger
2 | # License: MIT
3 |
4 | # ERC777 Token Sender
5 | # https://eips.ethereum.org/EIPS/eip-777
6 |
7 | # Interface for ERC1820 registry contract
8 | # https://eips.ethereum.org/EIPS/eip-1820
9 | contract ERC1820Registry:
10 | def setInterfaceImplementer(
11 | _addr: address,
12 | _interfaceHash: bytes32,
13 | _implementer: address,
14 | ): modifying
15 |
16 |
17 | TokensSent: event({
18 | _operator: indexed(address),
19 | _from: indexed(address),
20 | _to: indexed(address),
21 | _amount: uint256,
22 | _data: bytes[256],
23 | _operatorData: bytes[256]
24 | })
25 |
26 |
27 | erc1820Registry: ERC1820Registry
28 | erc1820RegistryAddress: constant(address) = 0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24
29 |
30 |
31 | @public
32 | def __init__():
33 | self.erc1820Registry = ERC1820Registry(erc1820RegistryAddress)
34 | self.erc1820Registry.setInterfaceImplementer(self, keccak256("ERC777TokensSender"), self)
35 |
36 |
37 | @public
38 | def tokensToSend(
39 | _operator: address,
40 | _from: address,
41 | _to: address,
42 | _amount: uint256,
43 | _data: bytes[256],
44 | _operatorData: bytes[256]
45 | ):
46 | log.TokensSent(_operator, _from, _to, _amount, _data, _operatorData)
47 |
--------------------------------------------------------------------------------
/erc777/contracts/helpers/ERC1820Registry.sol:
--------------------------------------------------------------------------------
1 | /* ERC1820 Pseudo-introspection Registry Contract
2 | * This standard defines a universal registry smart contract where any address (contract or regular account) can
3 | * register which interface it supports and which smart contract is responsible for its implementation.
4 | *
5 | * Written in 2019 by Jordi Baylina and Jacques Dafflon
6 | *
7 | * To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to
8 | * this software to the public domain worldwide. This software is distributed without any warranty.
9 | *
10 | * You should have received a copy of the CC0 Public Domain Dedication along with this software. If not, see
11 | * .
12 | *
13 | * ███████╗██████╗ ██████╗ ██╗ █████╗ ██████╗ ██████╗
14 | * ██╔════╝██╔══██╗██╔════╝███║██╔══██╗╚════██╗██╔═████╗
15 | * █████╗ ██████╔╝██║ ╚██║╚█████╔╝ █████╔╝██║██╔██║
16 | * ██╔══╝ ██╔══██╗██║ ██║██╔══██╗██╔═══╝ ████╔╝██║
17 | * ███████╗██║ ██║╚██████╗ ██║╚█████╔╝███████╗╚██████╔╝
18 | * ╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚════╝ ╚══════╝ ╚═════╝
19 | *
20 | * ██████╗ ███████╗ ██████╗ ██╗███████╗████████╗██████╗ ██╗ ██╗
21 | * ██╔══██╗██╔════╝██╔════╝ ██║██╔════╝╚══██╔══╝██╔══██╗╚██╗ ██╔╝
22 | * ██████╔╝█████╗ ██║ ███╗██║███████╗ ██║ ██████╔╝ ╚████╔╝
23 | * ██╔══██╗██╔══╝ ██║ ██║██║╚════██║ ██║ ██╔══██╗ ╚██╔╝
24 | * ██║ ██║███████╗╚██████╔╝██║███████║ ██║ ██║ ██║ ██║
25 | * ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝
26 | *
27 | */
28 | pragma solidity 0.5.8;
29 | // IV is value needed to have a vanity address starting with '0x1820'.
30 | // IV: 53759
31 |
32 | /// @dev The interface a contract MUST implement if it is the implementer of
33 | /// some (other) interface for any address other than itself.
34 | interface ERC1820ImplementerInterface {
35 | /// @notice Indicates whether the contract implements the interface 'interfaceHash' for the address 'addr' or not.
36 | /// @param interfaceHash keccak256 hash of the name of the interface
37 | /// @param addr Address for which the contract will implement the interface
38 | /// @return ERC1820_ACCEPT_MAGIC only if the contract implements 'interfaceHash' for the address 'addr'.
39 | function canImplementInterfaceForAddress(bytes32 interfaceHash, address addr) external view returns(bytes32);
40 | }
41 |
42 |
43 | /// @title ERC1820 Pseudo-introspection Registry Contract
44 | /// @author Jordi Baylina and Jacques Dafflon
45 | /// @notice This contract is the official implementation of the ERC1820 Registry.
46 | /// @notice For more details, see https://eips.ethereum.org/EIPS/eip-1820
47 | contract ERC1820Registry {
48 | /// @notice ERC165 Invalid ID.
49 | bytes4 constant internal INVALID_ID = 0xffffffff;
50 | /// @notice Method ID for the ERC165 supportsInterface method (= `bytes4(keccak256('supportsInterface(bytes4)'))`).
51 | bytes4 constant internal ERC165ID = 0x01ffc9a7;
52 | /// @notice Magic value which is returned if a contract implements an interface on behalf of some other address.
53 | bytes32 constant internal ERC1820_ACCEPT_MAGIC = keccak256(abi.encodePacked("ERC1820_ACCEPT_MAGIC"));
54 |
55 | /// @notice mapping from addresses and interface hashes to their implementers.
56 | mapping(address => mapping(bytes32 => address)) internal interfaces;
57 | /// @notice mapping from addresses to their manager.
58 | mapping(address => address) internal managers;
59 | /// @notice flag for each address and erc165 interface to indicate if it is cached.
60 | mapping(address => mapping(bytes4 => bool)) internal erc165Cached;
61 |
62 | /// @notice Indicates a contract is the 'implementer' of 'interfaceHash' for 'addr'.
63 | event InterfaceImplementerSet(address indexed addr, bytes32 indexed interfaceHash, address indexed implementer);
64 | /// @notice Indicates 'newManager' is the address of the new manager for 'addr'.
65 | event ManagerChanged(address indexed addr, address indexed newManager);
66 |
67 | /// @notice Query if an address implements an interface and through which contract.
68 | /// @param _addr Address being queried for the implementer of an interface.
69 | /// (If '_addr' is the zero address then 'msg.sender' is assumed.)
70 | /// @param _interfaceHash Keccak256 hash of the name of the interface as a string.
71 | /// E.g., 'web3.utils.keccak256("ERC777TokensRecipient")' for the 'ERC777TokensRecipient' interface.
72 | /// @return The address of the contract which implements the interface '_interfaceHash' for '_addr'
73 | /// or '0' if '_addr' did not register an implementer for this interface.
74 | function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) external view returns (address) {
75 | address addr = _addr == address(0) ? msg.sender : _addr;
76 | if (isERC165Interface(_interfaceHash)) {
77 | bytes4 erc165InterfaceHash = bytes4(_interfaceHash);
78 | return implementsERC165Interface(addr, erc165InterfaceHash) ? addr : address(0);
79 | }
80 | return interfaces[addr][_interfaceHash];
81 | }
82 |
83 | /// @notice Sets the contract which implements a specific interface for an address.
84 | /// Only the manager defined for that address can set it.
85 | /// (Each address is the manager for itself until it sets a new manager.)
86 | /// @param _addr Address for which to set the interface.
87 | /// (If '_addr' is the zero address then 'msg.sender' is assumed.)
88 | /// @param _interfaceHash Keccak256 hash of the name of the interface as a string.
89 | /// E.g., 'web3.utils.keccak256("ERC777TokensRecipient")' for the 'ERC777TokensRecipient' interface.
90 | /// @param _implementer Contract address implementing '_interfaceHash' for '_addr'.
91 | function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external {
92 | address addr = _addr == address(0) ? msg.sender : _addr;
93 | require(getManager(addr) == msg.sender, "Not the manager");
94 |
95 | require(!isERC165Interface(_interfaceHash), "Must not be an ERC165 hash");
96 | if (_implementer != address(0) && _implementer != msg.sender) {
97 | require(
98 | ERC1820ImplementerInterface(_implementer)
99 | .canImplementInterfaceForAddress(_interfaceHash, addr) == ERC1820_ACCEPT_MAGIC,
100 | "Does not implement the interface"
101 | );
102 | }
103 | interfaces[addr][_interfaceHash] = _implementer;
104 | emit InterfaceImplementerSet(addr, _interfaceHash, _implementer);
105 | }
106 |
107 | /// @notice Sets '_newManager' as manager for '_addr'.
108 | /// The new manager will be able to call 'setInterfaceImplementer' for '_addr'.
109 | /// @param _addr Address for which to set the new manager.
110 | /// @param _newManager Address of the new manager for 'addr'. (Pass '0x0' to reset the manager to '_addr'.)
111 | function setManager(address _addr, address _newManager) external {
112 | require(getManager(_addr) == msg.sender, "Not the manager");
113 | managers[_addr] = _newManager == _addr ? address(0) : _newManager;
114 | emit ManagerChanged(_addr, _newManager);
115 | }
116 |
117 | /// @notice Get the manager of an address.
118 | /// @param _addr Address for which to return the manager.
119 | /// @return Address of the manager for a given address.
120 | function getManager(address _addr) public view returns(address) {
121 | // By default the manager of an address is the same address
122 | if (managers[_addr] == address(0)) {
123 | return _addr;
124 | } else {
125 | return managers[_addr];
126 | }
127 | }
128 |
129 | /// @notice Compute the keccak256 hash of an interface given its name.
130 | /// @param _interfaceName Name of the interface.
131 | /// @return The keccak256 hash of an interface name.
132 | function interfaceHash(string calldata _interfaceName) external pure returns(bytes32) {
133 | return keccak256(abi.encodePacked(_interfaceName));
134 | }
135 |
136 | /* --- ERC165 Related Functions --- */
137 | /* --- Developed in collaboration with William Entriken. --- */
138 |
139 | /// @notice Updates the cache with whether the contract implements an ERC165 interface or not.
140 | /// @param _contract Address of the contract for which to update the cache.
141 | /// @param _interfaceId ERC165 interface for which to update the cache.
142 | function updateERC165Cache(address _contract, bytes4 _interfaceId) external {
143 | interfaces[_contract][_interfaceId] = implementsERC165InterfaceNoCache(
144 | _contract, _interfaceId) ? _contract : address(0);
145 | erc165Cached[_contract][_interfaceId] = true;
146 | }
147 |
148 | /// @notice Checks whether a contract implements an ERC165 interface or not.
149 | // If the result is not cached a direct lookup on the contract address is performed.
150 | // If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling
151 | // 'updateERC165Cache' with the contract address.
152 | /// @param _contract Address of the contract to check.
153 | /// @param _interfaceId ERC165 interface to check.
154 | /// @return True if '_contract' implements '_interfaceId', false otherwise.
155 | function implementsERC165Interface(address _contract, bytes4 _interfaceId) public view returns (bool) {
156 | if (!erc165Cached[_contract][_interfaceId]) {
157 | return implementsERC165InterfaceNoCache(_contract, _interfaceId);
158 | }
159 | return interfaces[_contract][_interfaceId] == _contract;
160 | }
161 |
162 | /// @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache.
163 | /// @param _contract Address of the contract to check.
164 | /// @param _interfaceId ERC165 interface to check.
165 | /// @return True if '_contract' implements '_interfaceId', false otherwise.
166 | function implementsERC165InterfaceNoCache(address _contract, bytes4 _interfaceId) public view returns (bool) {
167 | uint256 success;
168 | uint256 result;
169 |
170 | (success, result) = noThrowCall(_contract, ERC165ID);
171 | if (success == 0 || result == 0) {
172 | return false;
173 | }
174 |
175 | (success, result) = noThrowCall(_contract, INVALID_ID);
176 | if (success == 0 || result != 0) {
177 | return false;
178 | }
179 |
180 | (success, result) = noThrowCall(_contract, _interfaceId);
181 | if (success == 1 && result == 1) {
182 | return true;
183 | }
184 | return false;
185 | }
186 |
187 | /// @notice Checks whether the hash is a ERC165 interface (ending with 28 zeroes) or not.
188 | /// @param _interfaceHash The hash to check.
189 | /// @return True if '_interfaceHash' is an ERC165 interface (ending with 28 zeroes), false otherwise.
190 | function isERC165Interface(bytes32 _interfaceHash) internal pure returns (bool) {
191 | return _interfaceHash & 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0;
192 | }
193 |
194 | /// @dev Make a call on a contract without throwing if the function does not exist.
195 | function noThrowCall(address _contract, bytes4 _interfaceId)
196 | internal view returns (uint256 success, uint256 result)
197 | {
198 | bytes4 erc165ID = ERC165ID;
199 |
200 | assembly {
201 | let x := mload(0x40) // Find empty storage location using "free memory pointer"
202 | mstore(x, erc165ID) // Place signature at beginning of empty storage
203 | mstore(add(x, 0x04), _interfaceId) // Place first argument directly next to signature
204 |
205 | success := staticcall(
206 | 30000, // 30k gas
207 | _contract, // To addr
208 | x, // Inputs are stored at location x
209 | 0x24, // Inputs are 36 (4 + 32) bytes long
210 | x, // Store output over input (saves space)
211 | 0x20 // Outputs are 32 bytes long
212 | )
213 |
214 | result := mload(x) // Load the result
215 | }
216 | }
217 | }
218 |
--------------------------------------------------------------------------------
/erc777/migrations/1_initial_migration.js:
--------------------------------------------------------------------------------
1 | const checkForERC182Registry = require('./helpers/checkForERC182Registry.js');
2 |
3 | const Migrations = artifacts.require("./Migrations.sol");
4 |
5 | module.exports = async function(deployer) {
6 | await checkForERC182Registry(web3, deployer);
7 |
8 | deployer.deploy(Migrations);
9 | };
10 |
--------------------------------------------------------------------------------
/erc777/migrations/2_deploy_contracts.js:
--------------------------------------------------------------------------------
1 | const erc777 = artifacts.require('erc777');
2 |
3 | // NOTE: this address has to match the 'from' address used in 'truffle.js'
4 | const truffleFromAddress = '0x954e72fdc51Cf919203067406fB337Ed4bDC8CdA';
5 |
6 | const args = {
7 | name: 'My777Token',
8 | symbol: 'MT777',
9 | totalSupply: 100000000,
10 | granularity: 1,
11 | defaultOperators: [
12 | truffleFromAddress,
13 | '0x0000000000000000000000000000000000000001',
14 | '0x0000000000000000000000000000000000000002',
15 | '0x0000000000000000000000000000000000000003',
16 | ],
17 | }
18 |
19 | module.exports = function(deployer) {
20 | deployer.deploy(
21 | erc777,
22 | args.name,
23 | args.symbol,
24 | args.totalSupply,
25 | args.granularity,
26 | args.defaultOperators
27 | );
28 | };
29 |
--------------------------------------------------------------------------------
/erc777/migrations/helpers/checkForERC182Registry.js:
--------------------------------------------------------------------------------
1 | // NOTE: this address has to match the 'from' address used in 'truffle.js'
2 | const truffleFromAddress = '0x954e72fdc51Cf919203067406fB337Ed4bDC8CdA';
3 |
4 | module.exports = async function(web3, deployer) {
5 | // check if erc1820 registry contract exists on network
6 | // http://eips.ethereum.org/EIPS/eip-1820
7 |
8 | console.log('checking if erc1820Registry contract exists on network');
9 |
10 | // check for code at the erc1820Registry address
11 | const code = await web3.eth.getCode('0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24');
12 |
13 | // NOTE: If no contract exists at address, '0x' is returned
14 | if (code == '0x') {
15 | // erc1820Registry does not exist on network
16 | // -> deploy it
17 | console.log('erc1820Registry contract not found -> deloying it now');
18 |
19 | // fund address from which the erc1820Registry contract will be deployed
20 | const fundSize = web3.utils.toWei('0.08', 'ether');
21 | await web3.eth.sendTransaction({
22 | from: truffleFromAddress,
23 | to: '0xa990077c3205cbDf861e17Fa532eeB069cE9fF96',
24 | value: fundSize
25 | });
26 |
27 | // the raw transaction that deployes the erc1820Registry
28 | const rawTx = '0xf90a388085174876e800830c35008080b909e5608060405234801561001057600080fd5b506109c5806100206000396000f3fe608060405234801561001057600080fd5b50600436106100a5576000357c010000000000000000000000000000000000000000000000000000000090048063a41e7d5111610078578063a41e7d51146101d4578063aabbb8ca1461020a578063b705676514610236578063f712f3e814610280576100a5565b806329965a1d146100aa5780633d584063146100e25780635df8122f1461012457806365ba36c114610152575b600080fd5b6100e0600480360360608110156100c057600080fd5b50600160a060020a038135811691602081013591604090910135166102b6565b005b610108600480360360208110156100f857600080fd5b5035600160a060020a0316610570565b60408051600160a060020a039092168252519081900360200190f35b6100e06004803603604081101561013a57600080fd5b50600160a060020a03813581169160200135166105bc565b6101c26004803603602081101561016857600080fd5b81019060208101813564010000000081111561018357600080fd5b82018360208201111561019557600080fd5b803590602001918460018302840111640100000000831117156101b757600080fd5b5090925090506106b3565b60408051918252519081900360200190f35b6100e0600480360360408110156101ea57600080fd5b508035600160a060020a03169060200135600160e060020a0319166106ee565b6101086004803603604081101561022057600080fd5b50600160a060020a038135169060200135610778565b61026c6004803603604081101561024c57600080fd5b508035600160a060020a03169060200135600160e060020a0319166107ef565b604080519115158252519081900360200190f35b61026c6004803603604081101561029657600080fd5b508035600160a060020a03169060200135600160e060020a0319166108aa565b6000600160a060020a038416156102cd57836102cf565b335b9050336102db82610570565b600160a060020a031614610339576040805160e560020a62461bcd02815260206004820152600f60248201527f4e6f7420746865206d616e616765720000000000000000000000000000000000604482015290519081900360640190fd5b6103428361092a565b15610397576040805160e560020a62461bcd02815260206004820152601a60248201527f4d757374206e6f7420626520616e204552433136352068617368000000000000604482015290519081900360640190fd5b600160a060020a038216158015906103b85750600160a060020a0382163314155b156104ff5760405160200180807f455243313832305f4143434550545f4d4147494300000000000000000000000081525060140190506040516020818303038152906040528051906020012082600160a060020a031663249cb3fa85846040518363ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004018083815260200182600160a060020a0316600160a060020a031681526020019250505060206040518083038186803b15801561047e57600080fd5b505afa158015610492573d6000803e3d6000fd5b505050506040513d60208110156104a857600080fd5b5051146104ff576040805160e560020a62461bcd02815260206004820181905260248201527f446f6573206e6f7420696d706c656d656e742074686520696e74657266616365604482015290519081900360640190fd5b600160a060020a03818116600081815260208181526040808320888452909152808220805473ffffffffffffffffffffffffffffffffffffffff19169487169485179055518692917f93baa6efbd2244243bfee6ce4cfdd1d04fc4c0e9a786abd3a41313bd352db15391a450505050565b600160a060020a03818116600090815260016020526040812054909116151561059a5750806105b7565b50600160a060020a03808216600090815260016020526040902054165b919050565b336105c683610570565b600160a060020a031614610624576040805160e560020a62461bcd02815260206004820152600f60248201527f4e6f7420746865206d616e616765720000000000000000000000000000000000604482015290519081900360640190fd5b81600160a060020a031681600160a060020a0316146106435780610646565b60005b600160a060020a03838116600081815260016020526040808220805473ffffffffffffffffffffffffffffffffffffffff19169585169590951790945592519184169290917f605c2dbf762e5f7d60a546d42e7205dcb1b011ebc62a61736a57c9089d3a43509190a35050565b600082826040516020018083838082843780830192505050925050506040516020818303038152906040528051906020012090505b92915050565b6106f882826107ef565b610703576000610705565b815b600160a060020a03928316600081815260208181526040808320600160e060020a031996909616808452958252808320805473ffffffffffffffffffffffffffffffffffffffff19169590971694909417909555908152600284528181209281529190925220805460ff19166001179055565b600080600160a060020a038416156107905783610792565b335b905061079d8361092a565b156107c357826107ad82826108aa565b6107b85760006107ba565b815b925050506106e8565b600160a060020a0390811660009081526020818152604080832086845290915290205416905092915050565b6000808061081d857f01ffc9a70000000000000000000000000000000000000000000000000000000061094c565b909250905081158061082d575080155b1561083d576000925050506106e8565b61084f85600160e060020a031961094c565b909250905081158061086057508015155b15610870576000925050506106e8565b61087a858561094c565b909250905060018214801561088f5750806001145b1561089f576001925050506106e8565b506000949350505050565b600160a060020a0382166000908152600260209081526040808320600160e060020a03198516845290915281205460ff1615156108f2576108eb83836107ef565b90506106e8565b50600160a060020a03808316600081815260208181526040808320600160e060020a0319871684529091529020549091161492915050565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff161590565b6040517f01ffc9a7000000000000000000000000000000000000000000000000000000008082526004820183905260009182919060208160248189617530fa90519096909550935050505056fea165627a7a72305820377f4a2d4301ede9949f163f319021a6e9c687c292a5e2b2c4734c126b524e6c00291ba01820182018201820182018201820182018201820182018201820182018201820a01820182018201820182018201820182018201820182018201820182018201820';
29 |
30 | // deploy erc1820 registry
31 | const erc1820Registry = await web3.eth.sendSignedTransaction(rawTx);
32 |
33 | } else {
34 | console.log('erc1820Registry contract found');
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/erc777/test/README.md:
--------------------------------------------------------------------------------
1 | ### Run local tests
2 |
3 | ```bash
4 | $ truffle test --network ganache
5 | ```
6 |
7 | More tests for this implementation can be found [here](https://github.com/0xjac/ERC777/tree/master/test).
8 |
--------------------------------------------------------------------------------
/erc777/truffle.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Use this file to configure your truffle project. It's seeded with some
3 | * common settings for different networks and features like migrations,
4 | * compilation and testing. Uncomment the ones you need or modify
5 | * them to suit your project as necessary.
6 | *
7 | * More information about configuration can be found at:
8 | *
9 | * truffleframework.com/docs/advanced/configuration
10 | *
11 | * To deploy via Infura you'll need a wallet provider (like truffle-hdwallet-provider)
12 | * to sign your transactions before they're sent to a remote public node. Infura API
13 | * keys are available for free at: infura.io/register
14 | *
15 | * You'll also need a mnemonic - the twelve word phrase the wallet uses to generate
16 | * public/private key pairs. If you're publishing your code to GitHub make sure you load this
17 | * phrase from a file you've .gitignored so it doesn't accidentally become public.
18 | *
19 | */
20 |
21 | module.exports = {
22 | networks: {
23 | ganache: {
24 | host: '127.0.0.1',
25 | port: 7545,
26 | network_id: '*', // match any network id
27 | from: '0x954e72fdc51Cf919203067406fB337Ed4bDC8CdA', // account address from which to deploy
28 | gas: 4000000,
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/linear_optimization_problem_bounty/.gitignore:
--------------------------------------------------------------------------------
1 | ./build
2 |
--------------------------------------------------------------------------------
/linear_optimization_problem_bounty/README.md:
--------------------------------------------------------------------------------
1 | # Linear optimization problem bounty
2 |
3 | This contract implements a bounty for solving linear optimization problems.
4 | An example problem is defined in the contracts `_calculateNewSolution(_x1: uint256, _x2: uint256)` method:
5 |
6 | ```python
7 | assert x1 <= 40
8 | assert x2 <= 35
9 | assert (3 * x1) + (2 * x2) <= 200
10 | assert x1 + x2 <= 120
11 | assert x1 > 0 and x2 > 0
12 | # calculate and return new solution
13 | return (4 * x1) + (6 * x2)
14 | ```
15 |
16 | ### How it works
17 |
18 | Users can submit solutions using the `submitSolution(_x1: uint256, _x2: uint256)` method.
19 | The contract checks the submitted values against the problems constraints and saves/rejects them depending on whether the solution respects all of them.
20 | When the end of the competition is reached, the address that submitted the best solution can call `claimBounty()` to claim the Ether that is locked in the contract.
21 |
22 | ## Run tests
23 | ```bash
24 | $ truffle test --network ganache
25 | ```
26 |
--------------------------------------------------------------------------------
/linear_optimization_problem_bounty/contracts/Migrations.sol:
--------------------------------------------------------------------------------
1 | pragma solidity >=0.4.21 <0.6.0;
2 |
3 | contract Migrations {
4 | address public owner;
5 | uint public last_completed_migration;
6 |
7 | constructor() public {
8 | owner = msg.sender;
9 | }
10 |
11 | modifier restricted() {
12 | if (msg.sender == owner) _;
13 | }
14 |
15 | function setCompleted(uint completed) public restricted {
16 | last_completed_migration = completed;
17 | }
18 |
19 | function upgrade(address new_address) public restricted {
20 | Migrations upgraded = Migrations(new_address);
21 | upgraded.setCompleted(last_completed_migration);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/linear_optimization_problem_bounty/contracts/linear_optimization_problem_bounty.vy:
--------------------------------------------------------------------------------
1 | # Author: Sören Steiger, github.com/ssteiger
2 | # License: MIT
3 |
4 | # EVENTS:
5 | NewSolutionFound: event({_addressOfWinner: indexed(address), _solution: uint256})
6 | BountyTransferred: event({_to: indexed(address), _amount: wei_value})
7 | BountyIncreased: event({_amount: wei_value})
8 | CompetitionTimeExtended: event({_to: uint256})
9 |
10 |
11 | # STATE VARIABLES:
12 | owner: public(address)
13 |
14 | x1: public(uint256)
15 | x2: public(uint256)
16 |
17 | bestSolution: public(uint256)
18 | addressOfWinner: public(address)
19 |
20 | durationInBlocks: public(uint256)
21 | competitionEnd: public(uint256)
22 | claimPeriodeLength: public(uint256)
23 |
24 |
25 | # METHODS:
26 | @public
27 | def __init__(_durationInBlocks: uint256):
28 | self.owner = msg.sender
29 | self.bestSolution = 0
30 | self.durationInBlocks = _durationInBlocks
31 | self.competitionEnd = block.number + _durationInBlocks
32 | self.addressOfWinner = ZERO_ADDRESS
33 | # set claim periode to three days
34 | # assuming an average blocktime of 14 seconds -> 86400/14
35 | self.claimPeriodeLength = 6172
36 |
37 |
38 | @public
39 | @payable
40 | def __default__():
41 | # return any funds sent to the contract address directly
42 | send(msg.sender, msg.value)
43 |
44 |
45 | @private
46 | def _calculateNewSolution(_x1: uint256, _x2: uint256) -> uint256:
47 | # check new parameters against constraints
48 | assert _x1 <= 40
49 | assert _x2 <= 35
50 | assert (3 * _x1) + (2 * _x2) <= 200
51 | assert _x1 + _x2 <= 120
52 | assert _x1 > 0 and _x2 > 0
53 | # calculate and return new solution
54 | return (4 * _x1) + (6 * _x2)
55 |
56 |
57 | @public
58 | def submitSolution(_x1: uint256, _x2: uint256) -> uint256:
59 | newSolution: uint256
60 | newSolution = self._calculateNewSolution(_x1, _x2)
61 | assert newSolution > self.bestSolution
62 | # save the solution and it's values
63 | self.x1 = _x1
64 | self.x2 = _x2
65 | self.bestSolution = newSolution
66 | self.addressOfWinner = msg.sender
67 | log.NewSolutionFound(msg.sender, newSolution)
68 | return newSolution
69 |
70 |
71 | @public
72 | def claimBounty():
73 | assert block.number > self.competitionEnd
74 | if (self.addressOfWinner == ZERO_ADDRESS):
75 | # no solution was found -> extend duration of competition
76 | self.competitionEnd = block.number + self.durationInBlocks
77 | else:
78 | assert block.number < (self.competitionEnd + self.claimPeriodeLength)
79 | assert msg.sender == self.addressOfWinner
80 | send(self.addressOfWinner, self.balance)
81 | # extend duration of competition
82 | self.competitionEnd = block.number + self.durationInBlocks
83 | log.BountyTransferred(self.addressOfWinner, self.balance)
84 |
85 |
86 | @public
87 | @payable
88 | def topUpBounty():
89 | log.BountyIncreased(msg.value)
90 |
91 |
92 | @public
93 | def extendCompetition():
94 | # only if no valid solution has been submitted
95 | assert self.addressOfWinner == ZERO_ADDRESS
96 | assert block.number > (self.competitionEnd + self.claimPeriodeLength)
97 | # extend duration of competition
98 | self.competitionEnd = block.number + self.durationInBlocks
99 | # reset winner address
100 | self.addressOfWinner = ZERO_ADDRESS
101 | log.CompetitionTimeExtended(self.competitionEnd)
102 |
--------------------------------------------------------------------------------
/linear_optimization_problem_bounty/migrations/1_initial_migration.js:
--------------------------------------------------------------------------------
1 | var Migrations = artifacts.require("./Migrations.sol");
2 |
3 | module.exports = function(deployer) {
4 | deployer.deploy(Migrations);
5 | };
6 |
--------------------------------------------------------------------------------
/linear_optimization_problem_bounty/migrations/2_deploy_contracts.js:
--------------------------------------------------------------------------------
1 | var LOPB = artifacts.require("linear_optimization_problem_bounty");
2 |
3 | module.exports = function(deployer) {
4 | const durationInBlocks = 100;
5 | deployer.deploy(durationInBlocks);
6 | };
7 |
--------------------------------------------------------------------------------
/linear_optimization_problem_bounty/test/linear_optimization_problem_bounty.js:
--------------------------------------------------------------------------------
1 | const LOPB = artifacts.require("linear_optimization_problem_bounty");
2 |
3 | const args = {
4 | durationInBlocks: 10,
5 | }
6 |
7 | contract("linear_optimization_problem_bounty", async accounts => {
8 | it("...should set correct initial paramters.", async () => {
9 | const instance = await LOPB.deployed({ ...args });
10 | const owner = await instance.owner();
11 | const bestSolution = await instance.bestSolution();
12 | const durationInBlocks = await instance.durationInBlocks();
13 | const competitionEnd = await instance.competitionEnd();
14 | const addressOfWinner = await instance.addressOfWinner();
15 | const claimPeriodeLength = await instance.claimPeriodeLength();
16 |
17 | const minedBlockNumber = await web3.eth.getBlockNumber();
18 |
19 | assert.equal(
20 | owner,
21 | accounts[0],
22 | "Owner wasn't correctly set."
23 | );
24 | assert.equal(
25 | bestSolution,
26 | 0,
27 | "BestSolution wasn't initialized with 0."
28 | );
29 | assert.equal(
30 | durationInBlocks,
31 | args.durationInBlocks,
32 | "DurationInBlocks wasn't correctly set."
33 | );
34 | assert.equal(
35 | competitionEnd,
36 | minedBlockNumber + durationInBlocks,
37 | "DurationInBlocks wasn't correctly set."
38 | );
39 | assert.equal(
40 | addressOfWinner,
41 | '0x0000000000000000000000000000000000000000',
42 | "AddressOfWinner wasn't correctly initialized with ZERO_ADDRESS."
43 | );
44 | assert.equal(
45 | claimPeriodeLength,
46 | args.durationInBlocks,
47 | "ClaimPeriodeLength wasn't correctly set."
48 | );
49 | });
50 |
51 | it("...should set correct values when submitting new valid solution.", async () => {
52 | const instance = await LOPB.deployed({ ...args });
53 | const x1 = 1;
54 | const x2 = 1;
55 | await instance.submitSolution.call(x1, x2);
56 |
57 | const instance_x1 = await instance.x1();
58 | const instance_x2 = await instance.x2();
59 | const instance_bestSolution = await instance.bestSolution();
60 | const instance_addressOfWinner = await instance.addressOfWinner();
61 |
62 | assert.equal(
63 | instance_x1,
64 | x1,
65 | "x1 value wasn't correctly set."
66 | );
67 | assert.equal(
68 | instance_x2,
69 | x2,
70 | "x2 value wasn't correctly set."
71 | );
72 | // NOTE: update this if the problem in the contract is changed
73 | assert.equal(
74 | instance_bestSolution,
75 | (4 * x1) + (6 * x2),
76 | "BestSolution value wasn't correctly set."
77 | );
78 | assert.equal(
79 | instance_addressOfWinner,
80 | accounts[0],
81 | "AddressOfWinner wasn't correctly set."
82 | );
83 | });
84 |
85 | it("...should increase bounty.", async () => {
86 | const instance = await LOPB.deployed({ ...args });
87 | const contractBalance_BeforeTopUp = web3.eth.getBalance(instance);
88 | const transactionValue_inEth = 1;
89 | const transactionValue_inWei = web3.utils.toWei(transactionValue_inEth);
90 | // top up bounty
91 | await instance.topUpBounty.call({}, { from: account[0], value: transactionValue_inWei });
92 |
93 | const contractBalance_AfterTopUp = web3.eth.getBalance(instance);
94 | assert.equal(
95 | contractBalance_BeforeTopUp,
96 | contractBalance_AfterTopUp - transactionValue_inWei,
97 | "Bounty was not correctly increased."
98 | );
99 | });
100 |
101 | // TODO: this is not complete
102 | // TODO: Hmmm, need to create 'claimPeriodeLength = 6172' blocks
103 | // so that block.number > (self.competitionEnd + self.claimPeriodeLength)
104 | // not sure how to test this...
105 | it("...should claim bounty.", async () => {
106 | const instance = await LOPB.deployed({ ...args });
107 |
108 | const contractBalance_Before = web3.eth.getBalance(instance);
109 | const competitionEnd_Before = await instance.competitionEnd();
110 | const x1 = 1;
111 | const x2 = 1;
112 | const transactionValue_inEth = 1;
113 | const transactionValue_inWei = web3.utils.toWei(transactionValue_inEth);
114 | // top up bounty
115 | await instance.topUpBounty.call({}, { from: account[0], value: transactionValue_inWei });
116 | const contractBalance_After = web3.eth.getBalance(instance);
117 | await instance.claimBounty();
118 |
119 | const competitionEnd_After = await instance.competitionEnd();
120 |
121 | });
122 |
123 | // TODO:
124 | it("...should extend competition.", async () => {
125 | const instance = await LOPB.deployed({ durationInBlocks: 1 });
126 | const durationInBlocks = await instance.durationInBlocks();
127 | const competitionEnd_Before = await instance.competitionEnd();
128 |
129 | // TODO: Hmmm, need to create 'claimPeriodeLength = 6172' blocks
130 | // so that block.number > (self.competitionEnd + self.claimPeriodeLength)
131 | // not sure how to test this...
132 |
133 | /*
134 | // create dummy block
135 |
136 | const result = await web3.eth.sendTransaction({
137 | from: account[0],
138 | to: '0x0000000000000000000000000000000000000000',
139 | value: 1, // in wei
140 | });
141 | */
142 | });
143 | });
144 |
--------------------------------------------------------------------------------
/linear_optimization_problem_bounty/truffle.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | networks: {
3 | ganache: {
4 | host: '127.0.0.1',
5 | port: 7545,
6 | network_id: '*', // match any network id
7 | from: '0x954e72fdc51Cf919203067406fB337Ed4bDC8CdA', // account address from which to deploy
8 | gas: 4000000,
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/vyperStorage/.gitignore:
--------------------------------------------------------------------------------
1 | ./build
2 |
--------------------------------------------------------------------------------
/vyperStorage/README.md:
--------------------------------------------------------------------------------
1 | # Vyper Storage
2 |
3 | ## Run tests
4 | ```bash
5 | $ truffle test --network ganache
6 | ```
7 |
--------------------------------------------------------------------------------
/vyperStorage/contracts/Migrations.sol:
--------------------------------------------------------------------------------
1 | pragma solidity >=0.4.21 <0.6.0;
2 |
3 | contract Migrations {
4 | address public owner;
5 | uint public last_completed_migration;
6 |
7 | constructor() public {
8 | owner = msg.sender;
9 | }
10 |
11 | modifier restricted() {
12 | if (msg.sender == owner) _;
13 | }
14 |
15 | function setCompleted(uint completed) public restricted {
16 | last_completed_migration = completed;
17 | }
18 |
19 | function upgrade(address new_address) public restricted {
20 | Migrations upgraded = Migrations(new_address);
21 | upgraded.setCompleted(last_completed_migration);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/vyperStorage/contracts/vyperStorage.vy:
--------------------------------------------------------------------------------
1 | stored_data: public(uint256)
2 |
3 | @public
4 | def __init__():
5 | self.stored_data = 0
6 |
7 | @public
8 | def set(new_value: uint256):
9 | self.stored_data = new_value
10 |
--------------------------------------------------------------------------------
/vyperStorage/migrations/1_initial_migration.js:
--------------------------------------------------------------------------------
1 | var Migrations = artifacts.require("./Migrations.sol");
2 |
3 | module.exports = function(deployer) {
4 | deployer.deploy(Migrations);
5 | };
6 |
--------------------------------------------------------------------------------
/vyperStorage/migrations/2_deploy_contracts.js:
--------------------------------------------------------------------------------
1 | var VyperStorage = artifacts.require("VyperStorage");
2 |
3 | module.exports = function(deployer) {
4 | deployer.deploy(VyperStorage);
5 | };
6 |
--------------------------------------------------------------------------------
/vyperStorage/test/vyperStorage.js:
--------------------------------------------------------------------------------
1 | const vyperStorage = artifacts.require('vyperStorage');
2 |
3 | contract('vyperStorage', () => {
4 | it('...should initialize storage with 0.', async () => {
5 | const storage = await vyperStorage.deployed();
6 |
7 | const storedData = await storage.stored_data();
8 |
9 | assert.equal(
10 | storedData,
11 | 0,
12 | 'The value 89 was not stored.'
13 | );
14 | });
15 |
16 | it('...should store the value 89.', async () => {
17 | const storage = await vyperStorage.deployed();
18 |
19 | await storage.set(89);
20 |
21 | const storedData = await storage.stored_data();
22 |
23 | assert.equal(
24 | storedData,
25 | 89,
26 | 'The value 89 was not stored.'
27 | );
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/vyperStorage/truffle.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Use this file to configure your truffle project. It's seeded with some
3 | * common settings for different networks and features like migrations,
4 | * compilation and testing. Uncomment the ones you need or modify
5 | * them to suit your project as necessary.
6 | *
7 | * More information about configuration can be found at:
8 | *
9 | * truffleframework.com/docs/advanced/configuration
10 | *
11 | * To deploy via Infura you'll need a wallet provider (like truffle-hdwallet-provider)
12 | * to sign your transactions before they're sent to a remote public node. Infura API
13 | * keys are available for free at: infura.io/register
14 | *
15 | * You'll also need a mnemonic - the twelve word phrase the wallet uses to generate
16 | * public/private key pairs. If you're publishing your code to GitHub make sure you load this
17 | * phrase from a file you've .gitignored so it doesn't accidentally become public.
18 | *
19 | */
20 |
21 | module.exports = {
22 | networks: {
23 | ganache: {
24 | host: '127.0.0.1',
25 | port: 7545,
26 | network_id: '*', // match any network id
27 | from: '0x954e72fdc51Cf919203067406fB337Ed4bDC8CdA', // account address from which to deploy
28 | gas: 4000000,
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/wallet/.gitignore:
--------------------------------------------------------------------------------
1 | ./build
2 |
--------------------------------------------------------------------------------
/wallet/README.md:
--------------------------------------------------------------------------------
1 | # Wallet
2 |
3 | This wallet contract is capable of receiving, holding and sending Ether as well as tokens that comply with one
4 | of the following standards:
5 |
6 | * [ERC20](https://eips.ethereum.org/EIPS/eip-20)
7 | * [ERC721](https://eips.ethereum.org/EIPS/eip-721)
8 | * [ERC777](https://eips.ethereum.org/EIPS/eip-777)
9 |
10 | ## Run tests
11 |
12 | ```bash
13 | $ truffle test --network ganache
14 | ```
15 |
16 | The `wallet` contract accesses the `ERC1820Registry` contract in its constructor.
17 | It is therefore necessary that the `ERC1820Registry` contract exists on the (test) network to where the `wallet` contract gets deployed.
18 | In `migrations/1_initial_migration.js` a check is performed to determine if the `ERC1820Registry` contract exists - if it doesn't it is deployed.
19 |
--------------------------------------------------------------------------------
/wallet/contracts/Migrations.sol:
--------------------------------------------------------------------------------
1 | pragma solidity >=0.4.21 <0.6.0;
2 |
3 | contract Migrations {
4 | address public owner;
5 | uint public last_completed_migration;
6 |
7 | constructor() public {
8 | owner = msg.sender;
9 | }
10 |
11 | modifier restricted() {
12 | if (msg.sender == owner) _;
13 | }
14 |
15 | function setCompleted(uint completed) public restricted {
16 | last_completed_migration = completed;
17 | }
18 |
19 | function upgrade(address new_address) public restricted {
20 | Migrations upgraded = Migrations(new_address);
21 | upgraded.setCompleted(last_completed_migration);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/wallet/contracts/helpers/README.md:
--------------------------------------------------------------------------------
1 | NOTE: The contracts in this folder are used for testing purposes.
2 |
--------------------------------------------------------------------------------
/wallet/contracts/helpers/erc20.vy:
--------------------------------------------------------------------------------
1 | # Author: Sören Steiger, github.com/ssteiger
2 | # License: MIT
3 |
4 | # ERC20 Token Standard
5 | # https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
6 |
7 |
8 | from vyper.interfaces import ERC20
9 |
10 | implements: ERC20
11 |
12 | # EVENTS:
13 |
14 | # ----- Transfer -----
15 | # MUST trigger when tokens are transferred, including zero value transfers.
16 | # A token contract which creates new tokens SHOULD trigger a Transfer event
17 | # with the _from address set to 0x0 when tokens are created.
18 | Transfer: event({_from: indexed(address), _to: indexed(address), _value: uint256})
19 |
20 | # ----- Approval -----
21 | # MUST trigger on any successful call to approve(address _spender, uint256 _value).
22 | Approval: event({_owner: indexed(address), _spender: indexed(address), _value: uint256})
23 |
24 |
25 | # STATE VARIABLES:
26 | # values which are permanently stored in contract storage
27 |
28 | # ----- name -----
29 | # Returns the name of the token - e.g. "MyToken".
30 | # OPTIONAL - This method can be used to improve usability, but interfaces and
31 | # other contracts MUST NOT expect these values to be present.
32 | name: public(string[64]) # TODO: is this an acceptable size?
33 |
34 | # ----- symbol -----
35 | # Returns the symbol of the token. E.g. "HIX".
36 | # OPTIONAL - This method can be used to improve usability, but interfaces and
37 | # other contracts MUST NOT expect these values to be present.
38 | symbol: public(string[32]) # TODO: is this an acceptable size?
39 |
40 | # ----- decimals -----
41 | # Returns the number of decimals the token uses - e.g. 8, means to divide
42 | # the token amount by 100000000 to get its user representation.
43 | # OPTIONAL - This method can be used to improve usability, but interfaces and
44 | # other contracts MUST NOT expect these values to be present.
45 | decimals: public(uint256)
46 |
47 | # ----- totalSupply -----
48 | # Returns the total token supply.
49 | totalSupply: public(uint256)
50 |
51 | # mappings
52 | balanceOf: public(map(address, uint256))
53 | approvedFunds: map(address, map(address, uint256))
54 |
55 |
56 | @public
57 | def __init__(_name: string[64], _symbol: string[32], _decimals: uint256, _totalSupply: uint256):
58 | self.name = _name
59 | self.symbol = _symbol
60 | self.decimals = _decimals
61 | #self.totalSupply = _totalSupply * 10 ** _decimals
62 | self.totalSupply = _totalSupply
63 | # mint all tokens to the contract creator
64 | self.balanceOf[msg.sender] = self.totalSupply
65 | # fire transfer event
66 | log.Transfer(ZERO_ADDRESS, msg.sender, self.totalSupply)
67 |
68 |
69 | # METHODS:
70 |
71 | # NOTES:
72 | # Callers MUST handle false from returns (bool success).
73 | # Callers MUST NOT assume that false is never returned!
74 |
75 |
76 | # ----- balanceOf -----
77 | # Returns the account balance of another account with address _owner.
78 | # See: https://github.com/ethereum/vyper/issues/1241
79 | # And: https://vyper.readthedocs.io/en/v0.1.0-beta.8/types.html?highlight=getter#mappings
80 |
81 |
82 | # ----- transfer -----
83 | # Transfers _value amount of tokens to address _to, and MUST fire the Transfer
84 | # event. The function SHOULD throw if the _from account balance does not have
85 | # enough tokens to spend.
86 |
87 | # NOTE: Transfers of 0 values MUST be treated as normal transfers and fire the
88 | # Transfer event.
89 | @public
90 | def transfer(_to: address, _value: uint256) -> bool:
91 | # NOTE: vyper does not allow unterflows
92 | # so checks for sufficient funds are done implicitly
93 | # see https://github.com/ethereum/vyper/issues/1237#issuecomment-461957413
94 | # substract balance from sender
95 | self.balanceOf[msg.sender] -= _value
96 | # add balance to recipient
97 | self.balanceOf[_to] += _value
98 | # fire transfer event
99 | log.Transfer(msg.sender, _to, _value)
100 | return True
101 |
102 |
103 | # ----- transferFrom -----
104 | # Transfers _value amount of tokens from address _from to address _to,
105 | # and MUST fire the Transfer event.
106 |
107 | # The transferFrom method is used for a withdraw workflow, allowing contracts
108 | # to transfer tokens on your behalf. This can be used for example to allow a
109 | # contract to transfer tokens on your behalf and/or to charge fees in
110 | # sub-currencies. The function SHOULD throw unless the _from account has
111 | # deliberately authorized the sender of the message via some mechanism.
112 |
113 | # NOTE: Transfers of 0 values MUST be treated as normal transfers and fire the
114 | # Transfer event.
115 | @public
116 | def transferFrom(_from: address, _to: address, _value: uint256) -> bool:
117 | # NOTE: vyper does not allow unterflows
118 | # so checks for sufficient funds are done implicitly
119 | # see https://github.com/ethereum/vyper/issues/1237#issuecomment-461957413
120 | # update approved funds
121 | self.approvedFunds[_from][msg.sender] -= _value
122 | # update sender balance
123 | self.balanceOf[_from] -= _value
124 | # update recipient balance
125 | self.balanceOf[_to] += _value
126 | # fire transfer event
127 | log.Transfer(_from, _to, _value)
128 | return True
129 |
130 |
131 | # ----- approve -----
132 | # Allows _spender to withdraw from your account multiple times, up to the _value
133 | # amount. If this function is called again it overwrites the current allowance
134 | # with _value.
135 |
136 | # NOTE: To prevent attack vectors like the one described here and discussed here,
137 | # clients SHOULD make sure to create user interfaces in such a way that they set
138 | # the allowance first to 0 before setting it to another value for the same
139 | # spender. THOUGH The contract itself shouldn't enforce it, to allow backwards
140 | # compatibility with contracts deployed before.
141 | @public
142 | def approve(_spender: address, _value: uint256) -> bool:
143 | # overwrites the current allowance
144 | self.approvedFunds[msg.sender][_spender] = _value
145 | # fire approval event
146 | log.Approval(msg.sender, _spender, _value)
147 | return True
148 |
149 |
150 | # ----- allowance -----
151 | # Returns the amount which _spender is still allowed to withdraw from _owner.
152 | @public
153 | @constant
154 | def allowance(_owner: address, _spender: address) -> uint256:
155 | return self.approvedFunds[_owner][_spender]
156 |
--------------------------------------------------------------------------------
/wallet/contracts/helpers/erc721.vy:
--------------------------------------------------------------------------------
1 | # Author: Sören Steiger, github.com/ssteiger
2 | # License: MIT
3 |
4 | # ERC721 Token Standard
5 | # https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
6 |
7 | # @dev Note: the ERC-165 identifier for this interface is 0x150b7a02.
8 | # @notice Handle the receipt of an NFT
9 | # @dev The ERC721 smart contract calls this function on the recipient
10 | # after a `transfer`.
11 | # This function MAY throw to revert and reject the transfer.
12 | # Return of other than the magic value MUST result in the transaction
13 | # being reverted.
14 | # Note: the contract address is always the message sender.
15 | # @param _operator The address which called `safeTransferFrom` function
16 | # @param _from The address which previously owned the token
17 | # @param _tokenId The NFT identifier which is being transferred
18 | # @param _data Additional data with no specified format
19 | # @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
20 | # unless throwing
21 | # function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes _data) external returns(bytes4);
22 | contract ERC721TokenReceiver:
23 | def onERC721Received(
24 | _operator: address,
25 | _from: address,
26 | _tokenId: uint256,
27 | _data: bytes[256]
28 | ) -> bytes32: constant
29 |
30 |
31 | # EVENTS:
32 |
33 | # @dev This emits when ownership of any NFT changes by any mechanism.
34 | # This event emits when NFTs are created (`from` == 0) and destroyed
35 | # (`to` == 0).
36 | # Exception: during contract creation, any number of NFTs may be created
37 | # and assigned without emitting Transfer.
38 | # At the time of any transfer, the approved address for that NFT (if any)
39 | # is reset to none.
40 | Transfer: event({
41 | _from: indexed(address),
42 | _to: indexed(address),
43 | _tokenId: indexed(uint256)
44 | })
45 |
46 |
47 | # NOTE: This is not part of the standard
48 | Mint: event({
49 | _to: indexed(address),
50 | _tokenId: indexed(uint256)
51 | })
52 |
53 |
54 | # @dev This emits when the approved address for an NFT is changed or reaffirmed.
55 | # The zero address indicates there is no approved address.
56 | # When a Transfer event emits, this also indicates that the approved
57 | # address for that NFT (if any) is reset to none.
58 | Approval: event({
59 | _owner: indexed(address),
60 | _approved: indexed(address),
61 | _tokenId: indexed(uint256)
62 | })
63 |
64 |
65 | # @dev This emits when an operator is enabled or disabled for an owner.
66 | # The operator can manage all NFTs of the owner.
67 | ApprovalForAll: event({
68 | _owner: indexed(address),
69 | _operator: indexed(address),
70 | _approved: bool
71 | })
72 |
73 |
74 | # STATE VARIABLES:
75 |
76 | # NOTE: This is not part of the standard
77 | contractOwner: public(address)
78 | # Used for token id's
79 | nftSupply: uint256
80 |
81 | # Used to keep track of the number of tokens an address holds
82 | nftCount: public(map(address, uint256))
83 | ownerOfNFT: public(map(uint256, address))
84 |
85 | operatorFor: public(map(uint256, address))
86 | approvedForAll: public(map(address, map(address, bool)))
87 |
88 | # Interface detection as specified in ERC165
89 | # https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md
90 | supportedInterfaces: public(map(bytes32, bool))
91 | # ERC165 interface ID's
92 | ERC165_INTERFACE_ID: constant(bytes32) = 0x0000000000000000000000000000000000000000000000000000000001ffc9a7
93 | ERC721_INTERFACE_ID: constant(bytes32) = 0x0000000000000000000000000000000000000000000000000000000080ac58cd
94 |
95 |
96 | # METHODS:
97 | @public
98 | def __init__():
99 | # set initial supply (used for token id's)
100 | self.nftSupply = 0
101 | # set supported interfaces
102 | self.supportedInterfaces[ERC165_INTERFACE_ID] = True
103 | self.supportedInterfaces[ERC721_INTERFACE_ID] = True
104 | # set contract owner
105 | # NOTE: This is not part of the standard
106 | # only contractOwner can call mint()
107 | self.contractOwner = msg.sender
108 |
109 |
110 | @private
111 | def _checkIfIsOwnerOrOperatorOrApprovedForAll(_msgSender: address, _from: address, _tokenId: uint256):
112 | # Throws unless `msg.sender` is
113 | # the current owner
114 | isOwner: bool = self.ownerOfNFT[_tokenId] == _msgSender
115 | # an authorized operator
116 | isOperator: bool = self.operatorFor[_tokenId] == _msgSender
117 | # or the approved address for this NFT
118 | isApprovedForAll: bool = (self.approvedForAll[_from])[_msgSender]
119 | assert (isOwner or isOperator or isApprovedForAll)
120 |
121 |
122 | @private
123 | def _setNewOwner(_currentOwner: address, _newOwner: address, _tokenId: uint256):
124 | # set new owner
125 | self.ownerOfNFT[_tokenId] = _newOwner
126 | # updated balances
127 | self.nftCount[_currentOwner] -= 1
128 | self.nftCount[_newOwner] += 1
129 | # reset operator
130 | self.operatorFor[_tokenId] = ZERO_ADDRESS
131 |
132 |
133 | @private
134 | def _transfer(_from: address, _to: address, _tokenId: uint256):
135 | # Throws if `_from` is not the current owner.
136 | assert self.ownerOfNFT[_tokenId] == _from
137 | # Throws if `_to` is the zero address.
138 | assert _to != ZERO_ADDRESS
139 | # Throws if `_tokenId` is not a valid NFT.
140 | assert self.ownerOfNFT[_tokenId] != ZERO_ADDRESS
141 | # transfer to new owner
142 | self._setNewOwner(_from, _to, _tokenId)
143 | # log transfer
144 | log.Transfer(_from, _to, _tokenId)
145 |
146 |
147 | @public
148 | @constant
149 | def supportsInterface(_interfaceID: bytes32) -> bool:
150 | # Interface detection as specified in ERC165
151 | # https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md
152 | return self.supportedInterfaces[_interfaceID]
153 |
154 |
155 | # @notice Count all NFTs assigned to an owner
156 | # @dev NFTs assigned to the zero address are considered invalid, and this
157 | # function throws for queries about the zero address.
158 | # @param _owner An address for whom to query the balance
159 | # @return The number of NFTs owned by `_owner`, possibly zero
160 | # function balanceOf(address _owner) external view returns (uint256);
161 | @public
162 | @constant
163 | def balanceOf(_owner: address) -> uint256:
164 | # NFTs assigned to the zero address are considered invalid, and this
165 | # function throws for queries about the zero address.
166 | assert _owner != ZERO_ADDRESS
167 | return self.nftCount[_owner]
168 |
169 |
170 | # @notice Find the owner of an NFT
171 | # @dev NFTs assigned to zero address are considered invalid, and queries
172 | # about them do throw.
173 | # @param _tokenId The identifier for an NFT
174 | # @return The address of the owner of the NFT
175 | # function ownerOf(uint256 _tokenId) external view returns (address);
176 | @public
177 | @constant
178 | def ownerOf(_tokenId: uint256) -> address:
179 | # NFTs assigned to the zero address are considered invalid, and this
180 | # function throws for queries about the zero address.
181 | owner: address = self.ownerOfNFT[_tokenId]
182 | assert owner != ZERO_ADDRESS
183 | return owner
184 |
185 |
186 | # @notice Transfers the ownership of an NFT from one address to another address
187 | # @dev Throws unless `msg.sender` is
188 | # the current owner,
189 | # an authorized operator,
190 | # or the approved address for this NFT.
191 | # Throws if `_from` is not the current owner.
192 | # Throws if `_to` is the zero address.
193 | # Throws if `_tokenId` is not a valid NFT.
194 | # When transfer is complete, this function checks if `_to` is
195 | # a smart contract (code size > 0). If so, it calls
196 | # `onERC721Received` on `_to` and throws if the return value is not
197 | # `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
198 | # @param _from The current owner of the NFT
199 | # @param _to The new owner
200 | # @param _tokenId The NFT to transfer
201 | # @param data Additional data with no specified format, sent in call to `_to`
202 | # function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;
203 | @public
204 | @payable
205 | def safeTransferFrom(_from: address, _to: address, _tokenId: uint256, _data: bytes[256]=""):
206 | # Throws unless `msg.sender` is
207 | # the current owner,
208 | # an authorized operator,
209 | # or the approved address for this NFT.
210 | self._checkIfIsOwnerOrOperatorOrApprovedForAll(msg.sender, _from, _tokenId)
211 | # transfer
212 | self._transfer(_from, _to, _tokenId)
213 | # When transfer is complete,
214 | # this function checks if `_to` is a smart contract (code size > 0)
215 | if _to.is_contract:
216 | # If so, it calls `onERC721Received` on `_to` and throws if the return value is not
217 | # `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
218 | returnValue: bytes32 = ERC721TokenReceiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data)
219 | assert returnValue == method_id("onERC721Received(address,address,uint256,bytes)", bytes32)
220 |
221 |
222 | # @notice Transfers the ownership of an NFT from one address to another address
223 | # @dev This works identically to the other function with an extra data
224 | # parameter, except this function just sets data to "".
225 | # @param _from The current owner of the NFT
226 | # @param _to The new owner
227 | # @param _tokenId The NFT to transfer
228 | # function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;
229 |
230 |
231 | # @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
232 | # TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE
233 | # THEY MAY BE PERMANENTLY LOST
234 | # @dev Throws unless
235 | # `msg.sender` is the current owner,
236 | # an authorized operator,
237 | # or the approved address for this NFT.
238 | # Throws if `_from` is not the current owner.
239 | # Throws if `_to` is the zero address.
240 | # Throws if `_tokenId` is not a valid NFT.
241 | # @param _from The current owner of the NFT
242 | # @param _to The new owner
243 | # @param _tokenId The NFT to transfer
244 | # function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
245 | @public
246 | @payable
247 | def transferFrom(_from: address, _to: address, _tokenId: uint256):
248 | # Throws unless `msg.sender` is
249 | # the current owner,
250 | # an authorized operator,
251 | # or the approved address for this NFT.
252 | self._checkIfIsOwnerOrOperatorOrApprovedForAll(msg.sender, _from, _tokenId)
253 | # do transfer
254 | self._transfer(_from, _to, _tokenId)
255 |
256 |
257 | # @notice Change or reaffirm the approved address for an NFT
258 | # @dev The zero address indicates there is no approved address.
259 | # Throws unless `msg.sender` is
260 | # the current NFT owner,
261 | # or an authorized operator of the current owner.
262 | # @param _approved The new approved NFT controller
263 | # @param _tokenId The NFT to approve
264 | # function approve(address _approved, uint256 _tokenId) external payable;
265 | @public
266 | @payable
267 | def approve(_approved: address, _tokenId: uint256):
268 | # Throws if _tokenId is not owned / a valid NFT
269 | assert self.ownerOfNFT[_tokenId] != ZERO_ADDRESS
270 | # Throws unless `msg.sender` is the current NFT owner
271 | isOwner: bool = self.ownerOfNFT[_tokenId] == msg.sender
272 | # or an authorized operator of the current owner.
273 | isOperator: bool = self.operatorFor[_tokenId] == msg.sender
274 | # TODO: does the this include approvedForAll?
275 | assert (isOwner or isOperator)
276 | # set new approved address
277 | self.operatorFor[_tokenId] = _approved
278 | # log change
279 | log.Approval(msg.sender, _approved, _tokenId)
280 |
281 |
282 | # @notice Enable or disable approval for a third party ("operator") to manage
283 | # all of `msg.sender`'s assets
284 | # @dev Emits the ApprovalForAll event.
285 | # The contract MUST allow multiple operators per owner.
286 | # @param _operator Address to add to the set of authorized operators
287 | # @param _approved True if the operator is approved, false to revoke approval
288 | # function setApprovalForAll(address _operator, bool _approved) external;
289 | @public
290 | def setApprovalForAll(_operator: address, _approved: bool):
291 | # The contract MUST allow multiple operators per owner.
292 | self.approvedForAll[msg.sender][_operator] = _approved
293 | # log change
294 | log.ApprovalForAll(msg.sender, _operator, _approved)
295 |
296 |
297 | # @notice Get the approved address for a single NFT
298 | # @dev Throws if `_tokenId` is not a valid NFT.
299 | # @param _tokenId The NFT to find the approved address for
300 | # @return The approved address for this NFT, or the zero address if
301 | # there is none
302 | # function getApproved(uint256 _tokenId) external view returns (address);
303 | @public
304 | @constant
305 | def getApproved(_tokenId: uint256) -> address:
306 | # Throws if `_tokenId` is not a valid NFT.
307 | assert self.ownerOfNFT[_tokenId] != ZERO_ADDRESS
308 | return self.operatorFor[_tokenId]
309 |
310 |
311 | # @notice Query if an address is an authorized operator for another address
312 | # @param _owner The address that owns the NFTs
313 | # @param _operator The address that acts on behalf of the owner
314 | # @return True if `_operator` is an approved operator for `_owner`,
315 | # false otherwise
316 | # function isApprovedForAll(address _owner, address _operator) external view returns (bool);
317 | @public
318 | @constant
319 | def isApprovedForAll(_owner: address, _operator: address) -> bool:
320 | return (self.approvedForAll[_owner])[_operator]
321 |
322 |
323 | # NOTE: This is not part of the standard
324 | @public
325 | def mint() -> uint256:
326 | # only contractOwner is allowed to mint
327 | assert msg.sender == self.contractOwner
328 | # update supply
329 | tokenId: uint256 = self.nftSupply
330 | self.nftSupply += 1
331 | # update ownership
332 | self.ownerOfNFT[tokenId] = msg.sender
333 | self.nftCount[msg.sender] += 1
334 | self.operatorFor[tokenId] = ZERO_ADDRESS
335 | # log mint
336 | log.Mint(msg.sender, tokenId)
337 | return tokenId
338 |
--------------------------------------------------------------------------------
/wallet/contracts/helpers/erc777.vy:
--------------------------------------------------------------------------------
1 | # Author: Sören Steiger, github.com/ssteiger
2 | # License: MIT
3 |
4 | # ERC777 Token Standard
5 | # https://eips.ethereum.org/EIPS/eip-777
6 |
7 |
8 | # Interface for ERC1820 registry contract
9 | # https://eips.ethereum.org/EIPS/eip-1820
10 | contract ERC1820Registry:
11 | def setInterfaceImplementer(
12 | _addr: address,
13 | _interfaceHash: bytes32,
14 | _implementer: address,
15 | ): modifying
16 | def getInterfaceImplementer(
17 | _addr: address,
18 | _interfaceHash: bytes32,
19 | ) -> address: modifying
20 |
21 | # Interface for ERC777Tokens sender contracts
22 | contract ERC777TokensSender:
23 | def tokensToSend(
24 | _operator: address,
25 | _from: address,
26 | _to: address,
27 | _amount: uint256,
28 | _data: bytes[256],
29 | _operatorData: bytes[256]
30 | ): modifying
31 |
32 | # Interface for ERC777Tokens recipient contracts
33 | contract ERC777TokensRecipient:
34 | def tokensReceived(
35 | _operator: address,
36 | _from: address,
37 | _to: address,
38 | _amount: uint256,
39 | _data: bytes[256],
40 | _operatorData: bytes[256]
41 | ): modifying
42 |
43 |
44 | Sent: event({
45 | _operator: indexed(address), # Address which triggered the send.
46 | _from: indexed(address), # Token holder.
47 | _to: indexed(address), # Token recipient.
48 | _amount: uint256, # Number of tokens to send.
49 | _data: bytes[256], # Information provided by the token holder.
50 | _operatorData: bytes[256] # Information provided by the operator.
51 | })
52 |
53 | Minted: event({
54 | _operator: indexed(address), # Address which triggered the mint.
55 | _to: indexed(address), # Recipient of the tokens.
56 | _amount: uint256, # Number of tokens minted.
57 | _data: bytes[256], # Information provided for the recipient.
58 | _operatorData: bytes[256] # Information provided by the operator.
59 | })
60 |
61 | Burned: event({
62 | _operator: indexed(address), # Address which triggered the burn.
63 | _from: indexed(address), # Token holder whose tokens are burned.
64 | _amount: uint256, # Token holder whose tokens are burned.
65 | _data: bytes[256], # Information provided by the token holder.
66 | _operatorData: bytes[256] # Information provided by the operator.
67 | })
68 |
69 | AuthorizedOperator: event({
70 | _operator: indexed(address), # Address which became an operator of tokenHolder.
71 | _holder: indexed(address) # Address of a token holder which authorized the operator address as an operator.
72 | })
73 |
74 | RevokedOperator: event({
75 | _operator: indexed(address), # Address which was revoked as an operator of tokenHolder.
76 | _holder: indexed(address) # Address of a token holder which revoked the operator address as an operator.
77 | })
78 |
79 |
80 | erc1820Registry: ERC1820Registry
81 | erc1820RegistryAddress: constant(address) = 0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24
82 |
83 | name: public(string[64])
84 | symbol: public(string[32])
85 | totalSupply: public(uint256)
86 | granularity: public(uint256)
87 |
88 | balanceOf: public(map(address, uint256))
89 |
90 | defaultOperatorsList: address[4]
91 | defaultOperatorsMap: map(address, bool)
92 |
93 | operators: map(address, map(address, bool))
94 |
95 |
96 | @public
97 | def __init__(
98 | _name: string[64],
99 | _symbol: string[32],
100 | _totalSupply: uint256,
101 | _granularity: uint256,
102 | _defaultOperators: address[4]
103 | ):
104 | self.name = _name
105 | self.symbol = _symbol
106 | self.totalSupply = _totalSupply
107 | # The granularity value MUST be greater than or equal to 1
108 | assert _granularity >= 1
109 | self.granularity = _granularity
110 | self.defaultOperatorsList = _defaultOperators
111 | for i in range(4):
112 | assert _defaultOperators[i] != ZERO_ADDRESS
113 | self.defaultOperatorsMap[_defaultOperators[i]] = True
114 | self.erc1820Registry = ERC1820Registry(erc1820RegistryAddress)
115 | self.erc1820Registry.setInterfaceImplementer(self, keccak256("ERC777Token"), self)
116 |
117 |
118 | @private
119 | def _checkForERC777TokensInterface_Sender(
120 | _operator: address,
121 | _from: address,
122 | _to: address,
123 | _amount: uint256,
124 | _data: bytes[256]="",
125 | _operatorData: bytes[256]=""
126 | ):
127 | implementer: address = self.erc1820Registry.getInterfaceImplementer(_from, keccak256("ERC777TokensSender"))
128 | if implementer != ZERO_ADDRESS:
129 | ERC777TokensSender(_from).tokensToSend(_operator, _from, _to, _amount, _data, _operatorData)
130 |
131 |
132 | @private
133 | def _checkForERC777TokensInterface_Recipient(
134 | _operator: address,
135 | _from: address,
136 | _to: address,
137 | _amount: uint256,
138 | _data: bytes[256]="",
139 | _operatorData: bytes[256]=""
140 | ):
141 | implementer: address = self.erc1820Registry.getInterfaceImplementer(_to, keccak256("ERC777TokensRecipient"))
142 | if implementer != ZERO_ADDRESS:
143 | ERC777TokensRecipient(_to).tokensReceived(_operator, _from, _to, _amount, _data, _operatorData)
144 |
145 |
146 | @private
147 | def _transferFunds(
148 | _operator: address,
149 | _from: address,
150 | _to: address,
151 | _amount: uint256,
152 | _data: bytes[256]="",
153 | _operatorData: bytes[256]=""
154 | ):
155 | # any minting, sending or burning of tokens MUST be a multiple of the granularity value.
156 | assert _amount % self.granularity == 0
157 |
158 | # check for 'tokensToSend' hook
159 | if _from.is_contract:
160 | self._checkForERC777TokensInterface_Sender(_operator, _from, _to, _amount, _data, _operatorData)
161 |
162 | self.balanceOf[_from] -= _amount
163 | self.balanceOf[_to] += _amount
164 |
165 | # check for 'tokensReceived' hook
166 | # but only if transfer is not a burn
167 | if _to != ZERO_ADDRESS:
168 | if _to.is_contract:
169 | self._checkForERC777TokensInterface_Recipient(_operator, _from, _to, _amount, _data, _operatorData)
170 |
171 |
172 | @public
173 | @constant
174 | def defaultOperators() -> address[4]:
175 | return self.defaultOperatorsList
176 |
177 |
178 | @public
179 | @constant
180 | def isOperatorFor(_operator: address, _holder: address) -> bool:
181 | return (self.operators[_holder])[_operator] or self.defaultOperatorsMap[_operator] or _operator == _holder
182 |
183 |
184 | @public
185 | def authorizeOperator(_operator: address):
186 | (self.operators[msg.sender])[_operator] = True
187 | log.AuthorizedOperator(_operator, msg.sender)
188 |
189 |
190 | @public
191 | def revokeOperator(_operator: address):
192 | # MUST revert if it is called to revoke the holder as an operator for itself
193 | assert _operator != msg.sender
194 | (self.operators[msg.sender])[_operator] = False
195 | log.RevokedOperator(_operator, msg.sender)
196 |
197 |
198 | @public
199 | def send(_to: address, _amount: uint256, _data: bytes[256]=""):
200 | assert _to != ZERO_ADDRESS
201 | operatorData: bytes[256]=""
202 | self._transferFunds(msg.sender, msg.sender, _to, _amount, _data, operatorData)
203 | log.Sent(msg.sender, msg.sender, _to, _amount, _data, operatorData)
204 |
205 |
206 | @public
207 | def operatorSend(
208 | _from: address,
209 | _to: address,
210 | _amount: uint256,
211 | _data: bytes[256]="",
212 | _operatorData: bytes[256]=""
213 | ):
214 | assert _to != ZERO_ADDRESS
215 | assert self.isOperatorFor(msg.sender, _from)
216 | self._transferFunds(msg.sender, _from, _to, _amount, _data, _operatorData)
217 | log.Sent(msg.sender, _from, _to, _amount, _data, _operatorData)
218 |
219 |
220 | @public
221 | def burn(_amount: uint256, _data: bytes[256]=""):
222 | operatorData: bytes[256]=""
223 | self._transferFunds(msg.sender, msg.sender, ZERO_ADDRESS, _amount, _data, operatorData)
224 | self.totalSupply -= _amount
225 | log.Burned(msg.sender, msg.sender, _amount, _data, operatorData)
226 |
227 |
228 | @public
229 | def operatorBurn(
230 | _from: address,
231 | _amount: uint256,
232 | _data: bytes[256]="",
233 | _operatorData: bytes[256]=""
234 | ):
235 | # _from: Token holder whose tokens will be burned (or 0x0 to set from to msg.sender).
236 | fromAddress: address
237 | if _from == ZERO_ADDRESS:
238 | fromAddress = msg.sender
239 | else:
240 | fromAddress = _from
241 | assert self.isOperatorFor(msg.sender, fromAddress)
242 | self._transferFunds(msg.sender, fromAddress, ZERO_ADDRESS, _amount, _data, _operatorData)
243 | self.totalSupply -= _amount
244 | log.Burned(msg.sender, fromAddress, _amount, _data, _operatorData)
245 |
246 |
247 | # NOTE: ERC777 intentionally does not define specific functions to mint tokens.
248 | @public
249 | def mint(
250 | _to: address,
251 | _amount: uint256,
252 | _operatorData: bytes[256]=""
253 | ):
254 | assert _to != ZERO_ADDRESS
255 | # any minting, sending or burning of tokens MUST be a multiple of the granularity value.
256 | assert _amount % self.granularity == 0
257 | # only operators are allowed to mint
258 | assert self.defaultOperatorsMap[msg.sender]
259 | self.balanceOf[_to] += _amount
260 | self.totalSupply += _amount
261 | data: bytes[256]=""
262 | if _to.is_contract:
263 | self._checkForERC777TokensInterface_Recipient(msg.sender, ZERO_ADDRESS, _to, _amount, data, _operatorData)
264 | log.Minted(msg.sender, _to, _amount, data, _operatorData)
265 |
--------------------------------------------------------------------------------
/wallet/contracts/helpers/erc777TokenReceiver.vy:
--------------------------------------------------------------------------------
1 | # Author: Sören Steiger, github.com/ssteiger
2 | # License: MIT
3 |
4 | # ERC777 Token Receiver (https://eips.ethereum.org/EIPS/eip-777)
5 |
6 | # Interface for ERC1820 registry contract (http://eips.ethereum.org/EIPS/eip-1820)
7 | contract ERC1820Registry:
8 | def setInterfaceImplementer(
9 | _addr: address,
10 | _interfaceHash: bytes32,
11 | _implementer: address,
12 | ): modifying
13 |
14 |
15 | TokensReceived: event({
16 | _operator: indexed(address),
17 | _from: indexed(address),
18 | _to: indexed(address),
19 | _amount: uint256,
20 | _data: bytes[256],
21 | _operatorData: bytes[256]
22 | })
23 |
24 |
25 | erc1820Registry: ERC1820Registry
26 | erc1820RegistryAddress: constant(address) = 0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24
27 |
28 |
29 | @public
30 | def __init__():
31 | self.erc1820Registry = ERC1820Registry(erc1820RegistryAddress)
32 | self.erc1820Registry.setInterfaceImplementer(self, keccak256("ERC777TokensRecipient"), self)
33 |
34 |
35 | @public
36 | def tokensReceived(
37 | _operator: address,
38 | _from: address,
39 | _to: address,
40 | _amount: uint256,
41 | _data: bytes[256],
42 | _operatorData: bytes[256]
43 | ):
44 | log.TokensReceived(_operator, _from, _to, _amount, _data, _operatorData)
45 |
--------------------------------------------------------------------------------
/wallet/contracts/wallet.vy:
--------------------------------------------------------------------------------
1 | # Author: Sören Steiger, github.com/ssteiger
2 | # License: MIT
3 |
4 | contract ERC1820Registry:
5 | def setInterfaceImplementer(
6 | _addr: address,
7 | _interfaceHash: bytes32,
8 | _implementer: address
9 | ): modifying
10 |
11 | contract ERC20Token:
12 | def transfer(
13 | _to: address,
14 | _value: uint256
15 | ) -> bool: modifying
16 |
17 | contract ERC721Token:
18 | def safeTransferFrom(
19 | _from: address,
20 | _to: address,
21 | _tokenId: uint256,
22 | _data: bytes[256]
23 | ): modifying
24 |
25 | contract ERC777Token:
26 | def send(
27 | _to: address,
28 | _amount: uint256,
29 | _data: bytes[256]
30 | ): modifying
31 |
32 |
33 | ETHReceived: event({
34 | _from: address,
35 | _amount: wei_value
36 | })
37 |
38 | ETHSent: event({
39 | _to: address,
40 | _amount: uint256
41 | })
42 |
43 | # TODO:
44 | #ERC20Received: event({
45 | #})
46 |
47 | ERC20Sent: event({
48 | _token: address,
49 | _to: address,
50 | _amount: uint256
51 | })
52 |
53 | ERC721Received: event({
54 | _token: address,
55 | _from: address,
56 | _tokenId: uint256,
57 | _data: bytes32
58 | })
59 |
60 | ERC721Sent: event({
61 | _token: address,
62 | _from: address,
63 | _to: address,
64 | _tokenId: uint256,
65 | _data: bytes[256]
66 | })
67 |
68 | ERC777Received: event({
69 | _operator: indexed(address),
70 | _from: indexed(address),
71 | _to: indexed(address),
72 | _amount: uint256,
73 | _data: bytes[256],
74 | _operatorData: bytes[256]
75 | })
76 |
77 | ERC777Sent: event({
78 | _operator: indexed(address),
79 | _from: indexed(address),
80 | _to: indexed(address),
81 | _amount: uint256,
82 | _data: bytes[256],
83 | _operatorData: bytes[256]
84 | })
85 |
86 |
87 | erc1820Registry: ERC1820Registry
88 | erc1820RegistryAddress: constant(address) = 0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24
89 |
90 | owner: public(address)
91 |
92 |
93 | @public
94 | def __init__():
95 | self.owner = msg.sender
96 | self.erc1820Registry = ERC1820Registry(erc1820RegistryAddress)
97 | self.erc1820Registry.setInterfaceImplementer(self, keccak256("ERC777TokensRecipient"), self)
98 | self.erc1820Registry.setInterfaceImplementer(self, keccak256("ERC777TokensSender"), self)
99 |
100 |
101 | # ----- ETH -----
102 | @public
103 | @payable
104 | def __default__():
105 | log.ETHReceived(msg.sender, msg.value)
106 |
107 | @public
108 | def sendETH(
109 | _to: address,
110 | _amount: uint256
111 | ):
112 | assert msg.sender == self.owner
113 | send(_to, _amount)
114 | log.ETHSent(_to, _amount)
115 |
116 |
117 | # ----- ERC20 -----
118 | @public
119 | def sendERC20(
120 | _token: address,
121 | _to: address,
122 | _amount: uint256
123 | ):
124 | assert msg.sender == self.owner
125 | ERC20Token(_token).transfer(_to, _amount)
126 | log.ERC20Sent(_token, _to, _amount)
127 |
128 | #@public
129 | #def onERC20Received():
130 |
131 |
132 | # ----- ERC721 -----
133 | @public
134 | def sendERC721(
135 | _token: address,
136 | _to: address,
137 | _tokenId: uint256,
138 | _data: bytes[256]=""
139 | ):
140 | assert msg.sender == self.owner
141 | ERC721Token(_token).safeTransferFrom(self, _to, _tokenId, _data)
142 | log.ERC721Sent(_token, self, _to, _tokenId, _data)
143 |
144 | @public
145 | def onERC721Received(
146 | _token: address,
147 | _from: address,
148 | _tokenId: uint256,
149 | _data: bytes32
150 | ) -> bytes32:
151 | log.ERC721Received(_token, _from, _tokenId, _data)
152 | # TODO: need to return bytes4
153 | return keccak256("onERC721Received(address,address,uint256,bytes)")
154 |
155 |
156 | # ----- ERC777 -----
157 | @public
158 | def sendERC777(
159 | _token: address,
160 | _to: address,
161 | _amount: uint256,
162 | _data: bytes[256]=""
163 | ):
164 | assert msg.sender == self.owner
165 | ERC777Token(_token).send(_to, _amount, _data)
166 |
167 | @public
168 | def tokensToSend(
169 | _operator: address,
170 | _from: address,
171 | _to: address,
172 | _amount: uint256,
173 | _data: bytes[256],
174 | _operatorData: bytes[256]
175 | ):
176 | log.ERC777Sent(_operator, _from, _to, _amount, _data, _operatorData)
177 |
178 | @public
179 | def tokensReceived(
180 | _operator: address,
181 | _from: address,
182 | _to: address,
183 | _amount: uint256,
184 | _data: bytes[256],
185 | _operatorData: bytes[256]
186 | ):
187 | log.ERC777Received(_operator, _from, _to, _amount, _data, _operatorData)
188 |
--------------------------------------------------------------------------------
/wallet/migrations/1_initial_migration.js:
--------------------------------------------------------------------------------
1 | const checkForERC182Registry = require('./helpers/checkForERC182Registry.js');
2 |
3 | const Migrations = artifacts.require('./Migrations.sol');
4 |
5 | module.exports = async function(deployer) {
6 | await checkForERC182Registry(web3, deployer);
7 |
8 | deployer.deploy(Migrations);
9 | };
10 |
--------------------------------------------------------------------------------
/wallet/migrations/2_deploy_contracts.js:
--------------------------------------------------------------------------------
1 | const erc20 = artifacts.require('erc20');
2 | const erc721 = artifacts.require('erc721');
3 | const erc777 = artifacts.require('erc777');
4 | const wallet = artifacts.require('wallet');
5 |
6 | const truffleFromAddress = '0x954e72fdc51Cf919203067406fB337Ed4bDC8CdA';
7 |
8 | const args = {
9 | erc20: {
10 | name: 'My20Token',
11 | symbol: 'MT20',
12 | decimals: 18,
13 | totalSupply: 100000000,
14 | },
15 | erc777: {
16 | name: 'My777Token',
17 | symbol: 'MT777',
18 | totalSupply: 100000000,
19 | granularity: 1,
20 | defaultOperators: [
21 | truffleFromAddress,
22 | '0x0000000000000000000000000000000000000001',
23 | '0x0000000000000000000000000000000000000002',
24 | '0x0000000000000000000000000000000000000003',
25 | ],
26 | }
27 | }
28 |
29 | module.exports = function(deployer) {
30 | if (deployer.network == 'ganache') {
31 | // deploy contracts necessary for testing
32 | deployer.deploy(
33 | erc20,
34 | args.erc20.name,
35 | args.erc20.symbol,
36 | args.erc20.decimals,
37 | args.erc20.totalSupply,
38 | );
39 |
40 | deployer.deploy(erc721);
41 |
42 | deployer.deploy(
43 | erc777,
44 | args.erc777.name,
45 | args.erc777.symbol,
46 | args.erc777.totalSupply,
47 | args.erc777.granularity,
48 | args.erc777.defaultOperators,
49 | );
50 | } // if deployer.network == 'ganache'
51 |
52 | // deploy wallet contract
53 | deployer.deploy(wallet);
54 | };
55 |
--------------------------------------------------------------------------------
/wallet/migrations/helpers/checkForERC182Registry.js:
--------------------------------------------------------------------------------
1 | // NOTE: this address has to match the 'from' address used in 'truffle.js'
2 | const truffleFromAddress = '0x954e72fdc51Cf919203067406fB337Ed4bDC8CdA';
3 |
4 | module.exports = async function(web3, deployer) {
5 | // check if erc1820 registry contract exists on network
6 | // http://eips.ethereum.org/EIPS/eip-1820
7 |
8 | console.log('checking if erc1820Registry contract exists on network');
9 |
10 | // check for code at the erc1820Registry address
11 | const code = await web3.eth.getCode('0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24');
12 |
13 | // NOTE: If no contract exists at address, '0x' is returned
14 | if (code == '0x') {
15 | // erc1820Registry does not exist on network
16 | // -> deploy it
17 | console.log('erc1820Registry contract not found -> deloying it now');
18 |
19 | // fund address from which the erc1820Registry contract will be deployed
20 | const fundSize = web3.utils.toWei('0.08', 'ether');
21 | await web3.eth.sendTransaction({
22 | from: truffleFromAddress,
23 | to: '0xa990077c3205cbDf861e17Fa532eeB069cE9fF96',
24 | value: fundSize
25 | });
26 |
27 | // the raw transaction that deployes the erc1820Registry
28 | const rawTx = '0xf90a388085174876e800830c35008080b909e5608060405234801561001057600080fd5b506109c5806100206000396000f3fe608060405234801561001057600080fd5b50600436106100a5576000357c010000000000000000000000000000000000000000000000000000000090048063a41e7d5111610078578063a41e7d51146101d4578063aabbb8ca1461020a578063b705676514610236578063f712f3e814610280576100a5565b806329965a1d146100aa5780633d584063146100e25780635df8122f1461012457806365ba36c114610152575b600080fd5b6100e0600480360360608110156100c057600080fd5b50600160a060020a038135811691602081013591604090910135166102b6565b005b610108600480360360208110156100f857600080fd5b5035600160a060020a0316610570565b60408051600160a060020a039092168252519081900360200190f35b6100e06004803603604081101561013a57600080fd5b50600160a060020a03813581169160200135166105bc565b6101c26004803603602081101561016857600080fd5b81019060208101813564010000000081111561018357600080fd5b82018360208201111561019557600080fd5b803590602001918460018302840111640100000000831117156101b757600080fd5b5090925090506106b3565b60408051918252519081900360200190f35b6100e0600480360360408110156101ea57600080fd5b508035600160a060020a03169060200135600160e060020a0319166106ee565b6101086004803603604081101561022057600080fd5b50600160a060020a038135169060200135610778565b61026c6004803603604081101561024c57600080fd5b508035600160a060020a03169060200135600160e060020a0319166107ef565b604080519115158252519081900360200190f35b61026c6004803603604081101561029657600080fd5b508035600160a060020a03169060200135600160e060020a0319166108aa565b6000600160a060020a038416156102cd57836102cf565b335b9050336102db82610570565b600160a060020a031614610339576040805160e560020a62461bcd02815260206004820152600f60248201527f4e6f7420746865206d616e616765720000000000000000000000000000000000604482015290519081900360640190fd5b6103428361092a565b15610397576040805160e560020a62461bcd02815260206004820152601a60248201527f4d757374206e6f7420626520616e204552433136352068617368000000000000604482015290519081900360640190fd5b600160a060020a038216158015906103b85750600160a060020a0382163314155b156104ff5760405160200180807f455243313832305f4143434550545f4d4147494300000000000000000000000081525060140190506040516020818303038152906040528051906020012082600160a060020a031663249cb3fa85846040518363ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004018083815260200182600160a060020a0316600160a060020a031681526020019250505060206040518083038186803b15801561047e57600080fd5b505afa158015610492573d6000803e3d6000fd5b505050506040513d60208110156104a857600080fd5b5051146104ff576040805160e560020a62461bcd02815260206004820181905260248201527f446f6573206e6f7420696d706c656d656e742074686520696e74657266616365604482015290519081900360640190fd5b600160a060020a03818116600081815260208181526040808320888452909152808220805473ffffffffffffffffffffffffffffffffffffffff19169487169485179055518692917f93baa6efbd2244243bfee6ce4cfdd1d04fc4c0e9a786abd3a41313bd352db15391a450505050565b600160a060020a03818116600090815260016020526040812054909116151561059a5750806105b7565b50600160a060020a03808216600090815260016020526040902054165b919050565b336105c683610570565b600160a060020a031614610624576040805160e560020a62461bcd02815260206004820152600f60248201527f4e6f7420746865206d616e616765720000000000000000000000000000000000604482015290519081900360640190fd5b81600160a060020a031681600160a060020a0316146106435780610646565b60005b600160a060020a03838116600081815260016020526040808220805473ffffffffffffffffffffffffffffffffffffffff19169585169590951790945592519184169290917f605c2dbf762e5f7d60a546d42e7205dcb1b011ebc62a61736a57c9089d3a43509190a35050565b600082826040516020018083838082843780830192505050925050506040516020818303038152906040528051906020012090505b92915050565b6106f882826107ef565b610703576000610705565b815b600160a060020a03928316600081815260208181526040808320600160e060020a031996909616808452958252808320805473ffffffffffffffffffffffffffffffffffffffff19169590971694909417909555908152600284528181209281529190925220805460ff19166001179055565b600080600160a060020a038416156107905783610792565b335b905061079d8361092a565b156107c357826107ad82826108aa565b6107b85760006107ba565b815b925050506106e8565b600160a060020a0390811660009081526020818152604080832086845290915290205416905092915050565b6000808061081d857f01ffc9a70000000000000000000000000000000000000000000000000000000061094c565b909250905081158061082d575080155b1561083d576000925050506106e8565b61084f85600160e060020a031961094c565b909250905081158061086057508015155b15610870576000925050506106e8565b61087a858561094c565b909250905060018214801561088f5750806001145b1561089f576001925050506106e8565b506000949350505050565b600160a060020a0382166000908152600260209081526040808320600160e060020a03198516845290915281205460ff1615156108f2576108eb83836107ef565b90506106e8565b50600160a060020a03808316600081815260208181526040808320600160e060020a0319871684529091529020549091161492915050565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff161590565b6040517f01ffc9a7000000000000000000000000000000000000000000000000000000008082526004820183905260009182919060208160248189617530fa90519096909550935050505056fea165627a7a72305820377f4a2d4301ede9949f163f319021a6e9c687c292a5e2b2c4734c126b524e6c00291ba01820182018201820182018201820182018201820182018201820182018201820a01820182018201820182018201820182018201820182018201820182018201820';
29 |
30 | // deploy erc1820 registry
31 | const erc1820Registry = await web3.eth.sendSignedTransaction(rawTx);
32 |
33 | } else {
34 | console.log('erc1820Registry contract found');
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/wallet/test/wallet.js:
--------------------------------------------------------------------------------
1 | const erc20 = artifacts.require('erc20');
2 | const erc721 = artifacts.require('erc721');
3 | const erc777 = artifacts.require('erc777');
4 |
5 | const walletAbstraction = artifacts.require('wallet');
6 |
7 | let wallet, walletOwner;
8 |
9 | contract('wallet', accounts => {
10 | beforeEach(async () => {
11 | wallet = await walletAbstraction.new({ from: accounts[0] });
12 | walletOwner = accounts[0];
13 | });
14 |
15 | it('...should accept eth deposit.', async () => {
16 | const sender = accounts[0];
17 |
18 | const wallet_starting_balance = await web3.eth.getBalance(wallet.address);
19 |
20 | const amount = web3.utils.toWei('1', 'ether');
21 |
22 | await web3.eth.sendTransaction({
23 | from: sender,
24 | to: wallet.address,
25 | value: amount
26 | });
27 |
28 | const wallet_ending_balance = await web3.eth.getBalance(wallet.address);
29 |
30 | assert.equal(
31 | wallet_starting_balance,
32 | wallet_ending_balance - amount,
33 | 'ETH was not correctly deposited.'
34 | );
35 | });
36 |
37 | it('...should send eth.', async () => {
38 | const sender = accounts[0];
39 | const receiver = accounts[1];
40 |
41 | const amount = web3.utils.toWei('1', 'ether');
42 |
43 | await web3.eth.sendTransaction({
44 | from: sender,
45 | to: wallet.address,
46 | value: amount
47 | });
48 |
49 | const wallet_starting_balance = await web3.eth.getBalance(wallet.address);
50 | const receiver_starting_balance = await web3.eth.getBalance(receiver);
51 |
52 | await wallet.sendETH(receiver, amount, { from: walletOwner, gas: 40000 });
53 |
54 | const wallet_ending_balance = await web3.eth.getBalance(wallet.address);
55 | const receiver_ending_balance = await web3.eth.getBalance(receiver);
56 |
57 | assert.equal(
58 | wallet_starting_balance - amount,
59 | wallet_ending_balance,
60 | 'ETH was not correctly sent.'
61 | );
62 | assert.equal(
63 | receiver_starting_balance,
64 | receiver_ending_balance - amount,
65 | 'ETH was not correctly sent.'
66 | );
67 | });
68 |
69 | it('...should accept erc20 tokens deposit.', async () => {
70 | const sender = accounts[0];
71 |
72 | const erc20Token = await erc20.deployed();
73 |
74 | const wallet_starting_balance = await erc20Token.balanceOf.call(wallet.address);
75 |
76 | const amount = 100;
77 |
78 | await erc20Token.transfer(wallet.address, amount, { from: sender });
79 |
80 | const wallet_ending_balance = await erc20Token.balanceOf.call(wallet.address);
81 |
82 | assert.equal(
83 | wallet_starting_balance,
84 | wallet_ending_balance - amount,
85 | 'ERC20 tokens were not correctly deposited.'
86 | );
87 | });
88 |
89 | it('...should send erc20 tokens.', async () => {
90 | const receiver = accounts[1];
91 |
92 | const amount = 100;
93 |
94 | const erc20Token = await erc20.deployed();
95 | await erc20Token.transfer(wallet.address, amount, { from: accounts[0] });
96 |
97 | let receiver_starting_balance = await erc20Token.balanceOf.call(receiver);
98 |
99 | await wallet.sendERC20(erc20Token.address, receiver, amount, { from: walletOwner, gas: 4000000 });
100 |
101 | balance = await erc20Token.balanceOf.call(receiver);
102 | const receiver_ending_balance = balance.toString();
103 |
104 | assert.equal(
105 | receiver_starting_balance,
106 | receiver_ending_balance - amount,
107 | 'ERC20 tokens were not correctly credited.'
108 | );
109 | });
110 |
111 | it('...should accept erc721 token deposit.', async () => {
112 | const sender = accounts[0];
113 |
114 | const erc721Token = await erc721.deployed();
115 |
116 | const mintReturnData = await erc721Token.mint();
117 | const tokenId = mintReturnData.logs[0].args['1'].toString();
118 |
119 | let balance = await erc721Token.balanceOf.call(wallet.address);
120 | const wallet_starting_balance = balance.toString();
121 |
122 | await erc721Token.transferFrom(sender, wallet.address, tokenId, { from: sender });
123 |
124 | balance = await erc721Token.balanceOf.call(wallet.address);
125 | const wallet_ending_balance = balance.toString();
126 |
127 | const ownerOfNFT = await erc721Token.ownerOf.call(tokenId);
128 |
129 | assert.equal(
130 | wallet_starting_balance,
131 | wallet_ending_balance - 1,
132 | 'ERC721 token was not correctly deposited'
133 | );
134 | assert.equal(
135 | wallet.address,
136 | ownerOfNFT,
137 | 'ERC721 token was not correctly deposited.'
138 | );
139 | });
140 |
141 | it('...should accept erc777 tokens deposit.', async () => {
142 | const amount = 80;
143 |
144 | const erc777Token = await erc777.deployed();
145 |
146 | const wallet_starting_balance = await erc777Token.balanceOf.call(wallet.address);
147 |
148 | await erc777Token.mint(wallet.address, amount);
149 |
150 | const wallet_ending_balance = await erc777Token.balanceOf.call(wallet.address);
151 |
152 | assert.equal(
153 | wallet_starting_balance,
154 | wallet_ending_balance - amount,
155 | 'ERC777 token was not correctly deposited.'
156 | );
157 | });
158 |
159 | it('...should send erc777 tokens.', async () => {
160 | const receiver = accounts[1];
161 |
162 | const amount = 120;
163 |
164 | const erc777Token = await erc777.deployed();
165 | await erc777Token.mint(wallet.address, amount);
166 |
167 | const wallet_starting_balance = await erc777Token.balanceOf.call(wallet.address);
168 |
169 | await wallet.sendERC777(erc777Token.address, receiver, amount, { from: walletOwner, gas: 3000000 });
170 |
171 | const wallet_ending_balance = await erc777Token.balanceOf.call(wallet.address);
172 |
173 | assert.equal(
174 | wallet_starting_balance - amount,
175 | wallet_ending_balance,
176 | 'ERC777 token was not correctly sent.'
177 | );
178 | });
179 |
180 | });
181 |
--------------------------------------------------------------------------------
/wallet/truffle.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Use this file to configure your truffle project. It's seeded with some
3 | * common settings for different networks and features like migrations,
4 | * compilation and testing. Uncomment the ones you need or modify
5 | * them to suit your project as necessary.
6 | *
7 | * More information about configuration can be found at:
8 | *
9 | * truffleframework.com/docs/advanced/configuration
10 | *
11 | * To deploy via Infura you'll need a wallet provider (like truffle-hdwallet-provider)
12 | * to sign your transactions before they're sent to a remote public node. Infura API
13 | * keys are available for free at: infura.io/register
14 | *
15 | * You'll also need a mnemonic - the twelve word phrase the wallet uses to generate
16 | * public/private key pairs. If you're publishing your code to GitHub make sure you load this
17 | * phrase from a file you've .gitignored so it doesn't accidentally become public.
18 | *
19 | */
20 |
21 | module.exports = {
22 | networks: {
23 | ganache: {
24 | host: '127.0.0.1',
25 | port: 7545,
26 | network_id: '*', // match any network id
27 | from: '0x954e72fdc51Cf919203067406fB337Ed4bDC8CdA', // account address from which to deploy
28 | gas: 4000000,
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------