SocialLock is a smart contract based, decentralized, non-custodial lockbox that allows users to deposit ETH that can only be accessed by a recipient's Google account.
It works by storing a keccak256 hash of the recipient's email address in the SocialLock.sol
contract and holding a balance for it. The recipient can withdraw ETH from the lockbox by providing a valid Google JWT that contains the recipient email address. In other words, the only way to withdraw ETH from the lockbox is to provide a valid JWT (signed by Google), and thus to have access to the recipient's Google account.
Google rotates public keys every 48 hours which is why an oracle is needed to fetch the latest public keys. The JWKSOracle.sol
uses Chainlink to fetch the latest public keys from Google and store them in the contract. SocialLock.sol
uses JWKSOracle.sol
to verify the JWT.
For more info, check out this medium article.
- Clone this repo
npm i
npx hardhat test
The tests are based on a hardcoded JWT. The values for aud
, kid
, and n
(RSA Modulus) are calculated based on this hardcoded JWT. For a live implementation, use the Oracle implementation in JWKSOracle.sol
.
The smart contract deployment always follows the same pattern: Start with the JWKSOracle.sol
, the one that will retrieve the RSA public keys from Google, and then deploy the SocialLock.sol
contract. For the JWKSOracle.sol
you have two options: Use the ChainLink oracle or hardcode the public keys. Google's latest keys can be found here: https://www.googleapis.com/oauth2/v3/certs
For the oracle to work you need to be on a network that supports ChainLink. Our testnet oracle is deployed on Sepolia.
npx hardhat run scripts/deploy_JWKSOracle.ts --network <network>
(make sure to update the LINK token address in the contract in case your network is not sepolia)npx hardhat verify --network <network> <contract_address>
(verification is optional and only needed for easier interaction on Etherscan)- Fund the
JWKSOracle
contract with LINK. Use https://faucets.chain.link/sepolia to get LINK. - After funding the contract, call the
requestJWKS()
function. The easiest way to do this is via Etherscan. Each call torequestJWKS()
will cost 0.15 LINK. - The ChainLink Oracle will automatically call the
fullfill()
function for you. - Continue with SocialLock deployment
- Start a local node
npx hardhat node
npx hardhat run scripts/deploy_jwks.ts --network <network>
- Continue with SocialLock deployment
You need your aud
and jwks
values before deploying the SocialLock
contract.
- Deploy SocialLock using the hardhat task
npx hardhat deploySocialLock --aud <your_aud> --jwks <jwks_contract_address> --network <network>
- Verify the contract on Etherscan (optional)
npx hardhat verify --network <network> <contract_address> <your_aud> <jwks_contract_address>
Follow the official Google documentation to get your aud
value.
- Go to https://console.cloud.google.com/apis/credentials
- Create a new project
- Create a new OAuth client ID
- Select
Web application
as the application type - Enter
http://localhost:5173/
(or whatever your local URL is) as the authorized redirect URI - Click
Create
- Copy the
Client ID
value - It should look like this
1234567890-1234567890.apps.googleusercontent.com
. This valued is referred to asclientID
in the frontend, and it will be included as theaud
value in your JWT.
cd frontend
- Rename
.env.example
to.env.local
- Copy/paste the SocialLock contract address into
frontend/.env.local
- Copy/paste your Google
clientID
intofrontend/.env.local
npm i
npm run dev
- JWKSOracle: 0xC8af9d4254E8301D98f6D19bBBD06b06BD352Acb
- SocialLock: 0xEB75d68DA86f3550277074984AaB5aEC10Ceb48D
- ETH Deposit: 0x62f00b31b5e1826af1e6d8927a936924c90d109a236247689cb52b685c6bba94
- ETH Withdrawal: 0x1a17163b7187db3b87ff764de8a86e3d63abe9a9c0e5148cce5235cde64e8b97
- Token Deposit: 0x47ada14e65fa486afb3285b0d4c44fefb54400b755087f7c9b9f2caba60896cb
- Token Withdrawal: 0xbe15bab02f04d63dbc33fb2e258b05b5ff496099d00b819953a087d1f30908d3
- "JWKS not found" -> make sure to call the
requestJWKS()
function on theJWKSOracle
contract - "RSA signature invalid" -> make sure to use the correct
aud
value. Theaud
value is theclientID
value from your Google OAuth client ID. It should look like this1234567890-1234567890.apps.googleusercontent.com
. - "Nonce does not match sender" -> The account that triggers the recovery is included in the JWT. Make sure to send the withdraw transaction from the same account that is included in the JWT.
- Huge shoutout to @spalladino and OpenZeppelin for creating the Google Identity Contract and solidity-jwt which was the inspiration for this project.
- Special thanks to Mathias from https://glink.solutions/ for creating and hosting the Chainlink oracle job
- @hir0min for creating the Base64Url library. It would not have been possible to decode the JWKS keys without it. I had to change a single line to make it work hir0min/solidity-base64#1
- ChainLink