From 0fbd94e997e930089fadb56de2fe519b67089bfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matev=C5=BE=20Jekovec?= Date: Wed, 18 Oct 2023 18:33:08 +0200 Subject: [PATCH 1/2] examples/onchain-signer/test: Add support for Sapphire Testnet/Mainnet --- examples/onchain-signer/contracts/Gasless.sol | 22 ++++---- examples/onchain-signer/hardhat.config.ts | 3 ++ examples/onchain-signer/test/CommentBox.ts | 51 ++++++++++--------- 3 files changed, 39 insertions(+), 37 deletions(-) diff --git a/examples/onchain-signer/contracts/Gasless.sol b/examples/onchain-signer/contracts/Gasless.sol index f2b22956..03dccb56 100644 --- a/examples/onchain-signer/contracts/Gasless.sol +++ b/examples/onchain-signer/contracts/Gasless.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.19; import {EIP155Signer} from "@oasisprotocol/sapphire-contracts/contracts/EIP155Signer.sol"; +import {EthereumUtils} from "@oasisprotocol/sapphire-contracts/contracts/EthereumUtils.sol"; struct EthereumKeypair { address addr; @@ -9,22 +10,16 @@ struct EthereumKeypair { uint64 nonce; } -struct EthTx { - uint64 nonce; - uint256 gasPrice; - uint64 gasLimit; - address to; - uint256 value; - bytes data; - uint256 chainId; -} - // Proxy for gasless transaction. contract Gasless { + error KeyPairNotSet(); + EthereumKeypair private kp; - function setKeypair(EthereumKeypair memory keypair) external payable { - kp = keypair; + constructor () payable { + (kp.addr, kp.secret) = EthereumUtils.generateKeypair(); + + payable(kp.addr).transfer(msg.value); } function makeProxyTx(address innercallAddr, bytes memory innercall) @@ -33,6 +28,9 @@ contract Gasless { returns (bytes memory output) { bytes memory data = abi.encode(innercallAddr, innercall); + if (kp.secret == 0) { + revert KeyPairNotSet(); + } // Call will invoke proxy(). return diff --git a/examples/onchain-signer/hardhat.config.ts b/examples/onchain-signer/hardhat.config.ts index 7f6a9738..6998d993 100644 --- a/examples/onchain-signer/hardhat.config.ts +++ b/examples/onchain-signer/hardhat.config.ts @@ -38,6 +38,9 @@ const config: HardhatUserConfig = { }, }, solidity: '0.8.19', + mocha: { + timeout: 120_000_000, // Sapphire Mainnet/Testnet require more time. + }, }; export default config; diff --git a/examples/onchain-signer/test/CommentBox.ts b/examples/onchain-signer/test/CommentBox.ts index b7b64ee2..7b7c92b3 100644 --- a/examples/onchain-signer/test/CommentBox.ts +++ b/examples/onchain-signer/test/CommentBox.ts @@ -1,7 +1,7 @@ import { expect } from 'chai'; -import hre, { config, ethers } from 'hardhat'; +import { ethers } from 'hardhat'; import { CommentBox, Gasless } from '../typechain-types'; -import { HDAccountsUserConfig } from 'hardhat/types'; +import { parseEther } from 'ethers/lib/utils'; describe('CommentBox', function () { let commentBox: CommentBox; @@ -13,27 +13,8 @@ describe('CommentBox', function () { await commentBox.deployed(); const GaslessFactory = await ethers.getContractFactory('Gasless'); - gasless = await GaslessFactory.deploy(); + gasless = await GaslessFactory.deploy({value: parseEther('0.1')}); await gasless.deployed(); - - // Derive the private key of the 1st (counting from 0) builtin hardhat test account. - const accounts = config.networks.hardhat - .accounts as unknown as HDAccountsUserConfig; - const wallet1 = ethers.Wallet.fromMnemonic( - accounts.mnemonic, - accounts.path + `/1`, - ); - - // Use it as the relayer private key. - await expect( - await gasless.setKeypair({ - addr: wallet1.address, - secret: Uint8Array.from( - Buffer.from(wallet1.privateKey.substring(2), 'hex'), - ), - nonce: ethers.provider.getTransactionCount(wallet1.address), - }), - ).not.to.be.reverted; }); it('Should comment', async function () { @@ -41,6 +22,15 @@ describe('CommentBox', function () { const tx = await commentBox.comment('Hello, world!'); await tx.wait(); + + // Sapphire Mainnet/Testnet: Wait a few moments for nodes to catch up. + if ( + (await gasless.provider.getNetwork()).chainId == 23294 || + (await gasless.provider.getNetwork()).chainId == 23295 + ) { + await new Promise((r) => setTimeout(r, 6_000)); + } + expect(await commentBox.commentCount()).eq(prevCommentCount.add(1)); }); @@ -48,16 +38,27 @@ describe('CommentBox', function () { // You can set up sapphire-dev image and run the test like this: // docker run -it -p8545:8545 -p8546:8546 ghcr.io/oasisprotocol/sapphire-dev -to 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 // npx hardhat test --grep proxy --network sapphire-localnet - if ((await ethers.provider.getNetwork()).chainId == 1337) { + if ((await gasless.provider.getNetwork()).chainId == 1337) { this.skip(); } + const innercall = commentBox.interface.encodeFunctionData('comment', [ 'Hello, free world!', ]); + + // Sapphire Mainnet/Testnet: Wait a few moments for nodes to catch up. + if ( + (await gasless.provider.getNetwork()).chainId == 23294 || + (await gasless.provider.getNetwork()).chainId == 23295 + ) { + await new Promise((r) => setTimeout(r, 6_000)); + } + const tx = await gasless.makeProxyTx(commentBox.address, innercall); - const plainResp = await gasless.provider.sendTransaction(tx); - const receipt = await ethers.provider.waitForTransaction(plainResp.hash); + // TODO: https://github.com/oasisprotocol/sapphire-paratime/issues/179 + const response = await gasless.provider.sendTransaction(tx); + const receipt = await gasless.provider.waitForTransaction(response.hash); if (!receipt || receipt.status != 1) throw new Error('tx failed'); }); }); From f5c3a728d329472568eea426460386cdb714003c Mon Sep 17 00:00:00 2001 From: CedarMist <134699267+CedarMist@users.noreply.github.com> Date: Thu, 19 Oct 2023 14:52:05 +0100 Subject: [PATCH 2/2] examples/onchain-signer: fixed example to work with docs --- examples/onchain-signer/contracts/Gasless.sol | 15 +++++-------- examples/onchain-signer/test/CommentBox.ts | 22 +++++++++++++++++-- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/examples/onchain-signer/contracts/Gasless.sol b/examples/onchain-signer/contracts/Gasless.sol index 03dccb56..cb047888 100644 --- a/examples/onchain-signer/contracts/Gasless.sol +++ b/examples/onchain-signer/contracts/Gasless.sol @@ -2,7 +2,6 @@ pragma solidity ^0.8.19; import {EIP155Signer} from "@oasisprotocol/sapphire-contracts/contracts/EIP155Signer.sol"; -import {EthereumUtils} from "@oasisprotocol/sapphire-contracts/contracts/EthereumUtils.sol"; struct EthereumKeypair { address addr; @@ -12,14 +11,13 @@ struct EthereumKeypair { // Proxy for gasless transaction. contract Gasless { - error KeyPairNotSet(); - EthereumKeypair private kp; - constructor () payable { - (kp.addr, kp.secret) = EthereumUtils.generateKeypair(); - - payable(kp.addr).transfer(msg.value); + constructor (EthereumKeypair memory keypair) payable { + kp = keypair; + if( msg.value > 0 ) { + payable(kp.addr).transfer(msg.value); + } } function makeProxyTx(address innercallAddr, bytes memory innercall) @@ -28,9 +26,6 @@ contract Gasless { returns (bytes memory output) { bytes memory data = abi.encode(innercallAddr, innercall); - if (kp.secret == 0) { - revert KeyPairNotSet(); - } // Call will invoke proxy(). return diff --git a/examples/onchain-signer/test/CommentBox.ts b/examples/onchain-signer/test/CommentBox.ts index 7b7c92b3..f932cc19 100644 --- a/examples/onchain-signer/test/CommentBox.ts +++ b/examples/onchain-signer/test/CommentBox.ts @@ -1,19 +1,37 @@ import { expect } from 'chai'; -import { ethers } from 'hardhat'; +import { ethers, config } from 'hardhat'; import { CommentBox, Gasless } from '../typechain-types'; +import { EthereumKeypairStruct } from "../typechain-types/contracts/Gasless" import { parseEther } from 'ethers/lib/utils'; +import { HDAccountsUserConfig } from 'hardhat/types'; describe('CommentBox', function () { let commentBox: CommentBox; let gasless: Gasless; before(async () => { + // Derive the private key of the 1st (counting from 0) builtin hardhat test account. + const accounts = config.networks.hardhat + .accounts as unknown as HDAccountsUserConfig; + const wallet1 = ethers.Wallet.fromMnemonic( + accounts.mnemonic, + accounts.path + `/1`, + ); + + // Use it as the relayer private key. + // NOTE can be done by the contract with EthereumUtils.generateKeypair() + const keypair : EthereumKeypairStruct = { + addr: wallet1.address, + secret: wallet1.privateKey, + nonce: ethers.provider.getTransactionCount(wallet1.address), + }; + const CommentBoxFactory = await ethers.getContractFactory('CommentBox'); commentBox = await CommentBoxFactory.deploy(); await commentBox.deployed(); const GaslessFactory = await ethers.getContractFactory('Gasless'); - gasless = await GaslessFactory.deploy({value: parseEther('0.1')}); + gasless = await GaslessFactory.deploy(keypair, {value: parseEther('0.1')}); await gasless.deployed(); });