├── .DS_Store ├── .gitignore ├── LICENSE ├── README.md ├── blocks ├── .DS_Store ├── aave_flashloan.js ├── atomic_brag.js ├── atomic_tip.js ├── atomic_transaction_options.js ├── balance_swap.js ├── compound_borrow.js ├── compound_repay.js ├── compound_supply.js ├── defi_zaps.js ├── ens_resolver.js ├── erc20_transfer.js ├── eth_transfer.js ├── math.js ├── poolTogether_ticket.js ├── revert.js ├── text.js ├── uniswap_v1_swap.js ├── uniswap_v2_flashswap.js ├── uniswap_v2_swap.js └── vertical_extensions.js ├── buidler.config.js ├── contracts ├── Atomic.sol ├── Bragger.sol ├── ContractWithFlashLoan.sol ├── Create2.sol ├── Flashswap.sol ├── Loaner.sol ├── MockAave.sol ├── MockAtomic.sol ├── interfaces │ ├── IERC20.sol │ ├── IUniswapV2Router01.sol │ ├── IWETH.sol │ └── V1 │ │ ├── IUniswapV1Exchange.sol │ │ └── IUniswapV1Factory.sol └── lib │ ├── SafeMath.sol │ └── UniswapV2Library.sol ├── docs.html ├── favicon.ico ├── index.css ├── index.html ├── js ├── launchAtomic.js ├── lib │ ├── blockly_compressed_vertical.js │ ├── buffer.js │ ├── ethers.min.js │ ├── ethers.min.js.map │ ├── ipfs-htttp-client.js │ ├── jquery-3.5.1.slim.min.js │ ├── picoModal-3.0.0.min.js │ └── xmlToJSON.min.js ├── saveAtomic.js ├── setup.js ├── simulateAtomic.js └── toolbox.js ├── media ├── .DS_Store ├── 1x1.gif ├── AaveGhostVertical.svg ├── aaveghost.svg ├── atomic-ninja-icon.png ├── atomic-ninja-logo.png ├── atomic_brag.png ├── balancer.png ├── click.mp3 ├── click.ogg ├── click.wav ├── comment-arrow-down.svg ├── comment-arrow-up.svg ├── compound.svg ├── defiZaps.png ├── delete-x.svg ├── delete.mp3 ├── delete.ogg ├── delete.wav ├── dropdown-arrow-dark.svg ├── dropdown-arrow.svg ├── ens.png ├── erc20.png ├── ethereum.png ├── eyedropper.svg ├── green-flag.svg ├── handclosed.cur ├── handdelete.cur ├── handopen.cur ├── hatblock.png ├── icons │ ├── arrow.svg │ ├── arrow_button.svg │ ├── control_forever.svg │ ├── control_repeat.svg │ ├── control_stop.svg │ ├── control_wait.svg │ ├── event_broadcast_blue.svg │ ├── event_broadcast_coral.svg │ ├── event_broadcast_green.svg │ ├── event_broadcast_magenta.svg │ ├── event_broadcast_orange.svg │ ├── event_broadcast_purple.svg │ ├── event_when-broadcast-received_blue.svg │ ├── event_when-broadcast-received_coral.svg │ ├── event_when-broadcast-received_green.svg │ ├── event_when-broadcast-received_magenta.svg │ ├── event_when-broadcast-received_orange.svg │ ├── event_when-broadcast-received_purple.svg │ ├── event_whenflagclicked.svg │ ├── remove.svg │ ├── set-led_blue.svg │ ├── set-led_coral.svg │ ├── set-led_green.svg │ ├── set-led_magenta.svg │ ├── set-led_mystery.svg │ ├── set-led_orange.svg │ ├── set-led_purple.svg │ ├── set-led_white.svg │ ├── set-led_yellow.svg │ ├── wedo_motor-clockwise.svg │ ├── wedo_motor-counterclockwise.svg │ ├── wedo_motor-speed_fast.svg │ ├── wedo_motor-speed_med.svg │ ├── wedo_motor-speed_slow.svg │ ├── wedo_when-distance_close.svg │ ├── wedo_when-tilt-backward.svg │ ├── wedo_when-tilt-forward.svg │ ├── wedo_when-tilt-left.svg │ ├── wedo_when-tilt-right.svg │ └── wedo_when-tilt.svg ├── linearblock.png ├── ninjatx.png ├── pooltogetherBlock.svg ├── pooltogetherCategory.png ├── repeat.svg ├── revert.png ├── rotate-left.svg ├── rotate-right.svg ├── sandwitchblock.png ├── sprites.png ├── sprites.svg ├── status-not-ready.svg ├── status-ready.svg ├── tenderlySCs.png ├── uniswap.svg ├── uniswapToolbox.png ├── uniswapV1.png ├── zoom-in.svg ├── zoom-launchAtomic.svg ├── zoom-out.svg ├── zoom-reset.svg └── zoom-simulateAtomic.svg ├── package-lock.json ├── package.json └── test ├── compound_borrow.js ├── compound_supply.js ├── erc20_transfer.js ├── eth_transfer.js ├── flashLoans.js ├── testHelpers.js ├── uniswap_v2_flashswap.js └── uniswap_v2_swap.js /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossaDev/atomic/dad74f87b8bb0dd2e2352849ec136bec84fa59fb/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | .DS_Store 3 | 4 | # NPM 5 | /node_modules 6 | npm-* 7 | 8 | # solc/buidler 9 | /artifacts 10 | /cache 11 | 12 | # Localization / I18N 13 | common.pyc 14 | .settings 15 | .project 16 | *.pyc 17 | *.komodoproject 18 | /nbproject/private/ 19 | 20 | # Unused by scratch-blocks 21 | dart_compressed.js 22 | javascript_compressed.js 23 | lua_compressed.js 24 | php_compressed.js 25 | python_compressed.js 26 | 27 | # Editor 28 | .vscode 29 | 30 | /accessible/* 31 | /dist 32 | /msg/js/* 33 | !/msg/js/en.js 34 | /msg/json/* 35 | !/msg/json/en.json 36 | /blockly_compressed_horizontal.js 37 | /blockly_compressed_vertical.js 38 | /blockly_uncompressed_horizontal.js 39 | /blockly_uncompressed_vertical.js 40 | /blocks_compressed_horizontal.js 41 | /blocks_compressed_vertical.js 42 | /blocks_compressed.js 43 | /gh-pages/main.js 44 | /gh-pages/playgrounds 45 | /gh-pages/Gemfile.lock 46 | /gh-pages/closure-library 47 | /gh-pages/_site 48 | /*compiler*.jar 49 | /local_blockly_compressed_vertical.js 50 | /chromedriver -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Atomic Ninja 2 | [https://atomic.ninja](https://atomic.ninja) 3 | 4 | 5 | ### Smart contracts (mainnet) 6 | Atomic: 0x0cb0515d286d9b0c805df80c6186f55a1b3c7aad 7 | Flashloan: 0xa08E96Da1a5622B0dE11E07cf075966d48AF06d5 8 | Flashswap: 0x4f542647588ce6b2659aec3bd512f52c66d6ba7e 9 | Bragger: 0xe58bb525b1f0e2c67332a5a0504361d00dddc4ed -------------------------------------------------------------------------------- /blocks/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossaDev/atomic/dad74f87b8bb0dd2e2352849ec136bec84fa59fb/blocks/.DS_Store -------------------------------------------------------------------------------- /blocks/atomic_brag.js: -------------------------------------------------------------------------------- 1 | const braggerAbi = [{ 2 | "inputs": [], 3 | "name": "brag", 4 | "outputs": [], 5 | "stateMutability": "nonpayable", 6 | "type": "function" 7 | }] 8 | const braggerAddress = "0xe58bb525b1f0e2c67332a5a0504361d00dddc4ed" 9 | 10 | 11 | const bragger = { 12 | /** 13 | * @this Blockly.Block 14 | */ 15 | init: function () { 16 | this.jsonInit({ 17 | message0: "%1 %2 Brag", 18 | args0: [{ 19 | type: "field_image", 20 | src: "./media/atomic_brag.png", 21 | width: 33, 22 | height: 33, 23 | }, 24 | { 25 | type: "field_vertical_separator", 26 | }, 27 | ], 28 | colour: "#7300e6", 29 | tooltip: "Brag: Show off your balance like there is no tomorrow.", 30 | extensions: ["shape_statement"], 31 | }); 32 | }, 33 | category: "Atomic", 34 | encoder: function () { 35 | let braggerInterface = new ethers.utils.Interface(braggerAbi); 36 | let calldata = braggerInterface.functions.brag.encode([]); 37 | 38 | return { 39 | adds: [braggerAddress], 40 | values: ["0"], 41 | datas: [calldata], 42 | }; 43 | }, 44 | template: function () { 45 | return ( 46 | "" + 47 | '' + 48 | "" 49 | ); 50 | }, 51 | }; 52 | 53 | Blockly.Blocks["bragger"] = bragger; -------------------------------------------------------------------------------- /blocks/atomic_tip.js: -------------------------------------------------------------------------------- 1 | const atomic_tip = { 2 | /** 3 | * @this Blockly.Block 4 | */ 5 | init: function () { 6 | this.jsonInit({ 7 | message0: "%1 %2 Tip %3", 8 | args0: [ 9 | { 10 | type: "field_image", 11 | src: 12 | "./media/atomic-ninja-icon.png", 13 | width: 35, 14 | height: 35, 15 | }, 16 | { 17 | type: "field_vertical_separator", 18 | }, 19 | { 20 | type: "input_value", 21 | name: "UNIT", // wei, beer or lunch 22 | }, 23 | ], 24 | colour: "#7300e6", 25 | category: Blockly.Categories.more, 26 | tooltip: "Atomic TIP: Tip the atomic team :)", 27 | extensions: ["shape_statement"], 28 | }); 29 | }, 30 | category: "Atomic", 31 | encoder: function (value) { 32 | return { 33 | adds: ["0x7e6045240203591B985F04FdB3C9B78537A17cb3"], 34 | values: [value], 35 | datas: ["0x0"], 36 | }; 37 | }, 38 | template: function () { 39 | return ( 40 | "" + 41 | '' + 42 | '' + 43 | '' + 44 | "" + 45 | "" 46 | ); 47 | }, 48 | }; 49 | 50 | Blockly.Blocks["atomic_tip"] = atomic_tip; 51 | 52 | Blockly.Blocks["atomic-tip-unit-list"] = { 53 | /** 54 | * @this Blockly.Block 55 | */ 56 | init: function () { 57 | this.jsonInit({ 58 | message0: "%1", 59 | args0: [ 60 | { 61 | type: "field_dropdown", 62 | name: "UNIT", 63 | options: [ 64 | ["🍺", "20000000000000000"], // (.02 ETH) 65 | ["🥪", "80000000000000000"], // (.08 ETH) 66 | ["1 WEI", "1"], 67 | ], 68 | }, 69 | ], 70 | colour: "#4d0099", 71 | extensions: ["output_string"], 72 | }); 73 | }, 74 | }; 75 | -------------------------------------------------------------------------------- /blocks/atomic_transaction_options.js: -------------------------------------------------------------------------------- 1 | Blockly.Blocks["atomic_transaction_options"] = { 2 | /** 3 | * Block for when loudness/timer/video motion is greater than the value. 4 | * @this Blockly.Block 5 | */ 6 | init: function () { 7 | this.jsonInit({ 8 | message0: "%1 %2 Transaction %3ETH", 9 | args0: [ 10 | { 11 | type: "field_image", 12 | src: "./media/atomic-ninja-icon.png", 13 | width: 40, 14 | height: 40, 15 | }, 16 | { 17 | type: "field_vertical_separator", 18 | }, 19 | // { 20 | // type: "field_dropdown", 21 | // name: "GAS", 22 | // options: [ 23 | // ["High", "H"], 24 | // ["Med", "M"], 25 | // ["Low", "L"], 26 | // ], 27 | // }, 28 | { 29 | type: "input_value", 30 | name: "VALUE", 31 | }, 32 | ], 33 | colour: "#7300e6", 34 | // category: Blockly.Categories.event, 35 | tooltip: 36 | "Atomic transaction start block (w/ options): Use this block to start a transaction (or any of similar shape). This one is used if you want to send Eth along with your atomic transaction.", 37 | extensions: ["shape_hat"], 38 | }); 39 | }, 40 | category: "Atomic", 41 | template: function () { 42 | return ( 43 | "" + 44 | '' + 45 | '' + 46 | '' + 47 | '10' + 48 | "" + 49 | "" + 50 | "" 51 | ); 52 | }, 53 | }; 54 | -------------------------------------------------------------------------------- /blocks/compound_repay.js: -------------------------------------------------------------------------------- 1 | const compound_repay = { 2 | /** 3 | * @this Blockly.Block 4 | */ 5 | init: function () { 6 | this.jsonInit({ 7 | message0: "%1 %2 Repay %3 %4", 8 | args0: [ 9 | { 10 | type: "field_image", 11 | src: "./media/compound.svg", 12 | width: 40, 13 | height: 40, 14 | }, 15 | { 16 | type: "field_vertical_separator", 17 | }, 18 | { 19 | type: "input_value", 20 | name: "VAL", 21 | }, 22 | { 23 | type: "input_value", 24 | name: "UNIT", 25 | }, 26 | ], 27 | colour: "#070a0e", 28 | category: Blockly.Categories.more, 29 | tooltip: "Compound Repay: Repay borrowed value from Compound.", 30 | extensions: ["shape_statement", "scratch_extension"], 31 | }); 32 | }, 33 | category: "Compound Finance", 34 | encoder: function (value, token) { 35 | // const cETH = "0x4ddc2d193948926d02f9b1fe9e1daa0718270ed5"; 36 | const cDAI = "0x5d3a536e4d6dbd6114cc1ead35777bab948e3643"; 37 | const DAI = "0x6b175474e89094c44da98b954eedeac495271d0f"; 38 | 39 | let substack = { 40 | adds: [], 41 | values: [], 42 | datas: [], 43 | }; 44 | 45 | //1. Approve the token 46 | let tkAddress; 47 | let tkData; 48 | let tkValue; 49 | if (token == "DAI") { 50 | tkAddress = DAI; 51 | let tkAbi = [ 52 | { 53 | constant: false, 54 | inputs: [ 55 | { 56 | internalType: "address", 57 | name: "spender", 58 | type: "address", 59 | }, 60 | { 61 | internalType: "uint256", 62 | name: "amount", 63 | type: "uint256", 64 | }, 65 | ], 66 | name: "approve", 67 | outputs: [ 68 | { 69 | internalType: "bool", 70 | name: "", 71 | type: "bool", 72 | }, 73 | ], 74 | payable: false, 75 | stateMutability: "nonpayable", 76 | type: "function", 77 | signature: "0x095ea7b3", 78 | }, 79 | ]; 80 | let inter = new ethers.utils.Interface(tkAbi); 81 | tkData = inter.functions.approve.encode([ 82 | cDAI, 83 | ethers.utils.parseEther(value), 84 | ]); 85 | tkValue = "0"; 86 | } 87 | substack.adds.push(tkAddress); 88 | substack.values.push(tkValue); 89 | substack.datas.push(tkData); 90 | 91 | //2. Repay Borrow 92 | const repayABI = [ 93 | { 94 | constant: false, 95 | inputs: [ 96 | { 97 | internalType: "uint256", 98 | name: "repayAmount", 99 | type: "uint256", 100 | }, 101 | ], 102 | name: "repayBorrow", 103 | outputs: [ 104 | { 105 | internalType: "uint256", 106 | name: "", 107 | type: "uint256", 108 | }, 109 | ], 110 | payable: false, 111 | stateMutability: "nonpayable", 112 | type: "function", 113 | signature: "0x0e752702", 114 | }, 115 | ]; 116 | 117 | //2 Enter Market with funds 118 | let repayInt = new ethers.utils.Interface(repayABI); 119 | let repayData = repayInt.functions.repayBorrow.encode([ 120 | ethers.utils.parseEther(value), 121 | ]); 122 | substack.adds.push(cDAI); 123 | substack.values.push("0"); 124 | substack.datas.push(repayData); 125 | 126 | return substack; 127 | }, 128 | template: function () { 129 | let ret = 130 | "" + 131 | '' + 132 | '' + 133 | '' + 134 | '10' + 135 | "" + 136 | "" + 137 | '' + 138 | '' + 139 | "" + 140 | ""; 141 | return ret; 142 | }, 143 | }; 144 | 145 | Blockly.Blocks["compound_repay"] = compound_repay; 146 | 147 | // Blockly.Blocks["compound-unit-list"] = { 148 | // /** 149 | // * @this Blockly.Block 150 | // */ 151 | // init: function () { 152 | // this.jsonInit({ 153 | // message0: "%1", 154 | // args0: [ 155 | // { 156 | // type: "field_dropdown", 157 | // name: "UNIT", 158 | // options: [ 159 | // ["ETH", "ETHER"], 160 | // ["DAI", "DAI"], 161 | // ], 162 | // }, 163 | // ], 164 | // colour: "#070a0e", 165 | // extensions: ["colours_pen", "output_string"], 166 | // }); 167 | // }, 168 | // }; 169 | -------------------------------------------------------------------------------- /blocks/compound_supply.js: -------------------------------------------------------------------------------- 1 | const compound_supply = { 2 | /** 3 | * @this Blockly.Block 4 | */ 5 | init: function () { 6 | this.jsonInit({ 7 | message0: "%1 %2 Supply %3 %4", 8 | args0: [ 9 | { 10 | type: "field_image", 11 | src: "./media/compound.svg", 12 | width: 40, 13 | height: 40, 14 | }, 15 | { 16 | type: "field_vertical_separator", 17 | }, 18 | { 19 | type: "input_value", 20 | name: "VAL", 21 | }, 22 | { 23 | type: "input_value", 24 | name: "UNIT", 25 | }, 26 | ], 27 | colour: "#070a0e", 28 | category: Blockly.Categories.more, 29 | tooltip: "Compound Supply: Supply liquidity on Compound.", 30 | extensions: ["shape_statement", "scratch_extension"], 31 | }); 32 | }, 33 | category: "Compound Finance", 34 | encoder: function (value, token) { 35 | const cETH = "0x4ddc2d193948926d02f9b1fe9e1daa0718270ed5"; 36 | const cDAI = "0x5d3a536e4d6dbd6114cc1ead35777bab948e3643"; 37 | 38 | let substack = { 39 | adds: [], 40 | values: [], 41 | datas: [], 42 | }; 43 | 44 | //1. Mint the desired collateral 45 | let tkAddress; 46 | let tkData; 47 | let tkValue; 48 | console.log(token); 49 | if (token == "ETH") { 50 | tkAddress = cETH; 51 | let tkAbi = [ 52 | { 53 | constant: false, 54 | inputs: [], 55 | name: "mint", 56 | outputs: [], 57 | payable: true, 58 | stateMutability: "payable", 59 | type: "function", 60 | signature: "0x1249c58b", 61 | }, 62 | ]; 63 | 64 | let inter = new ethers.utils.Interface(tkAbi); 65 | tkData = inter.functions.mint.encode([]); 66 | tkValue = ethers.utils.parseEther(value); 67 | } else if (token == "DAI") { 68 | tkAddress = cDAI; 69 | let tkAbi = [ 70 | { 71 | constant: false, 72 | inputs: [ 73 | { 74 | internalType: "uint256", 75 | name: "mintAmount", 76 | type: "uint256", 77 | }, 78 | ], 79 | name: "mint", 80 | outputs: [ 81 | { 82 | internalType: "uint256", 83 | name: "", 84 | type: "uint256", 85 | }, 86 | ], 87 | payable: false, 88 | stateMutability: "nonpayable", 89 | type: "function", 90 | signature: "0xa0712d68", 91 | }, 92 | ]; 93 | 94 | let inter = new ethers.utils.Interface(tkAbi); 95 | tkData = inter.functions.mint.encode([ethers.utils.parseEther(value)]); 96 | tkValue = "0"; 97 | } 98 | substack.adds.push(tkAddress); 99 | substack.values.push(tkValue); 100 | substack.datas.push(tkData); 101 | 102 | return substack; 103 | }, 104 | template: function () { 105 | let ret = 106 | "" + 107 | '' + 108 | '' + 109 | '' + 110 | '10' + 111 | "" + 112 | "" + 113 | '' + 114 | '' + 115 | "" + 116 | ""; 117 | return ret; 118 | }, 119 | }; 120 | 121 | Blockly.Blocks["compound_supply"] = compound_supply; 122 | 123 | Blockly.Blocks["compound-unit-list"] = { 124 | /** 125 | * @this Blockly.Block 126 | */ 127 | init: function () { 128 | this.jsonInit({ 129 | message0: "%1", 130 | args0: [ 131 | { 132 | type: "field_dropdown", 133 | name: "UNIT", 134 | options: [ 135 | ["ETH", "ETH"], 136 | ["DAI", "DAI"], 137 | ], 138 | }, 139 | ], 140 | colour: "#070a0e", 141 | extensions: ["colours_pen", "output_string"], 142 | }); 143 | }, 144 | }; 145 | -------------------------------------------------------------------------------- /blocks/defi_zaps.js: -------------------------------------------------------------------------------- 1 | const defi_zaps = { 2 | /** 3 | * @this Blockly.Block 4 | */ 5 | init: function () { 6 | this.jsonInit({ 7 | message0: "%1 %2 Invest %3 ETH in %4", 8 | args0: [ 9 | { 10 | type: "field_image", 11 | src: "./media/defiZaps.png", 12 | width: 40, 13 | height: 40, 14 | }, 15 | { 16 | type: "field_vertical_separator", 17 | }, 18 | { 19 | type: "input_value", 20 | name: "VALUE", 21 | }, 22 | { 23 | type: "input_value", 24 | name: "TO", 25 | }, 26 | ], 27 | colour: "#704CFE", 28 | category: Blockly.Categories.more, 29 | tooltip: 30 | "Defi Zaps: Choose one Zap on the dropdown and the value to supply liquidity to Uniswap pools and more.", 31 | extensions: ["shape_statement", "scratch_extension"], 32 | }); 33 | }, 34 | category: "DeFi Zaps", 35 | encoder: function (value, to) { 36 | // encoding for atomic 37 | let encoder = new ethers.utils.AbiCoder(); 38 | let types = ["address", "uint256", "bytes"]; // to, value, data 39 | 40 | return { 41 | adds: [to], 42 | values: [ethers.utils.parseUnits(value, "ether").toString()], 43 | datas: ["0x0"], 44 | }; 45 | }, 46 | template: function () { 47 | return ( 48 | "" + 49 | '' + 50 | '' + 51 | '' + 52 | '10' + 53 | "" + 54 | "" + 55 | '' + 56 | '' + 57 | '' + 58 | '' + 59 | // '' + 60 | "" + 61 | "" + 62 | "" + 63 | "" + 64 | "" 65 | ); 66 | }, 67 | }; 68 | 69 | Blockly.Blocks["defi_zaps"] = defi_zaps; 70 | 71 | Blockly.Blocks["zap-list"] = { 72 | /** 73 | * @this Blockly.Block 74 | */ 75 | init: function () { 76 | this.jsonInit({ 77 | message0: "%1", 78 | args0: [ 79 | { 80 | type: "field_dropdown", 81 | name: "TEXT", 82 | options: [ 83 | ["sETHUnipool.DeFiZap.eth", "sETHUnipool.DeFiZap.eth"], 84 | ["MKRUnipool.DeFiZap.eth", "MKRUnipool.DeFiZap.eth"], 85 | // ["ETH", "ETHER"], 86 | ], 87 | }, 88 | ], 89 | colour: "#660000", 90 | extensions: ["output_string"], 91 | }); 92 | }, 93 | }; 94 | -------------------------------------------------------------------------------- /blocks/ens_resolver.js: -------------------------------------------------------------------------------- 1 | Blockly.Blocks["ens_resolver"] = { 2 | /** 3 | * Block for string length operator. 4 | * @this Blockly.Block 5 | */ 6 | init: function () { 7 | this.jsonInit({ 8 | message0: "%1 %2 %3", 9 | args0: [ 10 | { 11 | type: "field_image", 12 | src: "./media/ens.png", 13 | width: 30, 14 | height: 30, 15 | }, 16 | { 17 | type: "field_vertical_separator", 18 | }, 19 | { 20 | type: "input_value", 21 | name: "STRING", 22 | }, 23 | ], 24 | category: Blockly.Categories.operators, 25 | tooltip: 26 | "ENS Resolver: Resolves ENS names to addresses (Also accepts addresses).", 27 | colour: "#5284ff", 28 | extensions: ["output_string"], 29 | }); 30 | }, 31 | category: "ENS", 32 | encoder: function () { 33 | // encoding for atomic 34 | let encoder = new ethers.utils.AbiCoder(); 35 | let types = ["address", "uint256", "bytes"]; // to, value, data 36 | 37 | return encoder.encode(types, ["0x0", 0, "0x0"]); 38 | }, 39 | template: function () { 40 | return ( 41 | "" + 42 | '' + 43 | '' + 44 | '' + 45 | 'vitalik.eth' + 46 | "" + 47 | "" + 48 | "" 49 | ); 50 | }, 51 | }; 52 | -------------------------------------------------------------------------------- /blocks/erc20_transfer.js: -------------------------------------------------------------------------------- 1 | Blockly.Blocks["erc20_transfer"] = { 2 | /** 3 | * @this Blockly.Block 4 | */ 5 | 6 | init: function () { 7 | this.jsonInit({ 8 | message0: "%1 %2 Transfer %3 %4 to %5", 9 | args0: [ 10 | { 11 | type: "field_image", 12 | src: "./media/erc20.png", 13 | width: 31, 14 | height: 31, 15 | }, 16 | { 17 | type: "field_vertical_separator", 18 | }, 19 | { 20 | type: "input_value", 21 | name: "VALUE", 22 | }, 23 | { 24 | type: "input_value", 25 | name: "TOKEN", 26 | }, 27 | { 28 | type: "input_value", 29 | name: "TO", 30 | }, 31 | ], 32 | //category: Blockly.Categories.more, 33 | colour: "#7300e6", 34 | tooltip: "ERC20 Transfer: Transfer any token with this block.", 35 | extensions: ["shape_statement", "scratch_extension"], 36 | }); 37 | }, 38 | category: "Atomic", 39 | encoder: function (value, tokenAddress, to) { 40 | let erc20TransferAbi = [ 41 | { 42 | constant: false, 43 | inputs: [ 44 | { 45 | internalType: "address", 46 | name: "dst", 47 | type: "address", 48 | }, 49 | { 50 | internalType: "uint256", 51 | name: "wad", 52 | type: "uint256", 53 | }, 54 | ], 55 | name: "transfer", 56 | outputs: [ 57 | { 58 | internalType: "bool", 59 | name: "", 60 | type: "bool", 61 | }, 62 | ], 63 | payable: false, 64 | stateMutability: "nonpayable", 65 | type: "function", 66 | }, 67 | ]; 68 | 69 | let erc20Interface = new ethers.utils.Interface(erc20TransferAbi); 70 | let calldata = erc20Interface.functions.transfer.encode([ 71 | to, 72 | ethers.utils.parseEther(value), 73 | ]); 74 | 75 | return { 76 | adds: [tokenAddress], 77 | values: ["0"], 78 | datas: [calldata], 79 | }; 80 | }, 81 | template: function () { 82 | return ( 83 | "" + 84 | '' + 85 | '' + 86 | '' + 87 | '10' + 88 | "" + 89 | "" + 90 | '' + 91 | '' + 92 | "" + 93 | '' + 94 | '' + 95 | '' + 96 | '' + 97 | 'vitalik.eth' + 98 | "" + 99 | "" + 100 | "" + 101 | "" + 102 | "" 103 | ); 104 | }, 105 | }; 106 | 107 | Blockly.Blocks["erc20_token_list"] = { 108 | /** 109 | * @this Blockly.Block 110 | */ 111 | init: function () { 112 | this.jsonInit({ 113 | message0: "%1", 114 | args0: [ 115 | { 116 | type: "field_dropdown", 117 | name: "TOKEN", 118 | options: [ 119 | ["DAI", "0x6b175474e89094c44da98b954eedeac495271d0f"], 120 | ["BAT", "0x0d8775f648430679a709e98d2b0cb6250d2887ef"], 121 | ["WETH", "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"], 122 | ], 123 | }, 124 | ], 125 | colour: "#4d0099", 126 | extensions: ["output_string"], 127 | }); 128 | }, 129 | }; 130 | -------------------------------------------------------------------------------- /blocks/eth_transfer.js: -------------------------------------------------------------------------------- 1 | const eth_transfer = { 2 | /** 3 | * @this Blockly.Block 4 | */ 5 | init: function () { 6 | this.jsonInit({ 7 | message0: "%1 %2 Send %3 %4 to %5", 8 | args0: [ 9 | { 10 | type: "field_image", 11 | src: 12 | "./media/ethereum.png", 13 | width: 25, 14 | height: 35, 15 | }, 16 | { 17 | type: "field_vertical_separator", 18 | }, 19 | { 20 | type: "input_value", 21 | name: "VALUE", 22 | }, 23 | { 24 | type: "input_value", 25 | name: "UNIT", 26 | }, 27 | { 28 | type: "input_value", 29 | name: "TO", 30 | }, 31 | ], 32 | colour: "#7300e6", 33 | category: Blockly.Categories.more, 34 | tooltip: "Send ETH: Transfer ether to the provided address.", 35 | extensions: ["shape_statement"], 36 | }); 37 | }, 38 | category: "Atomic", 39 | encoder: function (value, unit, to) { 40 | return { 41 | adds: [to], 42 | values: [ethers.utils.parseUnits(value, unit).toString()], 43 | datas: ["0x0"], 44 | }; 45 | }, 46 | template: function () { 47 | return ( 48 | "" + 49 | '' + 50 | '' + 51 | '' + 52 | '10' + 53 | "" + 54 | "" + 55 | '' + 56 | '' + 57 | "" + 58 | '' + 59 | '' + 60 | '' + 61 | '' + 62 | 'vitalik.eth' + 63 | "" + 64 | "" + 65 | "" + 66 | "" + 67 | "" 68 | ); 69 | }, 70 | }; 71 | 72 | Blockly.Blocks["eth_transfer"] = eth_transfer; 73 | 74 | Blockly.Blocks["eth-unit-list"] = { 75 | /** 76 | * @this Blockly.Block 77 | */ 78 | init: function () { 79 | this.jsonInit({ 80 | message0: "%1", 81 | args0: [ 82 | { 83 | type: "field_dropdown", 84 | name: "UNIT", 85 | options: [ 86 | ["WEI", "WEI"], 87 | ["GWEI", "GWEI"], 88 | ["ETH", "ETHER"], 89 | ], 90 | }, 91 | ], 92 | colour: "#4d0099", 93 | extensions: ["output_string"], 94 | }); 95 | }, 96 | }; 97 | -------------------------------------------------------------------------------- /blocks/math.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Visual Blocks Editor 4 | * 5 | * Copyright 2012 Google Inc. 6 | * https://developers.google.com/blockly/ 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | /** 22 | * @fileoverview Math blocks for Blockly. 23 | * @author q.neutron@gmail.com (Quynh Neutron) 24 | */ 25 | 'use strict'; 26 | 27 | goog.provide('Blockly.Blocks.math'); 28 | 29 | goog.require('Blockly.Blocks'); 30 | 31 | goog.require('Blockly.Colours'); 32 | 33 | goog.require('Blockly.constants'); 34 | 35 | Blockly.Blocks['math_number'] = { 36 | /** 37 | * Block for generic numeric value. 38 | * @this Blockly.Block 39 | */ 40 | init: function() { 41 | this.jsonInit({ 42 | "message0": "%1", 43 | "args0": [ 44 | { 45 | "type": "field_number", 46 | "name": "NUM", 47 | "value": "0" 48 | } 49 | ], 50 | "output": "Number", 51 | "outputShape": Blockly.OUTPUT_SHAPE_ROUND, 52 | "colour": Blockly.Colours.textField, 53 | "colourSecondary": Blockly.Colours.textField, 54 | "colourTertiary": Blockly.Colours.textField 55 | }); 56 | } 57 | }; 58 | 59 | Blockly.Blocks['math_integer'] = { 60 | /** 61 | * Block for integer value (no decimal, + or -). 62 | * @this Blockly.Block 63 | */ 64 | init: function() { 65 | this.jsonInit({ 66 | "message0": "%1", 67 | "args0": [ 68 | { 69 | "type": "field_number", 70 | "name": "NUM", 71 | "precision": 1 72 | } 73 | ], 74 | "output": "Number", 75 | "outputShape": Blockly.OUTPUT_SHAPE_ROUND, 76 | "colour": Blockly.Colours.textField, 77 | "colourSecondary": Blockly.Colours.textField, 78 | "colourTertiary": Blockly.Colours.textField 79 | }); 80 | } 81 | }; 82 | 83 | Blockly.Blocks['math_whole_number'] = { 84 | /** 85 | * Block for whole number value, no negatives or decimals. 86 | * @this Blockly.Block 87 | */ 88 | init: function() { 89 | this.jsonInit({ 90 | "message0": "%1", 91 | "args0": [ 92 | { 93 | "type": "field_number", 94 | "name": "NUM", 95 | "min": 0, 96 | "precision": 1 97 | } 98 | ], 99 | "output": "Number", 100 | "outputShape": Blockly.OUTPUT_SHAPE_ROUND, 101 | "colour": Blockly.Colours.textField, 102 | "colourSecondary": Blockly.Colours.textField, 103 | "colourTertiary": Blockly.Colours.textField 104 | }); 105 | } 106 | }; 107 | 108 | Blockly.Blocks['math_positive_number'] = { 109 | /** 110 | * Block for positive number value, with decimal. 111 | * @this Blockly.Block 112 | */ 113 | init: function() { 114 | this.jsonInit({ 115 | "message0": "%1", 116 | "args0": [ 117 | { 118 | "type": "field_number", 119 | "name": "NUM", 120 | "min": 0 121 | } 122 | ], 123 | "output": "Number", 124 | "outputShape": Blockly.OUTPUT_SHAPE_ROUND, 125 | "colour": Blockly.Colours.textField, 126 | "colourSecondary": Blockly.Colours.textField, 127 | "colourTertiary": Blockly.Colours.textField 128 | }); 129 | } 130 | }; 131 | 132 | Blockly.Blocks['math_angle'] = { 133 | /** 134 | * Block for angle picker. 135 | * @this Blockly.Block 136 | */ 137 | init: function() { 138 | this.jsonInit({ 139 | "message0": "%1", 140 | "args0": [ 141 | { 142 | "type": "field_angle", 143 | "name": "NUM", 144 | "value": 90 145 | } 146 | ], 147 | "output": "Number", 148 | "outputShape": Blockly.OUTPUT_SHAPE_ROUND, 149 | "colour": Blockly.Colours.textField, 150 | "colourSecondary": Blockly.Colours.textField, 151 | "colourTertiary": Blockly.Colours.textField 152 | }); 153 | } 154 | }; 155 | -------------------------------------------------------------------------------- /blocks/poolTogether_ticket.js: -------------------------------------------------------------------------------- 1 | const poolTogether_ticket = { 2 | /** 3 | * @this Blockly.Block 4 | */ 5 | init: function () { 6 | this.jsonInit({ 7 | message0: "%1 %2 Buy %3 tickets", 8 | args0: [ 9 | { 10 | type: "field_image", 11 | src: 12 | "./media/pooltogetherBlock.svg", 13 | width: 40, 14 | height: 40, 15 | }, 16 | { 17 | type: "field_vertical_separator", 18 | }, 19 | { 20 | type: "input_value", 21 | name: "VAL", 22 | }, 23 | ], 24 | colour: "#793DF6", 25 | category: Blockly.Categories.more, 26 | tooltip: "Pool Together Ticket: Buy tickets from Pool together.", 27 | extensions: ["shape_statement", "scratch_extension"], 28 | }); 29 | }, 30 | category: "Pool Together", 31 | encoder: function (value) { 32 | const Pool = "0x29fe7D60DdF151E5b52e5FAB4f1325da6b2bD958"; 33 | const DAI = "0x6b175474e89094c44da98b954eedeac495271d0f"; 34 | let substack = { 35 | adds: [], 36 | values: [], 37 | datas: [], 38 | }; 39 | 40 | //1. Approve DAI 41 | 42 | let tkAddress = DAI; 43 | let tkData; 44 | let tkValue; 45 | let tkAbi = [ 46 | { 47 | constant: false, 48 | inputs: [ 49 | { 50 | internalType: "address", 51 | name: "spender", 52 | type: "address", 53 | }, 54 | { 55 | internalType: "uint256", 56 | name: "amount", 57 | type: "uint256", 58 | }, 59 | ], 60 | name: "approve", 61 | outputs: [ 62 | { 63 | internalType: "bool", 64 | name: "", 65 | type: "bool", 66 | }, 67 | ], 68 | payable: false, 69 | stateMutability: "nonpayable", 70 | type: "function", 71 | signature: "0x095ea7b3", 72 | }, 73 | ]; 74 | let inter = new ethers.utils.Interface(tkAbi); 75 | tkData = inter.functions.approve.encode([ 76 | Pool, 77 | ethers.utils.parseEther(value), 78 | ]); 79 | tkValue = "0"; 80 | 81 | substack.adds.push(tkAddress); 82 | substack.values.push(tkValue); 83 | substack.datas.push(tkData); 84 | 85 | // Buy Tickets 86 | const ticketABI = [ 87 | { 88 | constant: false, 89 | inputs: [{ internalType: "uint256", name: "_amount", type: "uint256" }], 90 | name: "depositPool", 91 | outputs: [], 92 | payable: false, 93 | stateMutability: "nonpayable", 94 | type: "function", 95 | }, 96 | ]; 97 | 98 | let ticketInter = new ethers.utils.Interface(ticketABI); 99 | ticketData = ticketInter.functions.depositPool.encode([ 100 | ethers.utils.parseEther(value), 101 | ]); 102 | 103 | substack.adds.push(Pool); 104 | substack.values.push("0"); 105 | substack.datas.push(ticketData); 106 | 107 | return substack; 108 | }, 109 | template: function () { 110 | let ret = 111 | "" + 112 | '' + 113 | '' + 114 | '' + 115 | '10' + 116 | "" + 117 | "" + 118 | ""; 119 | return ret; 120 | }, 121 | }; 122 | 123 | Blockly.Blocks["poolTogether_ticket"] = poolTogether_ticket; 124 | -------------------------------------------------------------------------------- /blocks/revert.js: -------------------------------------------------------------------------------- 1 | // import { ethers } from "ethers"; 2 | 3 | Blockly.Blocks["revert"] = { 4 | /** 5 | * Block for "delete this clone." 6 | * @this Blockly.Block 7 | */ 8 | init: function () { 9 | this.jsonInit({ 10 | message0: "%1 %2 Revert", 11 | args0: [ 12 | { 13 | type: "field_image", 14 | src: 15 | "./media/revert.png", 16 | width: 30, 17 | height: 30, 18 | }, 19 | { 20 | type: "field_vertical_separator", 21 | }, 22 | ], 23 | // category: Blockly.Categories.control, 24 | colour: "#7300e6", 25 | tooltip: "Use this block to forcibly revert a transaction", 26 | extensions: ["shape_end"], 27 | }); 28 | }, 29 | category: "Atomic", 30 | encoder: function () { 31 | return { 32 | adds: ["0x0000000000000000000000000000000000000000"], 33 | values: [ethers.constants.MaxUint256], 34 | datas: ["0x0"], 35 | }; 36 | }, 37 | template: function () { 38 | return "" + '' + ""; 39 | }, 40 | }; 41 | -------------------------------------------------------------------------------- /blocks/text.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Visual Blocks Editor 4 | * 5 | * Copyright 2012 Google Inc. 6 | * https://developers.google.com/blockly/ 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | /** 22 | * @fileoverview Text blocks for Blockly. 23 | * @author fraser@google.com (Neil Fraser) 24 | */ 25 | 'use strict'; 26 | 27 | goog.provide('Blockly.Blocks.texts'); 28 | 29 | goog.require('Blockly.Blocks'); 30 | 31 | goog.require('Blockly.Colours'); 32 | 33 | goog.require('Blockly.constants'); 34 | 35 | Blockly.Blocks['text'] = { 36 | /** 37 | * Block for text value. 38 | * @this Blockly.Block 39 | */ 40 | init: function() { 41 | this.jsonInit({ 42 | "message0": "%1", 43 | "args0": [ 44 | { 45 | "type": "field_input", 46 | "name": "TEXT" 47 | } 48 | ], 49 | "output": "String", 50 | "outputShape": Blockly.OUTPUT_SHAPE_ROUND, 51 | "colour": Blockly.Colours.textField, 52 | "colourSecondary": Blockly.Colours.textField, 53 | "colourTertiary": Blockly.Colours.textField 54 | }); 55 | } 56 | }; 57 | -------------------------------------------------------------------------------- /buidler.config.js: -------------------------------------------------------------------------------- 1 | usePlugin("@nomiclabs/buidler-ethers"); 2 | module.exports = { 3 | solc: { 4 | version: "0.6.8", 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /contracts/Atomic.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.6.8; 2 | pragma experimental ABIEncoderV2; 3 | 4 | import "./Create2.sol"; 5 | 6 | interface IERC20 { 7 | function balanceOf(address account) external view returns (uint256); 8 | function transfer(address recipient, uint256 amount) external returns (bool); 9 | } 10 | 11 | contract AtomicProxy { 12 | address payable public owner; 13 | address payable public factory; 14 | uint256 public time; 15 | 16 | receive() external payable {} 17 | 18 | fallback () external payable { 19 | owner = tx.origin; 20 | time = now; 21 | (bool success, ) = factory.delegatecall(msg.data); 22 | if (!success) { 23 | assembly { 24 | returndatacopy(0, 0, returndatasize()) 25 | revert(0, returndatasize()) 26 | } 27 | } 28 | } 29 | } 30 | 31 | contract Atomic { 32 | address payable public owner = msg.sender; 33 | address payable private factory; //needed to keep the storage layout equal to AtomicProxy 34 | uint256 public time; 35 | 36 | mapping (address => uint) public atomicNonce; 37 | 38 | bytes proxyBytecode; 39 | 40 | event atomicLaunch(address atomicContract); 41 | 42 | constructor (bytes memory _proxyBytecode) public { 43 | proxyBytecode = _proxyBytecode; 44 | } 45 | 46 | function launchAtomic(address[] calldata _to, uint[] calldata _value, bytes[] calldata _data) external payable returns (bool success) { 47 | require (_to.length == _value.length && _value.length == _data.length, "Parameters are incorrect"); 48 | 49 | address payable atomicProxy = Create2.deploy(getSalt(msg.sender), proxyBytecode); 50 | atomicNonce[msg.sender]++; 51 | emit atomicLaunch(atomicProxy); 52 | 53 | bytes memory txData = abi.encodeWithSelector( 54 | Atomic.execute.selector, 55 | _to, 56 | _value, 57 | _data 58 | ); 59 | (success, ) = atomicProxy.call{value: msg.value}(txData); 60 | if (!success) { 61 | assembly { 62 | returndatacopy(0, 0, returndatasize()) 63 | revert(0, returndatasize()) 64 | } 65 | } 66 | } 67 | 68 | function getNextAtomic(address _owner) public view returns (address) { 69 | return Create2.computeAddress( 70 | getSalt(_owner), 71 | proxyBytecode 72 | ); 73 | } 74 | 75 | function getSalt(address _owner) internal view returns (bytes32 salt) { 76 | return keccak256(abi.encodePacked(_owner,atomicNonce[_owner])); 77 | } 78 | 79 | function execute(address[] calldata _to, uint[] calldata _value, bytes[] calldata _data) payable external returns (bool success) { 80 | require (time == now, "Execution can only be performed once"); 81 | for (uint i = 0; i < _data.length; i++) { 82 | 83 | // revert block 84 | if (_value[i] == uint(0 - 1)) // max uint 85 | revert("Revert Block: Transaction was reverted by the revert block."); 86 | 87 | (success, ) = payable(_to[i]).call{value: _value[i]}(_data[i]); 88 | if (!success) { 89 | assembly { 90 | returndatacopy(0, 0, returndatasize()) 91 | revert(0, returndatasize()) 92 | } 93 | } 94 | } 95 | } 96 | 97 | function drain(address _token) public { 98 | if (_token == address(0)) 99 | owner.call{value: address(this).balance}(""); 100 | else { 101 | IERC20 token = IERC20(_token); 102 | token.transfer(owner, token.balanceOf(address(this))); 103 | } 104 | } 105 | 106 | fallback () external payable { 107 | revert("Not correctly encoded for execute or drain."); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /contracts/Bragger.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.6.8; 2 | 3 | contract Bragger { 4 | // maxBrag is stored on chain, 5 | // for data analythics (top XX), look at Brag event 6 | uint public maxBrag; 7 | address public maxBragger; 8 | event Brag(address bragger, uint balance); 9 | 10 | function brag() public { 11 | emit Brag(msg.sender, address(msg.sender).balance); 12 | if (address(msg.sender).balance > maxBrag) { 13 | maxBrag = address(msg.sender).balance; 14 | maxBragger = msg.sender; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /contracts/ContractWithFlashLoan.sol: -------------------------------------------------------------------------------- 1 | // pragma solidity ^0.5.0; 2 | // pragma experimental ABIEncoderV2; 3 | 4 | // import "@studydefi/money-legos/aave/contracts/ILendingPool.sol"; 5 | // import "@studydefi/money-legos/aave/contracts/IFlashLoanReceiver.sol"; 6 | // import "@studydefi/money-legos/aave/contracts/FlashloanReceiverBase.sol"; 7 | 8 | // import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 9 | 10 | // contract ContractWithFlashLoan is FlashLoanReceiverBase { 11 | // address constant AaveLendingPoolAddressProviderAddress = 0x24a42fD28C976A61Df5D00D0599C34c4f90748c8; 12 | 13 | // struct MyCustomData { 14 | // address a; 15 | // uint b; 16 | // } 17 | 18 | // function executeOperation( 19 | // address _reserve, 20 | // uint _amount, 21 | // uint _fee, 22 | // bytes calldata _params 23 | // ) external { 24 | // // You can pass in some byte-encoded params 25 | // MyCustomData memory myCustomData = abi.decode(_params, (MyCustomData)); 26 | // // myCustomData.a 27 | 28 | // // Function is called when loan is given to contract 29 | // // Do your logic here, e.g. arbitrage, liquidate compound, etc 30 | // // Note that if you don't do your logic, it WILL fail 31 | 32 | // // TODO: Change line below 33 | // revert("Hello, you haven't implemented your flashloan logic"); 34 | 35 | // transferFundsBackToPoolInternal(_reserve, _amount.add(_fee)); 36 | // } 37 | 38 | // // Entry point 39 | // function initateFlashLoan( 40 | // address contractWithFlashLoan, 41 | // address assetToFlashLoan, 42 | // uint amountToLoan, 43 | // bytes calldata _params 44 | // ) external { 45 | // // Get Aave lending pool 46 | // ILendingPool lendingPool = ILendingPool( 47 | // ILendingPoolAddressesProvider(AaveLendingPoolAddressProviderAddress) 48 | // .getLendingPool() 49 | // ); 50 | 51 | // // Ask for a flashloan 52 | // // LendingPool will now execute the `executeOperation` function above 53 | // lendingPool.flashLoan( 54 | // contractWithFlashLoan, // Which address to callback into, alternatively: address(this) 55 | // assetToFlashLoan, 56 | // amountToLoan, 57 | // _params 58 | // ); 59 | // } 60 | // } -------------------------------------------------------------------------------- /contracts/Create2.sol: -------------------------------------------------------------------------------- 1 | // Taken from: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/970f687f04d20e01138a3e8ccf9278b1d4b3997b/contracts/utils/Create2.sol 2 | 3 | pragma solidity ^0.6.6; 4 | 5 | /** 6 | * @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer. 7 | * `CREATE2` can be used to compute in advance the address where a smart 8 | * contract will be deployed, which allows for interesting new mechanisms known 9 | * as 'counterfactual interactions'. 10 | * 11 | * See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more 12 | * information. 13 | */ 14 | library Create2 { 15 | /** 16 | * @dev Deploys a contract using `CREATE2`. The address where the contract 17 | * will be deployed can be known in advance via {computeAddress}. Note that 18 | * a contract cannot be deployed twice using the same salt. 19 | */ 20 | function deploy(bytes32 salt, bytes memory bytecode) internal returns (address payable) { 21 | address payable addr; 22 | // solhint-disable-next-line no-inline-assembly 23 | assembly { 24 | addr := create2(0, add(bytecode, 0x20), mload(bytecode), salt) 25 | } 26 | require(addr != address(0), "CREATE2_FAILED"); 27 | return addr; 28 | } 29 | 30 | /** 31 | * @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the `bytecode` 32 | * or `salt` will result in a new destination address. 33 | */ 34 | function computeAddress(bytes32 salt, bytes memory bytecode) internal view returns (address) { 35 | return computeAddress(salt, bytecode, address(this)); 36 | } 37 | 38 | /** 39 | * @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at 40 | * `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}. 41 | */ 42 | function computeAddress(bytes32 salt, bytes memory bytecodeHash, address deployer) internal pure returns (address) { 43 | bytes32 bytecodeHashHash = keccak256(bytecodeHash); 44 | bytes32 _data = keccak256( 45 | abi.encodePacked(bytes1(0xff), deployer, salt, bytecodeHashHash) 46 | ); 47 | return address(bytes20(_data << 96)); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /contracts/Flashswap.sol: -------------------------------------------------------------------------------- 1 | pragma solidity =0.6.8; 2 | 3 | interface IUniswapV2Callee { 4 | function uniswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data) external; 5 | } 6 | 7 | interface IUniswapV2Factory { 8 | function getPair(address tokenA, address tokenB) external view returns (address pair); 9 | } 10 | 11 | interface IUniswapV2Exchange { 12 | function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external; 13 | } 14 | 15 | interface IERC20 { 16 | function transfer(address recipient, uint256 amount) external returns (bool); 17 | function balanceOf(address who) external view returns (uint); 18 | } 19 | 20 | contract Flashswap is IUniswapV2Callee { 21 | 22 | address payable public atomic; 23 | address public weth = 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f; 24 | address public asset; 25 | 26 | event emitFallback(uint msgValue, bytes data); 27 | event emitReceive(uint msgValue); 28 | event initiate(); 29 | event callback(); 30 | 31 | receive() external payable { 32 | emit emitReceive(msg.value); 33 | } 34 | 35 | fallback() external payable { 36 | emit emitFallback(msg.value, msg.data); 37 | } 38 | 39 | function initateFlashswap( 40 | address assetToFlashSwap, 41 | uint amountToLoan, 42 | bytes calldata _params) 43 | external payable{ 44 | atomic = msg.sender; 45 | asset = assetToFlashSwap; 46 | emit initiate(); 47 | 48 | //Get Uniswap V2 Pool (weth => asset) 49 | address exchangeAddress = IUniswapV2Factory(weth).getPair(address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2), assetToFlashSwap); 50 | 51 | // Flashswap 52 | // Pool will now execute the `uniswapV2Call` function below 53 | IUniswapV2Exchange(exchangeAddress).swap(1000, amountToLoan, address(this), _params); // need to pass 1 wei of base currency, repay within substack 54 | } 55 | 56 | function uniswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data) external override { 57 | emit callback(); 58 | 59 | (bool success,) = atomic.call.value(0)(data); 60 | if (!success) { 61 | assembly { 62 | returndatacopy(0, 0, returndatasize()) 63 | revert(0, returndatasize()) 64 | } 65 | } 66 | 67 | // block will have to payback amount before finishing execution of substack 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /contracts/Loaner.sol: -------------------------------------------------------------------------------- 1 | interface ILendingPoolAddressesProvider { 2 | function getLendingPool() external view returns (address); 3 | } 4 | 5 | 6 | interface ILendingPool { 7 | function flashLoan( 8 | address _receiver, 9 | address _reserve, 10 | uint256 _amount, 11 | bytes calldata _params 12 | ) external; 13 | } 14 | 15 | 16 | contract Loaner { 17 | address payable public sender; 18 | address constant ETHADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; 19 | ILendingPoolAddressesProvider public addressesProvider = ILendingPoolAddressesProvider( 20 | 0x24a42fD28C976A61Df5D00D0599C34c4f90748c8 21 | ); 22 | 23 | function initateFlashLoan( 24 | address assetToFlashLoan, 25 | uint256 amountToLoan, 26 | bytes calldata _params 27 | ) external payable { 28 | //Sender is the atomicProxy contract 29 | sender = msg.sender; 30 | 31 | //Get Aave lending pool 32 | ILendingPool lendingPool = ILendingPool( 33 | addressesProvider.getLendingPool() 34 | ); 35 | // // Ask for a flashloan 36 | // // LendingPool will now execute the `executeOperation` function above 37 | lendingPool.flashLoan( 38 | address(this), // Which address to callback into, alternatively: address(this) 39 | assetToFlashLoan, 40 | amountToLoan, 41 | _params 42 | ); 43 | } 44 | 45 | function executeOperation( 46 | address _reserve, 47 | uint256 _amount, 48 | uint256 _fee, 49 | bytes calldata _params 50 | ) external { 51 | 52 | (bool success, ) = sender.call.value(_amount)(_params); 53 | 54 | if (!success) { 55 | assembly { 56 | returndatacopy(0, 0, returndatasize()) 57 | revert(0, returndatasize()) 58 | } 59 | } 60 | } 61 | 62 | fallback() external payable {} 63 | } 64 | -------------------------------------------------------------------------------- /contracts/MockAave.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.6.8; 2 | 3 | import "./Loaner.sol"; 4 | 5 | contract MockAave { 6 | 7 | uint public _amount; 8 | uint public _fee; 9 | 10 | constructor() public payable { 11 | 12 | } 13 | 14 | function flashLoan(address payable reciever, address asset, uint amount, bytes calldata _params) external { 15 | reciever.call.value(amount)(""); 16 | uint fee = amount * 9 / 10000; 17 | Loaner(reciever).executeOperation(asset,amount,fee,_params); 18 | } 19 | 20 | fallback() external payable { 21 | _fee = msg.value; 22 | } 23 | } -------------------------------------------------------------------------------- /contracts/MockAtomic.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.6.8; 2 | pragma experimental ABIEncoderV2; 3 | 4 | 5 | import "./Atomic.sol"; 6 | 7 | contract MockAtomic{ 8 | 9 | uint public valueRec; 10 | 11 | constructor(address factory, bytes memory transactions) public payable { 12 | } 13 | 14 | // function exec(bytes memory transactions) public payable { 15 | // valueRec = 2323; 16 | // // dd = transactions; 17 | // assembly { 18 | // let length := mload(transactions) 19 | // let i := 0x20 20 | // for { } lt(i, length) { } { 21 | // let to := mload(add(transactions, i)) 22 | // let value := mload(add(transactions, add(i, 0x20))) 23 | // let dataLength := mload(add(transactions, add(i, 0x60))) 24 | // let data := add(transactions, add(i, 0x80)) 25 | // let success := call(210000000000000, to, value, data, dataLength, 0, 0) 26 | // if eq(success, 0) { revert(0, "One of the transactions failed") } 27 | // i := add(i, add(0x80, mul(div(add(dataLength, 0x1f), 0x20), 0x20))) 28 | // } 29 | // } 30 | // } 31 | 32 | // function execc() public payable returns(bool){ 33 | // // dd = transactions; 34 | // valueRec = 6666699; 35 | // return true; 36 | // } 37 | 38 | fallback() external payable { 39 | valueRec = 333; 40 | // valueRec = msg.value; 41 | // dd = msg.data; 42 | // exec(msg.data); 43 | } 44 | } -------------------------------------------------------------------------------- /contracts/interfaces/IERC20.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.5.0; 2 | 3 | interface IERC20 { 4 | event Approval(address indexed owner, address indexed spender, uint value); 5 | event Transfer(address indexed from, address indexed to, uint value); 6 | 7 | function name() external view returns (string memory); 8 | function symbol() external view returns (string memory); 9 | function decimals() external view returns (uint8); 10 | function totalSupply() external view returns (uint); 11 | function balanceOf(address owner) external view returns (uint); 12 | function allowance(address owner, address spender) external view returns (uint); 13 | 14 | function approve(address spender, uint value) external returns (bool); 15 | function transfer(address to, uint value) external returns (bool); 16 | function transferFrom(address from, address to, uint value) external returns (bool); 17 | } 18 | -------------------------------------------------------------------------------- /contracts/interfaces/IUniswapV2Router01.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.6.2; 2 | 3 | interface IUniswapV2Router01 { 4 | function factory() external pure returns (address); 5 | function WETH() external pure returns (address); 6 | 7 | function addLiquidity( 8 | address tokenA, 9 | address tokenB, 10 | uint amountADesired, 11 | uint amountBDesired, 12 | uint amountAMin, 13 | uint amountBMin, 14 | address to, 15 | uint deadline 16 | ) external returns (uint amountA, uint amountB, uint liquidity); 17 | function addLiquidityETH( 18 | address token, 19 | uint amountTokenDesired, 20 | uint amountTokenMin, 21 | uint amountETHMin, 22 | address to, 23 | uint deadline 24 | ) external payable returns (uint amountToken, uint amountETH, uint liquidity); 25 | function removeLiquidity( 26 | address tokenA, 27 | address tokenB, 28 | uint liquidity, 29 | uint amountAMin, 30 | uint amountBMin, 31 | address to, 32 | uint deadline 33 | ) external returns (uint amountA, uint amountB); 34 | function removeLiquidityETH( 35 | address token, 36 | uint liquidity, 37 | uint amountTokenMin, 38 | uint amountETHMin, 39 | address to, 40 | uint deadline 41 | ) external returns (uint amountToken, uint amountETH); 42 | function removeLiquidityWithPermit( 43 | address tokenA, 44 | address tokenB, 45 | uint liquidity, 46 | uint amountAMin, 47 | uint amountBMin, 48 | address to, 49 | uint deadline, 50 | bool approveMax, uint8 v, bytes32 r, bytes32 s 51 | ) external returns (uint amountA, uint amountB); 52 | function removeLiquidityETHWithPermit( 53 | address token, 54 | uint liquidity, 55 | uint amountTokenMin, 56 | uint amountETHMin, 57 | address to, 58 | uint deadline, 59 | bool approveMax, uint8 v, bytes32 r, bytes32 s 60 | ) external returns (uint amountToken, uint amountETH); 61 | function swapExactTokensForTokens( 62 | uint amountIn, 63 | uint amountOutMin, 64 | address[] calldata path, 65 | address to, 66 | uint deadline 67 | ) external returns (uint[] memory amounts); 68 | function swapTokensForExactTokens( 69 | uint amountOut, 70 | uint amountInMax, 71 | address[] calldata path, 72 | address to, 73 | uint deadline 74 | ) external returns (uint[] memory amounts); 75 | function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline) 76 | external 77 | payable 78 | returns (uint[] memory amounts); 79 | function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline) 80 | external 81 | returns (uint[] memory amounts); 82 | function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) 83 | external 84 | returns (uint[] memory amounts); 85 | function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline) 86 | external 87 | payable 88 | returns (uint[] memory amounts); 89 | 90 | function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB); 91 | function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut); 92 | function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn); 93 | function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts); 94 | function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts); 95 | } 96 | -------------------------------------------------------------------------------- /contracts/interfaces/IWETH.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.5.0; 2 | 3 | interface IWETH { 4 | function deposit() external payable; 5 | function transfer(address to, uint value) external returns (bool); 6 | function withdraw(uint) external; 7 | } 8 | -------------------------------------------------------------------------------- /contracts/interfaces/V1/IUniswapV1Exchange.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.5.0; 2 | 3 | interface IUniswapV1Exchange { 4 | function balanceOf(address owner) external view returns (uint); 5 | function transferFrom(address from, address to, uint value) external returns (bool); 6 | function removeLiquidity(uint, uint, uint, uint) external returns (uint, uint); 7 | function tokenToEthSwapInput(uint, uint, uint) external returns (uint); 8 | function ethToTokenSwapInput(uint, uint) external payable returns (uint); 9 | } 10 | -------------------------------------------------------------------------------- /contracts/interfaces/V1/IUniswapV1Factory.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.5.0; 2 | 3 | interface IUniswapV1Factory { 4 | function getExchange(address) external view returns (address); 5 | } 6 | -------------------------------------------------------------------------------- /contracts/lib/SafeMath.sol: -------------------------------------------------------------------------------- 1 | pragma solidity =0.6.8; 2 | 3 | // a library for performing overflow-safe math, courtesy of DappHub (https://github.com/dapphub/ds-math) 4 | 5 | library SafeMath { 6 | function add(uint x, uint y) internal pure returns (uint z) { 7 | require((z = x + y) >= x, 'ds-math-add-overflow'); 8 | } 9 | 10 | function sub(uint x, uint y) internal pure returns (uint z) { 11 | require((z = x - y) <= x, 'ds-math-sub-underflow'); 12 | } 13 | 14 | function mul(uint x, uint y) internal pure returns (uint z) { 15 | require(y == 0 || (z = x * y) / y == x, 'ds-math-mul-overflow'); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /contracts/lib/UniswapV2Library.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.5.0; 2 | 3 | import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol'; 4 | 5 | import "./SafeMath.sol"; 6 | 7 | library UniswapV2Library { 8 | using SafeMath for uint; 9 | 10 | // returns sorted token addresses, used to handle return values from pairs sorted in this order 11 | function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) { 12 | require(tokenA != tokenB, 'UniswapV2Library: IDENTICAL_ADDRESSES'); 13 | (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); 14 | require(token0 != address(0), 'UniswapV2Library: ZERO_ADDRESS'); 15 | } 16 | 17 | // calculates the CREATE2 address for a pair without making any external calls 18 | function pairFor(address factory, address tokenA, address tokenB) internal pure returns (address pair) { 19 | (address token0, address token1) = sortTokens(tokenA, tokenB); 20 | pair = address(uint(keccak256(abi.encodePacked( 21 | hex'ff', 22 | factory, 23 | keccak256(abi.encodePacked(token0, token1)), 24 | hex'96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f' // init code hash 25 | )))); 26 | } 27 | 28 | // fetches and sorts the reserves for a pair 29 | function getReserves(address factory, address tokenA, address tokenB) internal view returns (uint reserveA, uint reserveB) { 30 | (address token0,) = sortTokens(tokenA, tokenB); 31 | (uint reserve0, uint reserve1,) = IUniswapV2Pair(pairFor(factory, tokenA, tokenB)).getReserves(); 32 | (reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0); 33 | } 34 | 35 | // given some amount of an asset and pair reserves, returns an equivalent amount of the other asset 36 | function quote(uint amountA, uint reserveA, uint reserveB) internal pure returns (uint amountB) { 37 | require(amountA > 0, 'UniswapV2Library: INSUFFICIENT_AMOUNT'); 38 | require(reserveA > 0 && reserveB > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY'); 39 | amountB = amountA.mul(reserveB) / reserveA; 40 | } 41 | 42 | // given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset 43 | function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) internal pure returns (uint amountOut) { 44 | require(amountIn > 0, 'UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT'); 45 | require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY'); 46 | uint amountInWithFee = amountIn.mul(997); 47 | uint numerator = amountInWithFee.mul(reserveOut); 48 | uint denominator = reserveIn.mul(1000).add(amountInWithFee); 49 | amountOut = numerator / denominator; 50 | } 51 | 52 | // given an output amount of an asset and pair reserves, returns a required input amount of the other asset 53 | function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) internal pure returns (uint amountIn) { 54 | require(amountOut > 0, 'UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT'); 55 | require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY'); 56 | uint numerator = reserveIn.mul(amountOut).mul(1000); 57 | uint denominator = reserveOut.sub(amountOut).mul(997); 58 | amountIn = (numerator / denominator).add(1); 59 | } 60 | 61 | // performs chained getAmountOut calculations on any number of pairs 62 | function getAmountsOut(address factory, uint amountIn, address[] memory path) internal view returns (uint[] memory amounts) { 63 | require(path.length >= 2, 'UniswapV2Library: INVALID_PATH'); 64 | amounts = new uint[](path.length); 65 | amounts[0] = amountIn; 66 | for (uint i; i < path.length - 1; i++) { 67 | (uint reserveIn, uint reserveOut) = getReserves(factory, path[i], path[i + 1]); 68 | amounts[i + 1] = getAmountOut(amounts[i], reserveIn, reserveOut); 69 | } 70 | } 71 | 72 | // performs chained getAmountIn calculations on any number of pairs 73 | function getAmountsIn(address factory, uint amountOut, address[] memory path) internal view returns (uint[] memory amounts) { 74 | require(path.length >= 2, 'UniswapV2Library: INVALID_PATH'); 75 | amounts = new uint[](path.length); 76 | amounts[amounts.length - 1] = amountOut; 77 | for (uint i = path.length - 1; i > 0; i--) { 78 | (uint reserveIn, uint reserveOut) = getReserves(factory, path[i - 1], path[i]); 79 | amounts[i - 1] = getAmountIn(amounts[i], reserveIn, reserveOut); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossaDev/atomic/dad74f87b8bb0dd2e2352849ec136bec84fa59fb/favicon.ico -------------------------------------------------------------------------------- /index.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | /** Buttons */ 4 | .btn-toggler, 5 | .btn-link { 6 | outline-width: 0 !important; 7 | background-color: transparent; 8 | font-family: arial, sans-serif; 9 | } 10 | 11 | .btn-link { 12 | font-size: 16px; 13 | border-width: 0; 14 | padding: 0; 15 | text-decoration: underline; 16 | cursor: pointer; 17 | } 18 | 19 | .btn-toggler { 20 | font-size: 1.5rem; 21 | line-height: 1; 22 | text-transform: uppercase; 23 | padding: 0.25rem 0.5rem; 24 | min-width: 50px; 25 | color: tomato; 26 | border: 2px solid currentColor; 27 | } 28 | 29 | .btn-toggler:not(:last-of-type) { 30 | margin-right: 0.5rem; 31 | } 32 | 33 | .btn-menu[aria-expanded='true'], 34 | .btn-close[aria-expanded='false'] { 35 | display: none; 36 | } 37 | 38 | .btn-submenu span { 39 | font-size: 0.5em; 40 | line-height: 1.75; 41 | } 42 | 43 | /** Nav menus */ 44 | .nav-menu ul { 45 | display: flex; 46 | flex-direction: column; 47 | list-style: none; 48 | padding: 0; 49 | } 50 | 51 | .nav-menu a, 52 | .nav-menu .btn-link { 53 | display: flex; 54 | box-sizing: border-box; 55 | padding: 0.5rem; 56 | width: 100%; 57 | text-decoration: none; 58 | text-transform: uppercase; 59 | color: tomato; 60 | } 61 | 62 | .nav-menu a:hover, 63 | .nav-menu .btn-link:hover { 64 | background-color: rgba(0, 0, 0, 0.1); 65 | } 66 | 67 | /** Hide element */ 68 | .hide { 69 | display: none !important; 70 | } 71 | 72 | /** Media breakpoints */ 73 | @media (min-width: 768px) { 74 | .d-md-none { 75 | display: none; 76 | } 77 | 78 | /** Show nav menu */ 79 | .nav-menu { 80 | display: flex !important; 81 | } 82 | 83 | /** Display menu items horizonatally */ 84 | .nav-menu>ul { 85 | flex-direction: row; 86 | } 87 | } -------------------------------------------------------------------------------- /js/lib/xmlToJSON.min.js: -------------------------------------------------------------------------------- 1 | var xmlToJSON = function () { this.version = "1.3.4"; var e = { mergeCDATA: !0, grokAttr: !0, grokText: !0, normalize: !0, xmlns: !0, namespaceKey: "_ns", textKey: "_text", valueKey: "_value", attrKey: "_attr", cdataKey: "_cdata", attrsAsObject: !0, stripAttrPrefix: !0, stripElemPrefix: !0, childrenAsArray: !0 }, t = new RegExp(/(?!xmlns)^.*:/), r = new RegExp(/^\s+|\s+$/g); return this.grokType = function (e) { return /^\s*$/.test(e) ? null : /^(?:true|false)$/i.test(e) ? "true" === e.toLowerCase() : isFinite(e) ? parseFloat(e) : e }, this.parseString = function (e, t) { return this.parseXML(this.stringToXML(e), t) }, this.parseXML = function (a, n) { for (var s in n) e[s] = n[s]; var l = {}, i = 0, o = ""; if (e.xmlns && a.namespaceURI && (l[e.namespaceKey] = a.namespaceURI), a.attributes && a.attributes.length > 0) { var c = {}; for (i; i < a.attributes.length; i++) { var u = a.attributes.item(i); m = {}; var p = ""; p = e.stripAttrPrefix ? u.name.replace(t, "") : u.name, e.grokAttr ? m[e.valueKey] = this.grokType(u.value.replace(r, "")) : m[e.valueKey] = u.value.replace(r, ""), e.xmlns && u.namespaceURI && (m[e.namespaceKey] = u.namespaceURI), e.attrsAsObject ? c[p] = m : l[e.attrKey + p] = m } e.attrsAsObject && (l[e.attrKey] = c) } if (a.hasChildNodes()) for (var y, d, m, h = 0; h < a.childNodes.length; h++)4 === (y = a.childNodes.item(h)).nodeType ? e.mergeCDATA ? o += y.nodeValue : l.hasOwnProperty(e.cdataKey) ? (l[e.cdataKey].constructor !== Array && (l[e.cdataKey] = [l[e.cdataKey]]), l[e.cdataKey].push(y.nodeValue)) : e.childrenAsArray ? (l[e.cdataKey] = [], l[e.cdataKey].push(y.nodeValue)) : l[e.cdataKey] = y.nodeValue : 3 === y.nodeType ? o += y.nodeValue : 1 === y.nodeType && (0 === i && (l = {}), d = e.stripElemPrefix ? y.nodeName.replace(t, "") : y.nodeName, m = xmlToJSON.parseXML(y), l.hasOwnProperty(d) ? (l[d].constructor !== Array && (l[d] = [l[d]]), l[d].push(m)) : (e.childrenAsArray ? (l[d] = [], l[d].push(m)) : l[d] = m, i++)); else o || (e.childrenAsArray ? (l[e.textKey] = [], l[e.textKey].push(null)) : l[e.textKey] = null); if (o) if (e.grokText) { var x = this.grokType(o.replace(r, "")); null !== x && void 0 !== x && (l[e.textKey] = x) } else e.normalize ? l[e.textKey] = o.replace(r, "").replace(/\s+/g, " ") : l[e.textKey] = o.replace(r, ""); return l }, this.xmlToString = function (e) { try { return e.xml ? e.xml : (new XMLSerializer).serializeToString(e) } catch (e) { return null } }, this.stringToXML = function (e) { try { var t = null; return window.DOMParser ? t = (new DOMParser).parseFromString(e, "text/xml") : (t = new ActiveXObject("Microsoft.XMLDOM"), t.async = !1, t.loadXML(e), t) } catch (e) { return null } }, this }.call({}); "undefined" != typeof module && null !== module && module.exports ? module.exports = xmlToJSON : "function" == typeof define && define.amd && define(function () { return xmlToJSON }); -------------------------------------------------------------------------------- /js/saveAtomic.js: -------------------------------------------------------------------------------- 1 | const copyToClipboard = str => { 2 | const el = document.createElement('textarea'); 3 | el.value = str; 4 | el.setAttribute('readonly', ''); 5 | el.style.position = 'absolute'; 6 | el.style.left = '-9999px'; 7 | document.body.appendChild(el); 8 | el.select(); 9 | document.execCommand('copy'); 10 | document.body.removeChild(el); 11 | }; 12 | 13 | const saveAtomic = () => { 14 | let xml = Blockly.Xml.workspaceToDom(Blockly.mainWorkspace) 15 | let txName = prompt("Save as", "My Atomic Tx"); 16 | localStorage.setItem(txName, Blockly.Xml.domToText(xml)) 17 | addToTxList(txName) 18 | } 19 | 20 | const openAtomic = (txName) => { 21 | // let txName = prompt("Open transaction", "My Atomic Tx"); 22 | let xml = Blockly.Xml.textToDom(localStorage.getItem(txName)) 23 | 24 | Blockly.mainWorkspace.clear() 25 | Blockly.Xml.domToWorkspace(Blockly.mainWorkspace, xml) 26 | } 27 | 28 | var modal 29 | 30 | const openMenu = () => { 31 | let list = JSON.parse(localStorage.getItem('txList')); 32 | 33 | let modalContent = "

