Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

📝 Creating a draft for upcoming cross-contract tutorial #857

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
209 changes: 209 additions & 0 deletions docs/zkapps/tutorials/12-cross-contract-calls.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
---
title: 'Tutorial 12: Cross Contract Calls'
hide_title: true
sidebar_label: 'Tutorial 12: Cross Contract Calls'
description: Guided steps to learn about the ways to interact with a smart contract from another smart contract.
keywords:
- smart contracts
- zkapps
- zero knowledge proof programming
- zk proof
- zk
- cross contract call
- mina
---

:::info

Please note that zkApp programmability is not yet available on Mina Mainnet, but zkApps can now be deployed to Berkeley Testnet.

:::


# Tutorial 12: Cross Contract Calls

In this tutorial, you learn how smart contracts on a blockchain can interact by calling functions in each other's code, enabling building modular and complex decentralized applications.

Cross contract calls allow smart contracts on a blockchain to interact with each other. This enables the building of complex decentralized applications (Dapps) from multiple modular components. In a cross-contract call, a function in one smart contract can call a function in another smart contract to leverage existing code and functionality.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's add an example of a complex stack that will benefit from this pattern (can we describe a use case?) @LuffySama-Dev

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking the same !!
I was thinking of adding a image but an use case example sounds awesome.
Will check and add it in couple of days.

Thank You 🙌🏻


This tutorial demonstrates passing data between contracts, handling events, and returning values when contracts call each other.

