-
Notifications
You must be signed in to change notification settings - Fork 130
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
base: main
Are you sure you want to change the base?
Changes from 10 commits
7aa4b14
0976297
1519183
d099633
fb44ec5
3a1ce85
89090e2
e1ca418
321394b
4ca59b3
74d5b3e
0b32bae
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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. | ||
|
||
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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. where are the example files? are they in this PR? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Once this PR get's merges the link will start working. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi @iregina , Thank You There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like your question about comments @LuffySama-Dev Please add descriptive comments There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 ?
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. |
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', | ||
}, | ||
}; |
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 |
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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
#!/bin/sh | ||
. "$(dirname "$0")/_/husky.sh" | ||
|
||
npx lint-staged |
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 |
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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"semi": true, | ||
"singleQuote": true, | ||
"tabWidth": 2, | ||
"trailingComma": "es5" | ||
} |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 🙌🏻