From 7273f8379b990c7a1c6ee9fdeacfe6d08fe221d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matev=C5=BE=20Jekovec?= Date: Mon, 23 Dec 2024 15:51:46 +0100 Subject: [PATCH] examples/onchain-signer: Add encryptCallData() --- examples/onchain-signer/contracts/Gasless.sol | 24 +++++- examples/onchain-signer/hardhat.config.ts | 12 ++- examples/onchain-signer/test/CommentBox.ts | 74 +++++++++++-------- 3 files changed, 75 insertions(+), 35 deletions(-) diff --git a/examples/onchain-signer/contracts/Gasless.sol b/examples/onchain-signer/contracts/Gasless.sol index c1716987..8e0ea5d7 100644 --- a/examples/onchain-signer/contracts/Gasless.sol +++ b/examples/onchain-signer/contracts/Gasless.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.20; +import {encryptCallData} from "@oasisprotocol/sapphire-contracts/contracts/CalldataEncryption.sol"; import {EIP155Signer} from "@oasisprotocol/sapphire-contracts/contracts/EIP155Signer.sol"; struct EthereumKeypair { @@ -25,8 +26,27 @@ contract Gasless { bytes memory innercall ) external view returns (bytes memory output) { bytes memory data = abi.encode(innercallAddr, innercall); + return + EIP155Signer.sign( + kp.addr, + kp.secret, + EIP155Signer.EthTx({ + nonce: kp.nonce, + gasPrice: 100_000_000_000, + gasLimit: 250000, + to: address(this), + value: 0, + data: encryptCallData(abi.encodeCall(this.proxy, data)), + chainId: block.chainid + }) + ); + } - // Call will invoke proxy(). + function makeProxyTxPlain( + address innercallAddr, + bytes memory innercall + ) external view returns (bytes memory output) { + bytes memory data = abi.encode(innercallAddr, innercall); return EIP155Signer.sign( kp.addr, diff --git a/examples/onchain-signer/hardhat.config.ts b/examples/onchain-signer/hardhat.config.ts index cdfe50e0..35e363ff 100644 --- a/examples/onchain-signer/hardhat.config.ts +++ b/examples/onchain-signer/hardhat.config.ts @@ -26,7 +26,17 @@ const config: HardhatUserConfig = { 'sapphire-testnet': { ...sapphireTestnet, accounts }, 'sapphire-localnet': { ...sapphireLocalnet, accounts }, }, - solidity: '0.8.20', + solidity: { + version: '0.8.20', + settings: { + // XXX: Needs to match https://github.com/oasisprotocol/sapphire-paratime/blob/main/contracts/hardhat.config.ts + optimizer: { + enabled: true, + runs: (1 << 32) - 1, + }, + viaIR: true, + }, + }, }; export default config; diff --git a/examples/onchain-signer/test/CommentBox.ts b/examples/onchain-signer/test/CommentBox.ts index 5699a23e..bb3d32be 100644 --- a/examples/onchain-signer/test/CommentBox.ts +++ b/examples/onchain-signer/test/CommentBox.ts @@ -1,4 +1,5 @@ import { expect } from 'chai'; +import { Context } from 'mocha'; import { ethers } from 'hardhat'; import { parseEther, Wallet } from 'ethers'; import { CommentBox, Gasless } from '../typechain-types'; @@ -33,55 +34,64 @@ describe('CommentBox', function () { console.log(' . gasless pubkey', wallet.address); }); - it('Should comment', async function () { - this.timeout(10000); + async function commentGasless(t: Context, comment: string, plain: boolean) { + const provider = ethers.provider; + + const innercall = commentBox.interface.encodeFunctionData('comment', [ + comment, + ]); const prevCommentCount = await commentBox.commentCount(); + let tx: string; + if (plain) { + tx = await gasless.makeProxyTxPlain( + await commentBox.getAddress(), + innercall, + ); + } else { + tx = await gasless.makeProxyTx(await commentBox.getAddress(), innercall); + } - const tx = await commentBox.comment('Hello, world!'); - await tx.wait(); + // TODO: https://github.com/oasisprotocol/sapphire-paratime/issues/179 + const response = await provider.broadcastTransaction(tx); + await response.wait(); - // Sapphire Mainnet/Testnet: Wait a few moments for nodes to catch up. - const chainId = (await ethers.provider.getNetwork()).chainId; - if (chainId == BigInt(23294) || chainId == BigInt(23295)) { - await new Promise((r) => setTimeout(r, 6_000)); - } + const receipt = await provider.getTransactionReceipt(response.hash); + if (!receipt || receipt.status != 1) throw new Error('tx failed'); expect(await commentBox.commentCount()).eq(prevCommentCount + BigInt(1)); - }); + } - it('Should comment gasless', async function () { - this.timeout(10000); + it('Should comment', async function () { + const prevCommentCount = await commentBox.commentCount(); - const provider = ethers.provider; + const tx = await commentBox.comment('Hello, world!'); + await tx.wait(); + expect(await commentBox.commentCount()).eq(prevCommentCount + BigInt(1)); + }); + + it('Should comment gasless (encrypted)', async function () { // You can set up sapphire-localnet image and run the test like this: - // docker run -it -p8545:8545 -p8546:8546 ghcr.io/oasisprotocol/sapphire-localnet -to 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 + // docker run -it -p8544-8548:8544-8548 ghcr.io/oasisprotocol/sapphire-localnet // npx hardhat test --grep proxy --network sapphire-localnet - const chainId = (await provider.getNetwork()).chainId; + const chainId = (await ethers.provider.getNetwork()).chainId; if (chainId == BigInt(1337)) { this.skip(); } - const innercall = commentBox.interface.encodeFunctionData('comment', [ - 'Hello, free world!', - ]); + await commentGasless(this, 'Hello, c10l world', false); + }); - // Sapphire Mainnet/Testnet: Wait a few moments for nodes to catch up. - if (chainId == BigInt(23294) || chainId == BigInt(23295)) { - await new Promise((r) => setTimeout(r, 6_000)); + it('Should comment gasless (plain)', async function () { + // You can set up sapphire-localnet image and run the test like this: + // docker run -it -p8544-8548:8544-8548 ghcr.io/oasisprotocol/sapphire-localnet + // npx hardhat test --grep proxy --network sapphire-localnet + const chainId = (await ethers.provider.getNetwork()).chainId; + if (chainId == BigInt(1337)) { + this.skip(); } - const tx = await gasless.makeProxyTx( - await commentBox.getAddress(), - innercall, - ); - - // TODO: https://github.com/oasisprotocol/sapphire-paratime/issues/179 - const response = await provider.broadcastTransaction(tx); - await response.wait(); - - const receipt = await provider.getTransactionReceipt(response.hash); - if (!receipt || receipt.status != 1) throw new Error('tx failed'); + await commentGasless(this, 'Hello, plain world', true); }); });