Interoperable Lending with Zero Knowledge Proofs

There has been a growing interest in using zero-knowledge proofs in the financial industry in recent years. These cryptographic primitives allow one party to prove the validity of a statement to another party without revealing any additional information. This can potentially revolutionize how we think about privacy and security concerning blockchain transactions.

One area where zero-knowledge proofs could have a significant impact is in lending protocols. By leveraging off-chain data, identity, and credit score alongside key on-chain metrics, it is possible to offer more customizable loans to users while maintaining their privacy and security.

This article will explore the potential of zero-knowledge proofs in lending protocols and provide a tutorial on implementing interoperable lending using SnarkyJS on the Mina Protocol. We will also discuss this exciting technology's current challenges and future outlook.

More on Zero-Knowledge Proofs

A simple way to understand zero-knowledge proofs is through the find Waldo example, but let's consider a different scenario. Imagine you have a job as a smart-contract auditor, and you want to prove that a smart contract has vulnerabilities without revealing the specific vulnerabilities. One way to do this could be to generate a Merkle tree with the root hash derived from the execution steps of the smart contract. You could also create a witness, a point in the execution steps where a storage slot was maliciously overwritten. By sharing the root hash and witness with the verifier, you could prove that the smart contract has vulnerabilities without revealing the full details of how the vulnerability was exploited.

In simple terms, zero-knowledge proofs allow one party, known as the prover, to prove the validity of a statement to another party, known as the verifier, without revealing any additional information beyond the fact that the message is valid.

One widely used zero-knowledge proof system is zk-SNARKs (Zero-Knowledge Succinct Non-Interactive Argument of Knowledge), used in protocols such as Mina.

Lending Protocols and Zero-Knowledge Technology

In leading lending protocols like Aave, the collateral-to-loan ratio can be as high as 200%, depending on the asset, far from the trust established by traditional financial institutions or banks. Blockchains have the potential to make loans more accessible, but this is hindered by the anonymity inherent in the blockchain.

Zero-knowledge technology can be used in lending protocols by leveraging off-chain storage, identity, and credit rating combined with key on-chain metrics such as wallet activity. This allows users to prove their creditworthiness without revealing sensitive information such as their identity or financial history.

For example, imagine Peter wants to borrow money from a lending protocol. Instead of giving out his personal and financial information, he could use zero-knowledge proof to prove he meets their credit requirements. The protocol could then use this proof to check Peter's creditworthiness and offer him a loan without seeing his personal information.

This approach has several benefits, including interoperability. It allows users to maintain their privacy while still being able to access financial services.

SnarkyJS and the Mina Protocol

SnarkyJS is a TypeScript library for building zk programs using zk-SNARKs. It can be used to write smart contracts based on zero-knowledge proofs for the Mina Protocol, a Layer 1 blockchain that uses zero-knowledge proofs to enable scalable, secure, and decentralized applications.

With SnarkyJS, developers can write zkApps that execute client-side and have private inputs. The library is designed to be easy to use and can be used in major web browsers and Node.js.

One of the unique features of the Mina Protocol is its use of compressed on-chain storage, which allows the entire blockchain to be just 22kb in size. This is achieved by relegating expensive computations to the client side, reducing the amount of data that needs to be stored on-chain.

Getting Started:

This article focuses on interoperability and will walk you through composing interoperable lending using SnarkyJS (not building an actual zkApp).

Set up your development environment.

To start, you must set up your development environment by installing the necessary tools, including Node.js, TypeScript, and the Mina zkApp CLI.

# for windows
npm install -g zkapp-cli

# for linux/osx
sudo npm install -g zkapp-cli

Create a new zkApp

create a new project:

zk project <name_of_project>

You will be given the option to select a UI Framework, you can choose none

ZkApp Logic

After our setup, we must consider utilising three bedrock items to compose our zkApp (identity, credit scoring, and on-chain metrics).

With off-chain computation, we can implement API's client side and then generate proof of the data to be submitted on-chain.

Identity

We can use any API for this, such as custom identity API built on persona.

Example API implementation would be:

router.get('/identity', async (req, res) => {
    // Replace with your Persona API key and user ID
    const apiKey = 'YOUR_PERSONA_API_KEY';
    const {userId} = request.params;

    // Send a GET request to the Persona API to retrieve the user's identity details
    const response = await axios.get(`https://api.withpersona.com/v1/identities/${userId}`);

    // Return the identity details in the response
    res.json(response.data);
});

We can customize the endpoint to manipulate the data and return a simplified JSON Like:

{
    identityNum: <number>,
    validUntil: <number>,
    ...importantOpts
}

Off-Chain Score

How do we get the credit score? Several countries may have a way to fetch that, so we may implement a single route that returns a score.

