├── .DS_Store ├── README.md ├── swap-demo-tutorial-part-1 ├── index.html ├── index.js └── style.css ├── swap-demo-tutorial-part-2 ├── index.html ├── index.js └── style.css ├── swap-demo-tutorial-part-3 ├── index.html ├── index.js └── style.css ├── swap-demo-tutorial-part-4 ├── index.html ├── index.js └── style.css ├── swap-demo-tutorial-part-5 ├── index.html ├── index.js └── style.css ├── swap-demo-tutorial-part-6 ├── index.html ├── index.js └── style.css ├── swap-demo-tutorial-part-7 ├── index.html ├── index.js └── style.css ├── swap-demo-tutorial-part-8 ├── index.html ├── index.js └── style.css └── swap-demo-tutorial-part-9 ├── index.html ├── index.js └── style.css /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xProject/swap-demo-tutorial/bdb9e5a829b8b21ca0422ebdbbbffacb0822b79a/.DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > **Note** 2 | > 📣 All API requests require a 0x API key. Legacy code examples may contain requests without API keys. Follow [this guide](https://0x.org/docs/introduction/getting-started) for how to get a live API key and use it for any 0x products. 3 | 4 | 5 | > [!WARNING] 6 | > 0x API v1 was sunset on April 11, 2025. Please migrate to v2. For details, see the [migration guide](https://0x.org/docs/upgrading). See the [latest v2 examples](https://github.com/0xProject/0x-examples/tree/main?tab=readme-ov-file#v2-latest). 7 | 8 | 9 | # How to Build a Simple Token Swap DApp using the 0x Swap API 10 | 11 | 12 | Learn how to build a token swapping dapp, similar to a simple [Matcha.xyz](https://matcha.xyz/), using the [0x /swap API endpoint](https://docs.0x.org/0x-api-swap/api-references/get-swap-v1-quote). 13 | 14 | This app [aggregates liquidity across the greater DEX ecosystem using 0x](https://docs.0x.org/introduction/introduction-to-0x#the-0x-ecosystem) to surfaces the best price to the user and allow them to easily settle their trades! 15 | 16 | ## Follow Along to Learn 17 | 18 | * 💦 What is liquidity aggregation? 19 | * 🪟 Query + Display an ERC20 token list 20 | * ♻️ Use @0xProject API's /swap endpoint 21 | * 🧱 Build a Swap Dapp with Metamask and Web3.js 22 | 23 | ## Video and Written Tutorial 24 | 25 | * 🎥 Watch Video Walk-Through: https://www.youtube.com/watch?v=tVvZ1ivp4X0 26 | * 📖 Full Written Tutorial: https://0x.org/docs/0x-swap-api/guides/how-to-build-a-token-swap-dapp-with-0x-api 27 | * The repo is broken up by parts which correspond to parts in the written tutorial above ☝️ Each part builds upon the previous one. 28 | * Part 1. Walk-through Starter Code 29 | * Part 2. Connect to MetaMask 30 | * Part 3. Create a Modal for the Token List 31 | * Part 4. Fetch and Display Token List from CoinGecko API 32 | * Part 5. Display Selected Token Image and Symbol in Swap Box 33 | * Part 6. Get Price 34 | * Part 7. Get Quote 35 | * Part 8. Set a Token Allowance 36 | * Part 9. Perform the Swap 37 | 38 | ## Further Examples 39 | 40 | To find more demo apps, check out the official [0x examples repo](https://github.com/0xProject/0x-examples). 41 | 42 | Try out - 43 | 44 | - [Swap API Demo App (Next.js)](https://github.com/0xProject/0x-nextjs-demo-app/tree/main) 45 | - [Tx Relay API Demo App (Next.js App Router)](https://github.com/0xProject/0x-examples/tree/main/tx-relay-next-app) 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /swap-demo-tutorial-part-1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Swap Demo Tutorial 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 22 |
23 |
24 |
25 |

Swap

26 |
27 |
28 |
29 | SELECT A TOKEN 30 |
31 |
32 | 33 |
34 |
35 |
36 |
37 | SELECT A TOKEN 38 |
39 |
40 | 41 |
42 |
43 |
Estimated Gas:
44 | 45 |
46 |
47 |
48 |
49 | 50 | 51 | -------------------------------------------------------------------------------- /swap-demo-tutorial-part-1/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xProject/swap-demo-tutorial/bdb9e5a829b8b21ca0422ebdbbbffacb0822b79a/swap-demo-tutorial-part-1/index.js -------------------------------------------------------------------------------- /swap-demo-tutorial-part-1/style.css: -------------------------------------------------------------------------------- 1 | #window{ 2 | margin-top: 50px; 3 | background-color: #000; 4 | color: #fff; 5 | padding: 15px; 6 | border-radius: 20px; 7 | box-shadow: 0 0 5px black; 8 | } 9 | .swapbox_select { 10 | width: 50%; 11 | float: left; 12 | } 13 | .swapbox{ 14 | overflow: auto; 15 | margin: 20px 0; 16 | padding: 20px; 17 | background-color: #2f2f2f; 18 | border-radius: 20px; 19 | border: 1px solid #565656; 20 | } 21 | .token_select{ 22 | padding:5px 0; 23 | } 24 | .token_select:hover{ 25 | background-color: #464646; 26 | cursor: pointer; 27 | 28 | } 29 | .token_row{ 30 | padding: 5px 10px; 31 | } 32 | .token_row:hover{ 33 | background-color: #e4e4e4; 34 | cursor: pointer; 35 | } 36 | .gas_estimate_label{ 37 | padding:5px; 38 | } 39 | 40 | -------------------------------------------------------------------------------- /swap-demo-tutorial-part-2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Swap Demo Tutorial 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 25 |
26 |
27 |
28 |

Swap

29 |
30 |
31 |
32 | SELECT A TOKEN 33 |
34 |
35 | 36 |
37 |
38 |
39 |
40 | SELECT A TOKEN 41 |
42 |
43 | 44 |
45 |
46 |
Estimated Gas:
47 | 48 |
49 |
50 |
51 |
52 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /swap-demo-tutorial-part-2/index.js: -------------------------------------------------------------------------------- 1 | async function connect() { 2 | if (typeof window.ethereum !== "undefined") { 3 | try { 4 | console.log("connecting"); 5 | await ethereum.request({ method: "eth_requestAccounts" }); 6 | } catch (error) { 7 | console.log(error); 8 | } 9 | document.getElementById("login_button").innerHTML = "Connected"; 10 | document.getElementById("swap_button").disabled = false; 11 | } else { 12 | document.getElementById("login_button").innerHTML = 13 | "Please install MetaMask"; 14 | } 15 | } 16 | 17 | -------------------------------------------------------------------------------- /swap-demo-tutorial-part-2/style.css: -------------------------------------------------------------------------------- 1 | #window{ 2 | margin-top: 50px; 3 | background-color: #000; 4 | color: #fff; 5 | padding: 15px; 6 | border-radius: 20px; 7 | box-shadow: 0 0 5px black; 8 | } 9 | .swapbox_select { 10 | width: 50%; 11 | float: left; 12 | } 13 | .swapbox{ 14 | overflow: auto; 15 | margin: 20px 0; 16 | padding: 20px; 17 | background-color: #2f2f2f; 18 | border-radius: 20px; 19 | border: 1px solid #565656; 20 | } 21 | .token_select{ 22 | padding:5px 0; 23 | } 24 | .token_select:hover{ 25 | background-color: #464646; 26 | cursor: pointer; 27 | 28 | } 29 | .token_row{ 30 | padding: 5px 10px; 31 | } 32 | .token_row:hover{ 33 | background-color: #e4e4e4; 34 | cursor: pointer; 35 | } 36 | .gas_estimate_label{ 37 | padding:5px; 38 | } 39 | 40 | -------------------------------------------------------------------------------- /swap-demo-tutorial-part-3/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Swap Demo Tutorial 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 23 |
24 |
25 |
26 |

Swap

27 |
28 |
29 |
30 | SELECT A TOKEN 31 |
32 |
33 | 34 |
35 |
36 |
37 |
38 | SELECT A TOKEN 39 |
40 |
41 | 42 |
43 |
44 |
Estimated Gas:
45 | 46 |
47 |
48 |
49 |
50 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /swap-demo-tutorial-part-3/index.js: -------------------------------------------------------------------------------- 1 | // index.js 2 | async function connect() { 3 | if (typeof window.ethereum !== "undefined") { 4 | try { 5 | console.log("connecting"); 6 | await ethereum.request({ method: "eth_requestAccounts" }); 7 | } catch (error) { 8 | console.log(error); 9 | } 10 | document.getElementById("login_button").innerHTML = "Connected"; 11 | // const accounts = await ethereum.request({ method: "eth_accounts" }); 12 | document.getElementById("swap_button").disabled = false; 13 | } else { 14 | document.getElementById("login_button").innerHTML = 15 | "Please install MetaMask"; 16 | } 17 | } 18 | 19 | function openModal(){ 20 | document.getElementById("token_modal").style.display = "block"; 21 | } 22 | 23 | function closeModal(){ 24 | document.getElementById("token_modal").style.display = "none"; 25 | } 26 | 27 | document.getElementById("login_button").onclick = connect; 28 | document.getElementById("from_token_select").onclick = openModal; 29 | document.getElementById("modal_close").onclick = closeModal; -------------------------------------------------------------------------------- /swap-demo-tutorial-part-3/style.css: -------------------------------------------------------------------------------- 1 | #window{ 2 | margin-top: 50px; 3 | background-color: #000; 4 | color: #fff; 5 | padding: 15px; 6 | border-radius: 20px; 7 | box-shadow: 0 0 5px black; 8 | } 9 | .swapbox_select { 10 | width: 50%; 11 | float: left; 12 | } 13 | .swapbox{ 14 | overflow: auto; 15 | margin: 20px 0; 16 | padding: 20px; 17 | background-color: #2f2f2f; 18 | border-radius: 20px; 19 | border: 1px solid #565656; 20 | } 21 | .token_select{ 22 | padding:5px 0; 23 | } 24 | .token_select:hover{ 25 | background-color: #464646; 26 | cursor: pointer; 27 | 28 | } 29 | .token_row{ 30 | padding: 5px 10px; 31 | } 32 | .token_row:hover{ 33 | background-color: #e4e4e4; 34 | cursor: pointer; 35 | } 36 | .gas_estimate_label{ 37 | padding:5px; 38 | } 39 | 40 | -------------------------------------------------------------------------------- /swap-demo-tutorial-part-4/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Swap Demo Tutorial 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 23 |
24 |
25 |
26 |

Swap

27 |
28 |
29 |
30 | SELECT A TOKEN 31 |
32 |
33 | 34 |
35 |
36 |
37 |
38 | SELECT A TOKEN 39 |
40 |
41 | 42 |
43 |
44 |
Estimated Gas:
45 | 46 |
47 |
48 |
49 |
50 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /swap-demo-tutorial-part-4/index.js: -------------------------------------------------------------------------------- 1 | // index.js 2 | async function init() { 3 | await listAvailableTokens(); 4 | } 5 | 6 | async function listAvailableTokens(){ 7 | console.log("initializing"); 8 | let response = await fetch('https://tokens.coingecko.com/uniswap/all.json'); 9 | let tokenListJSON = await response.json(); 10 | console.log("listing available tokens: ", tokenListJSON); 11 | tokens = tokenListJSON.tokens; 12 | console.log("tokens: ", tokens); 13 | 14 | // Create token list for modal 15 | let parent = document.getElementById("token_list"); 16 | for (const i in tokens){ 17 | // Token row in the modal token list 18 | let div = document.createElement("div"); 19 | div.className = "token_row"; 20 | let html = ` 21 | 22 | ${tokens[i].symbol} 23 | `; 24 | div.innerHTML = html; 25 | parent.appendChild(div); 26 | }; 27 | } 28 | 29 | async function connect() { 30 | if (typeof window.ethereum !== "undefined") { 31 | try { 32 | console.log("connecting"); 33 | await ethereum.request({ method: "eth_requestAccounts" }); 34 | } catch (error) { 35 | console.log(error); 36 | } 37 | document.getElementById("login_button").innerHTML = "Connected"; 38 | // const accounts = await ethereum.request({ method: "eth_accounts" }); 39 | document.getElementById("swap_button").disabled = false; 40 | } else { 41 | document.getElementById("login_button").innerHTML = 42 | "Please install MetaMask"; 43 | } 44 | } 45 | 46 | init(); 47 | 48 | function openModal(){ 49 | document.getElementById("token_modal").style.display = "block"; 50 | } 51 | 52 | function closeModal(){ 53 | document.getElementById("token_modal").style.display = "none"; 54 | } 55 | 56 | document.getElementById("login_button").onclick = connect; 57 | document.getElementById("from_token_select").onclick = openModal; 58 | document.getElementById("modal_close").onclick = closeModal; -------------------------------------------------------------------------------- /swap-demo-tutorial-part-4/style.css: -------------------------------------------------------------------------------- 1 | #window{ 2 | margin-top: 50px; 3 | background-color: #000; 4 | color: #fff; 5 | padding: 15px; 6 | border-radius: 20px; 7 | box-shadow: 0 0 5px black; 8 | } 9 | .swapbox_select { 10 | width: 50%; 11 | float: left; 12 | } 13 | .swapbox{ 14 | overflow: auto; 15 | margin: 20px 0; 16 | padding: 20px; 17 | background-color: #2f2f2f; 18 | border-radius: 20px; 19 | border: 1px solid #565656; 20 | } 21 | .token_select{ 22 | padding:5px 0; 23 | } 24 | .token_select:hover{ 25 | background-color: #464646; 26 | cursor: pointer; 27 | 28 | } 29 | .token_row{ 30 | padding: 5px 10px; 31 | } 32 | .token_row:hover{ 33 | background-color: #e4e4e4; 34 | cursor: pointer; 35 | } 36 | .gas_estimate_label{ 37 | padding:5px; 38 | } 39 | .modal-body{ 40 | height: 500px; 41 | overflow: scroll; 42 | } 43 | -------------------------------------------------------------------------------- /swap-demo-tutorial-part-5/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Javascript Test 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 23 |
24 |
25 |
26 |

Swap

27 |
28 |
29 |
30 | 31 | 32 |
33 |
34 | 35 |
36 |
37 |
38 |
39 | 40 | 41 |
42 |
43 | 44 |
45 |
46 |
Estimated Gas:
47 | 48 |
49 |
50 |
51 |
52 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /swap-demo-tutorial-part-5/index.js: -------------------------------------------------------------------------------- 1 | 2 | let currentTrade = {}; 3 | let currentSelectSide; 4 | let tokens; 5 | 6 | async function init(){ 7 | await listAvailableTokens(); 8 | } 9 | 10 | async function listAvailableTokens(){ 11 | console.log("initializing"); 12 | let response = await fetch('https://tokens.coingecko.com/uniswap/all.json'); 13 | let tokenListJSON = await response.json(); 14 | console.log("listing available tokens"); 15 | console.log(tokenListJSON); 16 | tokens = tokenListJSON.tokens 17 | console.log("tokens:", tokens); 18 | 19 | // create token list for modal 20 | let parent = document.getElementById("token_list"); 21 | for (const i in tokens){ 22 | // token row in the modal token list 23 | let div = document.createElement("div"); 24 | div.className = "token_row"; 25 | let html = ` 26 | 27 | ${tokens[i].symbol} 28 | `; 29 | div.innerHTML = html; 30 | div.onclick = () => { 31 | selectToken(tokens[i]); 32 | }; 33 | parent.appendChild(div); 34 | } 35 | } 36 | 37 | function selectToken(token) { 38 | closeModal(); 39 | currentTrade[currentSelectSide] = token; 40 | console.log("currentTrade:" , currentTrade); 41 | renderInterface(); 42 | } 43 | 44 | function renderInterface(){ 45 | if (currentTrade.from) { 46 | console.log(currentTrade.from) 47 | document.getElementById("from_token_img").src = currentTrade.from.logoURI; 48 | document.getElementById("from_token_text").innerHTML = currentTrade.from.symbol; 49 | } 50 | if (currentTrade.to) { 51 | document.getElementById("to_token_img").src = currentTrade.to.logoURI; 52 | document.getElementById("to_token_text").innerHTML = currentTrade.to.symbol; 53 | } 54 | 55 | } 56 | 57 | async function connect() { 58 | if (typeof window.ethereum !== "undefined") { 59 | try { 60 | console.log("connecting"); 61 | await ethereum.request({ method: "eth_requestAccounts" }); 62 | } catch (error) { 63 | console.log(error); 64 | } 65 | document.getElementById("login_button").innerHTML = "Connected"; 66 | // const accounts = await ethereum.request({ method: "eth_accounts" }); 67 | document.getElementById("swap_button").disabled = false; 68 | } else { 69 | document.getElementById("login_button").innerHTML = 70 | "Please install MetaMask"; 71 | } 72 | } 73 | 74 | function openModal(side){ 75 | currentSelectSide = side; 76 | document.getElementById("token_modal").style.display = "block"; 77 | } 78 | 79 | function closeModal(){ 80 | document.getElementById("token_modal").style.display = "none"; 81 | } 82 | 83 | init(); 84 | 85 | document.getElementById("login_button").onclick = connect; 86 | document.getElementById("from_token_select").onclick = () => { 87 | openModal("from"); 88 | }; 89 | document.getElementById("to_token_select").onclick = () => { 90 | openModal("to"); 91 | }; 92 | document.getElementById("modal_close").onclick = closeModal; 93 | 94 | 95 | -------------------------------------------------------------------------------- /swap-demo-tutorial-part-5/style.css: -------------------------------------------------------------------------------- 1 | #window{ 2 | margin-top: 50px; 3 | background-color: #000; 4 | color: #fff; 5 | padding: 15px; 6 | border-radius: 20px; 7 | box-shadow: 0 0 5px black; 8 | } 9 | .swapbox_select { 10 | width: 50%; 11 | float: left; 12 | } 13 | .swapbox{ 14 | overflow: auto; 15 | margin: 20px 0; 16 | padding: 20px; 17 | background-color: #2f2f2f; 18 | border-radius: 20px; 19 | border: 1px solid #565656; 20 | } 21 | .token_select{ 22 | padding:5px 0; 23 | } 24 | .token_select:hover{ 25 | background-color: #464646; 26 | cursor: pointer; 27 | 28 | } 29 | .token_row{ 30 | padding: 5px 10px; 31 | } 32 | .token_row:hover{ 33 | background-color: #e4e4e4; 34 | cursor: pointer; 35 | } 36 | .gas_estimate_label{ 37 | padding:5px; 38 | } 39 | .modal-body{ 40 | height: 500px; 41 | overflow: scroll; 42 | } 43 | 44 | 45 | -------------------------------------------------------------------------------- /swap-demo-tutorial-part-6/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Swap Demo Tutorial 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 23 |
24 |
25 |
26 |

Swap

27 |
28 |
29 |
30 | 31 | 32 |
33 |
34 | 35 |
36 |
37 |
38 |
39 | 40 | 41 |
42 |
43 | 44 |
45 |
46 |
Estimated Gas:
47 | 48 |
49 |
50 |
51 |
52 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /swap-demo-tutorial-part-6/index.js: -------------------------------------------------------------------------------- 1 | 2 | const qs = require('qs'); 3 | 4 | let currentTrade = {}; 5 | let currentSelectSide; 6 | let tokens; 7 | 8 | async function init(){ 9 | await listAvailableTokens(); 10 | } 11 | 12 | async function listAvailableTokens(){ 13 | console.log("initializing"); 14 | let response = await fetch('https://tokens.coingecko.com/uniswap/all.json'); 15 | let tokenListJSON = await response.json(); 16 | console.log("listing available tokens"); 17 | console.log(tokenListJSON); 18 | tokens = tokenListJSON.tokens 19 | console.log("tokens:", tokens); 20 | 21 | // create token list for modal 22 | let parent = document.getElementById("token_list"); 23 | for (const i in tokens){ 24 | // token row in the modal token list 25 | let div = document.createElement("div"); 26 | div.className = "token_row"; 27 | let html = ` 28 | 29 | ${tokens[i].symbol} 30 | `; 31 | div.innerHTML = html; 32 | div.onclick = () => { 33 | selectToken(tokens[i]); 34 | }; 35 | parent.appendChild(div); 36 | } 37 | } 38 | 39 | function selectToken(token) { 40 | closeModal(); 41 | currentTrade[currentSelectSide] = token; 42 | console.log("currentTrade:" , currentTrade); 43 | renderInterface(); 44 | } 45 | 46 | function renderInterface(){ 47 | if (currentTrade.from) { 48 | console.log(currentTrade.from) 49 | document.getElementById("from_token_img").src = currentTrade.from.logoURI; 50 | document.getElementById("from_token_text").innerHTML = currentTrade.from.symbol; 51 | } 52 | if (currentTrade.to) { 53 | document.getElementById("to_token_img").src = currentTrade.to.logoURI; 54 | document.getElementById("to_token_text").innerHTML = currentTrade.to.symbol; 55 | } 56 | 57 | } 58 | 59 | async function connect() { 60 | if (typeof window.ethereum !== "undefined") { 61 | try { 62 | console.log("connecting"); 63 | await ethereum.request({ method: "eth_requestAccounts" }); 64 | } catch (error) { 65 | console.log(error); 66 | } 67 | document.getElementById("login_button").innerHTML = "Connected"; 68 | // const accounts = await ethereum.request({ method: "eth_accounts" }); 69 | document.getElementById("swap_button").disabled = false; 70 | } else { 71 | document.getElementById("login_button").innerHTML = 72 | "Please install MetaMask"; 73 | } 74 | } 75 | 76 | function openModal(side){ 77 | currentSelectSide = side; 78 | document.getElementById("token_modal").style.display = "block"; 79 | } 80 | 81 | function closeModal(){ 82 | document.getElementById("token_modal").style.display = "none"; 83 | } 84 | 85 | async function getPrice(){ 86 | console.log("Getting Price"); 87 | 88 | if (!currentTrade.from || !currentTrade.to || !document.getElementById("from_amount").value) return; 89 | let amount = Number(document.getElementById("from_amount").value * 10 ** currentTrade.from.decimals); 90 | 91 | const params = { 92 | sellToken: currentTrade.from.address, 93 | buyToken: currentTrade.to.address, 94 | sellAmount: amount, 95 | } 96 | 97 | const headers = {'0x-api-key: [api-key]'}; // This is a placeholder. Get your live API key from the 0x Dashboard (https://dashboard.0x.org/apps) 98 | 99 | // Fetch the swap price. 100 | const response = await fetch( 101 | `https://api.0x.org/swap/v1/price?${qs.stringify(params), { hearders }` 102 | ); 103 | 104 | swapPriceJSON = await response.json(); 105 | console.log("Price: ", swapPriceJSON); 106 | 107 | document.getElementById("to_amount").value = swapPriceJSON.buyAmount / (10 ** currentTrade.to.decimals); 108 | document.getElementById("gas_estimate").innerHTML = swapPriceJSON.estimatedGas; 109 | } 110 | 111 | init(); 112 | 113 | document.getElementById("login_button").onclick = connect; 114 | document.getElementById("from_token_select").onclick = () => { 115 | openModal("from"); 116 | }; 117 | document.getElementById("to_token_select").onclick = () => { 118 | openModal("to"); 119 | }; 120 | document.getElementById("modal_close").onclick = closeModal; 121 | document.getElementById("from_amount").onblur = getPrice; 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /swap-demo-tutorial-part-6/style.css: -------------------------------------------------------------------------------- 1 | #window{ 2 | margin-top: 50px; 3 | background-color: #000; 4 | color: #fff; 5 | padding: 15px; 6 | border-radius: 20px; 7 | box-shadow: 0 0 5px black; 8 | } 9 | .swapbox_select { 10 | width: 50%; 11 | float: left; 12 | } 13 | .swapbox{ 14 | overflow: auto; 15 | margin: 20px 0; 16 | padding: 20px; 17 | background-color: #2f2f2f; 18 | border-radius: 20px; 19 | border: 1px solid #565656; 20 | } 21 | .token_select{ 22 | padding:5px 0; 23 | } 24 | .token_select:hover{ 25 | background-color: #464646; 26 | cursor: pointer; 27 | 28 | } 29 | .token_row{ 30 | padding: 5px 10px; 31 | } 32 | .token_row:hover{ 33 | background-color: #e4e4e4; 34 | cursor: pointer; 35 | } 36 | .gas_estimate_label{ 37 | padding:5px; 38 | } 39 | .modal-body{ 40 | height: 500px; 41 | overflow: scroll; 42 | } 43 | 44 | 45 | -------------------------------------------------------------------------------- /swap-demo-tutorial-part-7/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Swap Demo Tutorial 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 23 |
24 |
25 |
26 |

Swap

27 |
28 |
29 |
30 | 31 | 32 |
33 |
34 | 35 |
36 |
37 |
38 |
39 | 40 | 41 |
42 |
43 | 44 |
45 |
46 |
Estimated Gas:
47 | 48 |
49 |
50 |
51 |
52 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /swap-demo-tutorial-part-7/index.js: -------------------------------------------------------------------------------- 1 | const qs = require('qs'); 2 | 3 | let currentTrade = {}; 4 | let currentSelectSide; 5 | let tokens; 6 | 7 | async function init() { 8 | await listAvailableTokens(); 9 | } 10 | 11 | async function listAvailableTokens(){ 12 | console.log("initializing"); 13 | let response = await fetch('https://tokens.coingecko.com/uniswap/all.json'); 14 | let tokenListJSON = await response.json(); 15 | console.log("listing available tokens: ", tokenListJSON); 16 | tokens = tokenListJSON.tokens; 17 | console.log("tokens: ", tokens); 18 | 19 | // Create token list for modal 20 | let parent = document.getElementById("token_list"); 21 | for (const i in tokens){ 22 | // Token row in the modal token list 23 | let div = document.createElement("div"); 24 | div.className = "token_row"; 25 | let html = ` 26 | 27 | ${tokens[i].symbol} 28 | `; 29 | div.innerHTML = html; 30 | div.onclick = () => { 31 | selectToken(tokens[i]); 32 | }; 33 | parent.appendChild(div); 34 | }; 35 | } 36 | 37 | async function selectToken(token){ 38 | closeModal(); 39 | currentTrade[currentSelectSide] = token; 40 | console.log("currentTrade: ", currentTrade); 41 | renderInterface(); 42 | } 43 | 44 | function renderInterface(){ 45 | if (currentTrade.from){ 46 | console.log(currentTrade.from) 47 | document.getElementById("from_token_img").src = currentTrade.from.logoURI; 48 | document.getElementById("from_token_text").innerHTML = currentTrade.from.symbol; 49 | } 50 | if (currentTrade.to){ 51 | console.log(currentTrade.to) 52 | document.getElementById("to_token_img").src = currentTrade.to.logoURI; 53 | document.getElementById("to_token_text").innerHTML = currentTrade.to.symbol; 54 | } 55 | } 56 | 57 | async function connect() { 58 | if (typeof window.ethereum !== "undefined") { 59 | try { 60 | console.log("connecting"); 61 | await ethereum.request({ method: "eth_requestAccounts" }); 62 | } catch (error) { 63 | console.log(error); 64 | } 65 | document.getElementById("login_button").innerHTML = "Connected"; 66 | // const accounts = await ethereum.request({ method: "eth_accounts" }); 67 | document.getElementById("swap_button").disabled = false; 68 | } else { 69 | document.getElementById("login_button").innerHTML = "Please install MetaMask"; 70 | } 71 | } 72 | 73 | function openModal(side){ 74 | currentSelectSide = side; 75 | document.getElementById("token_modal").style.display = "block"; 76 | } 77 | 78 | function closeModal(){ 79 | document.getElementById("token_modal").style.display = "none"; 80 | } 81 | 82 | async function getPrice(){ 83 | console.log("Getting Price"); 84 | 85 | if (!currentTrade.from || !currentTrade.to || !document.getElementById("from_amount").value) return; 86 | let amount = Number(document.getElementById("from_amount").value * 10 ** currentTrade.from.decimals); 87 | 88 | const params = { 89 | sellToken: currentTrade.from.address, 90 | buyToken: currentTrade.to.address, 91 | sellAmount: amount, 92 | } 93 | 94 | const headers = {'0x-api-key: [api-key]'}; // This is a placeholder. Get your live API key from the 0x Dashboard (https://dashboard.0x.org/apps) 95 | 96 | // Fetch the swap price. 97 | const response = await fetch(`https://api.0x.org/swap/v1/price?${qs.stringify(params)}`, { headers }); 98 | 99 | swapPriceJSON = await response.json(); 100 | console.log("Price: ", swapPriceJSON); 101 | 102 | document.getElementById("to_amount").value = swapPriceJSON.buyAmount / (10 ** currentTrade.to.decimals); 103 | document.getElementById("gas_estimate").innerHTML = swapPriceJSON.estimatedGas; 104 | } 105 | 106 | async function getQuote(account){ 107 | console.log("Getting Quote"); 108 | 109 | if (!currentTrade.from || !currentTrade.to || !document.getElementById("from_amount").value) return; 110 | let amount = Number(document.getElementById("from_amount").value * 10 ** currentTrade.from.decimals); 111 | 112 | const params = { 113 | sellToken: currentTrade.from.address, 114 | buyToken: currentTrade.to.address, 115 | sellAmount: amount, 116 | takerAddress: account, 117 | } 118 | 119 | const headers = {'0x-api-key: [api-key]'}; // This is a placeholder. Get your live API key from the 0x Dashboard (https://dashboard.0x.org/apps) 120 | 121 | // Fetch the swap quote. 122 | const response = await fetch(`https://api.0x.org/swap/v1/quote?${qs.stringify(params)}`, { headers }); 123 | 124 | swapQuoteJSON = await response.json(); 125 | console.log("Quote: ", swapQuoteJSON); 126 | 127 | document.getElementById("to_amount").value = swapQuoteJSON.buyAmount / (10 ** currentTrade.to.decimals); 128 | document.getElementById("gas_estimate").innerHTML = swapQuoteJSON.estimatedGas; 129 | 130 | return swapQuoteJSON; 131 | } 132 | 133 | init(); 134 | 135 | document.getElementById("login_button").onclick = connect; 136 | document.getElementById("from_token_select").onclick = () => { 137 | openModal("from"); 138 | }; 139 | document.getElementById("to_token_select").onclick = () => { 140 | openModal("to"); 141 | }; 142 | document.getElementById("modal_close").onclick = closeModal; 143 | document.getElementById("from_amount").onblur = getPrice; 144 | -------------------------------------------------------------------------------- /swap-demo-tutorial-part-7/style.css: -------------------------------------------------------------------------------- 1 | #window{ 2 | margin-top: 50px; 3 | background-color: #000; 4 | color: #fff; 5 | padding: 15px; 6 | border-radius: 20px; 7 | box-shadow: 0 0 5px black; 8 | } 9 | .swapbox_select { 10 | width: 50%; 11 | float: left; 12 | } 13 | .swapbox{ 14 | overflow: auto; 15 | margin: 20px 0; 16 | padding: 20px; 17 | background-color: #2f2f2f; 18 | border-radius: 20px; 19 | border: 1px solid #565656; 20 | } 21 | .token_select{ 22 | padding:5px 0; 23 | } 24 | .token_select:hover{ 25 | background-color: #464646; 26 | cursor: pointer; 27 | 28 | } 29 | .token_row{ 30 | padding: 5px 10px; 31 | } 32 | .token_row:hover{ 33 | background-color: #e4e4e4; 34 | cursor: pointer; 35 | } 36 | .gas_estimate_label{ 37 | padding:5px; 38 | } 39 | .modal-body{ 40 | height: 500px; 41 | overflow: scroll; 42 | } 43 | -------------------------------------------------------------------------------- /swap-demo-tutorial-part-8/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Swap Demo Tutorial 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 23 |
24 |
25 |
26 |

Swap

27 |
28 |
29 |
30 | 31 | 32 |
33 |
34 | 35 |
36 |
37 |
38 |
39 | 40 | 41 |
42 |
43 | 44 |
45 |
46 |
Estimated Gas:
47 | 48 |
49 |
50 |
51 |
52 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /swap-demo-tutorial-part-8/index.js: -------------------------------------------------------------------------------- 1 | const BigNumber = require('bignumber.js'); 2 | const qs = require('qs'); 3 | const web3 = require('web3'); 4 | 5 | let currentTrade = {}; 6 | let currentSelectSide; 7 | let tokens; 8 | 9 | async function init() { 10 | await listAvailableTokens(); 11 | } 12 | 13 | async function listAvailableTokens(){ 14 | console.log("initializing"); 15 | let response = await fetch('https://tokens.coingecko.com/uniswap/all.json'); 16 | let tokenListJSON = await response.json(); 17 | console.log("listing available tokens: ", tokenListJSON); 18 | tokens = tokenListJSON.tokens; 19 | console.log("tokens: ", tokens); 20 | 21 | // Create token list for modal 22 | let parent = document.getElementById("token_list"); 23 | for (const i in tokens){ 24 | // Token row in the modal token list 25 | let div = document.createElement("div"); 26 | div.className = "token_row"; 27 | let html = ` 28 | 29 | ${tokens[i].symbol} 30 | `; 31 | div.innerHTML = html; 32 | div.onclick = () => { 33 | selectToken(tokens[i]); 34 | }; 35 | parent.appendChild(div); 36 | }; 37 | } 38 | 39 | async function selectToken(token){ 40 | closeModal(); 41 | currentTrade[currentSelectSide] = token; 42 | console.log("currentTrade: ", currentTrade); 43 | renderInterface(); 44 | } 45 | 46 | function renderInterface(){ 47 | if (currentTrade.from){ 48 | console.log(currentTrade.from) 49 | document.getElementById("from_token_img").src = currentTrade.from.logoURI; 50 | document.getElementById("from_token_text").innerHTML = currentTrade.from.symbol; 51 | } 52 | if (currentTrade.to){ 53 | console.log(currentTrade.to) 54 | document.getElementById("to_token_img").src = currentTrade.to.logoURI; 55 | document.getElementById("to_token_text").innerHTML = currentTrade.to.symbol; 56 | } 57 | } 58 | 59 | async function connect() { 60 | if (typeof window.ethereum !== "undefined") { 61 | try { 62 | console.log("connecting"); 63 | await ethereum.request({ method: "eth_requestAccounts" }); 64 | } catch (error) { 65 | console.log(error); 66 | } 67 | document.getElementById("login_button").innerHTML = "Connected"; 68 | // const accounts = await ethereum.request({ method: "eth_accounts" }); 69 | document.getElementById("swap_button").disabled = false; 70 | } else { 71 | document.getElementById("login_button").innerHTML = "Please install MetaMask"; 72 | } 73 | } 74 | 75 | function openModal(side){ 76 | currentSelectSide = side; 77 | document.getElementById("token_modal").style.display = "block"; 78 | } 79 | 80 | function closeModal(){ 81 | document.getElementById("token_modal").style.display = "none"; 82 | } 83 | 84 | async function getPrice(){ 85 | console.log("Getting Price"); 86 | 87 | if (!currentTrade.from || !currentTrade.to || !document.getElementById("from_amount").value) return; 88 | let amount = Number(document.getElementById("from_amount").value * 10 ** currentTrade.from.decimals); 89 | 90 | const params = { 91 | sellToken: currentTrade.from.address, 92 | buyToken: currentTrade.to.address, 93 | sellAmount: amount, 94 | } 95 | 96 | const headers = {'0x-api-key: [api-key]'}; // This is a placeholder. Get your live API key from the 0x Dashboard (https://dashboard.0x.org/apps) 97 | 98 | // Fetch the swap price. 99 | const response = await fetch(`https://api.0x.org/swap/v1/price?${qs.stringify(params)}`, { headers }); 100 | 101 | swapPriceJSON = await response.json(); 102 | console.log("Price: ", swapPriceJSON); 103 | 104 | document.getElementById("to_amount").value = swapPriceJSON.buyAmount / (10 ** currentTrade.to.decimals); 105 | document.getElementById("gas_estimate").innerHTML = swapPriceJSON.estimatedGas; 106 | } 107 | 108 | async function getQuote(account){ 109 | console.log("Getting Quote"); 110 | 111 | if (!currentTrade.from || !currentTrade.to || !document.getElementById("from_amount").value) return; 112 | let amount = Number(document.getElementById("from_amount").value * 10 ** currentTrade.from.decimals); 113 | 114 | const params = { 115 | sellToken: currentTrade.from.address, 116 | buyToken: currentTrade.to.address, 117 | sellAmount: amount, 118 | takerAddress: account, 119 | } 120 | 121 | const headers = {'0x-api-key: [api-key]'}; // This is a placeholder. Get your live API key from the 0x Dashboard (https://dashboard.0x.org/apps) 122 | 123 | // Fetch the swap quote. 124 | const response = await fetch(`https://api.0x.org/swap/v1/quote?${qs.stringify(params)}`, { headers }); 125 | 126 | swapQuoteJSON = await response.json(); 127 | console.log("Quote: ", swapQuoteJSON); 128 | 129 | document.getElementById("to_amount").value = swapQuoteJSON.buyAmount / (10 ** currentTrade.to.decimals); 130 | document.getElementById("gas_estimate").innerHTML = swapQuoteJSON.estimatedGas; 131 | 132 | return swapQuoteJSON; 133 | } 134 | 135 | async function trySwap(){ 136 | const erc20abi= [{ "inputs": [ { "internalType": "string", "name": "name", "type": "string" }, { "internalType": "string", "name": "symbol", "type": "string" }, { "internalType": "uint256", "name": "max_supply", "type": "uint256" } ], "stateMutability": "nonpayable", "type": "constructor" }, { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "address", "name": "owner", "type": "address" }, { "indexed": true, "internalType": "address", "name": "spender", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "value", "type": "uint256" } ], "name": "Approval", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "address", "name": "from", "type": "address" }, { "indexed": true, "internalType": "address", "name": "to", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "value", "type": "uint256" } ], "name": "Transfer", "type": "event" }, { "inputs": [ { "internalType": "address", "name": "owner", "type": "address" }, { "internalType": "address", "name": "spender", "type": "address" } ], "name": "allowance", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "spender", "type": "address" }, { "internalType": "uint256", "name": "amount", "type": "uint256" } ], "name": "approve", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "account", "type": "address" } ], "name": "balanceOf", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "uint256", "name": "amount", "type": "uint256" } ], "name": "burn", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "account", "type": "address" }, { "internalType": "uint256", "name": "amount", "type": "uint256" } ], "name": "burnFrom", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], "name": "decimals", "outputs": [ { "internalType": "uint8", "name": "", "type": "uint8" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "spender", "type": "address" }, { "internalType": "uint256", "name": "subtractedValue", "type": "uint256" } ], "name": "decreaseAllowance", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "spender", "type": "address" }, { "internalType": "uint256", "name": "addedValue", "type": "uint256" } ], "name": "increaseAllowance", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], "name": "name", "outputs": [ { "internalType": "string", "name": "", "type": "string" } ], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "symbol", "outputs": [ { "internalType": "string", "name": "", "type": "string" } ], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "totalSupply", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "recipient", "type": "address" }, { "internalType": "uint256", "name": "amount", "type": "uint256" } ], "name": "transfer", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "sender", "type": "address" }, { "internalType": "address", "name": "recipient", "type": "address" }, { "internalType": "uint256", "name": "amount", "type": "uint256" } ], "name": "transferFrom", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "stateMutability": "nonpayable", "type": "function" }] 137 | console.log("trying swap"); 138 | 139 | // Only work if MetaMask is connect 140 | // Connecting to Ethereum: Metamask 141 | const web3 = new Web3(Web3.givenProvider); 142 | 143 | // The address, if any, of the most recently used account that the caller is permitted to access 144 | let accounts = await ethereum.request({ method: "eth_accounts" }); 145 | let takerAddress = accounts[0]; 146 | console.log("takerAddress: ", takerAddress); 147 | 148 | const swapQuoteJSON = await getQuote(takerAddress); 149 | 150 | // Set Token Allowance 151 | // Set up approval amount 152 | const fromTokenAddress = currentTrade.from.address; 153 | const maxApproval = new BigNumber(2).pow(256).minus(1); 154 | console.log("approval amount: ", maxApproval); 155 | const ERC20TokenContract = new web3.eth.Contract(erc20abi, fromTokenAddress); 156 | console.log("setup ERC20TokenContract: ", ERC20TokenContract); 157 | 158 | // Grant the allowance target an allowance to spend our tokens. 159 | const tx = await ERC20TokenContract.methods.approve( 160 | swapQuoteJSON.allowanceTarget, 161 | maxApproval, 162 | ) 163 | .send({ from: takerAddress }) 164 | .then(tx => { 165 | console.log("tx: ", tx) 166 | }); 167 | } 168 | 169 | init(); 170 | 171 | document.getElementById("login_button").onclick = connect; 172 | document.getElementById("from_token_select").onclick = () => { 173 | openModal("from"); 174 | }; 175 | document.getElementById("to_token_select").onclick = () => { 176 | openModal("to"); 177 | }; 178 | document.getElementById("modal_close").onclick = closeModal; 179 | document.getElementById("from_amount").onblur = getPrice; 180 | document.getElementById("swap_button").onclick = trySwap; 181 | -------------------------------------------------------------------------------- /swap-demo-tutorial-part-8/style.css: -------------------------------------------------------------------------------- 1 | #window{ 2 | margin-top: 50px; 3 | background-color: #000; 4 | color: #fff; 5 | padding: 15px; 6 | border-radius: 20px; 7 | box-shadow: 0 0 5px black; 8 | } 9 | .swapbox_select { 10 | width: 50%; 11 | float: left; 12 | } 13 | .swapbox{ 14 | overflow: auto; 15 | margin: 20px 0; 16 | padding: 20px; 17 | background-color: #2f2f2f; 18 | border-radius: 20px; 19 | border: 1px solid #565656; 20 | } 21 | .token_select{ 22 | padding:5px 0; 23 | } 24 | .token_select:hover{ 25 | background-color: #464646; 26 | cursor: pointer; 27 | 28 | } 29 | .token_row{ 30 | padding: 5px 10px; 31 | } 32 | .token_row:hover{ 33 | background-color: #e4e4e4; 34 | cursor: pointer; 35 | } 36 | .gas_estimate_label{ 37 | padding:5px; 38 | } 39 | .modal-body{ 40 | height: 500px; 41 | overflow: scroll; 42 | } 43 | -------------------------------------------------------------------------------- /swap-demo-tutorial-part-9/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Swap Demo Tutorial 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 23 |
24 |
25 |
26 |

Swap

27 |
28 |
29 |
30 | 31 | 32 |
33 |
34 | 35 |
36 |
37 |
38 |
39 | 40 | 41 |
42 |
43 | 44 |
45 |
46 |
Estimated Gas:
47 | 48 |
49 |
50 |
51 |
52 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /swap-demo-tutorial-part-9/index.js: -------------------------------------------------------------------------------- 1 | const BigNumber = require('bignumber.js'); 2 | const qs = require('qs'); 3 | const web3 = require('web3'); 4 | 5 | let currentTrade = {}; 6 | let currentSelectSide; 7 | let tokens; 8 | 9 | async function init() { 10 | await listAvailableTokens(); 11 | } 12 | 13 | async function listAvailableTokens(){ 14 | console.log("initializing"); 15 | let response = await fetch('https://tokens.coingecko.com/uniswap/all.json'); 16 | let tokenListJSON = await response.json(); 17 | console.log("listing available tokens: ", tokenListJSON); 18 | tokens = tokenListJSON.tokens; 19 | console.log("tokens: ", tokens); 20 | 21 | // Create token list for modal 22 | let parent = document.getElementById("token_list"); 23 | for (const i in tokens){ 24 | // Token row in the modal token list 25 | let div = document.createElement("div"); 26 | div.className = "token_row"; 27 | let html = ` 28 | 29 | ${tokens[i].symbol} 30 | `; 31 | div.innerHTML = html; 32 | div.onclick = () => { 33 | selectToken(tokens[i]); 34 | }; 35 | parent.appendChild(div); 36 | }; 37 | } 38 | 39 | async function selectToken(token){ 40 | closeModal(); 41 | currentTrade[currentSelectSide] = token; 42 | console.log("currentTrade: ", currentTrade); 43 | renderInterface(); 44 | } 45 | 46 | function renderInterface(){ 47 | if (currentTrade.from){ 48 | console.log(currentTrade.from) 49 | document.getElementById("from_token_img").src = currentTrade.from.logoURI; 50 | document.getElementById("from_token_text").innerHTML = currentTrade.from.symbol; 51 | } 52 | if (currentTrade.to){ 53 | console.log(currentTrade.to) 54 | document.getElementById("to_token_img").src = currentTrade.to.logoURI; 55 | document.getElementById("to_token_text").innerHTML = currentTrade.to.symbol; 56 | } 57 | } 58 | 59 | async function connect() { 60 | if (typeof window.ethereum !== "undefined") { 61 | try { 62 | console.log("connecting"); 63 | await ethereum.request({ method: "eth_requestAccounts" }); 64 | } catch (error) { 65 | console.log(error); 66 | } 67 | document.getElementById("login_button").innerHTML = "Connected"; 68 | // const accounts = await ethereum.request({ method: "eth_accounts" }); 69 | document.getElementById("swap_button").disabled = false; 70 | } else { 71 | document.getElementById("login_button").innerHTML = "Please install MetaMask"; 72 | } 73 | } 74 | 75 | function openModal(side){ 76 | currentSelectSide = side; 77 | document.getElementById("token_modal").style.display = "block"; 78 | } 79 | 80 | function closeModal(){ 81 | document.getElementById("token_modal").style.display = "none"; 82 | } 83 | 84 | async function getPrice(){ 85 | console.log("Getting Price"); 86 | 87 | if (!currentTrade.from || !currentTrade.to || !document.getElementById("from_amount").value) return; 88 | let amount = Number(document.getElementById("from_amount").value * 10 ** currentTrade.from.decimals); 89 | 90 | const params = { 91 | sellToken: currentTrade.from.address, 92 | buyToken: currentTrade.to.address, 93 | sellAmount: amount, 94 | } 95 | 96 | const headers = {'0x-api-key: [api-key]'}; // This is a placeholder. Get your live API key from the 0x Dashboard (https://dashboard.0x.org/apps) 97 | 98 | // Fetch the swap price. 99 | const response = await fetch(`https://api.0x.org/swap/v1/price?${qs.stringify(params)}`, { headers }); 100 | 101 | swapPriceJSON = await response.json(); 102 | console.log("Price: ", swapPriceJSON); 103 | 104 | document.getElementById("to_amount").value = swapPriceJSON.buyAmount / (10 ** currentTrade.to.decimals); 105 | document.getElementById("gas_estimate").innerHTML = swapPriceJSON.estimatedGas; 106 | } 107 | 108 | async function getQuote(account){ 109 | console.log("Getting Quote"); 110 | 111 | if (!currentTrade.from || !currentTrade.to || !document.getElementById("from_amount").value) return; 112 | let amount = Number(document.getElementById("from_amount").value * 10 ** currentTrade.from.decimals); 113 | 114 | const params = { 115 | sellToken: currentTrade.from.address, 116 | buyToken: currentTrade.to.address, 117 | sellAmount: amount, 118 | takerAddress: account, 119 | } 120 | 121 | const headers = {'0x-api-key: [api-key]'}; // This is a placeholder. Get your live API key from the 0x Dashboard (https://dashboard.0x.org/apps) 122 | 123 | // Fetch the swap quote. 124 | const response = await fetch(`https://api.0x.org/swap/v1/quote?${qs.stringify(params)}`, { headers }); 125 | 126 | swapQuoteJSON = await response.json(); 127 | console.log("Quote: ", swapQuoteJSON); 128 | 129 | document.getElementById("to_amount").value = swapQuoteJSON.buyAmount / (10 ** currentTrade.to.decimals); 130 | document.getElementById("gas_estimate").innerHTML = swapQuoteJSON.estimatedGas; 131 | 132 | return swapQuoteJSON; 133 | } 134 | 135 | async function trySwap(){ 136 | const erc20abi= [{ "inputs": [ { "internalType": "string", "name": "name", "type": "string" }, { "internalType": "string", "name": "symbol", "type": "string" }, { "internalType": "uint256", "name": "max_supply", "type": "uint256" } ], "stateMutability": "nonpayable", "type": "constructor" }, { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "address", "name": "owner", "type": "address" }, { "indexed": true, "internalType": "address", "name": "spender", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "value", "type": "uint256" } ], "name": "Approval", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "address", "name": "from", "type": "address" }, { "indexed": true, "internalType": "address", "name": "to", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "value", "type": "uint256" } ], "name": "Transfer", "type": "event" }, { "inputs": [ { "internalType": "address", "name": "owner", "type": "address" }, { "internalType": "address", "name": "spender", "type": "address" } ], "name": "allowance", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "spender", "type": "address" }, { "internalType": "uint256", "name": "amount", "type": "uint256" } ], "name": "approve", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "account", "type": "address" } ], "name": "balanceOf", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "uint256", "name": "amount", "type": "uint256" } ], "name": "burn", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "account", "type": "address" }, { "internalType": "uint256", "name": "amount", "type": "uint256" } ], "name": "burnFrom", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], "name": "decimals", "outputs": [ { "internalType": "uint8", "name": "", "type": "uint8" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "spender", "type": "address" }, { "internalType": "uint256", "name": "subtractedValue", "type": "uint256" } ], "name": "decreaseAllowance", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "spender", "type": "address" }, { "internalType": "uint256", "name": "addedValue", "type": "uint256" } ], "name": "increaseAllowance", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [], "name": "name", "outputs": [ { "internalType": "string", "name": "", "type": "string" } ], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "symbol", "outputs": [ { "internalType": "string", "name": "", "type": "string" } ], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "totalSupply", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "recipient", "type": "address" }, { "internalType": "uint256", "name": "amount", "type": "uint256" } ], "name": "transfer", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "address", "name": "sender", "type": "address" }, { "internalType": "address", "name": "recipient", "type": "address" }, { "internalType": "uint256", "name": "amount", "type": "uint256" } ], "name": "transferFrom", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "stateMutability": "nonpayable", "type": "function" }] 137 | console.log("trying swap"); 138 | 139 | // Only work if MetaMask is connect 140 | // Connecting to Ethereum: Metamask 141 | const web3 = new Web3(Web3.givenProvider); 142 | 143 | // The address, if any, of the most recently used account that the caller is permitted to access 144 | let accounts = await ethereum.request({ method: "eth_accounts" }); 145 | let takerAddress = accounts[0]; 146 | console.log("takerAddress: ", takerAddress); 147 | 148 | const swapQuoteJSON = await getQuote(takerAddress); 149 | 150 | // Set Token Allowance 151 | // Set up approval amount 152 | const fromTokenAddress = currentTrade.from.address; 153 | const maxApproval = new BigNumber(2).pow(256).minus(1); 154 | console.log("approval amount: ", maxApproval); 155 | const ERC20TokenContract = new web3.eth.Contract(erc20abi, fromTokenAddress); 156 | console.log("setup ERC20TokenContract: ", ERC20TokenContract); 157 | 158 | // Grant the allowance target an allowance to spend our tokens. 159 | const tx = await ERC20TokenContract.methods.approve( 160 | swapQuoteJSON.allowanceTarget, 161 | maxApproval, 162 | ) 163 | .send({ from: takerAddress }) 164 | .then(tx => { 165 | console.log("tx: ", tx) 166 | }); 167 | 168 | // Perform the swap 169 | const receipt = await web3.eth.sendTransaction(swapQuoteJSON); 170 | console.log("receipt: ", receipt); 171 | } 172 | 173 | init(); 174 | 175 | document.getElementById("login_button").onclick = connect; 176 | document.getElementById("from_token_select").onclick = () => { 177 | openModal("from"); 178 | }; 179 | document.getElementById("to_token_select").onclick = () => { 180 | openModal("to"); 181 | }; 182 | document.getElementById("modal_close").onclick = closeModal; 183 | document.getElementById("from_amount").onblur = getPrice; 184 | document.getElementById("swap_button").onclick = trySwap; 185 | -------------------------------------------------------------------------------- /swap-demo-tutorial-part-9/style.css: -------------------------------------------------------------------------------- 1 | #window{ 2 | margin-top: 50px; 3 | background-color: #000; 4 | color: #fff; 5 | padding: 15px; 6 | border-radius: 20px; 7 | box-shadow: 0 0 5px black; 8 | } 9 | .swapbox_select { 10 | width: 50%; 11 | float: left; 12 | } 13 | .swapbox{ 14 | overflow: auto; 15 | margin: 20px 0; 16 | padding: 20px; 17 | background-color: #2f2f2f; 18 | border-radius: 20px; 19 | border: 1px solid #565656; 20 | } 21 | .token_select{ 22 | padding:5px 0; 23 | } 24 | .token_select:hover{ 25 | background-color: #464646; 26 | cursor: pointer; 27 | 28 | } 29 | .token_row{ 30 | padding: 5px 10px; 31 | } 32 | .token_row:hover{ 33 | background-color: #e4e4e4; 34 | cursor: pointer; 35 | } 36 | .gas_estimate_label{ 37 | padding:5px; 38 | } 39 | .modal-body{ 40 | height: 500px; 41 | overflow: scroll; 42 | } 43 | --------------------------------------------------------------------------------