-
Notifications
You must be signed in to change notification settings - Fork 380
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Create a step-by-step tutorial on using Multichain DAO Contract (#2255)
* feat: create a step-by-step tutorial on using Multichain DAO Contract * fix: link to Telegram chat * re-organize text to simplify explanations * fix: minor changes * fix: sidebar format * fix: link --------- Co-authored-by: Damián Parrino <[email protected]> Co-authored-by: Guille <[email protected]> Co-authored-by: Guillermo Alejandro Gallardo Diez <[email protected]>
- Loading branch information
1 parent
fa1da09
commit 2bf351a
Showing
7 changed files
with
337 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
--- | ||
id: introduction | ||
title: Near Multi-Chain DAO Governance | ||
sidebar_label: Introduction | ||
--- | ||
|
||
import Tabs from '@theme/Tabs'; | ||
import TabItem from '@theme/TabItem'; | ||
|
||
Many multi-chain organizations deploy copies of a same smart contract across multiple EVM chains, which need to be kept in sync. | ||
|
||
Generally, this is tackled by handling multiple DAOs - one per chain - where the same action is voted simultaneously, a process that is not only time-consuming, but also very expensive and error-prone. | ||
|
||
 | ||
|
||
To solve this, we have built the [Abstract DAO](https://github.com/nearuaguild/abstract-dao), which enables organizations to **vote once** in NEAR, and then **execute the same action** across **multiple chains**. | ||
|
||
 | ||
|
||
:::warning | ||
Abstract DAO is an example, and as such it has not been audited, use it at your own risk | ||
::: | ||
|
||
--- | ||
|
||
### How It Works? | ||
|
||
The [Abstract DAO](https://github.com/nearuaguild/abstract-dao) acts as an intermediary between a NEAR DAOs and the EVM chains. | ||
|
||
The process of voting and executing an action looks like this: | ||
|
||
1. **Craft an EIP-1559 Payload**: You create the transaction that will execute across all chain, specifying the recipient address, nonce, value, and transaction data. Only the gas parameters are not set, as they will vary across chains. | ||
|
||
2. **Choose a Single Allowed Account**: As part of the voting process, your organization chooses an "allowed account," which is the member who will be responsible to setup gas parameters, and generate signatures for all the chains. | ||
|
||
3. **Vote on the Request**: Your decentralized organization votes once on NEAR to approve this request. Each member can review the transaction, then cast their vote to confirm or reject it. | ||
|
||
4. **Generate Signatures**: Once the request has enough confirmations, the transaction is approved. The "allowed account" can now interact with the [Abstract DAO](https://github.com/nearuaguild/abstract-dao) to generate signatures for as many EVM-compatible chains as needed. | ||
|
||
The result is a drastically simplified governance process (one vote, one confirmation) and the ability to sign and execute transactions across multiple chains in a coordinated manner. | ||
|
||
:::tip Handling Gas | ||
Since Gas price can vary widely across chains, the gas price of the transaction is not set until the signatures is generated | ||
|
||
This means that the "allowed account" will be the one in charge of setting the gas price for each transaction being signed | ||
::: | ||
|
||
|
||
--- | ||
|
||
## Prerequisites | ||
|
||
To complete this tutorial successfully, you will need [Near CLI](/tools/near-cli#installation) to be installed, and have a general understanding on how [Chain Signatures](/build/chain-abstraction/chain-signatures/getting-started) work. | ||
|
||
--- | ||
|
||
## Next steps | ||
|
||
Ready to start? Let's jump to the first step, in which we will understand how the Abstract DAO contract works | ||
|
||
--- | ||
|
||
:::note Versioning for this article | ||
|
||
- near-cli: `0.12.0` | ||
- rustc: `1.78.0` | ||
- cargo: `1.80.1` | ||
- cargo-near: `0.6.2` | ||
- rustc: `1.78.0` | ||
- node: `21.6.1` | ||
|
||
::: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
--- | ||
id: request | ||
title: "Abstract DAO: Requests" | ||
--- | ||
|
||
import Tabs from '@theme/Tabs'; | ||
import TabItem from '@theme/TabItem'; | ||
|
||
The Abstract DAO contract works as an intermediary between decentralized organizations in NEAR and EVM networks. To better understand how it works it is better to start by using it by itself, without using a DAO yet. | ||
|
||
Join us as we explore how to create a request in the Abstract DAO contract, that will be later used to derive signatures for foreign EVM chains. | ||
|
||
:::tip | ||
We have deployed the Abstract DAO in two environments: | ||
|
||
1. Testnet: `abstract-dao.testnet` | ||
2. Dev (unstable): `dev.abstract-dao.testnet` | ||
::: | ||
|
||
--- | ||
|
||
## Ethereum Function Call | ||
|
||
Imagine that our organization agreed on changing a value in a simple [counter we deployed on Sepolia Ethereum](https://sepolia.etherscan.io/address/0xe2a01146FFfC8432497ae49A7a6cBa5B9Abd71A3), and now want to leave this intent in the Abstract DAO. | ||
|
||
For this, we will call the **`register_signature_request`** function on Abstract Dao saying: | ||
|
||
*We allow **executor.testnet** to request signatures for one of our Ethereum accounts, making it set a counter to `1000`*. | ||
|
||
Here are the parameters, take a quick glance for now, since we will go over each one of them: | ||
|
||
```js | ||
{ | ||
"request": { | ||
"derivation_seed_number": 0, | ||
"allowed_account_id": "executor.testnet", | ||
"transaction_payload": { | ||
"to": "0xe2a01146FFfC8432497ae49A7a6cBa5B9Abd71A3", | ||
"nonce": "0", | ||
"function_data": { | ||
"function_abi": { | ||
"inputs": [ | ||
{ | ||
"internalType": "uint256", | ||
"name": "_num", | ||
"type": "uint256" | ||
} | ||
], | ||
"name": "set", | ||
"outputs": [], | ||
"stateMutability": "nonpayable", | ||
"type": "function" | ||
}, | ||
"arguments": [ | ||
{ | ||
"Uint": "A97" | ||
} | ||
] | ||
} | ||
}, | ||
} | ||
} | ||
``` | ||
|
||
There are 3 arguments in the call above: `derivation_seed_number`, `transaction_payload`, and `allowed_account_id` lets see them in depth. | ||
|
||
<hr class="subsection" /> | ||
|
||
### Derivation Path | ||
|
||
The parameter `derivation seed number` will be used to derive which external address we will be requesting signatures from, the address will be derived as: | ||
|
||
`DAO Address` + `Derivation Path` + `Contract Address` = `EVM Address` | ||
|
||
For example, if we register a request from the address `...` using the derivation path `0` we will obtain control the `0x...` account. | ||
|
||
:::tip | ||
Learn more about derivation paths [here](../../chain-abstraction/chain-signatures.md) | ||
::: | ||
|
||
<hr class="subsection" /> | ||
|
||
### Transaction Payload | ||
|
||
The `transaction_payload` contains all the information on the transaction that we want to perform, particularly: | ||
|
||
- `to`: The recipient address of the transaction | ||
- `nonce`: The transaction nonce, used to ensure uniqueness | ||
- `function_data`: (optional) Defines the function that will be called on the recipient's contract, including: | ||
- `function_abi`: The ABI of the function being called | ||
- `arguments`: The input arguments for the function, all ABI encoded (e.g. integers are `base64`) | ||
|
||
There are a couple important points to notice about this `transaction_payload`: | ||
|
||
- **Readable Payload:** The parameters make it easy for anyone to quickly understand what transaction will be executed externally. The Abstract DAO is designed to be transparent and easy to audit, abstracting away the complexity of creating the transaction. | ||
|
||
- **We Are Setting a Nonce:** By setting the nonce, we make sure that the transaction will only be valid once, as future transactions will need higher `nonces` | ||
|
||
- **We Are Not Setting the GAS:** Gas prices are expected to vary wildly across EVMs, for which it makes no sense to setup a fixed gas amount and gas price for all the networks, for this is that we use the last parameter `allowed_account` | ||
|
||
<hr class="subsection" /> | ||
|
||
### Allowed Account | ||
|
||
In this case, the `allowed_account` will be the one in charge of generating the signatures. At the time of generating the signature, the account will also set the `gas_price` for the transaction on a per-chain basis. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
--- | ||
id: signing | ||
title: "Abstract Dao: Signatures" | ||
--- | ||
|
||
In the previous section, we saw how to register a signature request on the Abstract DAO contract. Now, it is time to sign the transaction for different chains and relay it to the target EVM network. | ||
|
||
--- | ||
|
||
## Signing the Transaction | ||
|
||
To sign a transaction for a specific chain, the allowed account needs to call the `get_signature` function, passing the `request_id` (generated on the previous section) and all the necessary info to finish creating the transaction before signing it. | ||
|
||
For example, to sign the transaction for the Sepolia Testnet, the following command can be used: | ||
|
||
```bash | ||
near contract call-function as-transaction abstract-dao.testnet get_signature json-args '{ | ||
"request_id": 1, | ||
"other_payload": { | ||
"chain_id": 11155111, | ||
"max_fee_per_gas": "1000000000", | ||
"max_priority_fee_per_gas": "100000000" | ||
} | ||
}' prepaid-gas '300.0 Tgas' attached-deposit '0.05 NEAR' sign-as executor.testnet network-config testnet | ||
``` | ||
|
||
Note that all we are specifying now is the `chain_id` (to identify the destination chain), the `max_fee_per_gas`, and the `max_priority_fee_per_gas` (to set the transaction fee). | ||
|
||
The account authorized to call `get_signature` - in this case `executor.testnet` cannot change any parameter of the transaction being signed besides setting a gas fee per chain. | ||
|
||
--- | ||
|
||
## Signature Response | ||
|
||
The signature response is going to look like this: | ||
|
||
```json | ||
{ | ||
"big_r": { | ||
"signature": { | ||
"affine_point": "02D532992B0ECBF67800DB14E04530D9BA55609AD31213CC7ABDB554E8FDA986D3" | ||
}, | ||
"recovery_id": 1, | ||
"s": { | ||
"scalar": "40E81711B8174712B9F34B2540EE0F642802387D15543CBFC84211BB04B83AC3" | ||
} | ||
}, | ||
"tx": "0x02f85083aa36a702850485034c878517a4eb0789829dd094e2a01146fffc8432497ae49a7a6cba5b9abd71a380a460fe47b1000000000000000000000000000000000000000000000000000000000000a84bc0" | ||
} | ||
``` | ||
|
||
As we can see, it is not the signed transaction itself, but instead the data we need to reconstruct it. We have created an [script](https://github.com/nearuaguild/multichain-dao-scripts) to automate this process, as well as the relaying to the target EVM network. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
--- | ||
id: voting | ||
title: MultiSig Voting | ||
--- | ||
|
||
import Tabs from '@theme/Tabs'; | ||
import TabItem from '@theme/TabItem'; | ||
|
||
Now that we understand how the Abstract DAO works, it is time to use it in within an organization. Lets see how to deploy a MultiSig contract, where users will vote on a multi-chain proposal. | ||
|
||
--- | ||
|
||
## Creating a MultiSig Contract | ||
|
||
As a first step we need to create an account and deploy a MultiSig contract on it, so users can start creating proposals and voting on them. | ||
|
||
:::info Deploy Multisig | ||
You can download the [compiled multisig](https://github.com/near/core-contracts/raw/refs/heads/master/multisig2/res/multisig2.wasm) from the near repository | ||
::: | ||
|
||
|
||
```bash | ||
near create-account <accountId> --useFaucet | ||
|
||
near deploy <accountId> multisig2.wasm | ||
``` | ||
|
||
:::tip | ||
See the github repository for instructions on how to [initialize the multisig contract](https://github.com/near/core-contracts/tree/master/multisig2) | ||
::: | ||
|
||
--- | ||
|
||
## Creating a Request on the Multisig Contract | ||
|
||
To call `register_signature_request` on the Multi-Chain DAO Governance Contract, you need to submit a request through your Multisig contract. This ensures that the decision to generate a signature is confirmed by the necessary members. | ||
|
||
```bash | ||
near contract call-function as-transaction multisignature.testnet add_request json-args '{ | ||
"request": { | ||
"receiver_id": "abstract-dao.testnet", | ||
"actions": [ | ||
{ | ||
"type": "FunctionCall", | ||
"method_name": "register_signature_request", | ||
"args": { | ||
"request": { | ||
"allowed_account_id": "executor.testnet", | ||
"derivation_seed_number": 0, | ||
"transaction_payload": { | ||
"to": "0xe2a01146FFfC8432497ae49A7a6cBa5B9Abd71A3", | ||
"nonce": "0", | ||
"function_data": { | ||
"function_abi": { | ||
"inputs": [ | ||
{ | ||
"internalType": "uint256", | ||
"name": "_num", | ||
"type": "uint256" | ||
} | ||
], | ||
"name": "set", | ||
"outputs": [], | ||
"stateMutability": "nonpayable", | ||
"type": "function" | ||
}, | ||
"arguments": [ | ||
{ | ||
"Uint": "A97" | ||
} | ||
] | ||
} | ||
} | ||
} | ||
}, | ||
"gas": "100000000000000", | ||
"deposit": "0.1" | ||
} | ||
] | ||
} | ||
}' prepaid-gas '100.0 Tgas' attached-deposit '1 yoctoNEAR' sign-as executor.testnet network-config testnet | ||
``` | ||
|
||
--- | ||
|
||
## Voting on the Request | ||
|
||
Once the request is submitted, members of the multisig contract have a set amount of time to vote to either Confirm or Reject the request. Each member needs to cast their vote using the following command: | ||
|
||
```bash | ||
near contract call-function as-transaction multisignature.testnet confirm json-args '{"request_id": 1}' prepaid-gas '100.0 Tgas' attached-deposit '1 yoctoNEAR' sign-as account.testnet network-config testnet | ||
``` | ||
|
||
:::note | ||
Replace provided `request_id` with value retrieved from the response when creating the request | ||
::: | ||
|
||
Once the request has received enough confirmations, it will be automatically executed. At this point, the signature request is successfully registered on the Multi-Chain DAO Governance Contract. | ||
|
||
Now, the allowed account (specified in the request) can generate signatures for the transaction just as we saw in the [previous section](./2-signing.md). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.