router.get('/score', async (req, res) => {
 
    const {userId} = request.params;

    // Send a GET request to the Persona API to retrieve the user's identity details
    const response = await axios.get(`https://<score provider>${userId}`);

    // Return the off-chain score in the response
    // assuming score is included in the object
    res.json(response.data.score);
});

On-Chain Metrics

The third part is on-chain metrics. On-chain metrics are simply inferences gained from wallet activities example: Tokens owned, NFT owned, Wallet Age, Transaction Volume etc. We can use Nomis, a wallet-scoring solution, to get a single score representing these metrics.

For that, we may have to implement a wallet-score route.

router.get('/wallet-score', async (req, res) => {

    const {wallet_address} = request.params;

   // fetch score from nomis
   
    // assuming score is included in the object
    res.json(response.data.score);
});

With these three routes, we can implement an oracle in our zkApp to submit proof that this data is correct.

Lending Oracle

For a zk oracle that uses this off-chain data, we can model our smart contract to verify the accuracy of the data from our API.

// oracle contract written with snarkyjs
import { Field, UInt64, SmartContract, state, State, method, Permissions, PublicKey, Signature, AccountUpdate, PrivateKey, Struct, } from 'snarkyjs';

export const ORACLE_PUBLIC_KEY =
  'B62qp2oQ8LS4qzXAhQXBrurVowJpEwD4coYvejU4FvZGVDeFf1AMRES';

export class Oracle extends SmartContract {
  @state(PublicKey) oraclePublicKey = State<PublicKey>();
  @state(Field) minWorthy = State<Field>();

  init() {
    super.init();
    this.account.permissions.set({
      ...Permissions.default(),
      editState: Permissions.proofOrSignature(),
    });
    this.oraclePublicKey.set(PublicKey.fromBase58(ORACLE_PUBLIC_KEY));
    this.minWorthy.set(Field(50));
      this.requireSignature();
  }

  @method verify(pb: PublicKey, indentity: Field, score: Field, walletScore: Field, signature: Signature) {
    // Get Oracle Public Key
    const oraclePublicKey = this.oraclePublicKey.get();
    this.oraclePublicKey.assertEquals(oraclePublicKey);

    const pbToField = Field.fromFields(pb.toFields());
    // Verify Signature
    const validSignature = signature.verify(oraclePublicKey, [
      pbToField,
      identity,
        score,
        walletScore
    ]);
    validSignature.assertTrue();
      
      const mw = this.minWorthy.get();
      this.minWorthy.assertEquals(mw);
      
      const eligible = score.add(walletScore).div(Field(2))

    // Check if the user is worthy
    eligible.assertGreaterThanOrEquals(mw, 'Not worthy');
  }
}

We are initializing the contract and setting the minimum average score to 50. Whenever we callverify in the Oracle contract with our data, we should verify that the data represents worthiness, we don’t need to know what the data is, but we can prove that the identity meets the minimum Worthiness.

We can extend this by implementing private inputs.

Private Input

@method verify(pb: PublicKey, salt: Feild, indentity: Field, score: Field, walletScore: Field, signature: Signature) {
  ...
  Poseidon.hash([ salt, indentity, score, walletScore ]);
  ...
}

here we are modifying the verifier to verify a hash of the data instead;

Interoperability

Finally, when building our lending protocol, we can leverage this Oracle to verify the data for each identity, enabling us to offer better lending conditions.

But one thing is missing. We don’t know whether such identity is owning another protocol.
How do we share this identity across protocols so your score is automatically 0 if you owe another lending protocol?

This interaction between lending protocols can create a unique user experience. We can choose another route that checks if the user owes another protocol.

router.get('/check-for-debt', async (req, res) => {
   
    const {wallet_address} = request.params;

   // check off-chain-storage
   
    // 0 if not owning
    res.json(response.data.debt);
});


A unique situation where off-chain storage shines because we can store our smart-contract states off the chain.

Off-Chain Storage

Remember, most of our computation would be done on the client and for our data. We need to use the off-chain store.

Here is where the Merkle map comes in; we can store the root hash of our contents on-chain, keeping the rest off-chain.

As we examined in Interoperability, it is pertinent to keep track of who owes who off-chain so that when verifying the data in the Oracle, we can be sure that the identity is not owning another protocol.

How To Deploy

we can follow the official documentation to learn how to configure your zkApp for deployment to the Berkeley test network.

zk deploy

After which, you have to confirm the transaction.

Conclusion

This article explored how interoperable lending protocols can be implemented on the Mina blockchain. While there are challenges to overcome, including the complexity of zero-knowledge proofs, difficulties in implementing protocols that leverage this technology and the need for standardization and interoperability, the potential benefits are significant. By leveraging zero-knowledge technology, it may be possible to create near-native web2 experiences in DeFi lending, transcending the current limitations and creating a more secure and inclusive financial system.

Subscribe to Anyaogu
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.