Claude Barde
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    # Interacting with the multisig contract The `tezos-client` provides a simple multisig contract that you can originate and interact with directly from the command line. However, you may want to build a dapp that interacts with the contract, and writing the JavaScript to do so turns out to be tricky. After understanding the structure of the contract, you will be able to use Taquito to write code that will send a `transfer` transaction and a `change_keys` transaction to your originated multisig contract. ## What is the multisig contract? A multisig contract is a smart contract that allows a group of users to agree and control different actions executed through the contract. The multisig contract in the `tezos-client` allows three actions: - the receiving of tez through the default entrypoint - the transfer of tez stored in the contract to a given address - the update of the participants to the multisig contract > Note: > Only the first action is not subject to the agreement of the participants, anybody can send tez to the contract In order to transfer tez or to change the participants of the multisig contract, a transaction must be sent to the contract. The transaction must include a payload and a list of signatures generated from the payload, and the length of the list must match or exceed the current threshold stored in the contract storage. The storage of the contract includes also a counter that is incremented at every successful transaction to prevent operations from being replayed. ## Using the `transfer` entrypoint The multisig contract allows the participants to send a Michelson lambda along with a payload, for example, to authorize the transfer of a certain amount of tez to an implicit account. This operation requires multiple steps: - Writing a lambda in Michelson that will forge an operation to transfer the tez to the provided address - Writing the nested pair that will include the different values required by the contract - Packing the Michelson value created in the previous step - Having the participants of the contract sign the packed value - Sending a transaction to the contract with the payload and the obtained signatures > Note: > A `lambda` is an inline function made of Michelson code within curly braces that will be run during the contract execution. Let's see how that translates into JavaScript: ```typescript const lambda = `{ DROP ; NIL operation ; PUSH key_hash "${RECIPIENT_ADDRESS}" ; IMPLICIT_ACCOUNT ; PUSH mutez ${AMOUNT} ; UNIT ; TRANSFER_TOKENS ; CONS }`; ``` First, we write the Michelson lambda that will be executed to transfer the tez, where `RECIPIENT_ADDRESS` is the public key of the recipient of the tez and `AMOUNT` is the amount of tez to be sent. Next, we will use the lambda to create the required payload for this action: ```typescript= import {TezosToolkit } from "@taquito/taquito"; import { Parser, packDataBytes } from "@taquito/michel-codec"; const Tezos = new TezosToolkit(RPC_URL); const chainId = await Tezos.rpc.getChainId(); const contract = await Tezos.contract.at(MULTISIG_ADDRESS); const storage: any = await contract.storage(); const counter = storage.stored_counter.toNumber(); const p = new Parser(); const michelsonData = `( Pair "${chainId}" ( Pair "${MULTISIG_ADDRESS}" ( Pair ${counter} (Left ${lambda}) ) ) )`; const dataToPack = p.parseMichelineExpression(michelsonData); ``` The payload expected by the multisig contract is a nested pair that contains the chain id, the address of the contract, the current counter (from the contract storage) and the option set to `Left` with the lambda as a value. The payload is then parsed using the parser from the `@taquito/michel-codec` package. After that, we need to parse the payload type in a similar fashion: ```typescript= const michelsonType = ` (pair chain_id (pair address (pair nat (or (lambda unit (list operation)) (pair nat (list key) ) ) ) ) ) `; const typeToPack = p.parseMichelineExpression(michelsonType); ``` Now that both the value and its type have been properly parsed, we can pack them: ```typescript= const { bytes: payload } = packDataBytes( dataToPack as any, typeToPack as any ); ``` This action uses the `packDataBytes` method that you can find in the `@taquito/michel-codec` package to pack the data we created above locally. This will output the payload that will be signed. >Note: > `packDataBytes` allows local packing, which removes any risk of data corruption that exists when using the packing feature of a remote RPC node. ```typescript const sig = (await Tezos.signer.sign(payload, new Uint8Array())).prefixSig; ``` The instance of the `TezosToolkit` holds a signer that you can use to sign arbitrary data as shown above. It returns different values and we will keep the one under the `prefixSig` property. From there, the payload will be shared with the other participants. Each one of them will review it, and sign it and the initiator of the contract call will collect all the signatures to submit them with the transaction. Now the transaction can be forged and sent to the contract: ```typescript= try { const contract = await Tezos.contract.at(MULTISIG_ADDRESS); const storage: any = await contract.storage(); const op = await contract.methodsObject.main({ payload: { counter: storage.stored_counter.toNumber(), action: { operation: lambda } }, sigs: [sig, ...signatures] }); await op.confirmation(); } catch (err) { console.error(err); } ``` If everything works correctly, the counter of the multisig contract should be incremented by 1 and the given amount of tez should be transferred to the provided address. ## Using the `change_keys` entrypoint Sending a `change_keys` operation is going to be very similar to sending a `transfer` operation, with a little difference in the arguments you provide. The purpose of the `change_keys` operation is to update the threshold and the keys in the list of participants while asking all the current participants if they approve the update. First, we want to parse an array of keys into a Michelson value: ```typescript const listOfKeys = [KEY_1, KEY_2, KEY_3]; const michelineListOfKeys = `{ ${listOfKeys .map(key => `"${key}"`) .join(" ; ")} }`; ``` Next, we are going to pack the required nested pair in the same way we did earlier while changing some values in the pair: ```typescript= import {TezosToolkit } from "@taquito/taquito"; import { Parser } from "@taquito/michel-codec"; const Tezos = new TezosToolkit(RPC_URL); const chainId = await Tezos.rpc.getChainId(); const contract = await Tezos.contract.at(MULTISIG_ADDRESS); const storage: any = await contract.storage(); const counter = storage.stored_counter.toNumber(); const p = new Parser(); const newThreshold = 2; const michelsonData = `( Pair "${chainId}" ( Pair "${MULTISIG_ADDRESS}" ( Pair ${counter} (Right (Pair ${newThreshold} ${michelineListOfKeys})) ) ) )`; const dataToPack = p.parseMichelineExpression(michelsonData); const michelsonType = `(pair chain_id (pair address (pair nat (or (lambda unit (list operation)) (pair nat (list key))))))`; const typeToPack = p.parseMichelineExpression(michelsonType); const { bytes: payload } = packDataBytes( dataToPack as any, typeToPack as any ); ``` This creates the Michelson value and its type that will be ultimately packed in order to generate the payload that the other participants must sign. Now, the signatures can be collected and a transaction can be sent: ```typescript= const signatures = [SIG_1, SIG_2, SIG_3]; const forgerSig = (await Tezos.signer.sign(payload, new Uint8Array())).prefixSig; try { const contract = await Tezos.contract.at(MULTISIG_ADDRESS); const storage: any = await contract.storage(); const op = await contract.methodsObject.main({ payload: { counter: storage.stored_counter.toNumber(), action: { change_keys: { threshold: newThreshold, keys: listOfKeys } }, sigs: [forgerSig, ...signatures] }); await op.confirmation(); } catch (err) { console.error(err); } ``` After confirmation, the threshold and the list of keys in the contract will be updated accordingly. ## More about the `transfer` entrypoint Although called `transfer`, this entrypoint actually has a more general purpose, i.e. the execution of a Michelson lambda approved by the required minimum number of participants in the multisig (the `threshold` value in the storage). As an example, let's check how the exchange of 50 XTZ to tzBTC through the Liquidity Baking contract would work. The first step is to write the Michelson lambda: ```typescript= const minTokensBought = 360000; // Should be calculated before every transaction const lambda = `{ /* checks that the contract has enough balance */ PUSH mutez 50000000 ; BALANCE ; COMPARE ; GE ; IF { /* prepares payload for transaction */ NOW ; PUSH int 600 ; ADD @timestamp ; PUSH @minTokensBought nat ${minTokensBought} ; PAIR ; SELF_ADDRESS @to ; PAIR @xtzToToken ; /* creates contract parameter */ PUSH address "KT1TxqZ8QtKvLu3V3JH7Gx58n7Co8pgtpQU5" ; CONTRACT %xtzToToken (pair (address %to) (pair (nat %minTokensBought) (timestamp %deadline))) ; IF_NONE { PUSH string "UNKNOWN_TARGET_CONTRACT" ; FAILWITH ; } { SWAP ; PUSH mutez 50000000; SWAP ; TRANSFER_TOKENS ; NIL operation ; SWAP ; CONS ; } ; } { /* insufficient contract balance */ PUSH string "INSUFFICIENT_BALANCE" ; FAILWITH ; } ; }`; ``` The lambda here is going to prepare an operation to the liquidity baking DEX (also called "SIRIUS DEX"), the only value that you have to calculate beforehand is the minimum amount of tzBTC expected to be received in exchange for 50 XTZ: ```typescript= const tokenOut_ = new BigNumber(tokenOut).times(new BigNumber(1000)); const allowedSlippage_ = new BigNumber( Math.floor(allowedSlippage * 1000 * 100) ); const result = tokenOut_ .minus( tokenOut_.times(allowedSlippage_).dividedBy(new BigNumber(100000)) ) .dividedBy(1000); return BigNumber.maximum(result, new BigNumber(1)); const minTokensBought = (({ xtzIn, xtzPool, tokenPool, feePercent, burnPercent }: { xtzIn: number, xtzPool: number, tokenPool: number, feePercent?: number, burnPercent?: number }) => { xtzPool = xtzPool + 2_500_000; const fee = feePercent ? 1000 - Math.floor(feePercent * 10) : 1000; const burn = burnPercent ? 1000 - Math.floor(burnPercent * 10) : 1000; const feeMultiplier = fee * burn; if(xtzPool > 0 && tokenPool > 0 && xtzIn > 0) { const numerator = xtzIn * tokenPool * feeMultiplier; const denominator = (xtzPool * 1_000_000) + (xtzIn * feeMultiplier); return numerator / denominator; } else { return null; } })() ``` From this point forwards, you can repeat the same steps as in the `Using the transfer entrypoint` above to pack the data, get the signatures and send it to the DEX. ###### tags: `archived`

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully