Goldfinch is a decentralized lending protocol built on the blockchain. This is a monorepo containing Goldfinch's smart contracts, web3 frontend and other supporting code.
You will need the correct version of node/npm on your local machine.
Using nvm, you can do this with nvm install 12.18.3
. If you don't have nvm
, see here for installation instructions.
The repository is organized as a monorepo using lerna. Run the following to install lerna and then use it to install all package dependencies:
# Just the first time
npm install
npm run bootstrap
From here on out, every time you pull the repo and any packages change, you'll need to run
npm install
# Note use lerna bootstrap, and not npm run bootstrap. It's much faster
npx lerna bootstrap
All contracts are located under packages/protocol/contracts
- Make your changes
- Write tests, which should be placed under
packages/protocol/test
- There are two kinds of tests. "Regular" (all local state) and "mainnet forking" (uses state from mainnet). They are located in different folders. Sometimes you write both for the same feature. Use your judgement depending on the change.
- Write great commit messages, and put up your PR!
npm run start:local
- The simplest way to get going. All fresh, local state.
npm run start
- This will run a local, mainnet-forked blockchain. Extremely useful for certain changes.
- Requires an Alchemy API key. Sign up for free at https://www.alchemy.com/. To use it, see the one-time setup below.
Both options will start several processes, including your local blockchain and front-end server, which will pop up on http://localhost:3000. It takes a min to spin up.
-
Ensure you have Java installed (Firebase emulator requires the JVM)
-
Copy
.env.example
to.env.local
(the local will be ignored by git). -
Add the following into your new
.env.local
file. Our local dev scripts will use these vars to automatically send you test ETH, and give you a credit line and USDC to play with.ALCHEMY_API_KEY={your alchemy api key} TEST_USERS={your metamask address}` ALLOWED_SENDERS={your metamask address}`
-
If you want the
client
to use variables in your.env.local
, create a symlink to this file from inside thepackages/client
dir, or else create a separatepackages/client/.env.local
file.
Changes to the frontend should be automatically hotloaded using react-refresh.
Changes to smart contracts will require re-compiling and re-deploying. You can do this by re-running your start command.
npm run start:no-gasless
is available if gasless transactions are giving you trouble, or if you're having trouble finding the borrower contract address.
*Note When running with start:local
, the Fake USDC address that we create will also not be visible to Metamask by default. So you'll need to add this as well
by looking at the terminal output of the @goldfinch-eng/protocol
start command. Search "USDC Address", and you should see something. Take that address, and
then go to Add Token
in Metamask, and paste it in there. Your fake USDC balance should show up.
packages/
: Contains all typescript packages and contracts.protocol/
(@goldfinch-eng/protocol
): Solidity smart contracts and tests.client/
(@goldfinch-eng/client
): Web3 frontend using React.functions/
(@goldfinch-eng/functions
): Google cloud functions to support KYC and other server-side functionality.autotasks/
(@goldfinch-eng/functions
): Defender Autotasks and Relay code for supporting gasless transactions and triggering periodic on-chain calls.utils/
(@goldfinch-eng/utils
): Generally useful utilities that are shared across packages.
murmuration/
: Provisioning scripts for our cloud staging environment, called Murmuration.
We have the ability to debug/profile local transactions via Tenderly. To do this, get hold of a transaction hash and then run:
# Ensure tenderly-cli is installed via `brew tap tenderly/tenderly && brew install tenderly`
# And run this from the protocol directory
tenderly export --force <txhash>
To get a local transaction, run the app as normal, and make the transaction via the frontend, and get the hash from metamask after it's confirmed.
To get a test transaction, write a MainnetForking test, log the transaction hash in the test. Then run the mainnet forking test via:
# Run from the protocol directory
npm run test:tenderly
Pick up the transaction hash from the output of the test and run export as above
- See the
CONTRIBUTING.MD
- See the
SECURITY.MD
To support gasless transactions, we need to collect the signature from the client, perform some server side checks to validate
the sender and receiving contract (e.g. it's a known borrower interacting with our borrower contracts, we don't want to subsidize any arbitrary transaction).
We use Defender Relay to do this. For local development, we mimic this by running a local server that executes the gasless logic.
We've configured webpack to proxy to this server when running npm run start
for local development. If you're having problems with gasless transactions, make sure you've added your address to .env.local
's ALLOWED_SENDERS
.
- Run
npm test
to run tests for all packages. - Note if you want to only run tests for a particular test, then use
it.only
ordescribe.only
inside the test file itself, which will focus to only those tests. - If you want to run tests for a specific package, say just the protocol contracts, you can use lerna's
--scope
flag e.g.npm run test -- --scope @goldfinch-eng/protocol
.
- Start the app
- Connect with an account that is not golisted and no UID
- Navigate to
/verify
- Use the DevTools and press the
kyc
and setUS
- Use the DevTools to fund yourself with eth
- Refresh page, you should now be able to Create UID
Generally speaking, you shouldn't need to do this, since the test command automatically compiles. But if you need to independently compile, you can run:
npm run build
This will run npm run build
in all packages in the monorepo, including compiling the contracts.
Lerna provides commands to help run common tasks across the monorepo. Some of these commands have been encapsulated as scripts in the top-level package.json
. For example, when we run npm run test
, the underlying command is npx lerna run tests
. This automatically runs npm run test
in all subpackages that define an test
npm script.
We can run lerna for a subset of packages using globs. For example, to run tests for both the protocol and client (in parallel and with output), we can run:
npx lerna run test --stream --no-sort --scope "@goldfinch-eng/@(protocol|client)"
If we prefer using the top-level package.json scripts, we can achieve the same thing by running the following:
npm run test -- --no-sort --scope "@goldfinch-eng/@(protocol|client)"
For more info, have a look at npx lerna run -h
and npx lerna exec -h
.
Contract deployment is handled automatically through the npm run start
command, using hardhat-deploy and
custom build scripts in packages/protocol/blockchain_scripts
.
Right now, we (sort-of) support Rinkeby testnet. We are already deployed there. However, it's not used much. Re-running deployment on Rinkeby is idempotent. But if we want to blow away the existing deployments for whatever reason, we can do the following:
Redeploy with: TEST_USERS={YOUR_METAMASK_ADDRESS} npx buidler deploy --network {rinkeby} --export-all ./config/deployments.json --reset
Generally speaking, we only use Rinkeby to test deployment scripts in a more "real" setting. But we default to using mainnet forking for testing.
Contracts are already deployed to mainnet. We write custom scripts to do upgrades or deploy new contracts.
Front-end blockchain development is still early, and has rough edges. Here are some issues you might run into. If you see others, please add them here!
Authorization required
Make sure you have your Alchemy API key set in.env.local
Cannot set headers of undefined
. If you see this on the front-end, and the whole app blew up, then try switching your metamask off of the current network, and then back again (eg. to Ropsten and then back to Localhost)Error: [ethjs-rpc] rpc error with payload
. This may look like a failed transaction, and Metamask is just throwing some random error with no help. If you're pretty sure everything should be fine, then try to shut down your local server, restart it, and then before you try any transactions, reset your Metamask account, and switch away and back to the local network (eg. local -> rinkeby -> local). To reset your Metamask account, click Metamask --> Settings --> Advanced --> Reset Account. This is fast and painless- If Metamask is unable to / times-out while trying to connect to Localhost 8545:
rm deployments/localhost
, and then re-runningnpm run start:local
, was observed to fix this problem and enable Metamask to connect. Error: a provider or signer is needed to resolve ENS names
. You probably have an undefined address somewhere. But generally it means Ethers doesn't understand the address and is trying to interpret it as an ENS address.