The full example code is provided in the [12-cross-contract-calls/src/](https://github.com/o1-labs/docs2/tree/main/examples/zkapps/12-cross-contract-calls/src) example files.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where are the example files? are they in this PR?
let's put the example files mentioned in the tutorial in a new folder here https://github.com/o1-labs/docs2/tree/main/examples/zkapps

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The example files are added in initial commit in example folder.

7aa4b14#diff-40a308f55dcc34f6c479b6f2954062c4ef5bac47ad2928204dc9951e78eea6bc

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once this PR get's merges the link will start working.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't recognize all three smart contracts as being present, since in a single file. Thanks @iregina for testing the code!


## Prerequisites

- Make sure you have the latest version of the zkApp CLI installed:

```sh
$ npm install -g zkapp-cli
```

- Ensure your environment meets the [Prerequisites](/zkapps/tutorials#prerequisites) for zkApp Developer Tutorials.

This tutorial has been tested with:

- [zkApp CLI](https://github.com/o1-labs/zkapp-cli) version `0.16.0`
- [o1js](https://www.npmjs.com/package/o1js) version `0.16.2`

## Create a new project

Now that you have the tooling installed, you can start building your application.

1. Create or change to a directory where you have write privileges.
1. Now, create a project using the `zk project` command:

```sh
$ zk project 12-cross-contract-calls
```

As you learned in earlier tutorials, the `zk project` command creates the `12-cross-contract-calls` directory that contains the scaffolding for your project.

1. Change into the `12-cross-contract-calls` directory.

Like all projects, you run `zk` commands from the root of the `12-cross-contract-calls` directory as you work in the `src` directory on files that contain the TypeScript code for the smart contract.

Each time you make updates, then build or deploy, the TypeScript code is compiled into JavaScript in the `build` directory.

### Prepare the project

Like earlier tutorials, you can prepare your project by deleting the default files that come with the new project and creating a smart contract called `Composability`.

## Write the ZkProgram

Now, the fun part! Write your smart contract in the `src/Composability.ts` file.

A final version of the smart contract is provided in the [Composability.ts](https://github.com/o1-labs/docs2/blob/main/examples/zkapps/12-cross-contract-calls/src/Composability.ts) example file.


### Copy the example

Use the existing code in the [Composability.ts](https://github.com/o1-labs/docs2/blob/main/examples/zkapps/12-cross-contract-calls/src/Composability.ts) example file.

1. First, open the [Composability.ts](https://github.com/o1-labs/docs2/blob/main/examples/zkapps/12-cross-contract-calls/src/Composability.ts) example file.

1. Copy the file's entire contents into your project `src/Composability.ts` file.

### Imports and Incrementer smart contract

First, bring in imports and set up the first smart contract `Incrementer`.

```typescript
import {
Field,
method,
Mina,
AccountUpdate,
PrivateKey,
SmartContract,
state,
State,
} from 'o1js';

class Incrementer extends SmartContract {
@method increment(x: Field): Field {
return x.add(1);
}
}

```
This Incrementer contract adds `1` to the `Field` argument, which is passed.

### Adder smart contract

Now bring in the second contract `Adder` that returns the addition of two numbers and adds `1` to the result.
The addition of `1` to the result is outsourced to the `Incrementer` smart contract by creating a new object by passing its address.

```typescript
class Adder extends SmartContract {
@method addPlus1(x: Field, y: Field): Field {
let sum = x.add(y);
let incrementer = new Incrementer(incrementerAddress);
return incrementer.increment(sum);
}
}

```

### Caller smart contract

The final smart contract `Caller` calls the `addPlus1()` method of the `Adder` smart contract and emits the stored result that is returned.

```typescript
class Caller extends SmartContract {
@state(Field) sum = State<Field>();
events = { sum: Field };

@method callAddAndEmit(x: Field, y: Field) {
let adder = new Adder(adderAddress);
let sum = adder.addPlus1(x, y);
this.emitEvent('sum', sum);
this.sum.set(sum);
}
}

```

The code to interact with the smart contract:

```typescript
const doProofs = true;

let Local = Mina.LocalBlockchain({ proofsEnabled: doProofs });
Mina.setActiveInstance(Local);

let feePayerKey = Local.testAccounts[0].privateKey;
let feePayer = Local.testAccounts[0].publicKey;

let incrementerKey = PrivateKey.random();
let incrementerAddress = incrementerKey.toPublicKey();

let adderKey = PrivateKey.random();
let adderAddress = adderKey.toPublicKey();

let zkappKey = PrivateKey.random();
let zkappAddress = zkappKey.toPublicKey();

let zkapp = new Caller(zkappAddress);
let adderZkapp = new Adder(adderAddress);
let incrementerZkapp = new Incrementer(incrementerAddress);

if (doProofs) {
console.log('compile (incrementer)');
await Incrementer.compile();
console.log('compile (adder)');
await Adder.compile();
console.log('compile (caller)');
await Caller.compile();
}

console.log('deploy');
let tx = await Mina.transaction(feePayer, () => {
AccountUpdate.fundNewAccount(feePayer, 3);
zkapp.deploy();
adderZkapp.deploy();
incrementerZkapp.deploy();
});
await tx.sign([feePayerKey, zkappKey, adderKey, incrementerKey]).send();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@LuffySama-Dev Can you also leave a comment about what is happening here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @iregina ,
Yes sure. I will add a comment.

Thank You ☺️

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like your question about comments @LuffySama-Dev
Helpful comments in the code is the pathway to sharing knowledge

Please add descriptive comments

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @barriebyron , @iregina ,

I have committed the changes, can you please help to check and let me know if any changes are required ?

  • Also, I am planning on adding a flow diagram of how call is going from one contract to other an so.
    What do you think ?

Thank You 😃


console.log('call interaction');
tx = await Mina.transaction(feePayer, () => {
zkapp.callAddAndEmit(Field(5), Field(6));
});
console.log('proving (3 proofs.. can take a bit!)');
await tx.prove();
console.log(tx.toPretty());
await tx.sign([feePayerKey]).send();

console.log('state: ' + zkapp.sum.get());

```

In this example, you spin up the Mina chain and deploy all three smart contracts locally.

Then you call the `callAddAndEmit` method from the `Caller` smart contract, which takes two numbers as arguments, then call the `Adder` smart contract, which adds these two numbers and passes the result in the `Incrementer` smart contract, which increments the result by 1.

When run successfully, the state is equal to 12.

## Conclusion

Congratulations! You have learned how to implement cross contract calls, allowing smart contracts to interact and unlocking new possibilities for modular blockchain applications.
23 changes: 23 additions & 0 deletions examples/zkapps/12-cross-contract-calls/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module.exports = {
root: true,
env: {
browser: true,
node: true,
jest: true,
},
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/eslint-recommended',
'plugin:@typescript-eslint/recommended',
'plugin:o1js/recommended',
],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 'latest',
},
plugins: ['@typescript-eslint', 'o1js'],
rules: {
'no-constant-condition': 'off',
'prefer-const': 'off',
},
};
3 changes: 3 additions & 0 deletions examples/zkapps/12-cross-contract-calls/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Use line endings appropriate for the system. This prevents Git from
# complaining about project template line endings when committing on Windows.
* text=auto eol=lf
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#
# ci.yml
#
# Run tests for all pushed commits and opened pull requests on Github.
#

name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Set up NodeJS
uses: actions/setup-node@v4
with:
node-version: '16'
- name: Git checkout
uses: actions/checkout@v4
- name: NPM ci, build, & test
run: |
npm install
npm run build --if-present
npm test
env:
CI: true
13 changes: 13 additions & 0 deletions examples/zkapps/12-cross-contract-calls/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# NodeJS
node_modules
build
coverage

# Editor
.vscode

# System
.DS_Store

# Never commit keys to Git!
keys
4 changes: 4 additions & 0 deletions examples/zkapps/12-cross-contract-calls/.husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npx lint-staged
11 changes: 11 additions & 0 deletions examples/zkapps/12-cross-contract-calls/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# TypeScript files
src

# Editor
.vscode

# System
.DS_Store

# Never reveal your keys!
keys
14 changes: 14 additions & 0 deletions examples/zkapps/12-cross-contract-calls/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# NodeJS
node_modules
build
coverage
.husky

# Editor
.vscode

# System
.DS_Store

# Misc
LICENSE
6 changes: 6 additions & 0 deletions examples/zkapps/12-cross-contract-calls/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5"
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading