{"id":19903201,"url":"https://github.com/nethereum/optimismtemplate","last_synced_at":"2025-10-03T15:14:16.392Z","repository":{"id":89828959,"uuid":"351540747","full_name":"Nethereum/OptimismTemplate","owner":"Nethereum","description":"Simple template to get started working with Optimism","archived":false,"fork":false,"pushed_at":"2022-03-14T14:23:17.000Z","size":1480,"stargazers_count":12,"open_issues_count":1,"forks_count":4,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-28T08:48:48.376Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Nethereum.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-03-25T18:42:14.000Z","updated_at":"2024-04-24T09:02:51.000Z","dependencies_parsed_at":"2023-07-25T15:00:19.845Z","dependency_job_id":null,"html_url":"https://github.com/Nethereum/OptimismTemplate","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nethereum%2FOptimismTemplate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nethereum%2FOptimismTemplate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nethereum%2FOptimismTemplate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nethereum%2FOptimismTemplate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Nethereum","download_url":"https://codeload.github.com/Nethereum/OptimismTemplate/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252126359,"owners_count":21698962,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-12T20:22:28.998Z","updated_at":"2025-10-03T15:14:16.284Z","avatar_url":"https://github.com/Nethereum.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# OptimismTemplate\nSimple template to get started example of working with Optimism\n\n## SETUP Local Environment\nThe first thing is to setup your enviroment to run your local l1 and l2 chains\nYour will need docker, and if you are using windows you can run this in WSL2 too.\n1. Clone https://github.com/ethereum-optimism/optimism-integration\n2. run ```docker-compose pull```\n3. then ```make up```\n\nThe address manager provides a way to retrieve all the addresses\n\nADDRESS_MANAGER = \"0x3e4CFaa8730092552d9425575E49bB542e329981\"\n\nThe test accounts in L1 can be found here:\n\nhttps://github.com/Nethereum/OptimismTemplate/blob/main/OptimismTemplate.Testing/Erc20TokenTests.cs\n\nL1: \"http://localhost:9545\", CHAINID: 31337 \nL2: \"http://localhost:8545\", CHAINID: 420 \n\n## SETUP Kovan Environment\nYou will need some KETH (eth) from Kovan.\n1. L1 https://kovan.infura.io/v3/7238211010344719ad14a89db874158c CHAINID 42\n2. L2 https://kovan.optimism.io CHAINID 69\n\nThe address manager provides a way to retrieve all the addresses:\n\nADDRESS_MANAGER = \"0x100Dd3b414Df5BbA2B542864fF94aF8024aFdf3a\";\n\n## Install optimism contracts\n\nRUN at root\n```npm install @eth-optimism/contracts```\n\n\n## Code generation of smart contract definitions\nIt is already preconfigured to autogenerate your code too :)\n\n## Eth bride L1 \u003c-\u003e L2\n```csharp\n  var web3l1 = new Web3(new Account(\"YOUR PRIVATE KEY\", 42), \"https://kovan.infura.io/v3/\u003c\u003cInfuraId\u003e\u003e\");\n  var web3l2 = new Web3(new Account(\"YOUR PRIVATE KEY\", 69), \"https://kovan.optimism.io\");\n\n  var ourAdddress = web3l1.TransactionManager.Account.Address;\n  var watcher = new CrossMessagingWatcherService();\n\n  var addressManagerService = new Lib_AddressManagerService(web3l1, KOVAN_ADDRESS_MANAGER);\n  var L2CrossDomainMessengerAddress = await addressManagerService.GetAddressQueryAsync(\"L2CrossDomainMessenger\");\n  var L1StandardBridgeAddress = await addressManagerService.GetAddressQueryAsync(StandardAddressManagerKeys.L1StandardBridge);\n  var L1CrossDomainMessengerAddress = await addressManagerService.GetAddressQueryAsync(StandardAddressManagerKeys.L1CrossDomainMessenger);\n  var L2StandardBridgeAddress = PredeployedAddresses.L2StandardBridge;\n\n  var l2StandardBridgeService = new L2StandardBridgeService(web3l2, L2StandardBridgeAddress);\n  var l1StandardBridgeAddress = await l2StandardBridgeService.L1TokenBridgeQueryAsync();\n  var l1StandardBridgeService = new L1StandardBridgeService(web3l1, l1StandardBridgeAddress);\n\n\n  var amount = Web3.Convert.ToWei(0.05);\n  var currentBalanceInL2 = await web3l2.Eth.GetBalance.SendRequestAsync(ourAdddress);\n  var depositEther = new DepositETHFunction()\n  {\n      AmountToSend = amount,\n      L2Gas = 700000,\n      Data = \"0x\".HexToByteArray()\n  };\n\n  var estimateGas = await l1StandardBridgeService.ContractHandler.EstimateGasAsync(depositEther);\n\n  var receiptDeposit = await l1StandardBridgeService.DepositETHRequestAndWaitForReceiptAsync(depositEther);\n\n  var messageHashes = watcher.GetMessageHashes(receiptDeposit);\n\n  var txnReceipt = await watcher.GetCrossMessageMessageTransactionReceipt(web3l2, L2CrossDomainMessengerAddress, messageHashes.First());\n\n\n  if (txnReceipt.HasErrors() == true)\n  {\n      var error =\n           await web3l2.Eth.GetContractTransactionErrorReason.SendRequestAsync(txnReceipt.TransactionHash);\n      //throw new Exception(error);\n  }\n\n  var balancesInL2 = await web3l2.Eth.GetBalance.SendRequestAsync(ourAdddress); ;\n\n  Assert.Equal(amount, balancesInL2.Value - currentBalanceInL2.Value);\n\n  var withdrawEther = new WithdrawFunction()\n  {\n      L2Token = TokenAddresses.ETH,\n      Amount = amount,\n      //AmountToSend = amount,\n      L1Gas = 700000,\n      Data = \"0x\".HexToByteArray()\n  };\n  var receiptWidthdraw = await l2StandardBridgeService.WithdrawRequestAndWaitForReceiptAsync(withdrawEther);\n\n  messageHashes = watcher.GetMessageHashes(receiptWidthdraw);\n\n  //txnReceipt = await watcher.GetCrossMessageMessageTransactionReceipt(web3l1, L1CrossDomainMessengerAddress, messageHashes.First());\n\n  //balancesInL2 = await web3l2.Eth.GetBalance.SendRequestAsync(ourAdddress);\n\n  //Assert.Equal(currentBalanceInL2, balancesInL2);\n\n```\n\n## Token bridge L1 \u003c-\u003e L2\n\n```csharp\n  var web3l1 = new Web3(new Account(\"YOUR PRIVATE KEY\", 42), \"https://kovan.infura.io/v3/\u003c\u003cInfuraId\u003e\u003e\");\n  var web3l2 = new Web3(new Account(\"YOUR PRIVATE KEY\", 69), \"https://kovan.optimism.io\");\n  web3l2.TransactionManager.UseLegacyAsDefault = true;\n\n  var ourAdddress = web3l1.TransactionManager.Account.Address;\n  var watcher = new CrossMessagingWatcherService();\n\n  var addressManagerService = new Lib_AddressManagerService(web3l1, KOVAN_ADDRESS_MANAGER);\n  var L2CrossDomainMessengerAddress = await addressManagerService.GetAddressQueryAsync(\"L2CrossDomainMessenger\");\n  var L1StandardBridgeAddress = await addressManagerService.GetAddressQueryAsync(StandardAddressManagerKeys.L1StandardBridge);\n  var L1CrossDomainMessengerAddress = await addressManagerService.GetAddressQueryAsync(StandardAddressManagerKeys.L1CrossDomainMessenger);\n\n  var L2StandardBridgeAddress = PredeployedAddresses.L2StandardBridge;\n\n  var l2StandardBridgeService = new L2StandardBridgeService(web3l2, L2StandardBridgeAddress);\n  var l1StandardBridgeAddress = await l2StandardBridgeService.L1TokenBridgeQueryAsync();\n  var l1StandardBridgeService = new L1StandardBridgeService(web3l1, l1StandardBridgeAddress);\n\n  var tokenName = \"OPNETH\";\n  var tokenSymbol = \"OPNETH\";\n\n  var erc20TokenDeployment = new ERC20Deployment()\n      { Name = tokenName, InitialSupply = 100000, Symbol = tokenSymbol, Decimals = 18 };\n\n  //Deploy our custom token\n  var tokenDeploymentReceipt = await ERC20Service.DeployContractAndWaitForReceiptAsync(web3l1, erc20TokenDeployment);\n\n  var l2Erc20TokenDeployment = new L2StandardERC20Deployment();\n  l2Erc20TokenDeployment.L1Token = tokenDeploymentReceipt.ContractAddress;\n  l2Erc20TokenDeployment.L2Bridge = L2StandardBridgeAddress;\n  l2Erc20TokenDeployment.Name = tokenName;\n  l2Erc20TokenDeployment.Symbol = tokenSymbol;\n\n  var l2Erc20TokenDeploymentReceipt = await L2StandardERC20Service.DeployContractAndWaitForReceiptAsync(web3l2, l2Erc20TokenDeployment);\n\n  var l2StandardErc20Service = new L2StandardERC20Service(web3l2, l2Erc20TokenDeploymentReceipt.ContractAddress);\n  //Creating a new service\n  var tokenService = new ERC20Service(web3l1, tokenDeploymentReceipt.ContractAddress);\n\n\n\n  var balancesInL1 = await tokenService.BalanceOfQueryAsync(ourAdddress);\n  var receiptApproval = await tokenService.ApproveRequestAndWaitForReceiptAsync(l1StandardBridgeAddress, 1);\n\n\n  var receiptDeposit = await l1StandardBridgeService.DepositERC20RequestAndWaitForReceiptAsync(new DepositERC20Function()\n  {\n      L1Token = tokenDeploymentReceipt.ContractAddress,\n      L2Token = l2Erc20TokenDeploymentReceipt.ContractAddress,\n      Amount = 1, L2Gas = 2000000, Data = \"0x\".HexToByteArray()\n  });\n\n  balancesInL1 = await tokenService.BalanceOfQueryAsync(ourAdddress);\n  //what the watcher does.. we do already have the txn receipt.. but for demo purpouses\n  var messageHashes = watcher.GetMessageHashes(receiptDeposit);\n\n  var txnReceipt = await watcher.GetCrossMessageMessageTransactionReceipt(web3l2, L2CrossDomainMessengerAddress, messageHashes.First());\n\n  var balancesInL2 = await l2StandardErc20Service.BalanceOfQueryAsync(ourAdddress);\n\n  Assert.Equal(1, balancesInL2);\n\n  var withdrawErc20Token = new WithdrawFunction()\n  {\n      L2Token = l2Erc20TokenDeploymentReceipt.ContractAddress,\n      Amount = 1,\n      L1Gas = 2000000,\n      Data = \"0x\".HexToByteArray()\n  };\n\n  var receiptWidthdraw = await l2StandardBridgeService.WithdrawRequestAndWaitForReceiptAsync(withdrawErc20Token);\n\n   messageHashes = watcher.GetMessageHashes(receiptWidthdraw);\n\n   balancesInL2 = await l2StandardErc20Service.BalanceOfQueryAsync(ourAdddress);\n\n   Assert.Equal(0, balancesInL2);\n\n  //txnReceipt = await watcher.GetCrossMessageMessageTransactionReceipt(web3l1, L1CrossDomainMessengerAddress, messageHashes.First());\n\n```\n## NFT sample ERC721\nERC721 in optimism is the same as in mainnet\n\n```csharp          \n  var web3 = new Web3(new Account(\"YOUR PRIVATE KEY\", 69), \"https://kovan.optimism.io\");\n  web3.Eth.TransactionManager.UseLegacyAsDefault = true;\n\n  //creating our deployment information (this includes the bytecode already)\n  var erc721Deployment = new ERC721EnumerableUriStorageDeployment() { Name = \"Property Registry\", Symbol = \"PR\" };\n\n  //Deploy the erc721Minter\n  var deploymentReceipt = await ERC721EnumerableUriStorageService.DeployContractAndWaitForReceiptAsync(web3, erc721Deployment);\n\n  //creating a new service with the new contract address\n  var erc721Service = new ERC721EnumerableUriStorageService(web3, deploymentReceipt.ContractAddress);\n\n  //uploading to ipfs our image\n  var nftIpfsService = new NFTIpfsService(\"https://ipfs.infura.io:5001\");\n  var imageIpfs = await nftIpfsService.AddFileToIpfsAsync(\"Images/image1.png\");\n  //adding all our document ipfs links to the metadata and the description\n  var metadataNFT = new NftMetadata()\n  {\n      Name = \"Nethereum + Optimism\",\n      Image = \"ipfs://\" + imageIpfs.Hash, \n      ExternalUrl = \"https://github.com/Nethereum/OptimismTemplate\"\n\n  };\n\n  //Adding the metadata to ipfs\n  var metadataIpfs =\n      await nftIpfsService.AddNftsMetadataToIpfsAsync(metadataNFT, \"Metadata.json\");\n\n  var addressToRegisterOwnership = \"0xe612205919814b1995D861Bdf6C2fE2f20cDBd68\";\n\n  //Minting the nft with the url to the ipfs metadata\n  var mintReceipt = await erc721Service.MintRequestAndWaitForReceiptAsync(\n      addressToRegisterOwnership, \"ipfs://\" + metadataIpfs.Hash);\n\n  //we have just minted our first nft so the nft will have the id of 0. \n  var ownerOfToken = await erc721Service.OwnerOfQueryAsync(0);\n\n  Assert.True(ownerOfToken.IsTheSameAddress(addressToRegisterOwnership));\n\n  var addressOfToken = await erc721Service.TokenURIQueryAsync(0);\n\n  Assert.Equal(\"ipfs://\" + metadataIpfs.Hash, addressOfToken);\n\n  var ps = new ProcessStartInfo(\"https://ipfs.infura.io/ipfs/\" + imageIpfs.Hash)\n  {\n      UseShellExecute = true,\n      Verb = \"open\"\n  };\n  Process.Start(ps);\n\n```\nWhen you run this sample, you will see your browser open the NFT image:\n\n![image](https://user-images.githubusercontent.com/562371/112763780-d8b26580-8ffd-11eb-91a0-b4670d9434d0.png)\n\n\n## Credits\n* The Optimism team! \n\n## TODO: \n+ Move to Nethereum core =\n+ Improve Helper classes \n+ Message tracking (L1 mapping)\n+ New simple messaging example\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnethereum%2Foptimismtemplate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnethereum%2Foptimismtemplate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnethereum%2Foptimismtemplate/lists"}