-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
294 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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}`); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters