├── test └── .gitkeep ├── .DS_Store ├── .gitignore ├── contracts ├── .DS_Store ├── IStarknetCore.sol ├── graffiti.cairo └── StarkNetGraffiti.sol ├── migrations ├── .DS_Store ├── _1_deploy_graffer_L1.js ├── 4_emit_event.js ├── _3_graff_to_starknet.js └── _2_setup_graffer_L1.js └── README.md /test/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/l-henri/StarkNet-graffiti/HEAD/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | truffle-config.js 2 | node_modules 3 | *2_td_test.js* 4 | build/ 5 | env/ 6 | -------------------------------------------------------------------------------- /contracts/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/l-henri/StarkNet-graffiti/HEAD/contracts/.DS_Store -------------------------------------------------------------------------------- /migrations/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/l-henri/StarkNet-graffiti/HEAD/migrations/.DS_Store -------------------------------------------------------------------------------- /migrations/_1_deploy_graffer_L1.js: -------------------------------------------------------------------------------- 1 | 2 | var StarkNetGraffiti = artifacts.require("StarkNetGraffiti.sol"); 3 | 4 | 5 | module.exports = (deployer, network, accounts) => { 6 | deployer.then(async () => { 7 | await deployGraffer(deployer, network, accounts); 8 | }); 9 | }; 10 | 11 | async function deployGraffer(deployer, network, accounts) { 12 | StarkNetGraffitiDeployed = await StarkNetGraffiti.new({from:accounts[0]}) 13 | console.log("Graff contract " + StarkNetGraffitiDeployed.address) 14 | // console.log(StarkNetGraffitiDeployed) 15 | } 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /contracts/IStarknetCore.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.12; 2 | 3 | interface IStarknetCore { 4 | /** 5 | Sends a message to an L2 contract. 6 | 7 | Returns the hash of the message. 8 | */ 9 | function sendMessageToL2( 10 | uint256 to_address, 11 | uint256 selector, 12 | uint256[] calldata payload 13 | ) external returns (bytes32); 14 | 15 | /** 16 | Consumes a message that was sent from an L2 contract. 17 | 18 | Returns the hash of the message. 19 | */ 20 | function consumeMessageFromL2(uint256 fromAddress, uint256[] calldata payload) 21 | external 22 | returns (bytes32); 23 | } 24 | -------------------------------------------------------------------------------- /migrations/4_emit_event.js: -------------------------------------------------------------------------------- 1 | 2 | var StarkNetGraffiti = artifacts.require("StarkNetGraffiti.sol"); 3 | 4 | 5 | // var hexString 6 | 7 | module.exports = (deployer, network, accounts) => { 8 | deployer.then(async () => { 9 | 10 | await writeGraff(deployer, network, accounts); 11 | 12 | }); 13 | }; 14 | 15 | async function writeGraff(deployer, network, accounts) { 16 | 17 | var decimals = [ 18 | new web3.utils.BN("1566918057116160165641476021756193"), 19 | ] 20 | for (i =0; i < decimals.length; i++) 21 | { 22 | var string = web3.utils.fromDecimal(decimals[i]) 23 | console.log(web3.utils.toAscii(web3.utils.fromDecimal(decimals[i]))) 24 | var paddedString = "0x" + string.substring(2).padStart(64,'0') 25 | console.log(paddedString) 26 | } 27 | 28 | } 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /migrations/_3_graff_to_starknet.js: -------------------------------------------------------------------------------- 1 | 2 | var StarkNetGraffiti = artifacts.require("StarkNetGraffiti.sol"); 3 | 4 | // var myGraff = "Petit L2 deviendra grand..." 5 | var myGraff = "Looking good, StarkNet" 6 | // var myGraff = "MAINNET BABY!!!!" 7 | 8 | var ethereumGrafferAddress = "0xc010818276eb5dff6cc462217c66ee7648fb8d8b" 9 | 10 | module.exports = (deployer, network, accounts) => { 11 | deployer.then(async () => { 12 | // StarkNetGraffitiDeployed = await StarkNetGraffiti.at(ethereumGrafferAddress) 13 | 14 | await sendGraff(deployer, network, accounts); 15 | 16 | }); 17 | }; 18 | 19 | async function sendGraff(deployer, network, accounts) { 20 | 21 | // console.log("Graff contract " + StarkNetGraffitiDeployed.address) 22 | var paddedString = "0x" + web3.utils.fromAscii(myGraff).substring(2).padStart(64,'0') 23 | console.log(paddedString) 24 | 25 | // await StarkNetGraffitiDeployed.graffFromMainnetOnStarknet(paddedString); 26 | } 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /migrations/_2_setup_graffer_L1.js: -------------------------------------------------------------------------------- 1 | 2 | var StarkNetGraffiti = artifacts.require("StarkNetGraffiti.sol"); 3 | 4 | var ethereumGrafferAddress = "0x647dD2a964C10dF6588a4ac6F5Cf2C44B16E3e64" 5 | var starknetGrafferAddressAsInt = "3319278968080370309910878968659006603895909569061385119632795853997864250857" 6 | starknetCore_ = "0xde29d060d45901fb19ed6c6e959eb22d8626708e" 7 | 8 | module.exports = (deployer, network, accounts) => { 9 | deployer.then(async () => { 10 | 11 | await setGraffer(deployer, network, accounts); 12 | }); 13 | }; 14 | 15 | async function setGraffer(deployer, network, accounts) { 16 | StarkNetGraffitiDeployed = await StarkNetGraffiti.at(ethereumGrafferAddress) 17 | console.log("Graff contract " + StarkNetGraffitiDeployed.address) 18 | l2MessengerContractAddress_ = new web3.utils.BN(starknetGrafferAddressAsInt); 19 | GRAFF_SELECTOR_ = new web3.utils.BN("1556176496000738779866251419733342249912370993011992358749423751362128812898"); 20 | await StarkNetGraffitiDeployed.setL2Graffer(starknetCore_, l2MessengerContractAddress_, GRAFF_SELECTOR_) 21 | } 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /contracts/graffiti.cairo: -------------------------------------------------------------------------------- 1 | %lang starknet 2 | %builtins pedersen range_check 3 | 4 | from starkware.starknet.common.messages import send_message_to_l1 5 | from starkware.cairo.common.cairo_builtins import HashBuiltin 6 | from starkware.cairo.common.alloc import alloc 7 | from starkware.cairo.common.math import assert_nn 8 | 9 | ######### Storage variables 10 | 11 | # Store an int value received from mainnet. Will take the value of the latest message sent by the sequencer 12 | @storage_var 13 | func msg_received_from_mainnet() -> (msg_received_from_mainnet : felt): 14 | end 15 | 16 | # The L1 contract with which we exchange messages 17 | @storage_var 18 | func to_address() -> (to_address : felt): 19 | end 20 | 21 | # A variable to limit the total number of messages sent to mainnet 22 | @storage_var 23 | func remaining_messages_credit() -> (remaining_messages_credit : felt): 24 | end 25 | 26 | ######### Getters 27 | # Functions to display the above variables 28 | 29 | @view 30 | func message_received{syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr}() -> ( 31 | msg : felt): 32 | let (msg) = msg_received_from_mainnet.read() 33 | return (msg=msg) 34 | end 35 | 36 | @view 37 | func remaining_messages_credit_view{syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr}() -> ( 38 | current_remaining_messages_credit : felt): 39 | let (current_remaining_messages_credit) = remaining_messages_credit.read() 40 | return (current_remaining_messages_credit=current_remaining_messages_credit) 41 | end 42 | 43 | ######### Constructor 44 | # The L1 counterpart and the total number of credits for the contract are set at deployment to simplify access control 45 | 46 | @constructor 47 | func constructor{syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr}( 48 | l1_contract_address : felt, amount: felt): 49 | to_address.write(l1_contract_address) 50 | remaining_messages_credit.write(amount) 51 | return () 52 | end 53 | 54 | ######### External functions callable on StarkNet 55 | 56 | # Send a message to Starknet 57 | # msg is an int inferior to 2**251 58 | @external 59 | func paint_graffiti{syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr}(msg : felt): 60 | # Check that the contract still has credit 61 | let (current_remaining_messages_credit) = remaining_messages_credit.read() 62 | 63 | tempvar next_remaining_messages_credit = current_remaining_messages_credit - 1 64 | assert_nn(next_remaining_messages_credit) 65 | remaining_messages_credit.write(next_remaining_messages_credit) 66 | 67 | # Sending message to L1 68 | let (payload) = alloc() 69 | [payload] = msg 70 | let (address) = to_address.read() 71 | 72 | # Sending the message to L1 73 | send_message_to_l1(address, 1, payload) 74 | return () 75 | end 76 | 77 | ######### External functions used when receiving a message from L1 78 | # A L1 handler automatically receives the adress of the L1 sending contract. 79 | @l1_handler 80 | func get_painted{syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr}( 81 | from_address : felt, msg : felt): 82 | 83 | # Store the value received in msg in variable msg_received_from_mainnet 84 | msg_received_from_mainnet.write(msg) 85 | return () 86 | end 87 | -------------------------------------------------------------------------------- /contracts/StarkNetGraffiti.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.12; 2 | import './IStarknetCore.sol'; 3 | 4 | /** 5 | Demo contract for L1 <-> L2 interaction between an L2 StarkNet contract and this L1 solidity 6 | contract. 7 | */ 8 | contract StarkNetGraffiti { 9 | // The StarkNet core contract. 10 | IStarknetCore public starknetCore; 11 | 12 | // The selector of the "graffOnStarkNetFromMainnet" l1_handler. 13 | uint256 public GRAFF_SELECTOR; 14 | // The address of the L2 graff contract 15 | uint256 public l2MessengerContractAddress; 16 | 17 | // An owner who can modify the graff contract 18 | address public owner; 19 | 20 | event messageReceivedFromStarkNet(string stringMessage); 21 | event messageSentToStarkNet(string stringMessage); 22 | 23 | constructor() public 24 | { 25 | owner = msg.sender; 26 | } 27 | 28 | 29 | function graffFromStarknetOnMainnet(bytes32 messageToGraff) 30 | public 31 | { 32 | // Construct the withdrawal message's payload. 33 | uint256[] memory payload = new uint256[](1); 34 | payload[0] = uint256(messageToGraff); 35 | 36 | // Consume the message from the StarkNet core contract. 37 | // This will revert the (Ethereum) transaction if the message does not exist. 38 | starknetCore.consumeMessageFromL2(l2MessengerContractAddress, payload); 39 | emit messageReceivedFromStarkNet(customizedBytes32ToString(messageToGraff)); 40 | } 41 | 42 | function graffFromMainnetOnStarknet(bytes32 messageToGraff) 43 | public 44 | { 45 | 46 | // Construct the deposit message's payload. 47 | uint256[] memory payload = new uint256[](1); 48 | payload[0] = uint256(messageToGraff); 49 | 50 | // Send the message to the StarkNet core contract. 51 | starknetCore.sendMessageToL2(l2MessengerContractAddress, GRAFF_SELECTOR, payload); 52 | emit messageSentToStarkNet(customizedBytes32ToString(messageToGraff)); 53 | } 54 | 55 | function multiGraffs(bytes32[] memory messageToGraff) 56 | external 57 | { 58 | for (uint i = 0; i < messageToGraff.length; i++) 59 | { 60 | graffFromStarknetOnMainnet(messageToGraff[i]); 61 | } 62 | } 63 | 64 | // Setup, permissions and utility 65 | 66 | function setL2Graffer(IStarknetCore starknetCore_, uint256 l2MessengerContractAddress_, uint256 GRAFF_SELECTOR_) 67 | public 68 | onlyOwner 69 | { 70 | starknetCore = starknetCore_; 71 | l2MessengerContractAddress = l2MessengerContractAddress_; 72 | GRAFF_SELECTOR = GRAFF_SELECTOR_; 73 | } 74 | 75 | modifier onlyOwner() 76 | { 77 | 78 | require(msg.sender == owner); 79 | _; 80 | } 81 | 82 | function customizedBytes32ToString(bytes32 _bytes32) 83 | public 84 | pure 85 | returns (string memory) 86 | { 87 | uint8 i = 0; 88 | while(i < 32 && _bytes32[i] != 0) { 89 | i++; 90 | } 91 | uint offset = i; 92 | bytes memory bytesArray = new bytes(32-offset); 93 | for (i = 0; i < 32-offset; i++) { 94 | bytesArray[i] = _bytes32[i+offset]; 95 | } 96 | return string(bytesArray); 97 | } 98 | 99 | 100 | 101 | 102 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # StarkNet graffiti 2 | A smol project to send graffitis between StarkNet and Ethereum 3 | 4 | ## Introduction 5 | ### Disclaimer 6 | Don't expect any kind of benefit from using this, other than learning a bunch of cool stuff about StarNet, the first general purpose validity rollup on the Ethereum Mainnnet. 7 | 8 | ### How it works 9 | We use the [L1 <-> L2 messaging bridge](https://www.cairo-lang.org/docs/hello_starknet/l1l2.html) of Starknet to send a message to Mainnet. The message is then used in an event emitted on Mainnet, and is kept in the logs of Ethereum forever! 10 | 11 | In the alpha phase, transactions are free on StarkNet, so you can try this without having to bridge any money to Starknet. 12 | 13 | You can use this project to graff on Mainnet, graff on StarkNet, or deploy your own graffer. 14 | 15 | ### Why? 16 | Why not? I understand things things better when I tinker with them. This is a simple project to understand what it means for StarkNet to be deployed on Mainnet, and how messaging between StarkNet and Mainnet works. 17 | 18 | ## Setting up 19 | This project uses [Truffle](https://www.trufflesuite.com/truffle) and the [cairo-lang](https://pypi.org/project/cairo-lang/) python package. 20 | There are easier ways to manage your smart contracts deployments on StarkNet, and the project will evolve to integrate them. If you want to help with this, look at the "contributing" section below. 21 | 22 | ### Setting up truffle 23 | If you don't have truffle installed on your machine, run 24 | > npm install truffle 25 | To install it locally in the repo. Then [configure it](https://www.trufflesuite.com/guides/using-infura-custom-provider). If you want to try on testnet, select the Goerli testnet. 26 | 27 | ### Setting up Cairo 28 | Go [here](https://www.cairo-lang.org/docs/quickstart.html) for installation instructions. 29 | 30 | ### Graffing on Mainnet 31 | So you want to deface Testnet huh? Simple. 32 | - Make sure you [set up](###-Setting-up-Cairo) properly 33 | - Compile the program with `starknet-compile contracts/graffiti.cairo \ 34 | --abi contracts/graffiti-abi.json \ 35 | --output contracts/graffiti-compiled.json ` 36 | - Use [this website](https://onlineasciitools.com/convert-ascii-to-decimal) to encode your message in decimal. Make sure to disable "decimal spacing" so you get a value like "123456" rather than "12 34 56" 37 | - Select the mainnet Alpha network `export STARKNET_NETWORK=alpha-mainnet` 38 | - Send this command `starknet invoke \ 39 | --address 0x00813b30be862c2267ccf6dc2719297cb0cb8c782cb57091992d831aab4083d1 \ 40 | --abi contracts/graffiti-abi.json \ 41 | --function paint_graffiti \ 42 | --inputs ` 43 | - Congrats! Your message is en route to mainnet. 44 | - You can look up your transaction on [Voyager](https://voyager.online/). It may take a few hours before it is included in a block. Don't worry, it will show up there 45 | - You can see your message in the [Messaging section](https://voyager.online/contract/0x00813b30be862c2267ccf6dc2719297cb0cb8c782cb57091992d831aab4083d1#messages) of the explorer 46 | 47 | So now you wait for a few hours, for the state update to be published to mainnet. 48 | 49 | 50 | ### Graffing from Testnet 51 | Curious about going the opposite route? 52 | 53 | 54 | ## Deployments 55 | - Mainnet graffer [here](https://etherscan.io/address/0xc010818276eb5dff6cc462217c66ee7648fb8d8b) 56 | 57 | ## Contributing 58 | This project was done in a few hours, and can be made better; your contributions are welcome! Here are things that you can do to help: 59 | - Create a cool frontend to write from starknet to mainnet using [Starknet JS](https://www.starknetjs.com/index.html) 60 | - Create a cool frontend to write from mainnet to starknet using web3 61 | - Create a cool frontend to emit the event on mainnet using web3 62 | --------------------------------------------------------------------------------