Skip to content

Commit

Permalink
Unidirectional payment channel
Browse files Browse the repository at this point in the history
  • Loading branch information
antico5 committed Jan 5, 2022
1 parent 637eda7 commit fc37723
Show file tree
Hide file tree
Showing 10 changed files with 45,774 additions and 0 deletions.
2 changes: 2 additions & 0 deletions unidirectional_payment_channel/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALCHEMY_API_KEY=
ROPSTEN_PRIVATE_KEY=
8 changes: 8 additions & 0 deletions unidirectional_payment_channel/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
node_modules
.env

#Hardhat files
cache
artifacts
dist
typechain
16 changes: 16 additions & 0 deletions unidirectional_payment_channel/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"overrides": [
{
"files": "*.sol",
"options": {
"printWidth": 120,
"tabWidth": 2,
"useTabs": false,
"singleQuote": false,
"bracketSpacing": false,
"explicitTypes": "always",
"semi": true
}
}
]
}
52 changes: 52 additions & 0 deletions unidirectional_payment_channel/contracts/PaymentChannel.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.7;

import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";

contract PaymentChannel is ReentrancyGuard {
using ECDSA for bytes32;

address payable public payer;
address payable public receiver;

uint256 private constant DURATION = 7 * 24 * 60 * 60;
uint256 public immutable expiresAt;

constructor(address payable _receiver) {
require(_receiver != address(0), "receiver is zero address");
payer = payable(msg.sender);
receiver = _receiver;
expiresAt = block.timestamp + DURATION;
}

receive() external payable {}

function getHash(uint256 amount) public view returns (bytes32) {
return keccak256(abi.encodePacked(address(this), amount));
}

function getEthSignedHash(uint256 amount) public view returns (bytes32) {
return getHash(amount).toEthSignedMessageHash();
}

function verify(uint256 amount, bytes memory signature) public view returns (bool) {
return getEthSignedHash(amount).recover(signature) == payer;
}

function close(uint256 amount, bytes memory signature) external nonReentrant {
require(msg.sender == receiver, "!receiver");
require(verify(amount, signature), "invalid signature");

receiver.transfer(amount);

selfdestruct(payer);
}

function cancel() external {
require(msg.sender == payer, "!payer");
require(block.timestamp >= expiresAt, "channel not expired yet");

selfdestruct(payer);
}
}
56 changes: 56 additions & 0 deletions unidirectional_payment_channel/hardhat.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { config as dotenvConfig } from "dotenv";
import "@typechain/hardhat";
import "@nomiclabs/hardhat-ethers";
import "@nomiclabs/hardhat-waffle";
import "@nomiclabs/hardhat-etherscan";
import "solidity-coverage";
import "hardhat-gas-reporter";
import { HardhatUserConfig } from "hardhat/types/config";

dotenvConfig();

const gasPrice = parseInt(process.env.GAS_PRICE || "1000000000");

const config: HardhatUserConfig = {
solidity: {
compilers: [
{
version: "0.8.10",
settings: {
optimizer: {
enabled: true,
runs: 200,
},
},
},
],
},
networks: {
hardhat: {
initialBaseFeePerGas: 0,
},
rinkeby: {
url: process.env.RINKEBY_URL || "",
accounts:
process.env.PRIVATE_KEY !== undefined ? [process.env.PRIVATE_KEY] : [],
gasPrice,
},
mainnet: {
url: process.env.MAINNET_URL || "",
accounts:
process.env.PRIVATE_KEY !== undefined ? [process.env.PRIVATE_KEY] : [],
gasPrice,
},
},
gasReporter: {
enabled: process.env.REPORT_GAS !== undefined,
currency: "USD",
gasPrice: 120,
coinmarketcap: process.env.COINMARKETCAP_API_KEY,
},
etherscan: {
apiKey: process.env.ETHERSCAN_API_KEY,
},
};

export default config;
Loading

0 comments on commit fc37723

Please sign in to comment.