Skip to content
This repository was archived by the owner on Sep 14, 2023. It is now read-only.

add multisig proxy example #525

Closed
ryanleecode opened this issue Jan 26, 2023 · 9 comments
Closed

add multisig proxy example #525

ryanleecode opened this issue Jan 26, 2023 · 9 comments
Assignees
Labels

Comments

@ryanleecode
Copy link
Contributor

Add an example that utilizes a mutlisig behind a proxy. the example should be able to change who can access the proxy and thus also access the mutlisg.

@ryanleecode ryanleecode self-assigned this Jan 26, 2023
@tukwan
Copy link

tukwan commented Feb 2, 2023

Hi @ryanleecode! We are trying to model the multisig/proxy flow using CAPI. Would be great to get some help on it!

Diagram

c

Pseudo code

// 1. Create pure proxies by Alice
const stashProxy = await ALICE.proxy.createPure().signAndSend()
const aliceProxy = await ALICE.proxy.createPure().signAndSend()
const bobProxy = await ALICE.proxy.createPure().signAndSend()
const charlieProxy = await ALICE.proxy.createPure().signAndSend()

// 2. Get multisig address
const MULTISIG = Rune
  .resolve({
    signatories: [aliceProxy, bobProxy, charlieProxy],
    threshold: 2,
  })
  .as(MultisigRune, client) 

// 3. Top up proxies and multisig
await Balances
  .transfer({
    value: 2_000_000_000_000n,
    dest: MultiAddress.Id(MULTISIG.address),
  })
  .signed({ sender: ALICE })
  .sent()
  .logStatus("Existential deposit:")
  .finalized()
  .run()

await Balances
  .transfer({
    value: 2_000_000_000_000n,
    dest: stashProxy,
  })
  .signed({ sender: ALICE })
  .sent()
  .logStatus("Existential deposit:")
  .finalized()
  .run()
// ... repeat for: aliceProxy, bobProxy, charlieProxy

// 4. Set up proxies according to the diagram
await ALICE.proxy(stashProxy).addProxy(MULTISIG).signAndSend()
await ALICE.proxy(bobProxy).addProxy(BOB).signAndSend()
await ALICE.proxy(charlieProxy).addProxy(CHARLIE).signAndSend()

await ALICE.proxy(stashProxy).removeProxy(ALICE).signAndSend()
await ALICE.proxy(bobProxy).removeProxy(ALICE).signAndSend()
await ALICE.proxy(charlieProxy).removeProxy(ALICE).signAndSend()

// 5. Perform voting
// The to-be proposed and approved call
const sendFundsToDaveFromStashProxy = MULTISIG.proxy(stashProxy).extrinsic(
  Balances.transferKeepAlive({
    dest: DAVE.address,
    value: 1_230_000_000_000n,
  })
)

