Skip to content

Commit

Permalink
examples/onchain-signer: Add encryptCallData()
Browse files Browse the repository at this point in the history
  • Loading branch information
matevz committed Jan 6, 2025
1 parent 7968080 commit 1fc230e
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 34 deletions.
24 changes: 22 additions & 2 deletions examples/onchain-signer/contracts/Gasless.sol
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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,
Expand Down
12 changes: 11 additions & 1 deletion examples/onchain-signer/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
1 change: 1 addition & 0 deletions examples/onchain-signer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"@nomicfoundation/hardhat-verify": "^2.0.2",
"@oasisprotocol/sapphire-contracts": "workspace:^",
"@oasisprotocol/sapphire-hardhat": "workspace:^",
"@oasisprotocol/sapphire-paratime": "workspace:^",
"@typechain/ethers-v6": "^0.5.1",
"@typechain/hardhat": "^9.1.0",
"@types/mocha": "^9.1.1",
Expand Down
96 changes: 65 additions & 31 deletions examples/onchain-signer/test/CommentBox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import { ethers } from 'hardhat';
import { parseEther, Wallet } from 'ethers';
import { CommentBox, Gasless } from '../typechain-types';
import { EthereumKeypairStruct } from '../typechain-types/contracts/Gasless';
import {
isCalldataEnveloped,
wrapEthereumProvider,
} from '@oasisprotocol/sapphire-paratime';

describe('CommentBox', function () {
let commentBox: CommentBox;
Expand Down Expand Up @@ -33,55 +37,85 @@ describe('CommentBox', function () {
console.log(' . gasless pubkey', wallet.address);
});

it('Should comment', async function () {
this.timeout(10000);
// Request and send a gasless transaction. Set up sapphire-localnet image to
// run this test:
// docker run -it -p8544-8548:8544-8548 ghcr.io/oasisprotocol/sapphire-localnet
async function commentGasless(
comment: string,
plainProxy: boolean,
unwrappedProvider: boolean,
) {
const provider = unwrappedProvider
? ethers.provider
: wrapEthereumProvider(ethers.provider);

const innercall = commentBox.interface.encodeFunctionData('comment', [
comment,
]);

const prevCommentCount = await commentBox.commentCount();
let tx: string;
if (plainProxy) {
tx = await gasless.makeProxyTxPlain(
await commentBox.getAddress(),
innercall,
);
} else {
tx = await gasless.makeProxyTx(await commentBox.getAddress(), innercall);
}

const response = await provider.broadcastTransaction(tx);
expect(isCalldataEnveloped(response.data)).eq(!plainProxy);
await response.wait();

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', async function () {
const prevCommentCount = await commentBox.commentCount();

const tx = await commentBox.comment('Hello, world!');
await tx.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));
}

expect(await commentBox.commentCount()).eq(prevCommentCount + BigInt(1));
});

it('Should comment gasless', async function () {
this.timeout(10000);
it('Should comment gasless (encrypted tx, wrapped client)', async function () {
if ((await ethers.provider.getNetwork()).chainId == BigInt(1337)) {
this.skip();
}

const provider = ethers.provider;
await commentGasless('Hello, c10l world', false, false);
});

// 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
// npx hardhat test --grep proxy --network sapphire-localnet
const chainId = (await provider.getNetwork()).chainId;
if (chainId == BigInt(1337)) {
it('Should comment gasless (encrypted tx, unwrapped client)', async function () {
if ((await ethers.provider.getNetwork()).chainId == BigInt(1337)) {
this.skip();
}

const innercall = commentBox.interface.encodeFunctionData('comment', [
'Hello, free world!',
]);
await commentGasless('Hello, c10l world', false, true);
});

// 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 tx, wrapped client)', async function () {
// Set up sapphire-localnet image to run this test:
// docker run -it -p8544-8548:8544-8548 ghcr.io/oasisprotocol/sapphire-localnet
if ((await ethers.provider.getNetwork()).chainId == BigInt(1337)) {
this.skip();
}

const tx = await gasless.makeProxyTx(
await commentBox.getAddress(),
innercall,
);
await commentGasless('Hello, plain world', true, false);
});

// TODO: https://github.com/oasisprotocol/sapphire-paratime/issues/179
const response = await provider.broadcastTransaction(tx);
await response.wait();
it('Should comment gasless (plain tx, unwrapped client)', async function () {
// Set up sapphire-localnet image to run this test:
// docker run -it -p8544-8548:8544-8548 ghcr.io/oasisprotocol/sapphire-localnet
if ((await ethers.provider.getNetwork()).chainId == BigInt(1337)) {
this.skip();
}

const receipt = await provider.getTransactionReceipt(response.hash);
if (!receipt || receipt.status != 1) throw new Error('tx failed');
await commentGasless('Hello, plain world', true, true);
});
});

0 comments on commit 1fc230e

Please sign in to comment.