Open Transaction

" 34 | for (i = 0; i < list.length; i++) 35 | modalContent += "

" + list[i] + "

" 36 | 37 | modal = picoModal(modalContent).show(); 38 | return 39 | 40 | console.log(list) 41 | 42 | let txName = prompt("Please type the name of the tx to open (we'll improve this soon).", "My Atomic Tx"); 43 | openAtomic(txName) 44 | } 45 | 46 | const shareAtomic = () => { 47 | const ipfs = IpfsHttpClient('ipfs.infura.io', '5001', { 48 | protocol: "https" 49 | }) 50 | 51 | let xml = Blockly.Xml.workspaceToDom(Blockly.mainWorkspace) 52 | 53 | const transaction = buffer.Buffer(Blockly.Xml.domToText(xml)) 54 | ipfs.add(transaction, (err, result) => { 55 | if (err) { 56 | console.error(err) 57 | return 58 | } else { 59 | console.log(result) 60 | console.log('https://ipfs.io/ipfs/' + result[0].hash) 61 | copyToClipboard("https://atomic.ninja/?&tx=" + result[0].hash) 62 | alert("URL copied to clipboard. Paste it anywhere to share it. \n\n It's on IPFS, so if you want it to persist overtime you have to pin it. We recomment https://pinata.cloud/, they allow for pinning of thousasnds of transactions in their free tier. That said, your transaction should be good for the coming days. \n\n The IPFS hash of the transaction is: " + result[0].hash) 63 | 64 | // copying to clipboard 65 | 66 | } 67 | }); 68 | } 69 | 70 | const embedAtomic = () => { 71 | const ipfs = IpfsHttpClient('ipfs.infura.io', '5001', { 72 | protocol: "https" 73 | }) 74 | 75 | let xml = Blockly.Xml.workspaceToDom(Blockly.mainWorkspace) 76 | 77 | const transaction = buffer.Buffer(Blockly.Xml.domToText(xml)) 78 | ipfs.add(transaction, (err, result) => { 79 | if (err) { 80 | console.error(err) 81 | return 82 | } else { 83 | console.log(result) 84 | console.log('https://ipfs.io/ipfs/' + result[0].hash) 85 | copyToClipboard("https://atomic.ninja/?&tx=" + result[0].hash + "&embed=true") 86 | alert("URL copied to clipboard. iFrame it on your webpage and you'll be good to go (SSL required). \n\n It's on IPFS, so if you want it to persist overtime you have to pin it. We recomment https://pinata.cloud/, they allow for pinning of thousasnds of transactions in their free tier. That said, your transaction should be good for the coming days. \n\n The IPFS hash of the transaction is: " + result[0].hash) 87 | 88 | // copying to clipboard 89 | 90 | } 91 | }); 92 | } 93 | 94 | function addToTxList(txName) { 95 | var list; 96 | //is anything in localstorage? 97 | if (localStorage.getItem('txList') === null) { 98 | list = []; 99 | } else { 100 | list = JSON.parse(localStorage.getItem('txList')); 101 | } 102 | if (!list.includes(txName)) 103 | list.push(txName); 104 | localStorage.setItem('txList', JSON.stringify(list)); 105 | } -------------------------------------------------------------------------------- /js/setup.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var workspace = null; 4 | 5 | // routines that run when starting 6 | function getUrlVars() { 7 | var vars = {}; 8 | var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (m, key, value) { 9 | vars[key] = value; 10 | }); 11 | return vars; 12 | } 13 | 14 | function getToolboxElement() { 15 | var match = location.search.match(/toolbox=([^&]+)/); 16 | return document.getElementById('toolbox-' + (match ? match[1] : 'categories')); 17 | } 18 | 19 | async function setup() { 20 | 21 | 22 | let urlVars = getUrlVars() 23 | 24 | const ipfs = IpfsHttpClient('ipfs.infura.io', '5001', { 25 | protocol: "https" 26 | }) 27 | 28 | let readOnly = false 29 | let scrollbars = true 30 | if (urlVars.embed) { 31 | // hide stuff 32 | document.getElementById("navbar").style.display = "none"; 33 | document.getElementById("blocklyDiv").style.height = "100%"; 34 | document.getElementById("blocklyDiv").style.top = "-30px"; 35 | document.getElementById("embedLink").href = "https://atomic.ninja?&tx=" + urlVars.tx; 36 | readOnly = true 37 | scrollbars = false 38 | } else { 39 | document.getElementById("embedLogo").style.display = "none"; 40 | } 41 | 42 | // Create main workspace. 43 | workspace = Blockly.inject('blocklyDiv', { 44 | comments: true, 45 | disable: false, 46 | collapse: false, 47 | media: './media/', 48 | readOnly: readOnly, 49 | rtl: null, 50 | scrollbars: scrollbars, 51 | toolbox: getToolboxElement(), 52 | toolboxPosition: "start", 53 | horizontalLayout: false, 54 | sounds: true, 55 | zoom: { 56 | controls: true, 57 | wheel: true, 58 | startScale: 0.85, 59 | maxScale: 4, 60 | minScale: 0.25, 61 | scaleSpeed: 1.1 62 | }, 63 | colours: { 64 | fieldShadow: 'rgba(255, 255, 255, 0.3)', 65 | dragShadowOpacity: 0.6 66 | } 67 | }); 68 | 69 | // open tx 70 | if (urlVars.tx) { 71 | 72 | let txData 73 | if (urlVars.tx.substr(-1) == "#") 74 | txData = await ipfs.get(urlVars.tx.slice(0, -1)) 75 | else 76 | txData = await ipfs.get(urlVars.tx) 77 | 78 | 79 | let atomicTx = new TextDecoder("utf-8").decode(txData[0].content); 80 | 81 | let xml = Blockly.Xml.textToDom(atomicTx) 82 | Blockly.Xml.domToWorkspace(Blockly.mainWorkspace, xml) 83 | } 84 | 85 | if (!urlVars.embed) 86 | picoModal( 87 | "

Welcome to Atomic Ninja!

" + 88 | "

Send complex atomic transactions to Ethereum like never before.

" + 89 | "

Quick Start

" + 90 | "

On the left of the screen there is a list of sorted, disconnected blocks. This is your toolbox.

" + 91 | "

Drag the blocks to the workspace at the right, join them and edit their parameters to perform the actions you´d like.

" + 92 | "

Start with a hat shaped block such as , as they indicate the beginning of your transaction.

" + 93 | "

Once you are done, press to simulate your transaction, and to send it out!

" + 94 | "

Tip: Hover the blocks for details on how they work.

" + 95 | "

For more details, head over to the Atomic documentation.

" 96 | ).show(); 97 | 98 | 99 | } -------------------------------------------------------------------------------- /js/simulateAtomic.js: -------------------------------------------------------------------------------- 1 | function hexToString(hex) { 2 | var string = ''; 3 | for (var i = 0; i < hex.length; i += 2) { 4 | string += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); 5 | } 6 | return string; 7 | } 8 | 9 | const simulateAtomic = async (blocks) => { 10 | let topBlocks = getXml(blocks) 11 | 12 | let launchBlock = getLaunchBlock(topBlocks) 13 | if (!launchBlock) return 14 | 15 | let txParameters = await getTxParameters(launchBlock) 16 | 17 | console.log("Simulating atomic tx:", ...txParameters) 18 | // send atomic tx 19 | simulationCall(...txParameters) 20 | 21 | } 22 | 23 | const simulationCall = async function (value, gas, paramObj) { 24 | const defaultProvider = ethers.getDefaultProvider("homestead") 25 | 26 | // let factory = new ethers.ContractFactory(atomicAbi, atomicBytecode); 27 | let atomic = new ethers.utils.Interface(atomicAbi) 28 | 29 | let calldata = atomic.functions.execute.encode([paramObj.adds, paramObj.values, paramObj.datas]) 30 | 31 | let transaction = { 32 | to: atomicAddress, 33 | value: ethers.utils.parseEther(value).toHexString(), 34 | data: calldata, 35 | } 36 | 37 | let tx = await defaultProvider.call(transaction) 38 | 39 | console.log("Returned from eth_call: " + tx) 40 | if (tx == "0x") // failed 41 | alert("Your transaction is failing with no error message, check if all the blocks have enough balance to perform their operations.") 42 | else if (tx == "0x0000000000000000000000000000000000000000000000000000000000000001") 43 | alert("Transaction succeeded.") 44 | else if (tx.substring(0, 10) == "0x08c379a0") { // signature for Error(string) 45 | 46 | let hexErrorMsg = tx.slice(73).replace(/^0+(\d)|(\d)0+$/gm, '$1$2'); 47 | 48 | let errorMessage = hexToString(hexErrorMsg).replace(/[^A-Za-z 0-9 \.,\?""!@#\$%\^&\*\(\)-_=\+;:<>\/\\\|\}\{\[\]`~]*/g, '') 49 | 50 | console.log(errorMessage) 51 | 52 | alert("Your transaction is failing: " + errorMessage) 53 | } 54 | } 55 | 56 | -------------------------------------------------------------------------------- /media/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossaDev/atomic/dad74f87b8bb0dd2e2352849ec136bec84fa59fb/media/.DS_Store -------------------------------------------------------------------------------- /media/1x1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossaDev/atomic/dad74f87b8bb0dd2e2352849ec136bec84fa59fb/media/1x1.gif -------------------------------------------------------------------------------- /media/aaveghost.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /media/atomic-ninja-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossaDev/atomic/dad74f87b8bb0dd2e2352849ec136bec84fa59fb/media/atomic-ninja-icon.png -------------------------------------------------------------------------------- /media/atomic-ninja-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossaDev/atomic/dad74f87b8bb0dd2e2352849ec136bec84fa59fb/media/atomic-ninja-logo.png -------------------------------------------------------------------------------- /media/atomic_brag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossaDev/atomic/dad74f87b8bb0dd2e2352849ec136bec84fa59fb/media/atomic_brag.png -------------------------------------------------------------------------------- /media/balancer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossaDev/atomic/dad74f87b8bb0dd2e2352849ec136bec84fa59fb/media/balancer.png -------------------------------------------------------------------------------- /media/click.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossaDev/atomic/dad74f87b8bb0dd2e2352849ec136bec84fa59fb/media/click.mp3 -------------------------------------------------------------------------------- /media/click.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossaDev/atomic/dad74f87b8bb0dd2e2352849ec136bec84fa59fb/media/click.ogg -------------------------------------------------------------------------------- /media/click.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossaDev/atomic/dad74f87b8bb0dd2e2352849ec136bec84fa59fb/media/click.wav -------------------------------------------------------------------------------- /media/comment-arrow-down.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | dropdown-caret-up 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /media/comment-arrow-up.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | dropdown-caret-down 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /media/compound.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /media/defiZaps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossaDev/atomic/dad74f87b8bb0dd2e2352849ec136bec84fa59fb/media/defiZaps.png -------------------------------------------------------------------------------- /media/delete-x.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | delete-x 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /media/delete.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossaDev/atomic/dad74f87b8bb0dd2e2352849ec136bec84fa59fb/media/delete.mp3 -------------------------------------------------------------------------------- /media/delete.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossaDev/atomic/dad74f87b8bb0dd2e2352849ec136bec84fa59fb/media/delete.ogg -------------------------------------------------------------------------------- /media/delete.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossaDev/atomic/dad74f87b8bb0dd2e2352849ec136bec84fa59fb/media/delete.wav -------------------------------------------------------------------------------- /media/dropdown-arrow-dark.svg: -------------------------------------------------------------------------------- 1 | dropdown-arrow 2 | -------------------------------------------------------------------------------- /media/dropdown-arrow.svg: -------------------------------------------------------------------------------- 1 | dropdown-arrow -------------------------------------------------------------------------------- /media/ens.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossaDev/atomic/dad74f87b8bb0dd2e2352849ec136bec84fa59fb/media/ens.png -------------------------------------------------------------------------------- /media/erc20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossaDev/atomic/dad74f87b8bb0dd2e2352849ec136bec84fa59fb/media/erc20.png -------------------------------------------------------------------------------- /media/ethereum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossaDev/atomic/dad74f87b8bb0dd2e2352849ec136bec84fa59fb/media/ethereum.png -------------------------------------------------------------------------------- /media/eyedropper.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Artboard 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /media/green-flag.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | greenflag 10 | 15 | 17 | 18 | -------------------------------------------------------------------------------- /media/handclosed.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossaDev/atomic/dad74f87b8bb0dd2e2352849ec136bec84fa59fb/media/handclosed.cur -------------------------------------------------------------------------------- /media/handdelete.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossaDev/atomic/dad74f87b8bb0dd2e2352849ec136bec84fa59fb/media/handdelete.cur -------------------------------------------------------------------------------- /media/handopen.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossaDev/atomic/dad74f87b8bb0dd2e2352849ec136bec84fa59fb/media/handopen.cur -------------------------------------------------------------------------------- /media/hatblock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossaDev/atomic/dad74f87b8bb0dd2e2352849ec136bec84fa59fb/media/hatblock.png -------------------------------------------------------------------------------- /media/icons/arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | arrow 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /media/icons/arrow_button.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /media/icons/control_forever.svg: -------------------------------------------------------------------------------- 1 | control_forever -------------------------------------------------------------------------------- /media/icons/control_repeat.svg: -------------------------------------------------------------------------------- 1 | control_repeat -------------------------------------------------------------------------------- /media/icons/control_stop.svg: -------------------------------------------------------------------------------- 1 | control_stop -------------------------------------------------------------------------------- /media/icons/control_wait.svg: -------------------------------------------------------------------------------- 1 | wait -------------------------------------------------------------------------------- /media/icons/event_broadcast_blue.svg: -------------------------------------------------------------------------------- 1 | event_broadcast_blue -------------------------------------------------------------------------------- /media/icons/event_broadcast_coral.svg: -------------------------------------------------------------------------------- 1 | event_broadcast_coral -------------------------------------------------------------------------------- /media/icons/event_broadcast_green.svg: -------------------------------------------------------------------------------- 1 | event_broadcast_green -------------------------------------------------------------------------------- /media/icons/event_broadcast_magenta.svg: -------------------------------------------------------------------------------- 1 | event_broadcast_magenta -------------------------------------------------------------------------------- /media/icons/event_broadcast_orange.svg: -------------------------------------------------------------------------------- 1 | event_broadcast_orange -------------------------------------------------------------------------------- /media/icons/event_broadcast_purple.svg: -------------------------------------------------------------------------------- 1 | send-message-purple -------------------------------------------------------------------------------- /media/icons/event_when-broadcast-received_blue.svg: -------------------------------------------------------------------------------- 1 | LetterGet_Blue -------------------------------------------------------------------------------- /media/icons/event_when-broadcast-received_coral.svg: -------------------------------------------------------------------------------- 1 | LetterGet_Coral -------------------------------------------------------------------------------- /media/icons/event_when-broadcast-received_green.svg: -------------------------------------------------------------------------------- 1 | LetterGet_Green -------------------------------------------------------------------------------- /media/icons/event_when-broadcast-received_magenta.svg: -------------------------------------------------------------------------------- 1 | LetterGet_Magenta -------------------------------------------------------------------------------- /media/icons/event_when-broadcast-received_orange.svg: -------------------------------------------------------------------------------- 1 | LetterGet_Orange -------------------------------------------------------------------------------- /media/icons/event_when-broadcast-received_purple.svg: -------------------------------------------------------------------------------- 1 | LetterGet_Purple -------------------------------------------------------------------------------- /media/icons/event_whenflagclicked.svg: -------------------------------------------------------------------------------- 1 | greenflag -------------------------------------------------------------------------------- /media/icons/remove.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | delete-argument v2 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /media/icons/set-led_blue.svg: -------------------------------------------------------------------------------- 1 | set-led_blue -------------------------------------------------------------------------------- /media/icons/set-led_coral.svg: -------------------------------------------------------------------------------- 1 | set-led_coral -------------------------------------------------------------------------------- /media/icons/set-led_green.svg: -------------------------------------------------------------------------------- 1 | set-led_green -------------------------------------------------------------------------------- /media/icons/set-led_magenta.svg: -------------------------------------------------------------------------------- 1 | set-led-magenta -------------------------------------------------------------------------------- /media/icons/set-led_mystery.svg: -------------------------------------------------------------------------------- 1 | set-led-mystery -------------------------------------------------------------------------------- /media/icons/set-led_orange.svg: -------------------------------------------------------------------------------- 1 | set-led-orange -------------------------------------------------------------------------------- /media/icons/set-led_purple.svg: -------------------------------------------------------------------------------- 1 | set-led-purple -------------------------------------------------------------------------------- /media/icons/set-led_white.svg: -------------------------------------------------------------------------------- 1 | set-led-white -------------------------------------------------------------------------------- /media/icons/set-led_yellow.svg: -------------------------------------------------------------------------------- 1 | set-led-yellow -------------------------------------------------------------------------------- /media/icons/wedo_motor-clockwise.svg: -------------------------------------------------------------------------------- 1 | wedo_motorclockwise -------------------------------------------------------------------------------- /media/icons/wedo_motor-counterclockwise.svg: -------------------------------------------------------------------------------- 1 | wedo_motorclockwise -------------------------------------------------------------------------------- /media/icons/wedo_motor-speed_fast.svg: -------------------------------------------------------------------------------- 1 | set-motor-speed_fast -------------------------------------------------------------------------------- /media/icons/wedo_motor-speed_med.svg: -------------------------------------------------------------------------------- 1 | set-motor-speed_med -------------------------------------------------------------------------------- /media/icons/wedo_motor-speed_slow.svg: -------------------------------------------------------------------------------- 1 | set-motor-speed_slow -------------------------------------------------------------------------------- /media/icons/wedo_when-distance_close.svg: -------------------------------------------------------------------------------- 1 | wedo_whendistanceclose -------------------------------------------------------------------------------- /media/icons/wedo_when-tilt-backward.svg: -------------------------------------------------------------------------------- 1 | wedo_whentiltbackward -------------------------------------------------------------------------------- /media/icons/wedo_when-tilt-forward.svg: -------------------------------------------------------------------------------- 1 | start-when-tilted-forward -------------------------------------------------------------------------------- /media/icons/wedo_when-tilt-left.svg: -------------------------------------------------------------------------------- 1 | start-when-tilted-left -------------------------------------------------------------------------------- /media/icons/wedo_when-tilt-right.svg: -------------------------------------------------------------------------------- 1 | start-when-tilted-right -------------------------------------------------------------------------------- /media/icons/wedo_when-tilt.svg: -------------------------------------------------------------------------------- 1 | start-when-tilted-any -------------------------------------------------------------------------------- /media/linearblock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossaDev/atomic/dad74f87b8bb0dd2e2352849ec136bec84fa59fb/media/linearblock.png -------------------------------------------------------------------------------- /media/ninjatx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossaDev/atomic/dad74f87b8bb0dd2e2352849ec136bec84fa59fb/media/ninjatx.png -------------------------------------------------------------------------------- /media/pooltogetherCategory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossaDev/atomic/dad74f87b8bb0dd2e2352849ec136bec84fa59fb/media/pooltogetherCategory.png -------------------------------------------------------------------------------- /media/repeat.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | repeat 10 | 14 | 18 | 19 | -------------------------------------------------------------------------------- /media/revert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossaDev/atomic/dad74f87b8bb0dd2e2352849ec136bec84fa59fb/media/revert.png -------------------------------------------------------------------------------- /media/rotate-left.svg: -------------------------------------------------------------------------------- 1 | rotate-clockwise -------------------------------------------------------------------------------- /media/rotate-right.svg: -------------------------------------------------------------------------------- 1 | rotate-counter-clockwise -------------------------------------------------------------------------------- /media/sandwitchblock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossaDev/atomic/dad74f87b8bb0dd2e2352849ec136bec84fa59fb/media/sandwitchblock.png -------------------------------------------------------------------------------- /media/sprites.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossaDev/atomic/dad74f87b8bb0dd2e2352849ec136bec84fa59fb/media/sprites.png -------------------------------------------------------------------------------- /media/sprites.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /media/status-not-ready.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | not-connected 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /media/status-ready.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | connected 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /media/tenderlySCs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossaDev/atomic/dad74f87b8bb0dd2e2352849ec136bec84fa59fb/media/tenderlySCs.png -------------------------------------------------------------------------------- /media/uniswapToolbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossaDev/atomic/dad74f87b8bb0dd2e2352849ec136bec84fa59fb/media/uniswapToolbox.png -------------------------------------------------------------------------------- /media/uniswapV1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossaDev/atomic/dad74f87b8bb0dd2e2352849ec136bec84fa59fb/media/uniswapV1.png -------------------------------------------------------------------------------- /media/zoom-in.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /media/zoom-launchAtomic.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /media/zoom-out.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /media/zoom-reset.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /media/zoom-simulateAtomic.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "scratch-blocks", 3 | "version": "0.1.0", 4 | "description": "Scratch Blocks is a library for building creative computing interfaces.", 5 | "author": "Massachusetts Institute of Technology", 6 | "license": "Apache-2.0", 7 | "homepage": "https://github.com/LLK/scratch-blocks#readme", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/LLK/scratch-blocks.git" 11 | }, 12 | "main": "./dist/vertical.js", 13 | "browser": "./shim/vertical.js", 14 | "scripts": { 15 | "deploy": "rimraf gh-pages/closure-library/scripts/ci/CloseAdobeDialog.exe && gh-pages -t -d gh-pages -m \"Build for $(git log --pretty=format:%H -n1)\"", 16 | "prepublish": "python build.py && webpack", 17 | "test:unit": "node tests/jsunit/test_runner.js", 18 | "test:lint": "eslint .", 19 | "test:messages": "npm run translate && node i18n/test_scratch_msgs.js", 20 | "test": "npm run test:lint && npm run test:messages && npm run test:unit", 21 | "version": "json -f package.json -I -e \"this.repository.sha = '$(git log -n1 --pretty=format:%H)'\"" 22 | }, 23 | "dependencies": { 24 | "@studydefi/money-legos": "^2.1.0", 25 | "@uniswap/v2-core": "^1.0.1", 26 | "ethers": "^4.0.47", 27 | "exports-loader": "0.6.3", 28 | "imports-loader": "0.6.5" 29 | }, 30 | "devDependencies": { 31 | "@nomiclabs/buidler": "^1.3.2", 32 | "@nomiclabs/buidler-ethers": "^1.3.2", 33 | "async": "2.6.0", 34 | "chai": "^4.2.0", 35 | "chromedriver": "^75.0", 36 | "copy-webpack-plugin": "4.5.1", 37 | "eslint": "^4.16", 38 | "event-stream": "3.3.4", 39 | "ganache-core": "^2.10.2", 40 | "gh-pages": "0.12.0", 41 | "glob": "7.1.2", 42 | "google-closure-compiler": "20180402.0.0", 43 | "google-closure-library": "20180204.0.0", 44 | "graceful-fs": "4.1.11", 45 | "json": "9.0.4", 46 | "rimraf": "2.6.2", 47 | "scratch-l10n": "^3.8.20200325112845", 48 | "selenium-webdriver": "^4.0.0-alpha.1", 49 | "transifex": "1.6.6", 50 | "travis-after-all": "1.4.4", 51 | "uglifyjs-webpack-plugin": "^1.2.5", 52 | "webpack": "^4.22.0", 53 | "webpack-cli": "^3.1.1" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /test/compound_supply.js: -------------------------------------------------------------------------------- 1 | const { assert } = require("chai"); 2 | const { legos } = require("@studydefi/money-legos"); 3 | 4 | const { 5 | startChain, 6 | deployAtomic, 7 | swapEthForDai, 8 | encodeForAtomic, 9 | } = require("./testHelpers"); 10 | 11 | const encoder = (token, value) => { 12 | const cETH = "0x4ddc2d193948926d02f9b1fe9e1daa0718270ed5"; 13 | const cDAI = "0x5d3a536e4d6dbd6114cc1ead35777bab948e3643"; 14 | 15 | let substack = { 16 | adds: [], 17 | values: [], 18 | datas: [], 19 | }; 20 | 21 | //1. Mint the desired collateral 22 | let tkAddress; 23 | let tkData; 24 | let tkValue; 25 | if (token == "ETH") { 26 | tkAddress = cETH; 27 | let tkAbi = [ 28 | { 29 | constant: false, 30 | inputs: [], 31 | name: "mint", 32 | outputs: [], 33 | payable: true, 34 | stateMutability: "payable", 35 | type: "function", 36 | signature: "0x1249c58b", 37 | }, 38 | ]; 39 | 40 | let inter = new ethers.utils.Interface(tkAbi); 41 | tkData = inter.functions.mint.encode([]); 42 | tkValue = ethers.utils.parseEther(value); 43 | } else if (token == "DAI") { 44 | tkAddress = cDAI; 45 | let tkAbi = [ 46 | { 47 | constant: false, 48 | inputs: [ 49 | { 50 | internalType: "uint256", 51 | name: "mintAmount", 52 | type: "uint256", 53 | }, 54 | ], 55 | name: "mint", 56 | outputs: [ 57 | { 58 | internalType: "uint256", 59 | name: "", 60 | type: "uint256", 61 | }, 62 | ], 63 | payable: false, 64 | stateMutability: "nonpayable", 65 | type: "function", 66 | signature: "0xa0712d68", 67 | }, 68 | ]; 69 | 70 | let inter = new ethers.utils.Interface(tkAbi); 71 | tkData = inter.functions.mint.encode([ethers.utils.parseEther(value)]); 72 | tkValue = "0"; 73 | } 74 | substack.adds.push(tkAddress); 75 | substack.values.push(tkValue); 76 | substack.datas.push(tkData); 77 | 78 | return substack; 79 | }; 80 | 81 | describe("Compound Supply", function () { 82 | const cETH = "0x4ddc2d193948926d02f9b1fe9e1daa0718270ed5"; 83 | const cDAI = "0x5d3a536e4d6dbd6114cc1ead35777bab948e3643"; 84 | const comptroller = "0x3d9819210a31b4961b30ef54be2aed79b9c9cd3b"; 85 | 86 | let atomicFactory = {}; 87 | 88 | beforeEach(async () => { 89 | this.timeout(500000); 90 | 91 | ethers.provider = await startChain(); 92 | const AtomicFactory = await ethers.getContractFactory("Atomic"); 93 | atomicFactory = await AtomicFactory.deploy(); 94 | }); 95 | 96 | it("Should supply liquidity with ETH", async function () { 97 | let txs = encoder("ETH", "8"); 98 | // console.log(txs); 99 | 100 | let Daipayload = encoder("DAI", "10"); 101 | // console.log(Daipayload); 102 | 103 | await atomicFactory.launchAtomic(txs.adds, txs.values, txs.datas, { 104 | value: ethers.utils.parseUnits("10", "ether").toHexString(), 105 | gasPrice: 1, 106 | gasLimit: 6500000, 107 | }); 108 | 109 | let atd = await atomicFactory.factory(); 110 | // console.log("ato", atd); 111 | 112 | let proxy = new ethers.Contract( 113 | atd, 114 | atomicFactory.interface.abi, 115 | ethers.provider 116 | ); 117 | let own = await proxy.owner(); 118 | let cEThCoontract = new ethers.Contract( 119 | cETH, 120 | legos.erc20.abi, 121 | ethers.provider 122 | ); 123 | let bal = await cEThCoontract.balanceOf(proxy.address); 124 | console.log(bal.toString()); 125 | }); 126 | 127 | //it("Should supply liquidity with DAI", async function () { 128 | // let txs = encoder("DAI", "1"); 129 | // // console.log(txs); 130 | // let Daipayload = encoder("DAI", "10"); 131 | // // console.log(Daipayload); 132 | // await atomicFactory.launchAtomic(txs.adds, txs.values, txs.datas, { 133 | // value: ethers.utils.parseUnits("10", "ether").toHexString(), 134 | // gasPrice: 1, 135 | // gasLimit: 6500000, 136 | // }); 137 | // let atd = await atomicFactory.factory(); 138 | // // console.log("ato", atd); 139 | // let proxy = new ethers.Contract( 140 | // atd, 141 | // atomicFactory.interface.abi, 142 | // ethers.provider 143 | // ); 144 | // let own = await proxy.owner(); 145 | // let cEThCoontract = new ethers.Contract( 146 | // cETH, 147 | // legos.erc20.abi, 148 | // ethers.provider 149 | // ); 150 | // let bal = await cEThCoontract.balanceOf(own); 151 | // console.log(bal.toString()); 152 | //}); 153 | }); 154 | -------------------------------------------------------------------------------- /test/erc20_transfer.js: -------------------------------------------------------------------------------- 1 | const { 2 | assert 3 | } = require("chai"); 4 | 5 | const { 6 | startChain, 7 | swapEthForDai, 8 | mergeTxObjs 9 | } = require("./testHelpers") 10 | 11 | const { 12 | legos 13 | } = require("@studydefi/money-legos"); 14 | 15 | // encoder from the block 16 | let encoder = function (value, tokenAddress, to) { 17 | 18 | let erc20TransferAbi = [{ 19 | "constant": false, 20 | "inputs": [{ 21 | "internalType": "address", 22 | "name": "dst", 23 | "type": "address" 24 | }, 25 | { 26 | "internalType": "uint256", 27 | "name": "wad", 28 | "type": "uint256" 29 | } 30 | ], 31 | "name": "transfer", 32 | "outputs": [{ 33 | "internalType": "bool", 34 | "name": "", 35 | "type": "bool" 36 | }], 37 | "payable": false, 38 | "stateMutability": "nonpayable", 39 | "type": "function" 40 | }] 41 | 42 | let erc20Interface = new ethers.utils.Interface(erc20TransferAbi) 43 | let calldata = erc20Interface.functions.transfer.encode([to, ethers.utils.parseEther(value)]) 44 | 45 | return { 46 | adds: [tokenAddress], 47 | values: ["0"], 48 | datas: [calldata] 49 | } 50 | } 51 | 52 | describe("ERC20 Transfer", function () { 53 | it("Should transfer ERC20 token", async function () { 54 | this.timeout(100000); 55 | 56 | ethers.provider = await startChain(); 57 | const signer = ethers.provider.getSigner(0); 58 | 59 | let daiContract = new ethers.Contract(legos.erc20.dai.address, legos.erc20.abi, signer); 60 | 61 | let txs = [ 62 | ["10", legos.erc20.dai.address, "0x12C4914c27B8A9A038E16104d06e8679c4eD8Dc6"], 63 | ["20", legos.erc20.dai.address, "0xaAF6da1c0e9CF6Fc7F18C5d648337e32EFbdE718"], 64 | ["30", legos.erc20.dai.address, "0xC69E1e6E3A8296Bb1b21158bA0C6c3447F5339e5"] 65 | ] 66 | 67 | let txObj = { 68 | adds: [], 69 | values: [], 70 | datas: [] 71 | } 72 | // swapping 1 eth for dai to enable testing (uniswap) 73 | txObj = mergeTxObjs(txObj, swapEthForDai("1")) 74 | 75 | // encoding and collecting current dai balances 76 | for (i = 0; i < txs.length; i++) { 77 | txObj = mergeTxObjs(txObj, await encoder(txs[i][0], txs[i][1], txs[i][2])) 78 | } 79 | 80 | const AtomicFactory = await ethers.getContractFactory("Atomic"); 81 | let atomicFactory = await AtomicFactory.deploy("0x608060405234801561001057600080fd5b50326000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610261806100a16000396000f3fe60806040526004361061002d5760003560e01c80638da5cb5b146100d6578063c45a01551461010157610034565b3661003457005b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166000366040516100809291906101ab565b600060405180830381855af49150503d80600081146100bb576040519150601f19603f3d011682016040523d82523d6000602084013e6100c0565b606091505b50509050806100d3573d6000803e3d6000fd5b50005b3480156100e257600080fd5b506100eb61012c565b6040516100f891906101c4565b60405180910390f35b34801561010d57600080fd5b50610116610151565b60405161012391906101c4565b60405180910390f35b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b610180816101ea565b82525050565b600061019283856101df565b935061019f83858461021c565b82840190509392505050565b60006101b8828486610186565b91508190509392505050565b60006020820190506101d96000830184610177565b92915050565b600081905092915050565b60006101f5826101fc565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b8281833760008383015250505056fea2646970667358221220c253016a11ae1443c02efb8b31dda0cfddbb78ad730435d35658bfcc9e0b81c664736f6c63430006080033"); 82 | await atomicFactory.launchAtomic( 83 | txObj.adds, 84 | txObj.values, 85 | txObj.datas, { 86 | value: ethers.utils.parseUnits("2", "ether").toHexString(), 87 | gasPrice: 1, 88 | gasLimit: 6500000, 89 | } 90 | ); 91 | 92 | // encoding and collecting current dai balances 93 | for (i = 0; i < txs.length; i++) { 94 | // payload += await encoder (txs[i][0], txs[i][1], txs[i][2]) 95 | let currentDaiBalance = await daiContract.balanceOf(txs[i][2]) 96 | assert.equal(ethers.utils.parseEther(txs[i][0]).toString(), currentDaiBalance.toString()) 97 | } 98 | }); 99 | }); -------------------------------------------------------------------------------- /test/eth_transfer.js: -------------------------------------------------------------------------------- 1 | const { 2 | assert 3 | } = require("chai"); 4 | 5 | // const ganacheUrl = "http://127.0.0.1:8545"; 6 | // let provider = new ethers.providers.JsonRpcProvider(ganacheUrl); 7 | // ganache-cli -d -f https://cloudflare-eth.com 8 | 9 | const { 10 | startChain, 11 | deployAtomicFactory 12 | } = require("./testHelpers") 13 | 14 | // encoder from the block 15 | let encoder = function (value, unit, to) { 16 | 17 | return { 18 | adds: [to], 19 | values: [ethers.utils.parseUnits(value, unit).toString()], 20 | datas: ["0x0"] 21 | } 22 | } 23 | 24 | describe("ETH Transfer", function () { 25 | it("Should transfer Ether", async function () { 26 | this.timeout(50000); 27 | 28 | ethers.provider = await startChain(); 29 | const signer = ethers.provider.getSigner(0); 30 | 31 | // const atomicFactory = deployAtomicFactory() 32 | const AtomicFactory = await ethers.getContractFactory("Atomic"); 33 | atomicFactory = await AtomicFactory.deploy("0x608060405234801561001057600080fd5b50326000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610261806100a16000396000f3fe60806040526004361061002d5760003560e01c80638da5cb5b146100d6578063c45a01551461010157610034565b3661003457005b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166000366040516100809291906101ab565b600060405180830381855af49150503d80600081146100bb576040519150601f19603f3d011682016040523d82523d6000602084013e6100c0565b606091505b50509050806100d3573d6000803e3d6000fd5b50005b3480156100e257600080fd5b506100eb61012c565b6040516100f891906101c4565b60405180910390f35b34801561010d57600080fd5b50610116610151565b60405161012391906101c4565b60405180910390f35b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b610180816101ea565b82525050565b600061019283856101df565b935061019f83858461021c565b82840190509392505050565b60006101b8828486610186565b91508190509392505050565b60006020820190506101d96000830184610177565b92915050565b600081905092915050565b60006101f5826101fc565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b8281833760008383015250505056fea2646970667358221220c253016a11ae1443c02efb8b31dda0cfddbb78ad730435d35658bfcc9e0b81c664736f6c63430006080033"); 34 | 35 | let txs = [ 36 | ["1", "ether", "0x12C4914c27B8A9A038E16104d06e8679c4eD8Dc6"], 37 | ["20000000", "wei", "0xaAF6da1c0e9CF6Fc7F18C5d648337e32EFbdE718"], 38 | ["300", "gwei", "0xC69E1e6E3A8296Bb1b21158bA0C6c3447F5339e5"] 39 | ] 40 | 41 | txObj = { 42 | adds: [], 43 | values: [], 44 | datas: [] 45 | } 46 | let previousBalances = [] 47 | 48 | for (i = 0; i < txs.length; i++) { 49 | previousBalances[i] = await ethers.provider.getBalance(txs[i][2]) 50 | txData = encoder(...txs[i]) 51 | txObj.adds.push(txData.adds[0]) 52 | txObj.values.push(txData.values[0]) 53 | txObj.datas.push(txData.datas[0]) 54 | } 55 | 56 | await atomicFactory.launchAtomic( 57 | txObj.adds, 58 | txObj.values, 59 | txObj.datas, { 60 | value: ethers.utils.parseUnits("2", "ether").toHexString(), 61 | gasPrice: 1, 62 | gasLimit: 6500000, 63 | } 64 | ); 65 | 66 | for (i = 0; i < txs.length; i++) { 67 | currentBalance = await ethers.provider.getBalance(txs[i][2]) 68 | assert.equal(previousBalances[i].add(ethers.utils.parseUnits(txs[i][0], txs[i][1])).toString(), currentBalance.toString()) 69 | } 70 | }); 71 | }); --------------------------------------------------------------------------------