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 inpx 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
JWKSOraclecontract 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 applicationas the application type - Enter
http://localhost:5173/(or whatever your local URL is) as the authorized redirect URI - Click
Create - Copy the
Client IDvalue - It should look like this
1234567890-1234567890.apps.googleusercontent.com. This valued is referred to asclientIDin the frontend, and it will be included as theaudvalue in your JWT.
cd frontend- Rename
.env.exampleto.env.local - Copy/paste the SocialLock contract address into
frontend/.env.local - Copy/paste your Google
clientIDintofrontend/.env.local npm inpm 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 theJWKSOraclecontract - "RSA signature invalid" -> make sure to use the correct
audvalue. Theaudvalue is theclientIDvalue 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