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

Commit

Permalink
feat: virtual multisigs (#661)
Browse files Browse the repository at this point in the history
Co-authored-by: Ryan Lee <ryan@parity.io>
  • Loading branch information
harrysolovay and ryanleecode authored Mar 1, 2023
1 parent 76ba340 commit f819cc2
Show file tree
Hide file tree
Showing 9 changed files with 425 additions and 15 deletions.
4 changes: 1 addition & 3 deletions examples/multisig_transfer.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { alice, bob, charlie, dave, Rune, ValueRune } from "capi"
import { MultisigRune } from "capi/patterns/multisig/mod.ts"
import { Balances, chain, System } from "polkadot_dev/mod.ts"
// TODO: utilize type exposed from capi/patterns/MultisigRune.ts (when we enable client env specificity)
import { MultiAddress } from "polkadot_dev/types/sp_runtime/multiaddress.ts"

const multisig = Rune
.constant({
Expand All @@ -18,7 +16,7 @@ console.log("Dave initial balance:", await System.Account.entry([dave.publicKey]
await Balances
.transfer({
value: 2_000_000_000_000n,
dest: MultiAddress.Id(multisig.address),
dest: multisig.address,
})
.signed({ sender: alice })
.sent()
Expand Down
70 changes: 70 additions & 0 deletions examples/virtual_multisig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { alice, bob, charlie, dave, MultiAddress, Rune } from "capi"
import { VirtualMultisigRune } from "capi/patterns/multisig/mod.ts"
import { Balances, chain, System, Utility } from "polkadot_dev/mod.ts"
import { parse } from "../deps/std/flags.ts"

let { state } = parse(Deno.args, { string: ["state"] })
if (!state) {
state = await VirtualMultisigRune
.deployment(chain, {
founders: [alice.publicKey, bob.publicKey, charlie.publicKey],
threshold: 2,
deployer: alice,
})
.hex
.run()
}

console.log(`Virtual multisig state hex: ${state}`)

const vMultisig = VirtualMultisigRune.hydrate(chain, state)

const fundStash = Balances
.transfer({
dest: vMultisig.stash.map(MultiAddress.Id),
value: 20_000_000_000_000n,
})
.signed({ sender: alice })
.sent()
.dbgStatus("Fund Stash:")
.finalized()

const proposal = Balances.transfer({
dest: dave.address,
value: 1_234_000_000_000n,
})

const bobTx = Utility
.batchAll({
// @ts-ignore: fix upon #656
calls: Rune.array([
vMultisig.fundMemberProxy(bob.publicKey, 20_000_000_000_000n),
vMultisig.ratify(bob.publicKey, proposal),
]),
})
.signed({ sender: bob })
.sent()
.dbgStatus("Bob fund & ratify:")
.finalized()

const charlieTx = Utility
.batchAll({
// @ts-ignore: fix upon #656
calls: Rune.array([
vMultisig.fundMemberProxy(charlie.publicKey, 20_000_000_000_000n),
vMultisig.ratify(charlie.publicKey, proposal),
]),
})
.signed({ sender: charlie })
.sent()
.dbgStatus("Charlie fund & ratify:")
.finalized()

await Rune
.chain(() => vMultisig)
.chain(() => fundStash)
.chain(() => System.Account.entry([dave.publicKey]).dbg("Dave Balance Before:"))
.chain(() => bobTx)
.chain(() => charlieTx)
.chain(() => System.Account.entry([dave.publicKey]).dbg("Dave Balance After:"))
.run()
24 changes: 12 additions & 12 deletions patterns/multisig/MultisigRune.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,23 @@ export interface MultisigVoteProps {

export interface Multisig {
signatories: Uint8Array[]
threshold: number
threshold?: number
}

// TODO: swap out `Chain` constraints upon subset gen issue resolution... same for other patterns
export class MultisigRune<out U, out C extends Chain = Chain> extends Rune<Multisig, U> {
threshold = this.into(ValueRune).access("threshold")
address = this.into(ValueRune).map(({ signatories, threshold }) =>
multisigAccountId(signatories, threshold)
)

private pallet
private storage
threshold
accountId
address

constructor(_prime: MultisigRune<U>["_prime"], readonly chain: ChainRune<U, C>) {
super(_prime)
this.pallet = this.chain.metadata().pallet("Multisig")
this.storage = this.pallet.storage("Multisigs")
this.storage = this.chain.metadata().pallet("Multisig").storage("Multisigs")
const v = this.into(ValueRune)
this.threshold = v.map(({ threshold, signatories }) => threshold ?? signatories.length - 1)
this.accountId = Rune.fn(multisigAccountId).call(v.access("signatories"), this.threshold)
this.address = this.accountId.map(MultiAddress.Id)
}

otherSignatories<X>(...[sender]: RunicArgs<X, [sender: MultiAddress]>) {
Expand Down Expand Up @@ -122,15 +122,15 @@ export class MultisigRune<out U, out C extends Chain = Chain> extends Rune<Multi
}

proposals<X>(...[count]: RunicArgs<X, [count: number]>) {
return this.storage.keyPage(count, Rune.tuple([this.address]))
return this.storage.keyPage(count, Rune.tuple([this.accountId]))
}

proposal<X>(...[callHash]: RunicArgs<X, [callHash: Uint8Array]>) {
return this.storage.entry(Rune.tuple([this.address, callHash]))
return this.storage.entry(Rune.tuple([this.accountId, callHash]))
}

isProposed<X>(...[callHash]: RunicArgs<X, [callHash: Uint8Array]>) {
return this.storage.entryRaw(Rune.tuple([this.address, callHash]))
return this.storage.entryRaw(Rune.tuple([this.accountId, callHash]))
.map((entry) => entry !== null)
}
}
Expand Down
Loading

0 comments on commit f819cc2

Please sign in to comment.