├── Auction Smart Contract.md
├── Auction Smart Contract
└── Auction.sol
├── ChainLinkTask
├── CheckETHStatus.sol
└── deployment.md
├── Chainlinktask.md
├── Compile.md
├── Constant Product AMM
├── CPAMM.sol
└── deployment.md
├── MultiSig.md
├── README.md
├── SoulBoundTokenContract
└── soulboundToken.sol
├── Soulbound Tokens Smart Contract.md
├── Task2.ipynb
├── Task2new.ipynb
├── Uploading-submissions.md
├── create_btc_add.py
├── defi.md
├── task1.md
├── task2.md
└── task2new.py
/Auction Smart Contract.md:
--------------------------------------------------------------------------------
1 | # Auction Smart Contract
2 |
3 | A simple solidity smart contract for hosting an NFT auction.
4 |
5 | # To-do
6 |
7 | - [ ] Main Task
8 | 1. Seller of NFT deploys this contract.
9 | 2. While placing the bid, you should have amount greater than the highest bid.
10 | 3. Participants can bid by depositing ETH greater than the current highest bidder.
11 | 4. All bidders can withdraw their bid if it is not the current highest bid.
12 |
13 | - [ ] After the Auction
14 | 1. Highest bidder becomes the new owner of NFT.
15 | 2. The seller receives the highest bid of ETH.
16 |
17 | # Solving Bounty
18 |
19 | 1. While creating the code use proper comments.
20 | 2. Submission Deadline - 17th Jul. 11:00 pm IST to 19 Jul. 11:00pm IST
21 | 3. Fork the repo and create a pull request.
22 |
--------------------------------------------------------------------------------
/Auction Smart Contract/Auction.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.8.7;
3 |
4 | /**
5 | @dev interface for interacting with NFT
6 | */
7 | interface IERC721 {
8 | function transfer(address, uint) external;
9 |
10 | function transferFrom(
11 | address,
12 | address,
13 | uint
14 | ) external;
15 | }
16 |
17 | /**
18 | @title Auction contract for selling NFTs.
19 | @author Naman Vyas
20 | @notice The contract should be deployed by the seller. All functions and state variables
21 | are kept public to promote transparency.
22 | */
23 | contract Auction {
24 | /* State Variables */
25 |
26 | /// @dev Change this value in production
27 | uint public constant DURATION = 7 days;
28 |
29 | /// @dev i_variableName is a naming convention for immutable variables
30 | address payable public immutable i_seller;
31 |
32 | IERC721 public nftContract;
33 | uint public nftId;
34 | bool public started = false;
35 | uint public endAt;
36 | address payable public highestBidder;
37 | uint public highestBid;
38 | mapping(address => uint) balance;
39 |
40 | /* Events */
41 | event Start();
42 | event End();
43 | event Bid(address indexed bidder, uint bidAmount);
44 | event Withdraw(address indexed bidder, uint amount);
45 |
46 | /* Functions */
47 | constructor() {
48 | i_seller = payable(msg.sender);
49 | }
50 |
51 | /**
52 | @param {_nft} NFT contract's address
53 | @param {_nftid} NFT's unique id
54 | @param {_minBid} minimum bid amount
55 |
56 | @notice Start the auction. Only seller can start the auction.
57 |
58 | @custom:NOTE The seller should approve this contract to transfer NFT before
59 | calling this function.
60 | */
61 | function start(
62 | IERC721 _nft,
63 | uint _nftId,
64 | uint _minBid
65 | ) external {
66 | require(!started, "Already Started !!");
67 | require(
68 | payable(msg.sender) == i_seller,
69 | "Only seller can start the auction."
70 | );
71 |
72 | nftContract = _nft;
73 | nftId = _nftId;
74 | nftContract.transferFrom(msg.sender, address(this), nftId);
75 | highestBid = _minBid;
76 |
77 | started = true;
78 | endAt = block.timestamp + DURATION;
79 |
80 | emit Start();
81 | }
82 |
83 | /**
84 | @param {amount} amount added to previous bid.
85 |
86 | @notice New bid amount is calculated by adding {amount} to the balance of bidder.
87 | A bid is placed only when the new bid is greater than highest bid.
88 |
89 | @dev default value of balance[msg.sender] is ZERO
90 | */
91 | function bid(uint amount) external payable {
92 | require(started, "Not started.");
93 | require(endAt > block.timestamp, "Ended!");
94 | uint bidAmount = balance[msg.sender] + amount;
95 | require(bidAmount > highestBid, "Not enough amount.");
96 |
97 | highestBid = bidAmount;
98 | highestBidder = payable(msg.sender);
99 | balance[msg.sender] = bidAmount;
100 |
101 | emit Bid(highestBidder, highestBid);
102 | }
103 |
104 | /**
105 | @notice Exit from the auction and withdraw the balance. Highest bidder can't withdraw.
106 | */
107 | function withdraw() external {
108 | require(msg.sender != highestBidder, "Highest Bidder can't withdraw.");
109 | uint bal = balance[msg.sender];
110 |
111 | (bool sent, ) = payable(msg.sender).call{value: bal}("");
112 | require(sent, "Transfer failed!");
113 |
114 | delete balance[msg.sender];
115 |
116 | emit Withdraw(msg.sender, bal);
117 | }
118 |
119 | /**
120 | @notice End the auction and transfer NFT to the highest bidder. Transfer the highestBid
121 | amount to the seller. Auction can't be end before specified duration.
122 |
123 | @dev We'll allow everyone to end the auction because if the seller somehow lost the access
124 | to this contract then the funds of highest bidder will be locked.
125 | */
126 | function end() external {
127 | require(!started, "you need to start first.");
128 | require(block.timestamp < endAt, "Auction is still ongoing.");
129 |
130 | if (highestBidder != address(0)) {
131 | nftContract.transfer(highestBidder, nftId);
132 | (bool sent, ) = i_seller.call{value: highestBid}("");
133 | require(sent, "Couldn't pay seller!");
134 | } else {
135 | nftContract.transfer(i_seller, nftId);
136 | }
137 |
138 | started = false;
139 | delete balance[highestBidder];
140 | delete highestBidder;
141 | delete nft;
142 | delete nftId;
143 |
144 | emit End();
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/ChainLinkTask/CheckETHStatus.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.7;
3 |
4 | import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
5 | import "@chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol";
6 | import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
7 | import "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol";
8 | import "@openzeppelin/contracts/access/Ownable.sol";
9 |
10 | contract ChainLinkTask is VRFConsumerBaseV2, Ownable{
11 |
12 | AggregatorV3Interface internal priceFeed;
13 |
14 | VRFCoordinatorV2Interface COORDINATOR;
15 | LinkTokenInterface LINKTOKEN;
16 |
17 | // Your subscription ID.
18 | uint64 public s_subscriptionId;
19 |
20 | // Rinkeby coordinator.
21 | address vrfCoordinator = 0x6168499c0cFfCaCD319c818142124B7A15E857ab;
22 |
23 | // Rinkeby LINK token contract.
24 | address link_token_contract = 0x01BE23585060835E02B77ef475b0Cc51aA1e0709;
25 |
26 | // The gas lane to use, which specifies the maximum gas price to bump to.
27 | // For a list of available gas lanes on each network,
28 | // see https://docs.chain.link/docs/vrf-contracts/#configurations
29 | bytes32 keyHash = 0xd89b2bf150e3b9e13446986e571fb9cab24b13cea0a43ea20a6049a85cc807cc;
30 |
31 | // Depends on the number of requested values that you want sent to the
32 | // fulfillRandomWords() function. Storing each word costs about 20,000 gas,
33 | // so 100,000 is a safe default for this example contract. Test and adjust
34 | // this limit based on the network that you select, the size of the request,
35 | // and the processing of the callback request in the fulfillRandomWords()
36 | // function.
37 | uint32 callbackGasLimit = 100000;
38 |
39 | // The default is 3, but you can set this higher.
40 | uint16 requestConfirmations = 3;
41 |
42 | // For this example, retrieve 2 random values in one request.
43 | // Cannot exceed VRFCoordinatorV2.MAX_NUM_WORDS.
44 | uint32 numWords = 1;
45 |
46 | int256 public s_randomWords;
47 | uint256 public s_requestId;
48 | int public ETHPrice;
49 | address s_owner;
50 |
51 |
52 | constructor(uint64 subscriptionId) VRFConsumerBaseV2(vrfCoordinator){
53 | COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator);
54 | LINKTOKEN = LinkTokenInterface(link_token_contract);
55 | /**
56 | * Network: Rinkeby
57 | * Aggregator: ETH/USD
58 | * Address: 0x8A753747A1Fa494EC906cE90E9f37563A8AF630e
59 | */
60 | priceFeed = AggregatorV3Interface(0x8A753747A1Fa494EC906cE90E9f37563A8AF630e);
61 | s_subscriptionId = subscriptionId;
62 | }
63 |
64 |
65 |
66 | /**@dev fetches ETH price in USD*/
67 | function getETHPrice() public {
68 | (,int price,,,) = priceFeed.latestRoundData();
69 | ETHPrice = price / 10**8;
70 | }
71 |
72 |
73 |
74 | /**@dev processes and stores the received random values
75 | and makes sure that it is between 1000 and 200-
76 | */
77 | function fulfillRandomWords(
78 | uint256, /* requestId */
79 | uint256[] memory randomWords
80 | ) internal override {
81 | s_randomWords = int((randomWords[0] % 2000)+1000);
82 | if(s_randomWords > 2000){
83 | s_randomWords = s_randomWords - 1000;
84 | }
85 | }
86 |
87 |
88 | /**
89 | @dev check the ETH status
90 | returns true for ETH greater than random number
91 | and false for ETH price less than random number;
92 | */
93 | function checkETHStatus() external view returns(bool){
94 | bool status;
95 | if(ETHPrice > s_randomWords){
96 | status = true;
97 | }
98 | return status;
99 | }
100 |
101 |
102 | /**@dev request random number to be able to use vrf */
103 | // 1000000000000000000 = 1 LINK
104 | // Assumes the subscription is funded sufficiently.
105 | function requestRandomNumber() external onlyOwner {
106 | // Will revert if subscription is not set and funded.
107 | s_requestId = COORDINATOR.requestRandomWords(
108 | keyHash,
109 | s_subscriptionId,
110 | requestConfirmations,
111 | callbackGasLimit,
112 | numWords
113 | );
114 | }
115 |
116 | }
--------------------------------------------------------------------------------
/ChainLinkTask/deployment.md:
--------------------------------------------------------------------------------
1 | # Deployment
2 |
3 | 1. get Link token from [link faucet](https://faucets.chain.link/rinkeby?_ga=2.234874718.1328601376.1660094136-874318943.1659360958)
4 |
5 | 2. create a subscription here [subscription](https://vrf.chain.link/)
6 |
7 | 3. Copy the .sol file in this folder to remix. choose Injected web3 and network rinkeby.
8 | 4. Deploy the contract pass in your subscription to the constructor. Then add the deployed contract address has a consumer on your subscription page
9 | 5. call `getETHPrice()` to get ETH price
10 | 6. call `requestRandomNumber()` to get a random. `Note this usually takes time, you can go here to check the fulliment status your subscription Id `
11 | 7. call `checkETHStatus()` to check ETH status
12 |
13 |
--------------------------------------------------------------------------------
/Chainlinktask.md:
--------------------------------------------------------------------------------
1 | # chainlink bounty for Active members only
2 |
3 | this bounty is made public but only submissions from the active members will be considered for evaluation.
4 | - **Details**
5 | - this is a chainlink focused bounty
6 | - the task is to take help of chainlink's VRF and AggregatorV3Interface for the bounty
7 | - the task is to get the current price of ETH in USD and then generate a random number between 1000-2000 and compare them
8 | - if the price of ETH > Random number return true else return false.
9 | - we will only accept a solidity file and no JS or Python file will be accepted.
10 | - use ethereum test network for the process.
11 |
12 |
13 |
14 |
15 | - **evaluation createria:**
16 | - proper commenting
17 | - time to submission
18 | - efficiency of code
19 | - proper md file with one test case data and Screenshot of the test result.
20 | - plagerism percentage
21 |
22 |
23 | - **Rewards**
24 | - the winner of the bounty will recieve a bounty of RS 1000 INR
25 |
26 |
27 | - **Deadline**
28 | - the final date to submit your code is till friday 12th August 23:59 IST.
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/Compile.md:
--------------------------------------------------------------------------------
1 | # How to compile an ipynb file
2 | Run the .py file in a normal editor.
3 | The .ipynb file can be run quite easily in google colab or jupyter notebooks
4 | ipynb is used for computational notebooks like Jupyter and cannot be opened in a text editor like VS code
5 |
--------------------------------------------------------------------------------
/Constant Product AMM/CPAMM.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8;
3 |
4 | interface IERC20 {
5 | function totalSupply() external view returns (uint);
6 |
7 | function balanceOf(address account) external view returns (uint);
8 |
9 | function transfer(address recipient, uint amount) external returns (bool);
10 |
11 | function allowance(address owner, address spender)
12 | external
13 | view
14 | returns (uint);
15 |
16 | function approve(address spender, uint amount) external returns (bool);
17 |
18 | function transferFrom(
19 | address sender,
20 | address recipient,
21 | uint amount
22 | ) external returns (bool);
23 |
24 | event Transfer(address indexed from, address indexed to, uint amount);
25 | event Approval(address indexed owner, address indexed spender, uint amount);
26 | }
27 |
28 | /**
29 | @title Constant Product Automated Market Maker
30 | @author Naman Vyas
31 | */
32 | contract CPAMM {
33 | /* State Variables */
34 | uint public FEE_FACTOR = uint(997) / uint(1000); // fee = 0.3%
35 | IERC20 public immutable tokenX;
36 | IERC20 public immutable tokenY;
37 | uint public reserveX;
38 | uint public reserveY;
39 | uint public totalSupply;
40 | mapping(address => uint) public balanceOf;
41 |
42 | /* Events */
43 | event Swap(
44 | address indexed from,
45 | IERC20 indexed tokenIn,
46 | uint amountIn,
47 | IERC20 indexed tokenOut,
48 | uint amountOut
49 | );
50 | event AddLiquidity(
51 | address indexed to,
52 | uint amountX,
53 | uint amountY,
54 | uint shares
55 | );
56 | event RemoveLiquidity(
57 | address from,
58 | uint shares,
59 | uint amountX,
60 | uint amountY
61 | );
62 |
63 | constructor(address _tokenX, address _tokenY) {
64 | tokenX = IERC20(_tokenX);
65 | tokenY = IERC20(_tokenY);
66 | }
67 |
68 | /**
69 | @param _to investor
70 | @param _amount no. of shares.
71 | @notice Append shares to given address and update totalSupply.
72 | */
73 | function _mint(address _to, uint _amount) private {
74 | balanceOf[_to] += _amount;
75 | totalSupply += _amount;
76 | }
77 |
78 | /**
79 | @param _from investor
80 | @param _amount no. of shares.
81 | @notice Remove shares from given address and update totalSupply.
82 | */
83 | function _burn(address _from, uint _amount) private {
84 | balanceOf[_from] -= _amount;
85 | totalSupply -= _amount;
86 | }
87 |
88 | /**
89 | @param _reserveX updated value of reserveX
90 | @param _reserveY updated value of reserveY
91 | @notice update reserves.
92 | */
93 | function _update(uint _reserveX, uint _reserveY) private {
94 | reserveX = _reserveX;
95 | reserveY = _reserveY;
96 | }
97 |
98 | /**
99 | @param _tokenIn type of token.
100 | @param _amountIn no. of tokens.
101 |
102 | @notice Swap tokens by transferring {_amountIn} tokens of type {_tokenIn}
103 | to the contract (or pool). In returns Contract calculates the fee and
104 | transfer complimentary tokens to the user.
105 |
106 | @dev Formula used: dy = ydx / (x + dx).
107 | also, while updating reserves, we calculate the new value of reserves
108 | manually instead of relying on balance of contract of tokenX and tokenY.
109 | Because someone can transfer the tokens to the contract and manipulate the
110 | reserves of contract.
111 | */
112 | function swap(address _tokenIn, uint _amountIn) external {
113 | require(
114 | _tokenIn == address(tokenX) || _tokenIn == address(tokenY),
115 | "Invalid token."
116 | );
117 | require(_amountIn > 0, "amountIn = 0");
118 |
119 | bool isTokenX = _tokenIn == address(tokenX);
120 | (
121 | IERC20 tokenIn,
122 | IERC20 tokenOut,
123 | uint reserveIn,
124 | uint reserveOut
125 | ) = isTokenX
126 | ? (tokenX, tokenY, reserveX, reserveY)
127 | : (tokenY, tokenX, reserveY, reserveX);
128 |
129 | // Transfer tokens to the pool.
130 | tokenIn.transferFrom(msg.sender, address(this), _amountIn);
131 |
132 | /* Swapping */
133 |
134 | uint amountInWithFee = _amountIn * FEE_FACTOR;
135 |
136 | // formula for amountOut:-
137 | // dy = ydx / (x + dx);
138 | uint amountOut = (reserveOut * amountInWithFee) /
139 | (reserveIn + amountInWithFee);
140 |
141 | // transfer swapped tokens
142 | tokenOut.transfer(msg.sender, amountOut);
143 |
144 | // Update reserves
145 | (uint _newReserveX, uint _newReserveY) = isTokenX
146 | ? (reserveX + _amountIn, reserveY - amountOut)
147 | : (reserveX - amountOut, reserveY + _amountIn);
148 |
149 | _update(_newReserveX, _newReserveY);
150 |
151 | emit Swap(msg.sender, tokenIn, _amountIn, tokenOut, amountOut);
152 | }
153 |
154 | /**
155 | @param _amountX no. of tokenX
156 | @param _amountY no. of tokenY
157 |
158 | @notice Transfer {_amountX} of tokenX and {_amountY} of tokenY to
159 | the contract (or pool) and mint corresponding number of shares to
160 | the user. user must ensure that the ratio of {_amountX} and {_amountY}
161 | should be equal to current price. To get current price, use function
162 | getPrice().
163 | */
164 | function addLiquidity(uint _amountX, uint _amountY)
165 | external
166 | {
167 | tokenX.transferFrom(msg.sender, address(this), _amountX);
168 | tokenY.transferFrom(msg.sender, address(this), _amountY);
169 |
170 | if (reserveX > 0 || reserveY > 0) {
171 | // Check if the given amount corresponds to current price
172 | // price = x/y
173 | // hence, dx/dy should be equal to x/y
174 | require(reserveX * _amountY == reserveY * _amountX, "x / y != dx / dy");
175 | }
176 |
177 | /* Mint Shares */
178 |
179 | // Liquidity = f(x, y) = xy
180 | // shares should be proportional to liquidity, hence we've
181 | // chosen sqrt() function for shares.
182 | // shares = sqrt(Liquidity) = sqrt(xy).
183 | uint shares;
184 | if (totalSupply == 0) {
185 | // formula: sqrt(xy)
186 | shares = _sqrt(_amountX * _amountY);
187 | } else {
188 | // formula:-
189 | // shares = increase in liquidity * total liquidity
190 | // increase in liquidity = dx/x = dy/y
191 | // shares = (dx/x) * T = (dy/y) * T
192 | // for security reasons, we'll take minimum of these two. Although they
193 | // should be same in ideal case.
194 | shares = _min(
195 | (_amountX * totalSupply) / reserveX,
196 | (_amountY * totalSupply) / reserveY
197 | );
198 | }
199 | require(shares > 0, "shares == 0");
200 | _mint(msg.sender, shares);
201 |
202 | _update(reserveX + _amountX, reserveY + _amountY);
203 |
204 | emit AddLiquidity(
205 | msg.sender,
206 | _amountX,
207 | _amountY,
208 | shares
209 | );
210 | }
211 |
212 | /**
213 | @param _shares no. of shares to remove
214 |
215 | @notice Remove {_shares} no. of shares and transfer {amountX} of tokenX
216 | and {amountY} of tokenY to the user.
217 | */
218 | function removeLiquidity(uint _shares)
219 | external
220 | {
221 | // formula:-
222 | // dx = s / T * x
223 | // dy = s / T * y
224 | uint amountX = (_shares * reserveX) / totalSupply;
225 | uint amountY = (_shares * reserveY) / totalSupply;
226 | require(
227 | amountX > 0
228 | && amountY > 0,
229 | "amountX or amountY == 0"
230 | );
231 |
232 | _burn(msg.sender, _shares);
233 | _update(reserveX - amountX, reserveY - amountY);
234 |
235 | tokenX.transfer(msg.sender, amountX);
236 | tokenY.transfer(msg.sender, amountY);
237 |
238 | emit RemoveLiquidity(
239 | msg.sender,
240 | _shares,
241 | amountX,
242 | amountY
243 | );
244 | }
245 |
246 | function getPrice() public view returns (uint price) {
247 | price = reserveX / reserveY;
248 | }
249 |
250 | function _sqrt(uint y) private pure returns (uint z) {
251 | if (y > 3) {
252 | z = y;
253 | uint x = y / 2 + 1;
254 | while (x < z) {
255 | z = x;
256 | x = (y / x + x) / 2;
257 | }
258 | } else if (y != 0) {
259 | z = 1;
260 | }
261 | }
262 |
263 | function _min(uint x, uint y) private pure returns (uint) {
264 | return x <= y ? x : y;
265 | }
266 | }
--------------------------------------------------------------------------------
/Constant Product AMM/deployment.md:
--------------------------------------------------------------------------------
1 | During deployment ensure the following things.
2 |
3 | 1. Before deployment
4 | * Address of tokenX and tokenY - You need to provide these two as constructor args while deployment.
5 | 2. After deployment
6 | * Approve contract to execute transferFrom() function - if a user wants to enter the Pool, then S/he needs to approve the contract address for both tokens.
7 |
--------------------------------------------------------------------------------
/MultiSig.md:
--------------------------------------------------------------------------------
1 | # Multi-Signature Wallet Smart Contract
2 |
3 | A simple solidity smart contract for MultiSig Wallet.
4 |
5 | # To-do
6 |
7 | 1. A scalable multi-signature wallet contract, which requires a minimum of 60% authorization by the signatory wallets to perform a transaction.
8 | 2. Access registry contract that stores the signatories of this multi-sig wallet by address. This access registry contract has its own admin.
9 | 3. Capable of adding, revoking, renouncing, and transfer of signatory functionalities.
10 | 4. For a plus point, you can add your own stuff too, to make it more intersting.
11 | 5. Add comments where you used your own creativity.
12 |
13 | # On Solving Bounty
14 |
15 | 1. While creating the code use proper comments.
16 | 2. Cash Bounty for this task is Rs.1000 (INR).
17 | 3. Submission Deadline - 1st Sept. 3:00 pm IST to 4th Aug. 3:00pm IST
18 | 4. Don't forget to Fork the repo and create a pull request.
19 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Learnweb3
2 | solve basic tasks of web3
3 |
4 | Here we will be posting some basic tasks that are used in many blockchain projects and that are simple and independent and no not require much knowledge of development or coding.
5 | some of these will be done using solidity , or web3.js or ethers.js.
6 |
7 | you will also be using some basics of html and react.
8 |
9 | these tasks are for basic understandong of blockchians
10 |
--------------------------------------------------------------------------------
/SoulBoundTokenContract/soulboundToken.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.7;
3 |
4 | /* These Soulbound smart contract represents the NFTs which are certificates of
5 | an degree for students passed out from college.
6 | The owner(college) of the contract need to issue an certificate for a student so that
7 | the student can claim the certificate only once and not transferable
8 | */
9 | import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
10 | import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
11 | import "@openzeppelin/contracts/access/Ownable.sol";
12 | import "@openzeppelin/contracts/utils/Counters.sol";
13 |
14 | contract SoulBoundNFT is ERC721, ERC721URIStorage, Ownable {
15 | // counters used to track the number of ids and issuing ERC721 ids
16 | using Counters for Counters.Counter;
17 | Counters.Counter private _tokenIdCounter;
18 |
19 | // cerificates var maps between student address and the tokenURI that contains metadata
20 | mapping(address => string) public certificates;
21 |
22 | // issuedCerrtificates var maps student address and the status of the certificate issued
23 | mapping(address => bool) public issuedCertificate;
24 |
25 | constructor() ERC721("SoulDegree", "SDG") {}
26 |
27 | /* @dev checks whether certificate issued or not
28 | @param _studentAddress is the address to check whether certificate issued or not
29 | */
30 | function checkCertificateissued(address _studentAddress)
31 | public
32 | view
33 | returns (bool)
34 | {
35 | return issuedCertificate[_studentAddress];
36 | }
37 |
38 | // @dev issues certificate to a address mentioned by the owner only
39 | function issueCertificate(address _to) public onlyOwner {
40 | issuedCertificate[_to] = true;
41 | }
42 |
43 | /* @dev student claims certificate by minting NFT
44 | @param _tokenUri is the url to the metadata of the NFT
45 | */
46 | function claimCertificate(string memory _tokenUri) public {
47 | require(
48 | checkCertificateissued(msg.sender),
49 | "certificate is not issued or claimed"
50 | );
51 | issuedCertificate[msg.sender] = false;
52 | uint256 _tokenId = _tokenIdCounter.current();
53 | _tokenIdCounter.increment();
54 | _safeMint(msg.sender, _tokenId);
55 | _setTokenURI(_tokenId, _tokenUri);
56 | certificates[msg.sender] = _tokenUri;
57 | }
58 |
59 | /* @dev mints NFT directly to the student only by the owner
60 | @param _to is the address of the student to be minted
61 | @param _uri is the url of metadata associated with the specific student
62 | */
63 | function safeMint(address _to, string memory _uri) public onlyOwner {
64 | uint256 _tokenId = _tokenIdCounter.current();
65 | _tokenIdCounter.increment();
66 | _safeMint(_to, _tokenId);
67 | _setTokenURI(_tokenId, _uri);
68 | }
69 |
70 | /* @dev burns the NFT owned by owner , only owner can be allowed to burn token
71 | @param _tokenId is the id of the token to be burn
72 | */
73 | function burn(uint256 _tokenId) public {
74 | address owner = ERC721.ownerOf(_tokenId);
75 | require(owner == msg.sender, "Only Owner can burn token");
76 | _burn(_tokenId);
77 | }
78 |
79 | /* @dev revokes the NFT issued to someone by owner of the contract
80 | @param _tokenId is the id of the token to be revoked
81 | */
82 | function revokeToken(uint256 _tokenId) public onlyOwner {
83 | _burn(_tokenId);
84 | }
85 |
86 | /* @dev checks if @param from is set to zero, if yes then NFT will be transfered to new owner
87 | if No then the transaction to be minted is stopped so that the NFT will become
88 | non-transferable
89 | */
90 | function _beforeTokenTransfer(
91 | address from,
92 | address to,
93 | uint256 _tokenId
94 | ) internal virtual override {
95 | // checks if the revoke is happening which is done by owner
96 | if (msg.sender != owner()) {
97 | require(from == address(0), "token transfer is blocked");
98 | }
99 | super._beforeTokenTransfer(from, to, _tokenId);
100 | }
101 |
102 | // _burn and tokenURI are overridden from the parent contracts
103 | function _burn(uint256 _tokenId)
104 | internal
105 | override(ERC721, ERC721URIStorage)
106 | {
107 | super._burn(_tokenId);
108 | }
109 |
110 | function tokenURI(uint256 _tokenId)
111 | public
112 | view
113 | override(ERC721, ERC721URIStorage)
114 | returns (string memory)
115 | {
116 | return super.tokenURI(_tokenId);
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/Soulbound Tokens Smart Contract.md:
--------------------------------------------------------------------------------
1 | # SoulBound Tokens Smart Contract
2 |
3 | A simple solidity smart contract for Soulbound tokens.
4 |
5 | # To-do
6 | 1. Non-fungible tokens (NFTs) that are non-transferrable between wallets.
7 | 2. It should be mintable (burnable if you want extra creative points) and ownable.
8 | 3. It should have a function in which Owner can revoke the token which is minted or burnt.
9 | 4. For a plus point, you can add your own stuff too, to make it more intersting.
10 | 5. Add comments where you used your own creativity.
11 |
12 | # On Solving Bounty
13 |
14 | 1. While creating the code use proper comments.
15 | 2. Cash Bounty for this task is Rs.1000 (INR).
16 | 3. Submission Deadline - 20th Aug. 1:00 pm IST to 23rd Aug. 1:00pm IST
17 | 4. Fork the repo and create a pull request.
18 |
--------------------------------------------------------------------------------
/Task2.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "nbformat": 4,
3 | "nbformat_minor": 0,
4 | "metadata": {
5 | "colab": {
6 | "name": "Task2.ipynb",
7 | "provenance": [],
8 | "authorship_tag": "ABX9TyOIrjbI4EIOVp3Q8RvsqHve",
9 | "include_colab_link": true
10 | },
11 | "kernelspec": {
12 | "name": "python3",
13 | "display_name": "Python 3"
14 | },
15 | "language_info": {
16 | "name": "python"
17 | }
18 | },
19 | "cells": [
20 | {
21 | "cell_type": "markdown",
22 | "metadata": {
23 | "id": "view-in-github",
24 | "colab_type": "text"
25 | },
26 | "source": [
27 | ""
28 | ]
29 | },
30 | {
31 | "cell_type": "markdown",
32 | "source": [
33 | "# GENERATE PRIVATE KEY"
34 | ],
35 | "metadata": {
36 | "id": "7p7IL-z0hz4M"
37 | }
38 | },
39 | {
40 | "cell_type": "markdown",
41 | "source": [
42 | "## Generated using the strong RNG(secrets) provided by Python"
43 | ],
44 | "metadata": {
45 | "id": "DN6vPYG3iJyi"
46 | }
47 | },
48 | {
49 | "cell_type": "code",
50 | "execution_count": 3,
51 | "metadata": {
52 | "colab": {
53 | "base_uri": "https://localhost:8080/"
54 | },
55 | "id": "ObJenHKIhuE9",
56 | "outputId": "2a8916d3-ddae-4956-a20f-5ff39544726b"
57 | },
58 | "outputs": [
59 | {
60 | "output_type": "stream",
61 | "name": "stdout",
62 | "text": [
63 | "6a20747aabd06f0ccafd4cd996f830ad171ab9482de11e433a7e79b6d9f1678a\n"
64 | ]
65 | }
66 | ],
67 | "source": [
68 | "import secrets\n",
69 | "\n",
70 | "# create a random 256 address\n",
71 | "# take its hex and remoce first two elements (0x)\n",
72 | "privateKey1 = hex(secrets.randbits(256))[2:]\n",
73 | "\n",
74 | "print(privateKey1)"
75 | ]
76 | },
77 | {
78 | "cell_type": "markdown",
79 | "source": [
80 | "## Creating a simpler Bitaddress model\n",
81 | " * Code inspiration from ==> https://www.freecodecamp.org/news/how-to-generate-your-very-own-bitcoin-private-key-7ad0f4936e6c/\n"
82 | ],
83 | "metadata": {
84 | "id": "y6nwR4WAi04X"
85 | }
86 | },
87 | {
88 | "cell_type": "markdown",
89 | "source": [
90 | "Imports\n",
91 | "\n",
92 | "\n"
93 | ],
94 | "metadata": {
95 | "id": "v1DPP8GnkrI_"
96 | }
97 | },
98 | {
99 | "cell_type": "code",
100 | "source": [
101 | "import time\n",
102 | "import random"
103 | ],
104 | "metadata": {
105 | "id": "Df-NuowbngkC"
106 | },
107 | "execution_count": 14,
108 | "outputs": []
109 | },
110 | {
111 | "cell_type": "markdown",
112 | "source": [
113 | "Generator Class"
114 | ],
115 | "metadata": {
116 | "id": "pq_mGeorowLk"
117 | }
118 | },
119 | {
120 | "cell_type": "code",
121 | "source": [
122 | "class PrivateKeyGenerator:\n",
123 | " def __init__(self):\n",
124 | " self.POOL_SIZE = 256\n",
125 | " self.KEY_BYTES = 32\n",
126 | " self.CURVE_ORDER = int('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141', 16)\n",
127 | " self.pool = [0] * self.POOL_SIZE\n",
128 | " self.pool_pointer = 0\n",
129 | " self.prng_state = None\n",
130 | " self.__init_pool()\n",
131 | "\n",
132 | "# 1st Step : Initializing The Pool\n",
133 | " # Entropy using __seed_int and __seed_byte\n",
134 | " def __init_pool(self):\n",
135 | " for i in range(self.POOL_SIZE):\n",
136 | " # create and seed a random pool of 8 bits\n",
137 | " self.__seed_byte(secrets.randbits(8))\n",
138 | "\n",
139 | " # time for entropy\n",
140 | " self.__seed_int(int(time.time()))\n",
141 | "\n",
142 | " def __seed_int(self, n):\n",
143 | " self.__seed_byte(n)\n",
144 | " self.__seed_byte(n >> 8)\n",
145 | " self.__seed_byte(n >> 16)\n",
146 | " self.__seed_byte(n >> 24)\n",
147 | "\n",
148 | " def __seed_byte(self, n):\n",
149 | " self.pool[self.pool_pointer] ^= n & 255\n",
150 | " self.pool_pointer += 1\n",
151 | "\n",
152 | " if self.pool_pointer >= self.POOL_SIZE:\n",
153 | " self.pool_pointer = 0\n",
154 | "\n",
155 | "# 2nd Step: Seeding the Input\n",
156 | " # Put a timestamp and then input string\n",
157 | " def seed_input(self, str_input):\n",
158 | " self.__seed_int(int(time.time()))\n",
159 | "\n",
160 | " for char in str_input:\n",
161 | " self.__seed_byte(ord(char))\n",
162 | "\n",
163 | "# 3rd Step: Generating the Private Key\n",
164 | " # getstate and setstate used to conserve entropy\n",
165 | " # Key of range (1, Curve_Order)\n",
166 | " # Curve_Order is the order of secp256k1\n",
167 | " # Convert to hex and strip (\"0x\")\n",
168 | "\n",
169 | " def generate_key(self):\n",
170 | " bigInt = self.__generateBigInt()\n",
171 | " bigInt = bigInt % (self.CURVE_ORDER - 1) # key < curve order\n",
172 | " bigInt = bigInt + 1 # key > 0\n",
173 | "\n",
174 | " keyHex = hex(bigInt)\n",
175 | " key = keyHex[2:]\n",
176 | " return key\n",
177 | "\n",
178 | " def __generateBigInt(self):\n",
179 | " if self.prng_state is None:\n",
180 | " seed = int.from_bytes(self.pool, byteorder='big', signed=False)\n",
181 | " random.seed(seed)\n",
182 | "\n",
183 | " self.prng_state = random.getstate()\n",
184 | " random.setstate(self.prng_state)\n",
185 | " \n",
186 | " bigInt = random.getrandbits(self.KEY_BYTES * 8)\n",
187 | " self.prng_state = random.getstate()\n",
188 | " \n",
189 | " return bigInt"
190 | ],
191 | "metadata": {
192 | "id": "li0X-rBnnlM4"
193 | },
194 | "execution_count": 17,
195 | "outputs": []
196 | },
197 | {
198 | "cell_type": "markdown",
199 | "source": [
200 | "Trial Call"
201 | ],
202 | "metadata": {
203 | "id": "dG-DVwjYnFHu"
204 | }
205 | },
206 | {
207 | "cell_type": "code",
208 | "source": [
209 | "privKeyGen = PrivateKeyGenerator()\n",
210 | "privKeyGen.seed_input('Ranchoddas Shamaldas Chanchad :)')\n",
211 | "\n",
212 | "privateKey2 = privKeyGen.generate_key()\n",
213 | "print(privateKey2)"
214 | ],
215 | "metadata": {
216 | "colab": {
217 | "base_uri": "https://localhost:8080/"
218 | },
219 | "id": "XFgVj0p6nD8j",
220 | "outputId": "4cc70e26-3b8a-4b33-86ca-e82b5fe6dd47"
221 | },
222 | "execution_count": 19,
223 | "outputs": [
224 | {
225 | "output_type": "stream",
226 | "name": "stdout",
227 | "text": [
228 | "4979e71e76a5350404ca5af7a0b106c5e0cd65e35a7b81f272b34f2928d96275\n"
229 | ]
230 | }
231 | ]
232 | },
233 | {
234 | "cell_type": "markdown",
235 | "source": [
236 | "# GET PUBLIC KEY"
237 | ],
238 | "metadata": {
239 | "id": "Wp0oaY2mpxgK"
240 | }
241 | },
242 | {
243 | "cell_type": "markdown",
244 | "source": [
245 | "Code Inspiration from => https://medium.com/@kootsZhin/step-by-step-guide-to-getting-bitcoin-address-from-private-key-in-python-7ec15072b71b"
246 | ],
247 | "metadata": {
248 | "id": "rJjo1c_4rBZf"
249 | }
250 | },
251 | {
252 | "cell_type": "markdown",
253 | "source": [
254 | "## Imports"
255 | ],
256 | "metadata": {
257 | "id": "9lJ0fYY2rIUP"
258 | }
259 | },
260 | {
261 | "cell_type": "code",
262 | "source": [
263 | "import hashlib\n",
264 | "import base58\n",
265 | "import codecs\n",
266 | "import ecdsa"
267 | ],
268 | "metadata": {
269 | "id": "YgIZA6mJp6c4"
270 | },
271 | "execution_count": 23,
272 | "outputs": []
273 | },
274 | {
275 | "cell_type": "markdown",
276 | "source": [
277 | "## Step 1: Get uncompressed Public Key"
278 | ],
279 | "metadata": {
280 | "id": "pB-17KglrMt8"
281 | }
282 | },
283 | {
284 | "cell_type": "code",
285 | "source": [
286 | "# Hex decoding the private key to bytes using codecs library and Generating a public key in bytes using SECP256k1 & ecdsa library\n",
287 | "publicKeyBytes = (ecdsa.SigningKey.from_string(codecs.decode(privateKey2, 'hex'), curve=ecdsa.SECP256k1).verifying_key).to_string()\n",
288 | "\n",
289 | "# Hex encoding the public key from bytes\n",
290 | "publicKeyHex = codecs.encode(publicKeyBytes, 'hex')\n",
291 | "\n",
292 | "# Bitcoin public key begins with bytes 0x04 so we have to add the bytes at the start\n",
293 | "publicKey = (b'04' + publicKeyHex).decode(\"utf-8\")\n",
294 | "\n",
295 | "print(publicKey)"
296 | ],
297 | "metadata": {
298 | "colab": {
299 | "base_uri": "https://localhost:8080/"
300 | },
301 | "id": "8gHX0D7Kq322",
302 | "outputId": "e9e48aa2-8761-4db3-8c67-8f7361d1ea8e"
303 | },
304 | "execution_count": 27,
305 | "outputs": [
306 | {
307 | "output_type": "stream",
308 | "name": "stdout",
309 | "text": [
310 | "04f41172a93c094f1984cfedf490617ff86526820b044534c2221fc6de21fc4e4c25a11ef2c95f3a59c6f845a176094dccbd70c9ee5ac824a11fa88dc8994cd952\n"
311 | ]
312 | }
313 | ]
314 | },
315 | {
316 | "cell_type": "markdown",
317 | "source": [
318 | "## Step 2: Get Compressed Public Key"
319 | ],
320 | "metadata": {
321 | "id": "26JeZyiWsHFR"
322 | }
323 | },
324 | {
325 | "cell_type": "code",
326 | "source": [
327 | "# Checking if the last byte is odd or even\n",
328 | "if (ord(bytearray.fromhex(publicKey[-2:])) % 2 == 0):\n",
329 | " publicKeyCompressed = '02'\n",
330 | "else:\n",
331 | " publicKeyCompressed = '03'\n",
332 | " \n",
333 | "# Add bytes 0x02 to the X of the key if even or 0x03 if odd\n",
334 | "publicKeyCompressed += publicKey[2:66]\n",
335 | "\n",
336 | "print(publicKeyCompressed)"
337 | ],
338 | "metadata": {
339 | "colab": {
340 | "base_uri": "https://localhost:8080/"
341 | },
342 | "id": "NqzuQOqGsM1w",
343 | "outputId": "3d99be25-ad0f-4ec9-cd98-4cae094c98d4"
344 | },
345 | "execution_count": 28,
346 | "outputs": [
347 | {
348 | "output_type": "stream",
349 | "name": "stdout",
350 | "text": [
351 | "02f41172a93c094f1984cfedf490617ff86526820b044534c2221fc6de21fc4e4c\n"
352 | ]
353 | }
354 | ]
355 | },
356 | {
357 | "cell_type": "markdown",
358 | "source": [
359 | "## Step 3: Perform SHA-256 hashing on the compressed public key"
360 | ],
361 | "metadata": {
362 | "id": "PJR3poYksorz"
363 | }
364 | },
365 | {
366 | "cell_type": "code",
367 | "source": [
368 | "# Converting to bytearray for SHA-256 hashing\n",
369 | "hexStr = bytearray.fromhex(publicKeyCompressed)\n",
370 | "\n",
371 | "# Apply sha\n",
372 | "sha = hashlib.sha256()\n",
373 | "sha.update(hexStr)\n",
374 | "sha.hexdigest() # .hexdigest() is hex ASCII"
375 | ],
376 | "metadata": {
377 | "colab": {
378 | "base_uri": "https://localhost:8080/",
379 | "height": 35
380 | },
381 | "id": "dKFhmM_psr09",
382 | "outputId": "1cdf54fd-c7d3-4b34-f056-414b5ce7fa36"
383 | },
384 | "execution_count": 29,
385 | "outputs": [
386 | {
387 | "output_type": "execute_result",
388 | "data": {
389 | "text/plain": [
390 | "'433218daec7ea37fd88bd6fe1220254a859ae5436f6f1da320864f457b0f25bf'"
391 | ],
392 | "application/vnd.google.colaboratory.intrinsic+json": {
393 | "type": "string"
394 | }
395 | },
396 | "metadata": {},
397 | "execution_count": 29
398 | }
399 | ]
400 | },
401 | {
402 | "cell_type": "markdown",
403 | "source": [
404 | "## Step 4: Perform RIPMED-160 hashing on the result of SHA-256"
405 | ],
406 | "metadata": {
407 | "id": "KwI_LyEbs9Qs"
408 | }
409 | },
410 | {
411 | "cell_type": "code",
412 | "source": [
413 | "rip = hashlib.new('ripemd160')\n",
414 | "rip.update(sha.digest())\n",
415 | "\n",
416 | "keyHash = rip.hexdigest()\n",
417 | "print(keyHash) # Hash160"
418 | ],
419 | "metadata": {
420 | "colab": {
421 | "base_uri": "https://localhost:8080/"
422 | },
423 | "id": "XCdFQyF_tBZJ",
424 | "outputId": "52c4a0da-7f17-4ce7-d12e-415f5fda4c62"
425 | },
426 | "execution_count": 32,
427 | "outputs": [
428 | {
429 | "output_type": "stream",
430 | "name": "stdout",
431 | "text": [
432 | "3bbfe5ca08337d6861fbf3104c504dfe80ebc303\n"
433 | ]
434 | }
435 | ]
436 | },
437 | {
438 | "cell_type": "markdown",
439 | "source": [
440 | "## Step 5: Add version byte in front of RIPEMD-160 hash (0x00 for Main Network)"
441 | ],
442 | "metadata": {
443 | "id": "B-v5LicrtSAt"
444 | }
445 | },
446 | {
447 | "cell_type": "code",
448 | "source": [
449 | "modifiedKeyHash = \"00\" + keyHash\n",
450 | "print(modifiedKeyHash)"
451 | ],
452 | "metadata": {
453 | "colab": {
454 | "base_uri": "https://localhost:8080/"
455 | },
456 | "id": "6SIMbrgRtf_Y",
457 | "outputId": "fbc0f712-8ab6-4a97-da06-7c58a37501c8"
458 | },
459 | "execution_count": 33,
460 | "outputs": [
461 | {
462 | "output_type": "stream",
463 | "name": "stdout",
464 | "text": [
465 | "003bbfe5ca08337d6861fbf3104c504dfe80ebc303\n"
466 | ]
467 | }
468 | ]
469 | },
470 | {
471 | "cell_type": "markdown",
472 | "source": [
473 | "## Step 6: Perform SHA-256 hash on the extended RIPEMD-160 result (Base58Check encoding)"
474 | ],
475 | "metadata": {
476 | "id": "tfDdswxIuJ6i"
477 | }
478 | },
479 | {
480 | "cell_type": "code",
481 | "source": [
482 | "# Converting to bytearray for SHA-256 hashing\n",
483 | "hexString = bytearray.fromhex(modifiedKeyHash)\n",
484 | "\n",
485 | "# Apply sha\n",
486 | "sha2 = hashlib.sha256()\n",
487 | "sha2.update(hexString)\n",
488 | "sha2.hexdigest()"
489 | ],
490 | "metadata": {
491 | "colab": {
492 | "base_uri": "https://localhost:8080/",
493 | "height": 35
494 | },
495 | "id": "mmwrmh1-uQ-2",
496 | "outputId": "19c33aaa-de9a-4858-f5bd-915cc2e993b6"
497 | },
498 | "execution_count": 37,
499 | "outputs": [
500 | {
501 | "output_type": "execute_result",
502 | "data": {
503 | "text/plain": [
504 | "'b387601c7b426a4dfca774a2918705fb7e0fc36a5a56ea5a4486d71be59c31ca'"
505 | ],
506 | "application/vnd.google.colaboratory.intrinsic+json": {
507 | "type": "string"
508 | }
509 | },
510 | "metadata": {},
511 | "execution_count": 37
512 | }
513 | ]
514 | },
515 | {
516 | "cell_type": "markdown",
517 | "source": [
518 | "## Step 7: Perform SHA-256 hash on the result of the previous SHA-256 hash"
519 | ],
520 | "metadata": {
521 | "id": "GJ36bidquzwY"
522 | }
523 | },
524 | {
525 | "cell_type": "code",
526 | "source": [
527 | "sha3 = hashlib.sha256()\n",
528 | "sha3.update(sha.digest())\n",
529 | "sha3.hexdigest()"
530 | ],
531 | "metadata": {
532 | "colab": {
533 | "base_uri": "https://localhost:8080/",
534 | "height": 35
535 | },
536 | "id": "vvV7XX7Lu736",
537 | "outputId": "2f60c237-fbe2-4bba-d390-9dfa35dad46b"
538 | },
539 | "execution_count": 38,
540 | "outputs": [
541 | {
542 | "output_type": "execute_result",
543 | "data": {
544 | "text/plain": [
545 | "'ea1919789678de9e46a339abbb532e1dc10b00d6a24d417fed58f928c187a5f8'"
546 | ],
547 | "application/vnd.google.colaboratory.intrinsic+json": {
548 | "type": "string"
549 | }
550 | },
551 | "metadata": {},
552 | "execution_count": 38
553 | }
554 | ]
555 | },
556 | {
557 | "cell_type": "markdown",
558 | "source": [
559 | "## Step 8: Take the first 4 bytes of the second SHA-256 hash, this is the address checksum"
560 | ],
561 | "metadata": {
562 | "id": "Q1thhI9FvELp"
563 | }
564 | },
565 | {
566 | "cell_type": "code",
567 | "source": [
568 | "checkSum = sha3.hexdigest()[:8]\n",
569 | "\n",
570 | "print(checkSum)"
571 | ],
572 | "metadata": {
573 | "colab": {
574 | "base_uri": "https://localhost:8080/"
575 | },
576 | "id": "e29uAbrivJkp",
577 | "outputId": "0c1df17a-da29-4cdb-aa91-1dd32265712d"
578 | },
579 | "execution_count": 40,
580 | "outputs": [
581 | {
582 | "output_type": "stream",
583 | "name": "stdout",
584 | "text": [
585 | "ea191978\n"
586 | ]
587 | }
588 | ]
589 | },
590 | {
591 | "cell_type": "markdown",
592 | "source": [
593 | "## Step 9: Add the 4 checksum bytes from stage 8 at the end of extended RIPEMD-160 hash from stage 5, this is the 25-byte binary Bitcoin Address"
594 | ],
595 | "metadata": {
596 | "id": "XL1CYtWavYxx"
597 | }
598 | },
599 | {
600 | "cell_type": "code",
601 | "source": [
602 | "byte25Address = modifiedKeyHash + checkSum\n",
603 | "\n",
604 | "print(byte25Address)"
605 | ],
606 | "metadata": {
607 | "colab": {
608 | "base_uri": "https://localhost:8080/"
609 | },
610 | "id": "tb_raQP4vYcW",
611 | "outputId": "bc795618-9b69-4d15-fa4c-07f563c8c9ff"
612 | },
613 | "execution_count": 41,
614 | "outputs": [
615 | {
616 | "output_type": "stream",
617 | "name": "stdout",
618 | "text": [
619 | "003bbfe5ca08337d6861fbf3104c504dfe80ebc303ea191978\n"
620 | ]
621 | }
622 | ]
623 | },
624 | {
625 | "cell_type": "markdown",
626 | "source": [
627 | "## Step 10 (Final Result): Convert the result from a byte string into a base58 string using Base58Check encoding, this is the most commonly used Bitcoin Address format"
628 | ],
629 | "metadata": {
630 | "id": "KOO_4VBuvrbX"
631 | }
632 | },
633 | {
634 | "cell_type": "code",
635 | "source": [
636 | "address1 = base58.b58encode(bytes(bytearray.fromhex(byte25Address))).decode('utf-8')\n",
637 | "\n",
638 | "print(address1)"
639 | ],
640 | "metadata": {
641 | "colab": {
642 | "base_uri": "https://localhost:8080/"
643 | },
644 | "id": "rPir1Tt5vv7Z",
645 | "outputId": "6802844c-2812-489e-eda2-9ab7d1a85d9d"
646 | },
647 | "execution_count": 44,
648 | "outputs": [
649 | {
650 | "output_type": "stream",
651 | "name": "stdout",
652 | "text": [
653 | "16SvkbgZ4cqeWhTivTExWT4rvoT8Xs9bpj\n"
654 | ]
655 | }
656 | ]
657 | },
658 | {
659 | "cell_type": "markdown",
660 | "source": [
661 | "## ALTERNATIVE WAY: Making use of b58.encode_check from base58 library directly at Step 6"
662 | ],
663 | "metadata": {
664 | "id": "435LS3uKwtOm"
665 | }
666 | },
667 | {
668 | "cell_type": "code",
669 | "source": [
670 | "keyBytes = codecs.decode(modifiedKeyHash, 'hex')\n",
671 | "address2 = base58.b58encode_check(keyBytes).decode('utf-8')\n",
672 | "address2"
673 | ],
674 | "metadata": {
675 | "colab": {
676 | "base_uri": "https://localhost:8080/",
677 | "height": 35
678 | },
679 | "id": "GYHPm6cJwzxq",
680 | "outputId": "e25fc2f7-4a63-4880-9a73-82b5e431baa2"
681 | },
682 | "execution_count": 45,
683 | "outputs": [
684 | {
685 | "output_type": "execute_result",
686 | "data": {
687 | "text/plain": [
688 | "'16SvkbgZ4cqeWhTivTExWT4rvoT8Xs9bpj'"
689 | ],
690 | "application/vnd.google.colaboratory.intrinsic+json": {
691 | "type": "string"
692 | }
693 | },
694 | "metadata": {},
695 | "execution_count": 45
696 | }
697 | ]
698 | }
699 | ]
700 | }
--------------------------------------------------------------------------------
/Task2new.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "nbformat": 4,
3 | "nbformat_minor": 0,
4 | "metadata": {
5 | "colab": {
6 | "name": "Task2.ipynb",
7 | "provenance": []
8 | },
9 | "kernelspec": {
10 | "name": "python3",
11 | "display_name": "Python 3"
12 | },
13 | "language_info": {
14 | "name": "python"
15 | }
16 | },
17 | "cells": [
18 | {
19 | "cell_type": "markdown",
20 | "source": [
21 | "# GENERATE PRIVATE KEY"
22 | ],
23 | "metadata": {
24 | "id": "7p7IL-z0hz4M"
25 | }
26 | },
27 | {
28 | "cell_type": "markdown",
29 | "source": [
30 | "## Generated using the strong RNG(secrets) provided by Python"
31 | ],
32 | "metadata": {
33 | "id": "DN6vPYG3iJyi"
34 | }
35 | },
36 | {
37 | "cell_type": "code",
38 | "execution_count": null,
39 | "metadata": {
40 | "colab": {
41 | "base_uri": "https://localhost:8080/"
42 | },
43 | "id": "ObJenHKIhuE9",
44 | "outputId": "2a8916d3-ddae-4956-a20f-5ff39544726b"
45 | },
46 | "outputs": [
47 | {
48 | "output_type": "stream",
49 | "name": "stdout",
50 | "text": [
51 | "6a20747aabd06f0ccafd4cd996f830ad171ab9482de11e433a7e79b6d9f1678a\n"
52 | ]
53 | }
54 | ],
55 | "source": [
56 | "import secrets\n",
57 | "\n",
58 | "# create a random 256 address\n",
59 | "# take its hex and remoce first two elements (0x)\n",
60 | "privateKey1 = hex(secrets.randbits(256))[2:]\n",
61 | "\n",
62 | "print(privateKey1)"
63 | ]
64 | },
65 | {
66 | "cell_type": "markdown",
67 | "source": [
68 | "## Creating a simpler Bitaddress model\n",
69 | " * Code inspiration from ==> https://www.freecodecamp.org/news/how-to-generate-your-very-own-bitcoin-private-key-7ad0f4936e6c/\n"
70 | ],
71 | "metadata": {
72 | "id": "y6nwR4WAi04X"
73 | }
74 | },
75 | {
76 | "cell_type": "markdown",
77 | "source": [
78 | "Imports\n",
79 | "\n",
80 | "\n"
81 | ],
82 | "metadata": {
83 | "id": "v1DPP8GnkrI_"
84 | }
85 | },
86 | {
87 | "cell_type": "code",
88 | "source": [
89 | "import time\n",
90 | "import random"
91 | ],
92 | "metadata": {
93 | "id": "Df-NuowbngkC"
94 | },
95 | "execution_count": null,
96 | "outputs": []
97 | },
98 | {
99 | "cell_type": "markdown",
100 | "source": [
101 | "Generator Class"
102 | ],
103 | "metadata": {
104 | "id": "pq_mGeorowLk"
105 | }
106 | },
107 | {
108 | "cell_type": "code",
109 | "source": [
110 | "class PrivateKeyGenerator:\n",
111 | " def __init__(self):\n",
112 | " self.POOL_SIZE = 256\n",
113 | " self.KEY_BYTES = 32\n",
114 | " self.CURVE_ORDER = int('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141', 16)\n",
115 | " self.pool = [0] * self.POOL_SIZE\n",
116 | " self.pool_pointer = 0\n",
117 | " self.prng_state = None\n",
118 | " self.__init_pool()\n",
119 | "\n",
120 | "# 1st Step : Initializing The Pool\n",
121 | " # Entropy using __seed_int and __seed_byte\n",
122 | " def __init_pool(self):\n",
123 | " for i in range(self.POOL_SIZE):\n",
124 | " # create and seed a random pool of 8 bits\n",
125 | " self.__seed_byte(secrets.randbits(8))\n",
126 | "\n",
127 | " # time for entropy\n",
128 | " self.__seed_int(int(time.time()))\n",
129 | "\n",
130 | " def __seed_int(self, n):\n",
131 | " self.__seed_byte(n)\n",
132 | " self.__seed_byte(n >> 8)\n",
133 | " self.__seed_byte(n >> 16)\n",
134 | " self.__seed_byte(n >> 24)\n",
135 | "\n",
136 | " def __seed_byte(self, n):\n",
137 | " self.pool[self.pool_pointer] ^= n & 255\n",
138 | " self.pool_pointer += 1\n",
139 | "\n",
140 | " if self.pool_pointer >= self.POOL_SIZE:\n",
141 | " self.pool_pointer = 0\n",
142 | "\n",
143 | "# 2nd Step: Seeding the Input\n",
144 | " # Put a timestamp and then input string\n",
145 | " def seed_input(self, str_input):\n",
146 | " self.__seed_int(int(time.time()))\n",
147 | "\n",
148 | " for char in str_input:\n",
149 | " self.__seed_byte(ord(char))\n",
150 | "\n",
151 | "# 3rd Step: Generating the Private Key\n",
152 | " # getstate and setstate used to conserve entropy\n",
153 | " # Key of range (1, Curve_Order)\n",
154 | " # Curve_Order is the order of secp256k1\n",
155 | " # Convert to hex and strip (\"0x\")\n",
156 | "\n",
157 | " def generate_key(self):\n",
158 | " bigInt = self.__generateBigInt()\n",
159 | " bigInt = bigInt % (self.CURVE_ORDER - 1) # key < curve order\n",
160 | " bigInt = bigInt + 1 # key > 0\n",
161 | "\n",
162 | " keyHex = hex(bigInt)\n",
163 | " key = keyHex[2:]\n",
164 | " return key\n",
165 | "\n",
166 | " def __generateBigInt(self):\n",
167 | " if self.prng_state is None:\n",
168 | " seed = int.from_bytes(self.pool, byteorder='big', signed=False)\n",
169 | " random.seed(seed)\n",
170 | "\n",
171 | " self.prng_state = random.getstate()\n",
172 | " random.setstate(self.prng_state)\n",
173 | " \n",
174 | " bigInt = random.getrandbits(self.KEY_BYTES * 8)\n",
175 | " self.prng_state = random.getstate()\n",
176 | " \n",
177 | " return bigInt"
178 | ],
179 | "metadata": {
180 | "id": "li0X-rBnnlM4"
181 | },
182 | "execution_count": null,
183 | "outputs": []
184 | },
185 | {
186 | "cell_type": "markdown",
187 | "source": [
188 | "Trial Call"
189 | ],
190 | "metadata": {
191 | "id": "dG-DVwjYnFHu"
192 | }
193 | },
194 | {
195 | "cell_type": "code",
196 | "source": [
197 | "privKeyGen = PrivateKeyGenerator()\n",
198 | "privKeyGen.seed_input('Ranchoddas Shamaldas Chanchad :)')\n",
199 | "\n",
200 | "privateKey2 = privKeyGen.generate_key()\n",
201 | "print(privateKey2)"
202 | ],
203 | "metadata": {
204 | "colab": {
205 | "base_uri": "https://localhost:8080/"
206 | },
207 | "id": "XFgVj0p6nD8j",
208 | "outputId": "4cc70e26-3b8a-4b33-86ca-e82b5fe6dd47"
209 | },
210 | "execution_count": null,
211 | "outputs": [
212 | {
213 | "output_type": "stream",
214 | "name": "stdout",
215 | "text": [
216 | "4979e71e76a5350404ca5af7a0b106c5e0cd65e35a7b81f272b34f2928d96275\n"
217 | ]
218 | }
219 | ]
220 | },
221 | {
222 | "cell_type": "markdown",
223 | "source": [
224 | "# GET PUBLIC KEY"
225 | ],
226 | "metadata": {
227 | "id": "Wp0oaY2mpxgK"
228 | }
229 | },
230 | {
231 | "cell_type": "markdown",
232 | "source": [
233 | "Code Inspiration from => https://medium.com/@kootsZhin/step-by-step-guide-to-getting-bitcoin-address-from-private-key-in-python-7ec15072b71b"
234 | ],
235 | "metadata": {
236 | "id": "rJjo1c_4rBZf"
237 | }
238 | },
239 | {
240 | "cell_type": "markdown",
241 | "source": [
242 | "## Imports"
243 | ],
244 | "metadata": {
245 | "id": "9lJ0fYY2rIUP"
246 | }
247 | },
248 | {
249 | "cell_type": "code",
250 | "source": [
251 | "import hashlib\n",
252 | "import base58\n",
253 | "import codecs\n",
254 | "import ecdsa"
255 | ],
256 | "metadata": {
257 | "id": "YgIZA6mJp6c4"
258 | },
259 | "execution_count": null,
260 | "outputs": []
261 | },
262 | {
263 | "cell_type": "markdown",
264 | "source": [
265 | "## Step 1: Get uncompressed Public Key"
266 | ],
267 | "metadata": {
268 | "id": "pB-17KglrMt8"
269 | }
270 | },
271 | {
272 | "cell_type": "code",
273 | "source": [
274 | "# Hex decoding the private key to bytes using codecs library and Generating a public key in bytes using SECP256k1 & ecdsa library\n",
275 | "publicKeyBytes = (ecdsa.SigningKey.from_string(codecs.decode(privateKey2, 'hex'), curve=ecdsa.SECP256k1).verifying_key).to_string()\n",
276 | "\n",
277 | "# Hex encoding the public key from bytes\n",
278 | "publicKeyHex = codecs.encode(publicKeyBytes, 'hex')\n",
279 | "\n",
280 | "# Bitcoin public key begins with bytes 0x04 so we have to add the bytes at the start\n",
281 | "publicKey = (b'04' + publicKeyHex).decode(\"utf-8\")\n",
282 | "\n",
283 | "print(publicKey)"
284 | ],
285 | "metadata": {
286 | "colab": {
287 | "base_uri": "https://localhost:8080/"
288 | },
289 | "id": "8gHX0D7Kq322",
290 | "outputId": "e9e48aa2-8761-4db3-8c67-8f7361d1ea8e"
291 | },
292 | "execution_count": null,
293 | "outputs": [
294 | {
295 | "output_type": "stream",
296 | "name": "stdout",
297 | "text": [
298 | "04f41172a93c094f1984cfedf490617ff86526820b044534c2221fc6de21fc4e4c25a11ef2c95f3a59c6f845a176094dccbd70c9ee5ac824a11fa88dc8994cd952\n"
299 | ]
300 | }
301 | ]
302 | },
303 | {
304 | "cell_type": "markdown",
305 | "source": [
306 | "## Step 2: Get Compressed Public Key"
307 | ],
308 | "metadata": {
309 | "id": "26JeZyiWsHFR"
310 | }
311 | },
312 | {
313 | "cell_type": "code",
314 | "source": [
315 | "# Checking if the last byte is odd or even\n",
316 | "if (ord(bytearray.fromhex(publicKey[-2:])) % 2 == 0):\n",
317 | " publicKeyCompressed = '02'\n",
318 | "else:\n",
319 | " publicKeyCompressed = '03'\n",
320 | " \n",
321 | "# Add bytes 0x02 to the X of the key if even or 0x03 if odd\n",
322 | "publicKeyCompressed += publicKey[2:66]\n",
323 | "\n",
324 | "print(publicKeyCompressed)"
325 | ],
326 | "metadata": {
327 | "colab": {
328 | "base_uri": "https://localhost:8080/"
329 | },
330 | "id": "NqzuQOqGsM1w",
331 | "outputId": "3d99be25-ad0f-4ec9-cd98-4cae094c98d4"
332 | },
333 | "execution_count": null,
334 | "outputs": [
335 | {
336 | "output_type": "stream",
337 | "name": "stdout",
338 | "text": [
339 | "02f41172a93c094f1984cfedf490617ff86526820b044534c2221fc6de21fc4e4c\n"
340 | ]
341 | }
342 | ]
343 | },
344 | {
345 | "cell_type": "markdown",
346 | "source": [
347 | "## Step 3: Perform SHA-256 hashing on the compressed public key"
348 | ],
349 | "metadata": {
350 | "id": "PJR3poYksorz"
351 | }
352 | },
353 | {
354 | "cell_type": "code",
355 | "source": [
356 | "# Converting to bytearray for SHA-256 hashing\n",
357 | "hexStr = bytearray.fromhex(publicKeyCompressed)\n",
358 | "\n",
359 | "# Apply sha\n",
360 | "sha = hashlib.sha256()\n",
361 | "sha.update(hexStr)\n",
362 | "sha.hexdigest() # .hexdigest() is hex ASCII"
363 | ],
364 | "metadata": {
365 | "colab": {
366 | "base_uri": "https://localhost:8080/",
367 | "height": 35
368 | },
369 | "id": "dKFhmM_psr09",
370 | "outputId": "1cdf54fd-c7d3-4b34-f056-414b5ce7fa36"
371 | },
372 | "execution_count": null,
373 | "outputs": [
374 | {
375 | "output_type": "execute_result",
376 | "data": {
377 | "text/plain": [
378 | "'433218daec7ea37fd88bd6fe1220254a859ae5436f6f1da320864f457b0f25bf'"
379 | ],
380 | "application/vnd.google.colaboratory.intrinsic+json": {
381 | "type": "string"
382 | }
383 | },
384 | "metadata": {},
385 | "execution_count": 29
386 | }
387 | ]
388 | },
389 | {
390 | "cell_type": "markdown",
391 | "source": [
392 | "## Step 4: Perform RIPMED-160 hashing on the result of SHA-256"
393 | ],
394 | "metadata": {
395 | "id": "KwI_LyEbs9Qs"
396 | }
397 | },
398 | {
399 | "cell_type": "code",
400 | "source": [
401 | "rip = hashlib.new('ripemd160')\n",
402 | "rip.update(sha.digest())\n",
403 | "\n",
404 | "keyHash = rip.hexdigest()\n",
405 | "print(keyHash) # Hash160"
406 | ],
407 | "metadata": {
408 | "colab": {
409 | "base_uri": "https://localhost:8080/"
410 | },
411 | "id": "XCdFQyF_tBZJ",
412 | "outputId": "52c4a0da-7f17-4ce7-d12e-415f5fda4c62"
413 | },
414 | "execution_count": null,
415 | "outputs": [
416 | {
417 | "output_type": "stream",
418 | "name": "stdout",
419 | "text": [
420 | "3bbfe5ca08337d6861fbf3104c504dfe80ebc303\n"
421 | ]
422 | }
423 | ]
424 | },
425 | {
426 | "cell_type": "markdown",
427 | "source": [
428 | "## Step 5: Add version byte in front of RIPEMD-160 hash (0x00 for Main Network)"
429 | ],
430 | "metadata": {
431 | "id": "B-v5LicrtSAt"
432 | }
433 | },
434 | {
435 | "cell_type": "code",
436 | "source": [
437 | "modifiedKeyHash = \"00\" + keyHash\n",
438 | "print(modifiedKeyHash)"
439 | ],
440 | "metadata": {
441 | "colab": {
442 | "base_uri": "https://localhost:8080/"
443 | },
444 | "id": "6SIMbrgRtf_Y",
445 | "outputId": "fbc0f712-8ab6-4a97-da06-7c58a37501c8"
446 | },
447 | "execution_count": null,
448 | "outputs": [
449 | {
450 | "output_type": "stream",
451 | "name": "stdout",
452 | "text": [
453 | "003bbfe5ca08337d6861fbf3104c504dfe80ebc303\n"
454 | ]
455 | }
456 | ]
457 | },
458 | {
459 | "cell_type": "markdown",
460 | "source": [
461 | "## Step 6: Perform SHA-256 hash on the extended RIPEMD-160 result (Base58Check encoding)"
462 | ],
463 | "metadata": {
464 | "id": "tfDdswxIuJ6i"
465 | }
466 | },
467 | {
468 | "cell_type": "code",
469 | "source": [
470 | "# Converting to bytearray for SHA-256 hashing\n",
471 | "hexString = bytearray.fromhex(modifiedKeyHash)\n",
472 | "\n",
473 | "# Apply sha\n",
474 | "sha2 = hashlib.sha256()\n",
475 | "sha2.update(hexString)\n",
476 | "sha2.hexdigest()"
477 | ],
478 | "metadata": {
479 | "colab": {
480 | "base_uri": "https://localhost:8080/",
481 | "height": 35
482 | },
483 | "id": "mmwrmh1-uQ-2",
484 | "outputId": "19c33aaa-de9a-4858-f5bd-915cc2e993b6"
485 | },
486 | "execution_count": null,
487 | "outputs": [
488 | {
489 | "output_type": "execute_result",
490 | "data": {
491 | "text/plain": [
492 | "'b387601c7b426a4dfca774a2918705fb7e0fc36a5a56ea5a4486d71be59c31ca'"
493 | ],
494 | "application/vnd.google.colaboratory.intrinsic+json": {
495 | "type": "string"
496 | }
497 | },
498 | "metadata": {},
499 | "execution_count": 37
500 | }
501 | ]
502 | },
503 | {
504 | "cell_type": "markdown",
505 | "source": [
506 | "## Step 7: Perform SHA-256 hash on the result of the previous SHA-256 hash"
507 | ],
508 | "metadata": {
509 | "id": "GJ36bidquzwY"
510 | }
511 | },
512 | {
513 | "cell_type": "code",
514 | "source": [
515 | "sha3 = hashlib.sha256()\n",
516 | "sha3.update(sha.digest())\n",
517 | "sha3.hexdigest()"
518 | ],
519 | "metadata": {
520 | "colab": {
521 | "base_uri": "https://localhost:8080/",
522 | "height": 35
523 | },
524 | "id": "vvV7XX7Lu736",
525 | "outputId": "2f60c237-fbe2-4bba-d390-9dfa35dad46b"
526 | },
527 | "execution_count": null,
528 | "outputs": [
529 | {
530 | "output_type": "execute_result",
531 | "data": {
532 | "text/plain": [
533 | "'ea1919789678de9e46a339abbb532e1dc10b00d6a24d417fed58f928c187a5f8'"
534 | ],
535 | "application/vnd.google.colaboratory.intrinsic+json": {
536 | "type": "string"
537 | }
538 | },
539 | "metadata": {},
540 | "execution_count": 38
541 | }
542 | ]
543 | },
544 | {
545 | "cell_type": "markdown",
546 | "source": [
547 | "## Step 8: Take the first 4 bytes of the second SHA-256 hash, this is the address checksum"
548 | ],
549 | "metadata": {
550 | "id": "Q1thhI9FvELp"
551 | }
552 | },
553 | {
554 | "cell_type": "code",
555 | "source": [
556 | "checkSum = sha3.hexdigest()[:8]\n",
557 | "\n",
558 | "print(checkSum)"
559 | ],
560 | "metadata": {
561 | "colab": {
562 | "base_uri": "https://localhost:8080/"
563 | },
564 | "id": "e29uAbrivJkp",
565 | "outputId": "0c1df17a-da29-4cdb-aa91-1dd32265712d"
566 | },
567 | "execution_count": null,
568 | "outputs": [
569 | {
570 | "output_type": "stream",
571 | "name": "stdout",
572 | "text": [
573 | "ea191978\n"
574 | ]
575 | }
576 | ]
577 | },
578 | {
579 | "cell_type": "markdown",
580 | "source": [
581 | "## Step 9: Add the 4 checksum bytes from stage 8 at the end of extended RIPEMD-160 hash from stage 5, this is the 25-byte binary Bitcoin Address"
582 | ],
583 | "metadata": {
584 | "id": "XL1CYtWavYxx"
585 | }
586 | },
587 | {
588 | "cell_type": "code",
589 | "source": [
590 | "byte25Address = modifiedKeyHash + checkSum\n",
591 | "\n",
592 | "print(byte25Address)"
593 | ],
594 | "metadata": {
595 | "colab": {
596 | "base_uri": "https://localhost:8080/"
597 | },
598 | "id": "tb_raQP4vYcW",
599 | "outputId": "bc795618-9b69-4d15-fa4c-07f563c8c9ff"
600 | },
601 | "execution_count": null,
602 | "outputs": [
603 | {
604 | "output_type": "stream",
605 | "name": "stdout",
606 | "text": [
607 | "003bbfe5ca08337d6861fbf3104c504dfe80ebc303ea191978\n"
608 | ]
609 | }
610 | ]
611 | },
612 | {
613 | "cell_type": "markdown",
614 | "source": [
615 | "## Step 10 (Final Result): Convert the result from a byte string into a base58 string using Base58Check encoding, this is the most commonly used Bitcoin Address format"
616 | ],
617 | "metadata": {
618 | "id": "KOO_4VBuvrbX"
619 | }
620 | },
621 | {
622 | "cell_type": "code",
623 | "source": [
624 | "address1 = base58.b58encode(bytes(bytearray.fromhex(byte25Address))).decode('utf-8')\n",
625 | "\n",
626 | "print(address1)"
627 | ],
628 | "metadata": {
629 | "colab": {
630 | "base_uri": "https://localhost:8080/"
631 | },
632 | "id": "rPir1Tt5vv7Z",
633 | "outputId": "6802844c-2812-489e-eda2-9ab7d1a85d9d"
634 | },
635 | "execution_count": null,
636 | "outputs": [
637 | {
638 | "output_type": "stream",
639 | "name": "stdout",
640 | "text": [
641 | "16SvkbgZ4cqeWhTivTExWT4rvoT8Xs9bpj\n"
642 | ]
643 | }
644 | ]
645 | },
646 | {
647 | "cell_type": "markdown",
648 | "source": [
649 | "## ALTERNATIVE WAY: Making use of b58.encode_check from base58 library directly at Step 6"
650 | ],
651 | "metadata": {
652 | "id": "435LS3uKwtOm"
653 | }
654 | },
655 | {
656 | "cell_type": "code",
657 | "source": [
658 | "keyBytes = codecs.decode(modifiedKeyHash, 'hex')\n",
659 | "address2 = base58.b58encode_check(keyBytes).decode('utf-8')\n",
660 | "address2"
661 | ],
662 | "metadata": {
663 | "colab": {
664 | "base_uri": "https://localhost:8080/",
665 | "height": 35
666 | },
667 | "id": "GYHPm6cJwzxq",
668 | "outputId": "e25fc2f7-4a63-4880-9a73-82b5e431baa2"
669 | },
670 | "execution_count": null,
671 | "outputs": [
672 | {
673 | "output_type": "execute_result",
674 | "data": {
675 | "text/plain": [
676 | "'16SvkbgZ4cqeWhTivTExWT4rvoT8Xs9bpj'"
677 | ],
678 | "application/vnd.google.colaboratory.intrinsic+json": {
679 | "type": "string"
680 | }
681 | },
682 | "metadata": {},
683 | "execution_count": 45
684 | }
685 | ]
686 | }
687 | ]
688 | }
--------------------------------------------------------------------------------
/Uploading-submissions.md:
--------------------------------------------------------------------------------
1 | # follow the steps when making a submission
2 |
3 | - enter the repo where the bounty is created
4 | - on the right side of the page you will find a button that says fork click on it.
5 |
6 |
7 | 8 | 9 | 10 | 11 |12 | 13 |  14 |
15 | 16 | 17 | 18 | 19 |20 | 21 | -now you will be redirected to a new page 22 |
23 | 24 | 25 | 26 | 27 |28 |  29 |
30 | 31 | 32 | 33 | 34 |35 | - here you can choose to rename it but that is not aa compulsory step. you can continue with the default settings and then click on create fork 36 | - what this will do is create a copy of the repo owned by Atom-dao onto your personal github account 37 | - after completing your submissions and when you are ready to submit new files click on add files and then upload all the necessary files 38 | - after adding all your files make sure they are properly uploaded they should appear as such 39 |
40 | 41 | 42 | 43 | 44 |45 |  46 | 47 |
48 | 49 | 50 | 51 | 52 |53 | - currently we do allow you to directly push to the main branch so you can choose that option but sometime in the future that may change. you will be notified about that. then click on commit changes 54 | - now you can check if the files you have uploaded are present in your repo or not. give it some time but if it does not appear then again go through the process of uploading the files and making another commit. 55 | - you will find the following popup on your screen after you commit a new file. 56 |
57 | 58 | 59 | 60 | 61 |62 |  63 | 64 |
65 | 66 | 67 | 68 | 69 |70 | - click on contribute and then click on open pull request 71 |
72 | 73 | 74 | 75 | 76 |77 |  78 |
79 | 80 | 81 | 82 | 83 |84 | - once you see the able to merge tick go ahead with the following 85 | - rename the commit to whatever bounty you have been doing for exmaple "updated filecoin bounty" and leave the comment blank and then click on create pull request 86 |
87 | 88 | 89 | 90 | 91 |92 |  93 |
94 | 95 | 96 | 97 | 98 |99 | - that was the last step. you have completed the submission. 100 | - once it is done this the code will be reviewed by us. 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /create_btc_add.py: -------------------------------------------------------------------------------- 1 | import os 2 | import binascii 3 | import hashlib 4 | from secp256k1 import PrivateKey 5 | from Crypto.Hash import RIPEMD160 6 | import base58 7 | 8 | # randomness = time.time() 9 | # print(float(randomness)) 10 | 11 | n = 1000 12 | 13 | 14 | # # Step 1 : "Pick a number between 1 and 2^256." 15 | # # the private key can be any number between 1 and n - 1, where n is a constant (n = 1.158 * 1077, slightly less than 2 256) 16 | def create_random_number(n): 17 | return int(binascii.hexlify(os.urandom(n)), 16) 18 | 19 | 20 | def gen_private_key(): 21 | private_key_num_less = True 22 | 23 | while private_key_num_less: 24 | random_number = create_random_number(n) 25 | private_key_num_less = n > float(1.158 * 10 ** 77) 26 | 27 | encoded_number = str(random_number).encode() 28 | hashed_number = hashlib.sha256(encoded_number) 29 | hashed_number_upper = hashed_number.hexdigest().upper() 30 | print(f"Private Key: {hashed_number_upper}") 31 | return hashed_number_upper 32 | 33 | 34 | # The public key is calculated from the private key using elliptic curve multiplication 35 | # which is irreversible: K = k * G, where k is the private key, G is a constant point called the generator point, and K is the resulting public key. 36 | def gen_public_key(pri_key): 37 | privkey = PrivateKey(bytes(bytearray.fromhex(pri_key))) 38 | pubkey_ser_uncompressed = privkey.pubkey.serialize(compressed=False) 39 | pubkey_ser_uncompressed_hex = pubkey_ser_uncompressed.hex() 40 | print(f"Public Key: {pubkey_ser_uncompressed_hex}") 41 | return pubkey_ser_uncompressed_hex 42 | 43 | 44 | # Pass it through the sha256 function, then the ripemd160 function 45 | def ripem160_sha256_pub_key(pubkey_ser): 46 | pubkey_hash256_hex = hashlib.sha256(binascii.unhexlify(pubkey_ser)).digest() 47 | pubkey_hash256_ripem = ( 48 | hashlib.new("ripemd160", pubkey_hash256_hex).hexdigest().upper() 49 | ) 50 | print(f"RIPEM160 and Sha256 hashed Public Key: {pubkey_hash256_ripem}") 51 | return pubkey_hash256_ripem 52 | 53 | 54 | # Then take the four first bytes of the sha256 hash of the sha256 hash of this word and append it to the end. 55 | def sha256_sha256(pubkey_hash256_ripem): 56 | return hashlib.sha256( 57 | hashlib.sha256(binascii.unhexlify(pubkey_hash256_ripem)).digest() 58 | ).hexdigest()[:8] 59 | 60 | 61 | def create_btc_address(): 62 | private_key = gen_private_key() 63 | public_key = gen_public_key(private_key) 64 | pubkey_hash256_ripem = ripem160_sha256_pub_key(public_key) 65 | 66 | # Add 00 to the begining. It is called “network byte” and means we are on Bitcoin main network. 67 | pubkey_hash256_ripem = "00" + pubkey_hash256_ripem 68 | print(f"Add 00: {pubkey_hash256_ripem}") 69 | 70 | hash_hash_first_4bytes = sha256_sha256(pubkey_hash256_ripem) 71 | result = pubkey_hash256_ripem + hash_hash_first_4bytes 72 | print(f"Add first 4byte hash: {result}") 73 | 74 | check_enc = base58.b58encode(bytes.fromhex(result)) 75 | print((check_enc)) 76 | return check_enc 77 | 78 | 79 | if __name__ == "__main__": 80 | create_btc_address() 81 | -------------------------------------------------------------------------------- /defi.md: -------------------------------------------------------------------------------- 1 | # constant product AMM 2 | - this is the first defi smart contract bounty 3 | - you have to first understand what is a AMM(automated market maker) 4 | - then you will have to understand Constant product AMM 5 | - the code that you write needs to have proper use of comments so that anyone can read and undertand what the functions try to achieve 6 | - your code needs to have the swap function for swapping tokens 7 | - it also needs to have a liqiuidity adding function 8 | - finally it needs a removing liquidity function.\ 9 | 10 | if terms like liquildity doesnt make sense , once you understand what AMMs are you will get the knowledge of that. 11 | 12 | 13 | - the bounty reward for this is **INR 1000**. 14 | - you will have to submit a .sol file and a .md file explaining the deployment. also you have to mentions the variables that has to be given as input to the contract functions during call and deploy . 15 | -------------------------------------------------------------------------------- /task1.md: -------------------------------------------------------------------------------- 1 | 2 | # Build a metamsk login page 3 | 4 | 5 | you have to create a simple frontend and use your metamask to login. 6 | to verify that you have logged in create a pop up. 7 | 8 | you will need a metamask wallet to solve this task 9 | 10 | 11 | ## Tech Stack 12 | to create you can directly use 13 | - services: moralis or tatum 14 | - or use solidity, react and web3.js /ethers.js 15 | -------------------------------------------------------------------------------- /task2.md: -------------------------------------------------------------------------------- 1 | # Task2 2 | a exiciting bounty for creating a **PYTHON** program on private public keys and addresses. 3 | -------------------------------------------------------------------------------- /task2new.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Task2.ipynb 3 | 4 | Automatically generated by Colaboratory. 5 | 6 | Original file is located at 7 | https://colab.research.google.com/drive/1pTOWBieXK-cLprIeWk6TLqZMSkYOqbsn 8 | 9 | # GENERATE PRIVATE KEY 10 | 11 | ## Generated using the strong RNG(secrets) provided by Python 12 | """ 13 | 14 | import secrets 15 | 16 | # create a random 256 address 17 | # take its hex and remoce first two elements (0x) 18 | privateKey1 = hex(secrets.randbits(256))[2:] 19 | 20 | print(privateKey1) 21 | 22 | """## Creating a simpler Bitaddress model 23 | * Code inspiration from ==> https://www.freecodecamp.org/news/how-to-generate-your-very-own-bitcoin-private-key-7ad0f4936e6c/ 24 | 25 | Imports 26 | """ 27 | 28 | import time 29 | import random 30 | 31 | """Generator Class""" 32 | 33 | class PrivateKeyGenerator: 34 | def __init__(self): 35 | self.POOL_SIZE = 256 36 | self.KEY_BYTES = 32 37 | self.CURVE_ORDER = int('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141', 16) 38 | self.pool = [0] * self.POOL_SIZE 39 | self.pool_pointer = 0 40 | self.prng_state = None 41 | self.__init_pool() 42 | 43 | # 1st Step : Initializing The Pool 44 | # Entropy using __seed_int and __seed_byte 45 | def __init_pool(self): 46 | for i in range(self.POOL_SIZE): 47 | # create and seed a random pool of 8 bits 48 | self.__seed_byte(secrets.randbits(8)) 49 | 50 | # time for entropy 51 | self.__seed_int(int(time.time())) 52 | 53 | def __seed_int(self, n): 54 | self.__seed_byte(n) 55 | self.__seed_byte(n >> 8) 56 | self.__seed_byte(n >> 16) 57 | self.__seed_byte(n >> 24) 58 | 59 | def __seed_byte(self, n): 60 | self.pool[self.pool_pointer] ^= n & 255 61 | self.pool_pointer += 1 62 | 63 | if self.pool_pointer >= self.POOL_SIZE: 64 | self.pool_pointer = 0 65 | 66 | # 2nd Step: Seeding the Input 67 | # Put a timestamp and then input string 68 | def seed_input(self, str_input): 69 | self.__seed_int(int(time.time())) 70 | 71 | for char in str_input: 72 | self.__seed_byte(ord(char)) 73 | 74 | # 3rd Step: Generating the Private Key 75 | # getstate and setstate used to conserve entropy 76 | # Key of range (1, Curve_Order) 77 | # Curve_Order is the order of secp256k1 78 | # Convert to hex and strip ("0x") 79 | 80 | def generate_key(self): 81 | bigInt = self.__generateBigInt() 82 | bigInt = bigInt % (self.CURVE_ORDER - 1) # key < curve order 83 | bigInt = bigInt + 1 # key > 0 84 | 85 | keyHex = hex(bigInt) 86 | key = keyHex[2:] 87 | return key 88 | 89 | def __generateBigInt(self): 90 | if self.prng_state is None: 91 | seed = int.from_bytes(self.pool, byteorder='big', signed=False) 92 | random.seed(seed) 93 | 94 | self.prng_state = random.getstate() 95 | random.setstate(self.prng_state) 96 | 97 | bigInt = random.getrandbits(self.KEY_BYTES * 8) 98 | self.prng_state = random.getstate() 99 | 100 | return bigInt 101 | 102 | """Trial Call""" 103 | 104 | privKeyGen = PrivateKeyGenerator() 105 | privKeyGen.seed_input('Ranchoddas Shamaldas Chanchad :)') 106 | 107 | privateKey2 = privKeyGen.generate_key() 108 | print(privateKey2) 109 | 110 | """# GET PUBLIC KEY 111 | 112 | Code Inspiration from => https://medium.com/@kootsZhin/step-by-step-guide-to-getting-bitcoin-address-from-private-key-in-python-7ec15072b71b 113 | 114 | ## Imports 115 | """ 116 | 117 | import hashlib 118 | import base58 119 | import codecs 120 | import ecdsa 121 | 122 | """## Step 1: Get uncompressed Public Key""" 123 | 124 | # Hex decoding the private key to bytes using codecs library and Generating a public key in bytes using SECP256k1 & ecdsa library 125 | publicKeyBytes = (ecdsa.SigningKey.from_string(codecs.decode(privateKey2, 'hex'), curve=ecdsa.SECP256k1).verifying_key).to_string() 126 | 127 | # Hex encoding the public key from bytes 128 | publicKeyHex = codecs.encode(publicKeyBytes, 'hex') 129 | 130 | # Bitcoin public key begins with bytes 0x04 so we have to add the bytes at the start 131 | publicKey = (b'04' + publicKeyHex).decode("utf-8") 132 | 133 | print(publicKey) 134 | 135 | """## Step 2: Get Compressed Public Key""" 136 | 137 | # Checking if the last byte is odd or even 138 | if (ord(bytearray.fromhex(publicKey[-2:])) % 2 == 0): 139 | publicKeyCompressed = '02' 140 | else: 141 | publicKeyCompressed = '03' 142 | 143 | # Add bytes 0x02 to the X of the key if even or 0x03 if odd 144 | publicKeyCompressed += publicKey[2:66] 145 | 146 | print(publicKeyCompressed) 147 | 148 | """## Step 3: Perform SHA-256 hashing on the compressed public key""" 149 | 150 | # Converting to bytearray for SHA-256 hashing 151 | hexStr = bytearray.fromhex(publicKeyCompressed) 152 | 153 | # Apply sha 154 | sha = hashlib.sha256() 155 | sha.update(hexStr) 156 | sha.hexdigest() # .hexdigest() is hex ASCII 157 | 158 | """## Step 4: Perform RIPMED-160 hashing on the result of SHA-256""" 159 | 160 | rip = hashlib.new('ripemd160') 161 | rip.update(sha.digest()) 162 | 163 | keyHash = rip.hexdigest() 164 | print(keyHash) # Hash160 165 | 166 | """## Step 5: Add version byte in front of RIPEMD-160 hash (0x00 for Main Network)""" 167 | 168 | modifiedKeyHash = "00" + keyHash 169 | print(modifiedKeyHash) 170 | 171 | """## Step 6: Perform SHA-256 hash on the extended RIPEMD-160 result (Base58Check encoding)""" 172 | 173 | # Converting to bytearray for SHA-256 hashing 174 | hexString = bytearray.fromhex(modifiedKeyHash) 175 | 176 | # Apply sha 177 | sha2 = hashlib.sha256() 178 | sha2.update(hexString) 179 | sha2.hexdigest() 180 | 181 | """## Step 7: Perform SHA-256 hash on the result of the previous SHA-256 hash""" 182 | 183 | sha3 = hashlib.sha256() 184 | sha3.update(sha.digest()) 185 | sha3.hexdigest() 186 | 187 | """## Step 8: Take the first 4 bytes of the second SHA-256 hash, this is the address checksum""" 188 | 189 | checkSum = sha3.hexdigest()[:8] 190 | 191 | print(checkSum) 192 | 193 | """## Step 9: Add the 4 checksum bytes from stage 8 at the end of extended RIPEMD-160 hash from stage 5, this is the 25-byte binary Bitcoin Address""" 194 | 195 | byte25Address = modifiedKeyHash + checkSum 196 | 197 | print(byte25Address) 198 | 199 | """## Step 10 (Final Result): Convert the result from a byte string into a base58 string using Base58Check encoding, this is the most commonly used Bitcoin Address format""" 200 | 201 | address1 = base58.b58encode(bytes(bytearray.fromhex(byte25Address))).decode('utf-8') 202 | 203 | print(address1) 204 | 205 | """## ALTERNATIVE WAY: Making use of b58.encode_check from base58 library directly at Step 6""" 206 | 207 | keyBytes = codecs.decode(modifiedKeyHash, 'hex') 208 | address2 = base58.b58encode_check(keyBytes).decode('utf-8') 209 | address2 --------------------------------------------------------------------------------