Skip to content

Commit

Permalink
Create Passkey NPM Project
Browse files Browse the repository at this point in the history
  • Loading branch information
nlordell committed Mar 4, 2024
1 parent b162410 commit ed7d5bd
Show file tree
Hide file tree
Showing 13 changed files with 2,044 additions and 242 deletions.
33 changes: 33 additions & 0 deletions .github/workflows/ci_passkey.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: safe-modules-passkey
on: [push]

jobs:
tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
with:
node-version: 20.x
cache: npm
cache-dependency-path: package-lock.json
- run: npm ci
- run: npm run coverage -w modules/passkey
- uses: coverallsapp/github-action@master
with:
path-to-lcov: modules/passkey/coverage/lcov.info
github-token: ${{ secrets.GITHUB_TOKEN }}
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
with:
node-version: 20.x
cache: npm
cache-dependency-path: package-lock.json
- run: npm ci
- run: npm run lint -w modules/passkey
- run: npm run fmt:check -w modules/passkey
- run: npm run build -w modules/passkey

1 change: 0 additions & 1 deletion modules/4337/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@
"hardhat-deploy": "0.11.45",
"husky": "^9.0.11",
"solc": "^0.8.24",
"solhint": "^4.1.1",
"ts-node": "^10.9.2",
"typescript": "^5.3.3",
"yargs": "^17.7.2"
Expand Down
12 changes: 12 additions & 0 deletions modules/passkey/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
node_modules
.env

# Hardhat files
/build
/typechain
/typechain-types
/coverage
/coverage.json

# TypeScript files
/dist
18 changes: 18 additions & 0 deletions modules/passkey/.solhint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"extends": "solhint:recommended",
"plugins": [],
"rules": {
"compiler-version": "off",
"func-visibility": [
"warn",
{
"ignoreConstructors": true
}
],
"not-rely-on-time": "off",
"reason-string": "off",
"no-empty-blocks": "off",
"avoid-low-level-calls": "off",
"custom-errors": "off"
}
}
13 changes: 13 additions & 0 deletions modules/passkey/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Sample Hardhat Project

This project demonstrates a basic Hardhat use case. It comes with a sample contract, a test for that contract, and a script that deploys that contract.

Try running some of the following tasks:

```shell
npx hardhat help
npx hardhat test
REPORT_GAS=true npx hardhat test
npx hardhat node
npx hardhat run scripts/deploy.ts
```
31 changes: 31 additions & 0 deletions modules/passkey/contracts/Lock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

// Uncomment this line to use console.log
// import "hardhat/console.sol";

contract Lock {
uint256 public unlockTime;
address payable public owner;

event Withdrawal(uint256 amount, uint256 when);

constructor(uint256 _unlockTime) payable {
require(block.timestamp < _unlockTime, "Unlock time should be in the future");

unlockTime = _unlockTime;
owner = payable(msg.sender);
}

function withdraw() public {
// Uncomment this line, and the import of "hardhat/console.sol", to print a log in your terminal
// console.log("Unlock time is %o and block timestamp is %o", unlockTime, block.timestamp);

require(block.timestamp >= unlockTime, "You can't withdraw yet");
require(msg.sender == owner, "You aren't the owner");

emit Withdrawal(address(this).balance, block.timestamp);

owner.transfer(address(this).balance);
}
}
18 changes: 18 additions & 0 deletions modules/passkey/hardhat.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import '@nomicfoundation/hardhat-toolbox'
import type { HardhatUserConfig } from 'hardhat/config'
import 'hardhat-deploy'

const config: HardhatUserConfig = {
paths: {
artifacts: 'build/artifacts',
cache: 'build/cache',
deploy: 'src/deploy',
sources: 'contracts',
},
solidity: '0.8.24',
namedAccounts: {
deployer: 0,
},
}

export default config
46 changes: 46 additions & 0 deletions modules/passkey/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"name": "@safe-global/safe-passkey",
"version": "0.1.0",
"author": "@safe-global",
"description": "Safe Passkey Owner",
"homepage": "https://github.com/safe-global/safe-modules/4337",
"repository": {
"type": "git",
"url": "git+https://github.com/safe-global/safe-modules.git"
},
"bugs": {
"url": "https://github.com/safe-global/safe-modules/issues"
},
"keywords": [
"Ethereum",
"Wallet",
"Safe",
"Safe module"
],
"license": "GPL-3.0",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"files": [
"contracts",
"dist",
"src",
"build"
],
"scripts": {
"build": "npm run build:sol && npm run build:ts",
"build:sol": "hardhat compile",
"build:ts": "npx rimraf dist && tsc",
"coverage": "hardhat coverage",
"fmt": "prettier --write .",
"fmt:check": "prettier --check .",
"lint": "npm run lint:sol && npm run lint:ts",
"lint:sol": "solhint 'contracts/**/*.sol'",
"lint:ts": "eslint ./src && eslint ./test",
"test": "hardhat test"
},
"devDependencies": {
"@nomicfoundation/hardhat-toolbox": "^4.0.0",
"hardhat": "^2.20.1",
"hardhat-deploy": "^0.12.1"
}
}
22 changes: 22 additions & 0 deletions modules/passkey/src/deploy/lock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { time } from '@nomicfoundation/hardhat-toolbox/network-helpers'
import type { DeployFunction } from 'hardhat-deploy/types'

