Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom Auth EIP-1271 example #44

Draft
wants to merge 25 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
36d3ceb
chore: forge init
spacesailor24 Aug 6, 2024
6d27b60
forge install: forge-std
spacesailor24 Aug 6, 2024
a298523
Init example
spacesailor24 Aug 6, 2024
d0c0375
Revert to siweMessageHash
spacesailor24 Aug 6, 2024
411fa42
WIP
spacesailor24 Aug 21, 2024
f7dd510
Merge branch 'master' into wyatt/eip-1271-contract
spacesailor24 Aug 22, 2024
8352ac0
Finish implementation
spacesailor24 Aug 22, 2024
24274ef
Remove old index file
spacesailor24 Aug 22, 2024
df53b0f
Update ENV example
spacesailor24 Aug 22, 2024
254ac11
Move getPkpInfoFromMintReceipt to utils file
spacesailor24 Aug 22, 2024
8fad76f
Use LIT_RPC_URL
spacesailor24 Aug 22, 2024
55f04b9
Remove unused ENV. Update test descriptions
spacesailor24 Aug 23, 2024
08be47e
Init README
spacesailor24 Aug 23, 2024
487a852
Remove .github dir from contracts dir
spacesailor24 Aug 23, 2024
b2786db
Update eip-1271/nodejs/README.md
spacesailor24 Aug 23, 2024
81b89d9
Update eip-1271/nodejs/README.md
spacesailor24 Aug 23, 2024
ac2bedb
Update eip-1271/nodejs/README.md
spacesailor24 Aug 23, 2024
bb9dd05
Format ABI
spacesailor24 Aug 23, 2024
ec6cd55
Fix ethers import
spacesailor24 Aug 23, 2024
52dbb29
Add missing import
spacesailor24 Aug 23, 2024
08b904d
Merge remote-tracking branch 'origin/wyatt/eip-1271-contract' into wy…
spacesailor24 Aug 23, 2024
d1dc4cc
Bump Lit package versions
spacesailor24 Aug 23, 2024
c210d19
Bump balance threshold for multisig wallets
spacesailor24 Aug 23, 2024
004b945
WIP encryption test
spacesailor24 Aug 23, 2024
6661b93
Merge branch 'master' into wyatt/eip-1271-contract
spacesailor24 Sep 13, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[submodule "eip-1271/contracts/lib/forge-std"]
path = eip-1271/contracts/lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "custom-auth-telegram-example"]
path = custom-auth-telegram-example
url = https://github.com/LIT-Protocol/custom-auth-telegram-example.git
14 changes: 14 additions & 0 deletions eip-1271/contracts/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Compiler files
cache/
out/

# Ignores development broadcast logs
!/broadcast
/broadcast/*/31337/
/broadcast/**/dry-run/

# Docs
docs/

# Dotenv file
.env
66 changes: 66 additions & 0 deletions eip-1271/contracts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
## Foundry

**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.**

Foundry consists of:

- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools).
- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data.
- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network.
- **Chisel**: Fast, utilitarian, and verbose solidity REPL.

## Documentation

https://book.getfoundry.sh/

## Usage

### Build

```shell
$ forge build
```

### Test

```shell
$ forge test
```

### Format

```shell
$ forge fmt
```

### Gas Snapshots

```shell
$ forge snapshot
```

### Anvil

```shell
$ anvil
```

### Deploy

```shell
$ forge script script/Counter.s.sol:CounterScript --rpc-url <your_rpc_url> --private-key <your_private_key>
```

### Cast

```shell
$ cast <subcommand>
```

### Help

```shell
$ forge --help
$ anvil --help
$ cast --help
```
6 changes: 6 additions & 0 deletions eip-1271/contracts/foundry.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[profile.default]
src = "src"
out = "out"
libs = ["lib"]

# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options
1 change: 1 addition & 0 deletions eip-1271/contracts/lib/forge-std
Submodule forge-std added at 1714be
28 changes: 28 additions & 0 deletions eip-1271/contracts/script/WhitelistEIP1271.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "forge-std/Script.sol";
import "../src/WhitelistEIP1271.sol";

contract DeployWhitelistEIP1271 is Script {
function run() external {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(deployerPrivateKey);

// Sample whitelist addresses (replace with actual addresses if needed)
address[] memory whitelistedAddresses = new address[](3);
whitelistedAddresses[0] = 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266;
whitelistedAddresses[1] = 0x70997970C51812dc3A010C7d01b50e0d17dc79C8;
whitelistedAddresses[2] = 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC;

// Set the signature threshold
uint256 signatureThreshold = 2;

// Deploy the contract
WhitelistEIP1271 whitelistContract = new WhitelistEIP1271(whitelistedAddresses, signatureThreshold);

console.log("WhitelistEIP1271 deployed at:", address(whitelistContract));

vm.stopBroadcast();
}
}
95 changes: 95 additions & 0 deletions eip-1271/contracts/src/WhitelistEIP1271.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

contract WhitelistEIP1271 {
mapping(address => bool) public whitelist;
address[] public whitelistedAddresses;
mapping(bytes32 => bytes) public signatures;
uint256 public immutable SIGNATURE_THRESHOLD;

constructor(address[] memory _addresses, uint256 _threshold) {
for (uint256 i = 0; i < _addresses.length; i++) {
whitelist[_addresses[i]] = true;
whitelistedAddresses.push(_addresses[i]);
}
SIGNATURE_THRESHOLD = _threshold;
}

function isWhitelisted(address _address) public view returns (bool) {
return whitelist[_address];
}

function getWhitelistedAddresses() public view returns (address[] memory) {
return whitelistedAddresses;
}

function signTx(bytes32 _txHash, bytes memory _signature) public returns (bool) {
require(_signature.length == 65, "Invalid signature length");

bytes32 r;
bytes32 s;
uint8 v;

assembly {
r := mload(add(_signature, 32))
s := mload(add(_signature, 64))
v := byte(0, mload(add(_signature, 96)))
}

if (v < 27) {
v += 27;
}

address signer = ecrecover(_txHash, v, r, s);
require(signer != address(0), "ECDSA: invalid signature");
require(whitelist[signer], "Signer not whitelisted");

if (signatures[_txHash].length == 0) {
signatures[_txHash] = _signature;
} else {
signatures[_txHash] = abi.encodePacked(signatures[_txHash], _signature);
}

return true;
}

function isValidSignature(bytes32 _hash, bytes memory _signatures) public view returns (bytes4) {
if (_signatures.length == 0) {
return 0xffffffff; // No signatures provided
}

if (_signatures.length % 65 != 0) {
return 0xffffffff; // Invalid signatures length
}

uint256 signatureCount = _signatures.length / 65;
uint256 whitelistedSignerCount = 0;

for (uint256 i = 0; i < signatureCount; i++) {
bytes32 r;
bytes32 s;
uint8 v;

assembly {
r := mload(add(_signatures, add(32, mul(i, 65))))
s := mload(add(_signatures, add(64, mul(i, 65))))
v := byte(0, mload(add(_signatures, add(96, mul(i, 65)))))
}

if (v < 27) {
v += 27;
}

address signer = ecrecover(_hash, v, r, s);
if (signer != address(0) && whitelist[signer]) {
whitelistedSignerCount++;
}
}

if (whitelistedSignerCount >= SIGNATURE_THRESHOLD) {
return 0x1626ba7e; // bytes4(keccak256("isValidSignature(bytes32,bytes)")
} else {
return 0xffffffff; // Not enough valid signatures
}
}
}
94 changes: 94 additions & 0 deletions eip-1271/contracts/test/WhitelistEIP1271.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "forge-std/Test.sol";
import "../src/WhitelistEIP1271.sol";

contract WhitelistEIP1271Test is Test {
WhitelistEIP1271 public whitelistContract;
uint256 constant SIGNATURE_THRESHOLD = 2;
address[] whitelistedAddresses;
uint256[] privateKeys;

function setUp() public {
// Generate some test addresses and their private keys
for (uint i = 0; i < 3; i++) {
uint256 privateKey = uint256(keccak256(abi.encodePacked("test key", i)));
address addr = vm.addr(privateKey);
whitelistedAddresses.push(addr);
privateKeys.push(privateKey);
}

whitelistContract = new WhitelistEIP1271(whitelistedAddresses, SIGNATURE_THRESHOLD);
}

function testWhitelisting() public {
for (uint i = 0; i < whitelistedAddresses.length; i++) {
assertTrue(whitelistContract.isWhitelisted(whitelistedAddresses[i]));
}
assertFalse(whitelistContract.isWhitelisted(address(0xdead)));
}

function testGetWhitelistedAddresses() public {
address[] memory returnedAddresses = whitelistContract.getWhitelistedAddresses();
assertEq(returnedAddresses.length, whitelistedAddresses.length);
for (uint i = 0; i < whitelistedAddresses.length; i++) {
assertEq(returnedAddresses[i], whitelistedAddresses[i]);
}
}

function testIsValidSignature() public {
// Create a sample transaction
address to = address(0x1234567890123456789012345678901234567890);
uint256 value = 1 ether;
bytes memory data = abi.encodeWithSignature("transfer(address,uint256)", address(0xdead), 100);
uint256 nonce = 1;
uint256 gasPrice = 20 gwei;
uint256 gasLimit = 21000;
uint256 chainId = 1;

// Serialize the transaction
bytes memory serializedTx = abi.encode(
to,
value,
data,
nonce,
gasPrice,
gasLimit,
chainId
);

// Hash the serialized transaction
bytes32 txHash = keccak256(serializedTx);

bytes memory signatures = new bytes(0);

// Test with no signatures
assertEq(whitelistContract.isValidSignature(txHash, signatures), bytes4(0xffffffff));

// Test with one signature (below threshold)
(uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKeys[0], txHash);
signatures = abi.encodePacked(r, s, v);
assertEq(whitelistContract.isValidSignature(txHash, signatures), bytes4(0xffffffff));

// Test with two signatures (at threshold)
(v, r, s) = vm.sign(privateKeys[1], txHash);
signatures = abi.encodePacked(signatures, r, s, v);
assertEq(whitelistContract.isValidSignature(txHash, signatures), bytes4(0x1626ba7e));

// Test with three signatures (above threshold)
(v, r, s) = vm.sign(privateKeys[2], txHash);
signatures = abi.encodePacked(signatures, r, s, v);
assertEq(whitelistContract.isValidSignature(txHash, signatures), bytes4(0x1626ba7e));
}

function testInvalidSignature() public {
bytes32 messageHash = keccak256("Hello, World!");
uint256 nonWhitelistedPrivateKey = uint256(keccak256(abi.encodePacked("non-whitelisted key")));

(uint8 v, bytes32 r, bytes32 s) = vm.sign(nonWhitelistedPrivateKey, messageHash);
bytes memory signatures = abi.encodePacked(r, s, v);

assertEq(whitelistContract.isValidSignature(messageHash, signatures), bytes4(0xffffffff));
}
}
8 changes: 8 additions & 0 deletions eip-1271/nodejs/.env.example
spacesailor24 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
ETHEREUM_PRIVATE_KEY=

LIT_CAPACITY_CREDIT_TOKEN_ID=

ANVIL_PRIVATE_KEY_1=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
ANVIL_PRIVATE_KEY_2=0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d

DEPLOYED_EIP1271_WHITELIST_CONTRACT=0x54a772813Df0E75f20A0984f31D1400991eD6a33
4 changes: 4 additions & 0 deletions eip-1271/nodejs/.mocharc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"$schema": "https://json.schemastore.org/mocharc.json",
"require": "tsx"
}
Loading