// Submit a proposal to dispatch the call
await MULTISIG
  .ratify({ call: sendFundsToDaveFromStashProxy, sender: aliceProxy })
  .signed({ sender: ALICE.proxy(aliceProxy)
  .sent()
  .logStatus("Proposal:")
  .finalized()
  .run()

// Send the approve and execute if final approval
await MULTISIG
  .ratify({ call: sendFundsToDaveFromStashProxy, sender: charlieProxy })
  .signed({ sender: CHARLIE.proxy(charlieProxy)})
  .sent()
  .logStatus("Approval:")
  .finalized()
  .run()

// Dave received funds from STASH
console.log("Dave final balance:", await System.Account.entry([DAVE.publicKey]).run()) // await getBalance(DAVE) ?

Extrinsics

Polkadot.js app (visualisation)

a

@karl-kallavus
Copy link
Contributor

karl-kallavus commented Feb 2, 2023

I'll try to describe the flow we're trying to achieve with API.

Alice wants to configure the future multisig by adding Bob and Charlie as signatories with proxies and setting a signatory threshold.

Alice wants to create Pure Proxy that will become the "stash" in the multisig setup and attach the multisig setup to the stash.

(check the Diagram above)

In that case Alice has to create 4 pure proxies: AlicePureProxy, BobPureProxy, CharliePureProxy and StashPureProxy

It will be awesome if we introduce batch transaction to reduce the amount of Alice's signs.

First Alice construct a list of proxies to batch

const [aliceProxy, bobProxy, charlieProxy, stashProxy] = Rune
  .batch([
    Proxy.createPure(proxyType), // for Alice
    Proxy.createPure(proxyType), // for Bob
    Proxy.createPure(proxyType), // for Charlie
    Proxy.createPure(proxyType). // for Stash
  ]).
  .as(BatchRune, client)

Then Alice can create a multisig

const multisig = Rune
  .resolve({
    signatories: [aliceProxy.publicKey, bobProxy.publicKey, charlieProxy.publicKey],
    threshold: 2,
  })
  .as(MultisigRune, client)

Then Alice can top up all created proxies at once

const recipients = Rune.array([aliceProxy, bobProxy, charlieProxy, stashProxy]);

await Utility.batchAll({
  calls: recipients.mapArray((recipient) =>
    Balances.transfer({
      dest: recipient.access("address"),
      value: 12345n,
    })
  ),
})
  .signed({ sender: alice })
  .sent()
  .logStatus()
  .finalized()
  .unwrapError()
  .run()

Now Alice could assign proxies according to the diagram

await Utility.batchAll({
  calls: [
    proxy.addProxy({
      origin: aliceProxy,
      delegate: bobProxy.address
    }),
    proxy.addProxy({
      origin: aliceProxy,
      delegate: charlieProxy.address
    })
    proxy.addProxy({
      origin: aliceProxy,
      delegate: stashProxy.address
    })
  ],
})
  .signed({ sender: alice })
...

Then Alice could unregister a proxy account.

await Utility.batchAll({
  calls: [
    proxy. removeProxy({
      origin: aliceProxy,
      delegate: bobProxy.address
    }),
    proxy. removeProxy({
      origin: aliceProxy,
      delegate: charlieProxy.address
    })
    proxy. removeProxy({
      origin: aliceProxy,
      delegate: stashProxy.address
    })
  ],
})
  .signed({ sender: alice })
...

We could make additional funds transfer into the multisig account

And the Bob (or anyone else) through his proxy could submit a proposal to dispatch the call from multsig

await multisig
  .ratify({ call, sender: bobProxy.address })
  .signed({ sender: bobProxy })
  .sent()
  .logStatus("Proposal:")
  .finalized()
  .run()

and then we could initialize rest multisig flow (details are here

  • Check if the call has been proposed
  • Send a non-executing approval
  • Check for existing approval(s)
  • Send the executing (final) approval
  • Balance check

Overall:

This flow is similar to the @tamrai's flow. The only principal difference is that we need to use batch calls whenever we could because current scenario will require around ~14 approvals from Alice and that might be a bit boring process from UI perspective. Using batch calls we could reduce that amount to ~5. (still a lot but way much better)

@Goranch3
Copy link

Goranch3 commented Feb 3, 2023

@karl-kallavus is it possible to send funds to all pure-proxy accounts and multisig all at once, like on the diagram below? See number 4.
Frame 3399

@tukwan
Copy link

tukwan commented Feb 3, 2023

Great! I wanted to keep it simple at the beginning as a Capi example, but batch transactions are definitely something desired especially from the UX perspective! @Goranch3 Yes. We can send same existential deposits to proxies and maybe a more configured deposit to the Stash (bank).

@statictype
Copy link

@ryanleecode not sure this is true.
a deposit is reserved when creating the pure proxy. at the moment, the reserved balance contributes to the ED but with the upcoming changes in Substrate reserved balance will no longer keep the account alive. it will need to have a minimum free balance. paritytech/substrate#12951

a pureProxy address should behave the same way as any other address, meaning if you send funds to it after it was reaped it should be restored on-chain

@ryanleecode
Copy link
Contributor Author

@ryanleecode not sure this is true. a deposit is reserved when creating the pure proxy. at the moment, the reserved balance contributes to the ED but with the upcoming changes in Substrate reserved balance will no longer keep the account alive. it will need to have a minimum free balance. paritytech/substrate#12951

a pureProxy address should behave the same way as any other address, meaning if you send funds to it after it was reaped it should be restored on-chain

yeah forget what i said. just a bug on myside 😄

@ryanleecode
Copy link
Contributor Author

What is the point of approveAsMulti it seems completely useless and doesnt do anything

@tjjfvi
Copy link
Contributor

tjjfvi commented Feb 15, 2023

IIRC approveAsMulti allows you to approve by sending just the call hash, not the full call, which is less expensive. You only need to send the full call on the last approval.

@tjjfvi tjjfvi changed the title add multisig example add multisig proxy example Feb 20, 2023
@ryanleecode
Copy link
Contributor Author

closed by #661

@github-project-automation github-project-automation bot moved this to Done in Capi Mar 1, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
Status: Done
Development

Successfully merging a pull request may close this issue.

6 participants