const ONE_YEAR_IN_SECS = 365 * 24 * 60 * 60
const ONE_GWEI = 1_000_000_000

const deploy: DeployFunction = async ({ deployments, getNamedAccounts, ethers }) => {
const { deployer } = await getNamedAccounts()
const { deploy } = deployments

const lockedAmount = ONE_GWEI
const unlockTime = (await time.latest()) + ONE_YEAR_IN_SECS

await deploy('Lock', {
from: deployer,
args: [unlockTime],
value: ethers.toBeHex(lockedAmount),
log: true,
})
}

export default deploy
94 changes: 94 additions & 0 deletions modules/passkey/test/Lock.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { time } from '@nomicfoundation/hardhat-toolbox/network-helpers'
import { anyValue } from '@nomicfoundation/hardhat-chai-matchers/withArgs'
import { expect } from 'chai'
import { deployments, ethers } from 'hardhat'

describe('Lock', function () {
const setupTests = deployments.createFixture(async ({ deployments }) => {
const { Lock } = await deployments.fixture()

const [owner, otherAccount] = await ethers.getSigners()
const lock = await ethers.getContractAt('Lock', Lock.address)

const unlockTime = await lock.unlockTime()
const lockedAmount = await ethers.provider.getBalance(lock)

return { lock, unlockTime, lockedAmount, owner, otherAccount }
})

describe('Deployment', function () {
it('Should set the right unlockTime', async function () {
const { lock, unlockTime } = await setupTests()

expect(await lock.unlockTime()).to.equal(unlockTime)
})

it('Should set the right owner', async function () {
const { lock, owner } = await setupTests()

expect(await lock.owner()).to.equal(owner.address)
})

it('Should receive and store the funds to lock', async function () {
const { lock, lockedAmount } = await setupTests()

expect(await ethers.provider.getBalance(lock.target)).to.equal(lockedAmount)
})

it('Should fail if the unlockTime is not in the future', async function () {
// We don't use the fixture here because we want a different deployment
const latestTime = await time.latest()
const Lock = await ethers.getContractFactory('Lock')
await expect(Lock.deploy(latestTime, { value: 1 })).to.be.revertedWith('Unlock time should be in the future')
})
})

describe('Withdrawals', function () {
describe('Validations', function () {
it('Should revert with the right error if called too soon', async function () {
const { lock } = await setupTests()

await expect(lock.withdraw()).to.be.revertedWith("You can't withdraw yet")
})

it('Should revert with the right error if called from another account', async function () {
const { lock, unlockTime, otherAccount } = await setupTests()

// We can increase the time in Hardhat Network
await time.increaseTo(unlockTime)

// We use lock.connect() to send a transaction from another account
await expect(lock.connect(otherAccount).withdraw()).to.be.revertedWith("You aren't the owner")
})

it("Shouldn't fail if the unlockTime has arrived and the owner calls it", async function () {
const { lock, unlockTime } = await setupTests()

// Transactions are sent using the first signer by default
await time.increaseTo(unlockTime)

await expect(lock.withdraw()).not.to.be.reverted
})
})

describe('Events', function () {
it('Should emit an event on withdrawals', async function () {
const { lock, unlockTime, lockedAmount } = await setupTests()

await time.increaseTo(unlockTime)

await expect(lock.withdraw()).to.emit(lock, 'Withdrawal').withArgs(lockedAmount, anyValue) // We accept any value as `when` arg
})
})

describe('Transfers', function () {
it('Should transfer the funds to the owner', async function () {
const { lock, unlockTime, lockedAmount, owner } = await setupTests()

await time.increaseTo(unlockTime)

await expect(lock.withdraw()).to.changeEtherBalances([owner, lock], [lockedAmount, -lockedAmount])
})
})
})
})
12 changes: 12 additions & 0 deletions modules/passkey/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"compilerOptions": {
"target": "es2020",
"module": "commonjs",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"outDir": "./dist",
"strict": true,
"skipLibCheck": true,
"resolveJsonModule": true
}
}
Loading

0 comments on commit ed7d5bd

Please sign in to comment.