Skip to content

Commit

Permalink
adds basic test
Browse files Browse the repository at this point in the history
  • Loading branch information
netbonus committed Apr 21, 2024
1 parent b680f68 commit d688467
Show file tree
Hide file tree
Showing 5 changed files with 294 additions and 3 deletions.
48 changes: 48 additions & 0 deletions forge/src/NegativeAmountHandler.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol";

contract NegativeAmountHandler is EIP712 {
struct Payment {
address to;
int256 amount; // Can be negative to indicate deduction or penalty
uint256 nonce;
}

mapping(address => uint256) public nonces;

// EIP712 Domain Separator initialization in constructor
constructor() EIP712("NegativeAmountHandler", "1") {}

// Function to handle a negative amount logic
function handlePayment(Payment calldata payment, bytes calldata signature) external {
require(_verify(_hash(payment), signature), "Invalid signature");
require(payment.amount < 0, "Amount must be negative");

// Example logic for negative amount handling
// Here we just emit an event, but you could implement any logic you want
emit PaymentHandled(payment.to, payment.amount, msg.sender);

// Increment nonce after handling to prevent replay attacks
nonces[payment.to]++;
}

// Create a hash of the payment details (EIP712 Typed Data)
function _hash(Payment calldata payment) internal view returns (bytes32) {
return _hashTypedDataV4(keccak256(abi.encode(
keccak256("Payment(address to,int256 amount,uint256 nonce)"),
payment.to,
payment.amount,
payment.nonce
)));
}

// Verify the signature
function _verify(bytes32 digest, bytes calldata signature) internal view returns (bool) {
address signer = ECDSA.recover(digest, signature);
return signer == msg.sender && nonces[signer] == payment.nonce;
}

event PaymentHandled(address indexed to, int256 amount, address indexed executor);
}
161 changes: 161 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@
"e2e-sign-solana": "vitest src/__test__/e2e/signing/solana*",
"e2e-sign-unformatted": "vitest src/__test__/e2e/signing/unformatted.test.ts",
"e2e-wj": "vitest src/__test__/e2e/wallet-jobs.test.ts",
"e2e-api": "vitest src/__test__/e2e/api.test.ts"
"e2e-api": "vitest src/__test__/e2e/api.test.ts",
"contracts": "vitest src/__test__/e2e/contracts.test.ts"
},
"files": [
"dist"
Expand Down Expand Up @@ -92,6 +93,7 @@
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.0.0",
"ethereumjs-util": "^7.1.4",
"ethers": "^6.12.0",
"jsonc": "^2.0.0",
"lodash": ">=4.17.21",
"minimist": ">=0.2.1",
Expand Down
74 changes: 74 additions & 0 deletions src/__test__/e2e/contracts.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { Contract, JsonRpcProvider, parseUnits } from 'ethers';

import { Common, Hardfork } from '@ethereumjs/common';
import { TransactionFactory } from '@ethereumjs/tx';
import dotenv from 'dotenv';
import { question } from 'readline-sync';
import { encode as rlpEncode } from 'rlp';
import { pair, sign } from '../..';
import Counter from '../../../forge/out/Counter.sol/Counter.json';
import { setupClient } from '../utils/setup';
dotenv.config();

describe('Counter', async () => {
test('pair', async () => {
const isPaired = await setupClient();
if (!isPaired) {
const secret = question('Please enter the pairing secret: ');
await pair(secret.toUpperCase());
}
});
test('increment', async () => {
const provider = new JsonRpcProvider('http://localhost:8545');
const counter = new Contract(
'0x5FbDB2315678afecb367f032d93F642f64180aa3',
Counter.abi,
provider,
);

const txRequest = {
to: '0x5FbDB2315678afecb367f032d93F642f64180aa3',
from: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
data: counter.interface.encodeFunctionData('increment'),
nonce: await provider.getTransactionCount(
'0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
'latest',
),
gasLimit: 100000,
gasPrice: parseUnits('10', 'gwei'),
};

const common = Common.custom(
{ chainId: 31337 },
{ hardfork: Hardfork.London },
);
const tx = TransactionFactory.fromTxData(txRequest, { common });
const payload = rlpEncode(tx.getMessageToSign(false));

const signedTx = await sign(payload);

const v = `0x${Buffer.from(signedTx.sig.v).toString('hex')}`;
const r = `0x${Buffer.from(signedTx.sig.r).toString('hex')}`;
const s = `0x${Buffer.from(signedTx.sig.s).toString('hex')}`;

const txToBroadcast = TransactionFactory.fromTxData(
{
...txRequest,
v,
r,
s,
},
{ common },
);

const signedTxPayload = txToBroadcast.serialize();

// Broadcast the signed transaction
const txResponse = await provider.broadcastTransaction(
`0x${signedTxPayload.toString('hex')}`,
);
const receipt = await txResponse.wait();

console.log(`Transaction confirmed in block: ${receipt.blockNumber}`);
});
});
10 changes: 8 additions & 2 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@
"typeRoots": ["node_modules/@types", "src/**/types"],
"types": ["node", "jest", "vitest", "vitest/globals"]
},
"exclude": ["node_modules", "**/__test__", "**/*.spec.ts", "**/*.test.ts"],
"include": ["./src"]
"exclude": [
"node_modules",
"**/__test__",
"**/*.spec.ts",
"**/*.test.ts",
"forge"
],
"include": ["./src", "forge"]
}

0 comments on commit d688467

Please sign in to comment.