From b9c2dfe06e0802ad845f157afe24dec0526b814e Mon Sep 17 00:00:00 2001 From: Mehul Agarwal <42957482+agarwalml@users.noreply.github.com> Date: Mon, 6 Sep 2021 12:29:07 -0400 Subject: [PATCH] TURING OFFCHAIN COMPUTE: add uniswapv2 fee example (incomplete) - WIP (#315) * add uniswapv2 fee example (incomplete) * add offchain in uniswapv2pair * feat: update fee-test Co-authored-by: Mehul Agarwal Co-authored-by: souradeep-das Co-authored-by: CAPtheorem <79423264+CAPtheorem@users.noreply.github.com> --- .../contracts/boredBobaYachtClub.sol | 127 ++++++++++++ .../contracts/uniswapv2/TuringHelper.sol | 190 ++++++++++++++++++ omgx_examples/sushi/test/offchain-test.ts | 173 ++++++++++++++++ .../contracts/TuringHelper.sol | 2 +- .../contracts/uniswapv2/UniswapV2Pair.sol | 27 ++- .../omgx/offchain-prototype/test/fee-test.ts | 140 ++++++++++++- .../offchain-prototype/test/hello-test.ts | 149 ++++++-------- 7 files changed, 709 insertions(+), 99 deletions(-) create mode 100644 omgx_examples/hardhat_ERC721/contracts/boredBobaYachtClub.sol create mode 100644 omgx_examples/sushi/contracts/uniswapv2/TuringHelper.sol create mode 100644 omgx_examples/sushi/test/offchain-test.ts diff --git a/omgx_examples/hardhat_ERC721/contracts/boredBobaYachtClub.sol b/omgx_examples/hardhat_ERC721/contracts/boredBobaYachtClub.sol new file mode 100644 index 000000000000..7bbdf4900645 --- /dev/null +++ b/omgx_examples/hardhat_ERC721/contracts/boredBobaYachtClub.sol @@ -0,0 +1,127 @@ +// File: contracts/BoredBobaYachtClub.sol + + +pragma solidity ^0.7.0; + + + +/** + * @title BoredBobaYachtClub contract + * @dev Extends ERC721 Non-Fungible Token Standard basic implementation + */ +contract BoredBobaYachtClub is ERC721, Ownable { + using SafeMath for uint256; + + string public BAYC_PROVENANCE = ""; + + uint256 public startingIndexBlock; + + uint256 public startingIndex; + + uint256 public constant bobaPrice = 80000000000000000; //0.08 ETH + + uint public constant maxBobaPurchase = 20; + + uint256 public MAX_BOBAS; + + bool public saleIsActive = false; + + uint256 public REVEAL_TIMESTAMP; + + constructor(string memory name, string memory symbol, uint256 maxNftSupply, uint256 saleStart) ERC721(name, symbol) { + MAX_BOBAS = maxNftSupply; + REVEAL_TIMESTAMP = saleStart + (86400 * 9); + } + + function withdraw() public onlyOwner { + uint balance = address(this).balance; + msg.sender.transfer(balance); + } + + /** + * Set some Bored Bobas aside + */ + function reserveBobas() public onlyOwner { + uint supply = totalSupply(); + uint i; + for (i = 0; i < 30; i++) { + _safeMint(msg.sender, supply + i); + } + } + + /** + * DM Gargamel in Discord that you're standing right behind him. + */ + function setRevealTimestamp(uint256 revealTimeStamp) public onlyOwner { + REVEAL_TIMESTAMP = revealTimeStamp; + } + + /* + * Set provenance once it's calculated + */ + function setProvenanceHash(string memory provenanceHash) public onlyOwner { + BAYC_PROVENANCE = provenanceHash; + } + + function setBaseURI(string memory baseURI) public onlyOwner { + _setBaseURI(baseURI); + } + + /* + * Pause sale if active, make active if paused + */ + function flipSaleState() public onlyOwner { + saleIsActive = !saleIsActive; + } + + /** + * Mints Bored Bobas + */ + function mintBoba(uint numberOfTokens) public payable { + require(saleIsActive, "Sale must be active to mint Boba"); + require(numberOfTokens <= maxBobaPurchase, "Can only mint 20 tokens at a time"); + require(totalSupply().add(numberOfTokens) <= MAX_BOBAS, "Purchase would exceed max supply of Bobas"); + require(bobaPrice.mul(numberOfTokens) <= msg.value, "Ether value sent is not correct"); + + for(uint i = 0; i < numberOfTokens; i++) { + uint mintIndex = totalSupply(); + if (totalSupply() < MAX_BOBAS) { + _safeMint(msg.sender, mintIndex); + } + } + + // If we haven't set the starting index and this is either 1) the last saleable token or 2) the first token to be sold after + // the end of pre-sale, set the starting index block + if (startingIndexBlock == 0 && (totalSupply() == MAX_BOBAS || block.timestamp >= REVEAL_TIMESTAMP)) { + startingIndexBlock = block.number; + } + } + + /** + * Set the starting index for the collection + */ + function setStartingIndex() public { + require(startingIndex == 0, "Starting index is already set"); + require(startingIndexBlock != 0, "Starting index block must be set"); + + startingIndex = uint(blockhash(startingIndexBlock)) % MAX_BOBAS; + // Just a sanity case in the worst case if this function is called late (EVM only stores last 256 block hashes) + if (block.number.sub(startingIndexBlock) > 255) { + startingIndex = uint(blockhash(block.number - 1)) % MAX_BOBAS; + } + // Prevent default sequence + if (startingIndex == 0) { + startingIndex = startingIndex.add(1); + } + } + + /** + * Set the starting index block for the collection, essentially unblocking + * setting starting index + */ + function emergencySetStartingIndexBlock() public onlyOwner { + require(startingIndex == 0, "Starting index is already set"); + + startingIndexBlock = block.number; + } +} diff --git a/omgx_examples/sushi/contracts/uniswapv2/TuringHelper.sol b/omgx_examples/sushi/contracts/uniswapv2/TuringHelper.sol new file mode 100644 index 000000000000..995f7ff3385d --- /dev/null +++ b/omgx_examples/sushi/contracts/uniswapv2/TuringHelper.sol @@ -0,0 +1,190 @@ +//SPDX-License-Identifier: UNLICENSED + +pragma solidity ^0.7.0; + +import "hardhat/console.sol"; + +contract TuringHelper { + bytes data_URL; + TuringHelper Self; + bytes[] methods; + + constructor(string memory _url) { + console.log("Deploying a helper contract with data source:", _url); + data_URL = bytes(_url); + Self = TuringHelper(address(this)); + } + + function RegisterMethod(bytes memory methodName) public { + methods.push(methodName); + } + + function _lenCalc1(bytes memory item, uint32 len) internal pure + returns (uint8, uint32) + { + uint32 L = uint32(item.length); + uint8 prefix; + + if (L > 255) { + len += 3; + prefix = 0xb9; + } else if (L > 55) { + len += 2; + prefix = 0xb8; + } else { + len += 1; + prefix = 0x80 + uint8(L); + } + + return (prefix, len); + } + + function genRequestRLP(bytes memory method, bytes memory payload) internal view + returns (bytes memory) + { + // This function generates a Turing request consisting of a + // fixed prefix string followed by parameters in RLP encoding. + // The outer container is a 4-element array containing a + // single-byte version number and 3 strings: URL, method, request + // payload. The payload is passed as-is to the remote server, + // which is responsible for unpacking and interpreting it. + + // For now this is the only valid value and all others are reserved. + byte request_version = 0x01; + + bytes memory prefix = bytes("_OMGXTURING_"); + assert (prefix.length == 12); + uint i; + uint j; + + // Constrain these to simplify the RLP encoding logic. + require (data_URL.length < 65536, "data_URL is too long"); + require (payload.length < 65536, "payload is too long"); + + uint32 l1 = uint32(data_URL.length); + uint32 l2 = uint32(method.length); + uint32 l3 = uint32(payload.length); + + uint32 pLen = 1 + l1 + l2 + l3; // Payload length + inner headers + uint32 hLen = 1; // Extra length of list header + + uint8[4] memory pre; + + (pre[1], pLen) = _lenCalc1(data_URL, pLen); + (pre[2], pLen) = _lenCalc1(method, pLen); + (pre[3], pLen) = _lenCalc1(payload, pLen); + + // We now have the total length of the three items which will be in the list. + // This determines the encoding required for the list header + + if (pLen > 65535) { + hLen += 3; + pre[0] = 0xfa; + } else if (pLen > 255) { + hLen += 2; + pre[0] = 0xf9; + } else if (pLen > 55) { + hLen += 1; + pre[0] = 0xf8; + } else { + pre[0] = 0xc0 + uint8(pLen); + } + + bytes memory result = new bytes(hLen + pLen + prefix.length); + + for (i=0; i < prefix.length; i++) result[j++] = prefix[i]; + + result[j++] = bytes1(pre[0]); + + if (pre[0] > 0xf9) { + result[j++] = bytes1(uint8(pLen / 65536)); + pLen = pLen % 65536; + } + if (pre[0] > 0xf8) { + result[j++] = bytes1(uint8(pLen / 256)); + pLen = pLen % 256; + } + if (pre[0] > 0xf7) { + result[j++] = bytes1(uint8(pLen)); + } + + result[j++] = request_version; + + result[j++] = bytes1(pre[1]); + if (pre[1] > 0xb8) { + result[j++] = bytes1(uint8(l1 / 256)); + l1 = l1 % 256; + } + if (pre[1] > 0xb7) { + result[j++] = bytes1(uint8(l1)); + } + for (i=0; i 0xb8) { + result[j++] = bytes1(uint8(l2 / 256)); + l2 = l2 % 256; + } + if (pre[2] > 0xb7) { + result[j++] = bytes1(uint8(l2)); + } + for (i=0; i 0xb8) { + result[j++] = bytes1(uint8(l3 / 256)); + l3 = l3 % 256; + } + if (pre[3] > 0xb7) { + result[j++] = bytes1(uint8(l3)); + } + for (i=0; i 0); + + if (rType != 2) { + // The if() avoids calling genRequestRLP unnecessarily + require (rType == 2, string(genRequestRLP(methods[method_idx], _slot))); + } + return _slot; + } + + /* This is called from the external contract. It takes a method + selector and an abi-encoded request payload. The URL and the + list of allowed methods are supplied when the contract is + created. In the future some of this registration might be moved + into l2geth, allowing for security measures such as TLS client + certificates. A configurable timeout could also be added. + */ + function TuringCall(uint32 method_idx, bytes memory _payload) + public view returns (bytes memory) { + require (method_idx < methods.length, "Method not registered"); + require (_payload.length > 0, "Payload length was 0"); + + /* Initiate the request. This can't be a local function call + because that would stay inside the EVM and not give l2geth + a place to intercept and re-write the call. + */ + bytes memory response = Self.GetResponse(method_idx, 1, _payload); + return response; + } +} + diff --git a/omgx_examples/sushi/test/offchain-test.ts b/omgx_examples/sushi/test/offchain-test.ts new file mode 100644 index 000000000000..ecbfad48486e --- /dev/null +++ b/omgx_examples/sushi/test/offchain-test.ts @@ -0,0 +1,173 @@ +import { BigNumber, Contract, ContractFactory, providers, Wallet } from 'ethers' +import { ethers } from 'hardhat' +import chai, { expect } from 'chai' +import { solidity } from 'ethereum-waffle' +chai.use(solidity) +const abiDecoder = require('web3-eth-abi') + +import hre from 'hardhat' +const cfg = hre.network.config +const hPort = 1234 // Port for local HTTP server +var urlStr +const gasOverride = {} // Can specify e.g. {gasPrice:0, gasLimit:999999} if needed + +// import HelloTuringJson_1 from "../artifacts/contracts/HelloTuring.sol/HelloTuring.json" +// import HelloTuringJson_2 from "../artifacts-ovm/contracts/HelloTuring.sol/HelloTuring.json" +// import TuringHelper_1 from "../artifacts/contracts/TuringHelper.sol/TuringHelper.json" +// import TuringHelper_2 from "../artifacts-ovm/contracts/TuringHelper.sol/TuringHelper.json" +import UniswapV2PairJSON from "../artifacts-ovm/contracts/uniswapv2/UniswapV2Pair.sol/UniswapV2Pair.ovm.json" +import TuringHelper_2 from "../artifacts-ovm/contracts/uniswapv2/TuringHelper.sol/TuringHelper.json" + +let Factory__Hello: ContractFactory +let hello: Contract +let Factory__Helper: ContractFactory +let helper: Contract + + +const local_provider = new providers.JsonRpcProvider(cfg['url']) + +// Key for Hardhat test account #13 (0x1cbd3b2770909d4e10f157cabc84c7264073c9ec) +const testPrivateKey = '0x47c99abed3324a2707c28affff1267e45918ec8c3f20b8aa892e8b065d2942dd' +const testWallet = new Wallet(testPrivateKey, local_provider) +const L1 = !cfg['ovm'] + +describe("L2_Only", function() { + // It is no longer feasible to mock out enough of the l2geth functionality to support + // an L1 version of these tests. + + it("should not be run on an L1 chain", async() => { + expect(L1).to.be.false + }) +}) + +describe("HelloTest", function() { + + before(async () => { + var http = require('http'); + var ip = require("ip") + + var server = module.exports = http.createServer(function (req, res) { + if (req.headers['content-type'] === 'application/json') { + var body = ''; + req.on('data', function (chunk) { + body += chunk.toString(); + }); + + req.on('end', function () { + + var jBody = JSON.parse(body) + //console.log ("jBody", jBody) + + // if (jBody.method === "hello") { + // res.writeHead(200, {'Content-Type': 'application/json'}); + // var answer = "(UNDEFINED)" + // var v3=jBody.params[0] + // var v4 = abiDecoder.decodeParameter('string',v3) + + // switch(v4) { + // case 'EN_US': + // answer = "Hello World" + // break; + // case 'EN_GB': + // answer = "Top of the Morning" + // break; + // case 'FR': + // answer = "Bonjour le monde" + // break; + // default: + // answer = "(UNDEFINED)" // FIXME This should return an error. + // break; + // } + // console.log (" (HTTP) Returning off-chain response:", v4, "->", answer) + // var jResp = { + // "jsonrpc": "2.0", + // "id": jBody.id, + // "result": abiDecoder.encodeParameter('string',answer) + // } + // res.end(JSON.stringify(jResp)) + // server.emit('success', body); + // } + if (jBody.method === "add2") { + + let v1 = jBody.params[0] + + const args = abiDecoder.decodeParameters(['uint256','uint256'], v1) + + let sum = Number(args['0']) + Number(args['1']) + + res.writeHead(200, {'Content-Type': 'application/json'}); + console.log (" (HTTP) Returning off-chain response:", args, "->", sum) + var jResp2 = { + "jsonrpc": "2.0", + "id": jBody.id, + "result": abiDecoder.encodeParameter('uint256', sum) + } + res.end(JSON.stringify(jResp2)) + server.emit('success', body); + } else { + res.writeHead(400, {'Content-Type': 'text/plain'}); + res.end('Unknown method'); + } + + }); + + } else { + res.writeHead(400, {'Content-Type': 'text/plain'}); + res.end('Expected content-type: application/json'); + }; + }).listen(hPort); + + // Get a non-localhost IP address of the local machine, as the target for the off-chain request + urlStr = "http://" + ip.address() + ":" + hPort + console.log(" Created local HTTP server at", urlStr) + + Factory__Helper = new ContractFactory( + (L1 ? TuringHelper_1.abi : TuringHelper_2.abi), + (L1 ? TuringHelper_1.bytecode : TuringHelper_2.bytecode), + testWallet) + + helper = await Factory__Helper.deploy(urlStr, gasOverride) + console.log(" Helper contract deployed as", helper.address, "on","L2") + + await(helper.RegisterMethod(ethers.utils.toUtf8Bytes("hello"))); + await(helper.RegisterMethod(ethers.utils.toUtf8Bytes("add2"))); + + Factory__Hello = new ContractFactory( + (L1 ? HelloTuringJson_1.abi : HelloTuringJson_2.abi), + (L1 ? HelloTuringJson_1.bytecode : HelloTuringJson_2.bytecode), + testWallet) + + hello = await Factory__Hello.deploy(helper.address, gasOverride) + console.log(" Test contract deployed as", hello.address) + }) + + it("should return the EN_US greeting via eth_call", async() => { + let us_greeting = hello.CustomGreetingFor("EN_US", gasOverride) + expect (await us_greeting).to.equal("Hello World") + }) + + it("should allow the user to set a locale via eth_sendRawTransaction", async() => { + let loc1 = await hello.SetMyLocale("FR", gasOverride) + expect (await loc1.wait()).to.be.ok + }) + + it("should return the expected personal greeting", async() => { + let msg1 = hello.PersonalGreeting(gasOverride) + expect (await msg1).to.equal("Bonjour le monde") + }) + + it("should allow the user to change their locale", async() => { + let loc2 = await hello.SetMyLocale("EN_GB", gasOverride) + expect (await loc2.wait()).to.be.ok + }) + + it("should now return a different personal greeting", async() => { + let msg2 = hello.PersonalGreeting(gasOverride) + expect (await msg2).to.equal("Top of the Morning") + }) + + it("should support numerical datatypes", async() => { + let sum = hello.AddNumbers(20, 22) + expect (await sum).to.equal(42) + }) +}) diff --git a/packages/omgx/offchain-prototype/contracts/TuringHelper.sol b/packages/omgx/offchain-prototype/contracts/TuringHelper.sol index ccfd7938d6c4..900810b6d33c 100644 --- a/packages/omgx/offchain-prototype/contracts/TuringHelper.sol +++ b/packages/omgx/offchain-prototype/contracts/TuringHelper.sol @@ -9,7 +9,7 @@ contract TuringHelper { TuringHelper Self; bytes[] methods; - constructor(string memory _url) public { + constructor (string memory _url) public { console.log("Deploying a helper contract with data source:", _url); data_URL = bytes(_url); Self = TuringHelper(address(this)); diff --git a/packages/omgx/offchain-prototype/contracts/uniswapv2/UniswapV2Pair.sol b/packages/omgx/offchain-prototype/contracts/uniswapv2/UniswapV2Pair.sol index c67bd7f0fe6c..454d94fcf37b 100644 --- a/packages/omgx/offchain-prototype/contracts/uniswapv2/UniswapV2Pair.sol +++ b/packages/omgx/offchain-prototype/contracts/uniswapv2/UniswapV2Pair.sol @@ -200,13 +200,37 @@ contract UniswapV2Pair is UniswapV2ERC20 { // require(reserve0 > 0 && reserve1 > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY'); // // API Call: Comparing off-chain price to on-chain price - + // uint256 offchain_price; // //Hard-coding token0 and token1 address for tests (remove this code later) // address test0 = address(0x6B175474E89094C44Da98b954EedeAC495271d0F); //dai // address test1 = address(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48); //usdc // bytes memory encRequest = abi.encode(reserve0, reserve1); // bytes memory encResponse = myHelper.TuringCall(1, encRequest); + // offchain_price = abi.decode(encResponse,(uint256)); + + // uint256 onchain_price = reserve0.div(reserve1); + // uint256 priceDiff; + // priceDiff = ((uint256(abs(int256(onchain_price).sub(int256(offchain_price))))).mul(100)).div(onchain_price); + + // // If call fails or priceDiff <= 30, then return 3 + // feeX10 = 3; + + // if(priceDiff > 30 && priceDiff <= 60) + // { + // feeX10 = 6; + // } + + // else if(priceDiff > 60) + // { + // feeX10 = 10; + // } + + // uint256 c; + //bytes memory encRequest = abi.encode(reserve0, reserve1); + //bytes memory encResponse = myHelper.TuringCall(1, encRequest); + //c = abi.decode(encResponse,(uint256)); + //feeX10 = c; uint256 offchain_price; bytes memory encRequest = abi.encode(token0, token1); @@ -228,7 +252,6 @@ contract UniswapV2Pair is UniswapV2ERC20 { { feeX10 = 10; } - } // this low-level function should be called from a contract which performs important safety checks diff --git a/packages/omgx/offchain-prototype/test/fee-test.ts b/packages/omgx/offchain-prototype/test/fee-test.ts index e08eb33da08b..f911cc3cfe59 100644 --- a/packages/omgx/offchain-prototype/test/fee-test.ts +++ b/packages/omgx/offchain-prototype/test/fee-test.ts @@ -1,3 +1,138 @@ + +// import { BigNumber, Contract, ContractFactory, providers, Wallet } from 'ethers' +// import { ethers } from 'hardhat' +// import chai, { expect } from 'chai' +// import { solidity } from 'ethereum-waffle' +// chai.use(solidity) +// const abiDecoder = require('web3-eth-abi') +// import hre from 'hardhat' +// const cfg = hre.network.config +// const hPort = 1234 // Port for local HTTP server +// var urlStr +// const gasOverride = {} // Can specify e.g. {gasPrice:0, gasLimit:999999} if needed +// // import HelloTuringJson_1 from "../artifacts/contracts/HelloTuring.sol/HelloTuring.json" +// // import HelloTuringJson_2 from "../artifacts-ovm/contracts/HelloTuring.sol/HelloTuring.json" +// // import TuringHelper_1 from "../artifacts/contracts/TuringHelper.sol/TuringHelper.json" +// // import TuringHelper_2 from "../artifacts-ovm/contracts/TuringHelper.sol/TuringHelper.json" +// import UniswapV2PairJSON from "../artifacts-ovm/contracts/uniswapv2/UniswapV2Pair.sol/UniswapV2Pair.json" +// import TuringHelper_2 from "../artifacts-ovm/contracts/TuringHelper.sol/TuringHelper.json" +// let Factory__Uni: ContractFactory +// let uni: Contract +// let Factory__Helper: ContractFactory +// let helper: Contract +// const local_provider = new providers.JsonRpcProvider(cfg['url']) +// // Key for Hardhat test account #13 (0x1cbd3b2770909d4e10f157cabc84c7264073c9ec) +// const testPrivateKey = '0x47c99abed3324a2707c28affff1267e45918ec8c3f20b8aa892e8b065d2942dd' +// const testWallet = new Wallet(testPrivateKey, local_provider) +// const L1 = !cfg['ovm'] +// describe("L2_Only", function() { +// // It is no longer feasible to mock out enough of the l2geth functionality to support +// // an L1 version of these tests. +// it("should not be run on an L1 chain", async() => { +// expect(L1).to.be.false +// }) +// }) +// describe("HelloTest", function() { +// before(async () => { +// var http = require('http'); +// var ip = require("ip") +// var server = module.exports = http.createServer(function (req, res) { +// if (req.headers['content-type'] === 'application/json') { +// var body = ''; +// req.on('data', function (chunk) { +// body += chunk.toString(); +// }); +// req.on('end', function () { +// var jBody = JSON.parse(body) +// //console.log ("jBody", jBody) +// // if (jBody.method === "hello") { +// // res.writeHead(200, {'Content-Type': 'application/json'}); +// // var answer = "(UNDEFINED)" +// // var v3=jBody.params[0] +// // var v4 = abiDecoder.decodeParameter('string',v3) +// // switch(v4) { +// // case 'EN_US': +// // answer = "Hello World" +// // break; +// // case 'EN_GB': +// // answer = "Top of the Morning" +// // break; +// // case 'FR': +// // answer = "Bonjour le monde" +// // break; +// // default: +// // answer = "(UNDEFINED)" // FIXME This should return an error. +// // break; +// // } +// // console.log (" (HTTP) Returning off-chain response:", v4, "->", answer) +// // var jResp = { +// // "jsonrpc": "2.0", +// // "id": jBody.id, +// // "result": abiDecoder.encodeParameter('string',answer) +// // } +// // res.end(JSON.stringify(jResp)) +// // server.emit('success', body); +// // } +// if (jBody.method === "add2") { +// let v1 = jBody.params[0] +// // const args = abiDecoder.decodeParameters(['address','address'], v1) +// // const { ChainId, Fetcher, Route } = require('@uniswap/sdk'); +// // const chainId = ChainId.MAINNET; +// // // const tokenAddress0 = Number(args[0]); +// // // const tokenAddress1 = Number(args[1]); +// // const tokenAddress0 = 0x6B175474E89094C44Da98b954EedeAC495271d0F; //dai +// // const tokenAddress1 = 0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48; //usdc +// // const token0 = Fetcher.fetchTokenData(chainId, tokenAddress0); +// // const token1 = Fetcher.fetchTokenData(chainId, tokenAddress1); +// // const pair = Fetcher.fetchPairData(token0, token1); +// // const route = new Route([pair]); +// // let sum = route.midPrice; +// const args = abiDecoder.decodeParameters(['uint256','uint256'], v1) +// let sum = Number(args['0']) + Number(args['1']) +// res.writeHead(200, {'Content-Type': 'application/json'}); +// console.log (" (HTTP) Returning off-chain response:", args, "->", sum) +// var jResp2 = { +// "jsonrpc": "2.0", +// "id": jBody.id, +// "result": abiDecoder.encodeParameter('uint256', sum) +// } +// res.end(JSON.stringify(jResp2)) +// server.emit('success', body); +// } else { +// res.writeHead(400, {'Content-Type': 'text/plain'}); +// res.end('Unknown method'); +// } +// }); +// } else { +// res.writeHead(400, {'Content-Type': 'text/plain'}); +// res.end('Expected content-type: application/json'); +// }; +// }).listen(hPort); +// // Get a non-localhost IP address of the local machine, as the target for the off-chain request +// urlStr = "http://" + ip.address() + ":" + hPort +// console.log(" Created local HTTP server at", urlStr) +// Factory__Helper = new ContractFactory( +// (TuringHelper_2.abi), +// (TuringHelper_2.bytecode), +// testWallet) +// helper = await Factory__Helper.deploy(urlStr, gasOverride) +// console.log(" Helper contract deployed as", helper.address, "on","L2") +// await(helper.RegisterMethod(ethers.utils.toUtf8Bytes("hello"))); +// await(helper.RegisterMethod(ethers.utils.toUtf8Bytes("add2"))); +// Factory__Uni = new ContractFactory( +// (UniswapV2PairJSON.abi), +// (UniswapV2PairJSON.bytecode), +// testWallet) +// uni = await Factory__Uni.deploy(helper.address, gasOverride) +// console.log(" Test contract deployed as", uni.address) +// }) +// it("run uniswap with proper fees", async() => { +// let sum = uni.offChainCompare(1000, 1000) +// sum = await sum; +// console.log(sum) +// }) +// }) + import { BigNumber, Contract, ContractFactory, providers, Wallet } from 'ethers' import { ethers } from 'hardhat' import chai, { expect } from 'chai' @@ -11,10 +146,6 @@ const hPort = 5678 // Port for local HTTP server var urlStr const gasOverride = {} // Can specify e.g. {gasPrice:0, gasLimit:999999} if needed -// import HelloTuringJson_1 from "../artifacts/contracts/HelloTuring.sol/HelloTuring.json" -// import HelloTuringJson_2 from "../artifacts-ovm/contracts/HelloTuring.sol/HelloTuring.json" -// import TuringHelper_1 from "../artifacts/contracts/TuringHelper.sol/TuringHelper.json" -// import TuringHelper_2 from "../artifacts-ovm/contracts/TuringHelper.sol/TuringHelper.json" import UniswapV2PairJSON from "../artifacts-ovm/contracts/uniswapv2/UniswapV2Pair.sol/UniswapV2Pair.json" import TuringHelper_2 from "../artifacts-ovm/contracts/TuringHelper.sol/TuringHelper.json" @@ -23,7 +154,6 @@ let uni: Contract let Factory__Helper: ContractFactory let helper: Contract - const local_provider = new providers.JsonRpcProvider(cfg['url']) // Key for Hardhat test account #13 (0x1cbd3b2770909d4e10f157cabc84c7264073c9ec) diff --git a/packages/omgx/offchain-prototype/test/hello-test.ts b/packages/omgx/offchain-prototype/test/hello-test.ts index 69175c6c6e52..4fe9e3e58870 100644 --- a/packages/omgx/offchain-prototype/test/hello-test.ts +++ b/packages/omgx/offchain-prototype/test/hello-test.ts @@ -4,167 +4,134 @@ import chai, { expect } from 'chai' import { solidity } from 'ethereum-waffle' chai.use(solidity) const abiDecoder = require('web3-eth-abi') - import hre from 'hardhat' const cfg = hre.network.config const hPort = 1234 // Port for local HTTP server var urlStr const gasOverride = {} // Can specify e.g. {gasPrice:0, gasLimit:999999} if needed - -import HelloTuringJson_1 from "../artifacts/contracts/HelloTuring.sol/HelloTuring.json" +// import HelloTuringJson_1 from "../artifacts/contracts/HelloTuring.sol/HelloTuring.json" import HelloTuringJson_2 from "../artifacts-ovm/contracts/HelloTuring.sol/HelloTuring.json" -import TuringHelper_1 from "../artifacts/contracts/TuringHelper.sol/TuringHelper.json" +// import TuringHelper_1 from "../artifacts/contracts/TuringHelper.sol/TuringHelper.json" import TuringHelper_2 from "../artifacts-ovm/contracts/TuringHelper.sol/TuringHelper.json" - let Factory__Hello: ContractFactory let hello: Contract let Factory__Helper: ContractFactory let helper: Contract - - const local_provider = new providers.JsonRpcProvider(cfg['url']) - // Key for Hardhat test account #13 (0x1cbd3b2770909d4e10f157cabc84c7264073c9ec) const testPrivateKey = '0x47c99abed3324a2707c28affff1267e45918ec8c3f20b8aa892e8b065d2942dd' const testWallet = new Wallet(testPrivateKey, local_provider) const L1 = !cfg['ovm'] - describe("L2_Only", function() { // It is no longer feasible to mock out enough of the l2geth functionality to support // an L1 version of these tests. - it("should not be run on an L1 chain", async() => { expect(L1).to.be.false }) }) - describe("HelloTest", function() { - before(async () => { var http = require('http'); var ip = require("ip") - var server = module.exports = http.createServer(function (req, res) { if (req.headers['content-type'] === 'application/json') { - var body = ''; - req.on('data', function (chunk) { - body += chunk.toString(); - }); - - req.on('end', function () { - - var jBody = JSON.parse(body) - //console.log ("jBody", jBody) - - if (jBody.method === "hello") { - res.writeHead(200, {'Content-Type': 'application/json'}); - var answer = "(UNDEFINED)" - var v3=jBody.params[0] - var v4 = abiDecoder.decodeParameter('string',v3) - - switch(v4) { + var body = ''; + req.on('data', function (chunk) { + body += chunk.toString(); + }); + req.on('end', function () { + var jBody = JSON.parse(body) + //console.log ("jBody", jBody) + if (jBody.method === "hello") { + res.writeHead(200, {'Content-Type': 'application/json'}); + var answer = "(UNDEFINED)" + var v3=jBody.params[0] + var v4 = abiDecoder.decodeParameter('string',v3) + switch(v4) { case 'EN_US': - answer = "Hello World" - break; - case 'EN_GB': - answer = "Top of the Morning" - break; - case 'FR': - answer = "Bonjour le monde" - break; - default: - answer = "(UNDEFINED)" // FIXME This should return an error. - break; - } - console.log (" (HTTP) Returning off-chain response:", v4, "->", answer) - var jResp = { + answer = "Hello World" + break; + case 'EN_GB': + answer = "Top of the Morning" + break; + case 'FR': + answer = "Bonjour le monde" + break; + default: + answer = "(UNDEFINED)" // FIXME This should return an error. + break; + } + console.log (" (HTTP) Returning off-chain response:", v4, "->", answer) + var jResp = { "jsonrpc": "2.0", - "id": jBody.id, - "result": abiDecoder.encodeParameter('string',answer) - } - res.end(JSON.stringify(jResp)) - server.emit('success', body); + "id": jBody.id, + "result": abiDecoder.encodeParameter('string',answer) + } + res.end(JSON.stringify(jResp)) + server.emit('success', body); } else if (jBody.method === "add2") { - - let v1 = jBody.params[0] - - const args = abiDecoder.decodeParameters(['uint256','uint256'], v1) - - let sum = Number(args['0']) + Number(args['1']) - - res.writeHead(200, {'Content-Type': 'application/json'}); - console.log (" (HTTP) Returning off-chain response:", args, "->", sum) - var jResp2 = { - "jsonrpc": "2.0", - "id": jBody.id, - "result": abiDecoder.encodeParameter('uint256', sum) - } - res.end(JSON.stringify(jResp2)) - server.emit('success', body); - } else { - res.writeHead(400, {'Content-Type': 'text/plain'}); - res.end('Unknown method'); - } - - }); - + let v1 = jBody.params[0] + const args = abiDecoder.decodeParameters(['uint256','uint256'], v1) + let sum = Number(args['0']) + Number(args['1']) + res.writeHead(200, {'Content-Type': 'application/json'}); + console.log (" (HTTP) Returning off-chain response:", args, "->", sum) + var jResp2 = { + "jsonrpc": "2.0", + "id": jBody.id, + "result": abiDecoder.encodeParameter('uint256', sum) + } + res.end(JSON.stringify(jResp2)) + server.emit('success', body); + } else { + res.writeHead(400, {'Content-Type': 'text/plain'}); + res.end('Unknown method'); + } + }); } else { - res.writeHead(400, {'Content-Type': 'text/plain'}); - res.end('Expected content-type: application/json'); + res.writeHead(400, {'Content-Type': 'text/plain'}); + res.end('Expected content-type: application/json'); }; }).listen(hPort); - // Get a non-localhost IP address of the local machine, as the target for the off-chain request urlStr = "http://" + ip.address() + ":" + hPort console.log(" Created local HTTP server at", urlStr) - Factory__Helper = new ContractFactory( - (L1 ? TuringHelper_1.abi : TuringHelper_2.abi), - (L1 ? TuringHelper_1.bytecode : TuringHelper_2.bytecode), + (TuringHelper_2.abi), + (TuringHelper_2.bytecode), testWallet) - helper = await Factory__Helper.deploy(urlStr, gasOverride) console.log(" Helper contract deployed as", helper.address, "on","L2") - await(helper.RegisterMethod(ethers.utils.toUtf8Bytes("hello"))); await(helper.RegisterMethod(ethers.utils.toUtf8Bytes("add2"))); - Factory__Hello = new ContractFactory( - (L1 ? HelloTuringJson_1.abi : HelloTuringJson_2.abi), - (L1 ? HelloTuringJson_1.bytecode : HelloTuringJson_2.bytecode), + (HelloTuringJson_2.abi), + (HelloTuringJson_2.bytecode), testWallet) - hello = await Factory__Hello.deploy(helper.address, gasOverride) console.log(" Test contract deployed as", hello.address) }) - it("should return the EN_US greeting via eth_call", async() => { let us_greeting = hello.CustomGreetingFor("EN_US", gasOverride) expect (await us_greeting).to.equal("Hello World") }) - it("should allow the user to set a locale via eth_sendRawTransaction", async() => { let loc1 = await hello.SetMyLocale("FR", gasOverride) expect (await loc1.wait()).to.be.ok }) - it("should return the expected personal greeting", async() => { let msg1 = hello.PersonalGreeting(gasOverride) expect (await msg1).to.equal("Bonjour le monde") }) - it("should allow the user to change their locale", async() => { let loc2 = await hello.SetMyLocale("EN_GB", gasOverride) expect (await loc2.wait()).to.be.ok }) - it("should now return a different personal greeting", async() => { let msg2 = hello.PersonalGreeting(gasOverride) expect (await msg2).to.equal("Top of the Morning") }) - it("should support numerical datatypes", async() => { let sum = hello.AddNumbers(20, 22) expect (await sum).to.equal(42) }) -}) +}) \ No newline at end of file