2.42 FuturePass multisig (Native API example) At a glance What it is Make a multisig wallet with a FuturePass among its signers, and approve a transfer from it with a second signer. Futureverse’s Native API example for The Root Network, with its README and source. Where it sits Part II — Building · Chapter 2.42 Covers Examples · Source files Source github.com/futureversecom/trn-examples/tree/main/examples/substrate/use-fp-multisig-wallet
| At a glance | |
|---|---|
| What it is | Make a multisig wallet with a FuturePass among its signers, and approve a transfer from it with a second signer. Futureverse’s Native API example for The Root Network, with its README and source. |
| Where it sits | Part II — Building · Chapter 2.42 |
| Covers | Examples · Source files |
| Source | github.com/futureversecom/trn-examples/tree/main/examples/substrate/use-fp-multisig-wallet |
Make a multisig wallet with a FuturePass among its signers, and approve a transfer from it with a second signer. Reference: multisig pallet.
Copied from Futureverse’s
futureversecom/trn-examples, deleted with its GitHub organisation, at commit44c9e16of 2024-10-31. Apache-2.0: license. Before you run it, read Running the examples.
You don’t need a FuturePass
This page treats FuturePass as something to use. You don’t need one: where the page uses a FuturePass, use an account in your own wallet instead — any EIP-6963 wallet app, MetaMask preferred. If you have a FuturePass, you can move what’s in it to your wallet at any time: https://root.trn.codeology.co.nz/wallet/futurepass. FuturePass explains how it works.
Scripts to use FuturePass proxy extrinsic to multi sig an extrinsic
2.42.1 Examples
bash
# change your working directory to this example first
cd examples/substrate/use-fp-multisig-wallet
# export all required environments
export CALLER_PRIVATE_KEY=
Steps:
1. `createMultiSigWallet` - parameter of `signatores` (`-s`), `name` (`-n`) and `threshold` (`-t`)
Can replace signatores, threshold and wallet name in createMultiSigWallet script
```bash
pnpm call:createMultiSigWallet- Fund the multisig wallet address created above with some xrp, Can update the multiSigWallet address in script
bash
pnpm call:fundMultiSigWallet- Create futurepass proxy extrinsic Update the caller seed
CALLER_PRIVATE_KEYand signatories
bash
pnpm call:createProxyExtrinsic- Sign the multisig extrinsic with by other holder Update the caller seed
CALLER_PRIVATE_KEYto other holder and signatories
bash
pnpm call:signBySecondHolderIMPORTANT
Ensure the following ENV vars are available before running the examples
CALLER_PRIVATE_KEY- Private key of an account that submits the transaction. Follow this guide to create and fund an account with some test tokens on Porcini (testnet) if you don't have one yet.
2.42.2 Source files
src/createMultiSigWallet.ts2.42.2.1
ts
import { createTestKeyring } from "@polkadot/keyring";
import { KeyringInstance, KeyringOptions } from "@polkadot/keyring/types";
import { bnToBn, objectSpread, u8aSorted } from "@polkadot/util";
import { createKeyMulti } from "@polkadot/util-crypto";
import { withChainApi } from "@trne/utils/withChainApi";
export const command = "createMultiSigWallet";
export const desc = "Create a multisig wallet from given addresses";
// Create a multisig wallet from given addresses"
withChainApi("porcini", async (api) => {
const genesisHash = api.genesisHash;
const signatoryList = [
"0xFFfFffFF000000000000000000000000000003CD",
"0x25451A4de12dcCc2D166922fA938E900fCc4ED24",
"0x6D1eFDE1BbF146EF88c360AF255D9d54A5D39408",
];
// signatories.split(",");
console.log("signatoryList::", signatoryList);
const multiSigWalletName = "multiSigWallet";
const threshold = 2;
const ss58Format = 193;
const options = {
ss58Format,
type: "ethereum",
};
const keyring = createTestKeyring(options as KeyringOptions, true);
const multiSigOptions = {
genesisHash: genesisHash.toString(),
name: multiSigWalletName.trim(),
tags: [],
};
const multiSigAddress = addMultisig(signatoryList, threshold, multiSigOptions, keyring);
console.log("[1/3] Created multiSig address:", multiSigAddress);
process.exit(0);
});
function addMultisig(addresses, threshold, meta = {}, keyring: KeyringInstance) {
let address = createKeyMulti(addresses, threshold);
address = address.slice(0, 20); // for ethereum addresses
// we could use `sortAddresses`, but rather use internal encode/decode so we are 100%
const who = u8aSorted(addresses.map((who) => keyring.decodeAddress(who))).map((who) =>
keyring.encodeAddress(who)
);
const meta1 = objectSpread({}, meta, {
isMultisig: true,
threshold: bnToBn(threshold).toNumber(),
who,
});
const pair = keyring.addFromAddress(address, objectSpread({}, meta1, { isExternal: true }), null);
console.log("MultiSig address created::", pair.address);
return pair.address;
}src/createProxyExtrinsic.ts2.42.2.2
ts
import { u8aToHex } from "@polkadot/util";
import { sendExtrinsic } from "@trne/utils/sendExtrinsic";
import { withChainApi } from "@trne/utils/withChainApi";
export const command = "createProxyExtrinsic";
export const desc = "Create futurepass multisig proxy extrinsic";
withChainApi("porcini", async (api, caller, logger) => {
console.log("signer.address:", caller.address);
const fpassAccount = await api.query.futurepass.holders(caller.address);
console.log("futurepass account:", fpassAccount.toString());
const xrp = 2;
const amount = 100;
const randomAccount = "0xd094275a3eb8D3300faD74370a71633CfedDCA99";
// Extrinsic to be used for multi signature call
const extrinsic = api.tx.assets.transfer(xrp, randomAccount, amount);
const u8a = extrinsic.method.toU8a();
const encodedCallData = u8aToHex(u8a);
const signatoryList = [
"0x25451A4de12dcCc2D166922fA938E900fCc4ED24",
"0x6D1eFDE1BbF146EF88c360AF255D9d54A5D39408",
];
console.log("signatoryList::", signatoryList);
const threshold = signatoryList.length;
const maybeTimepoint = null;
const maxWeight = 0;
const multiSigCall = await api.tx.multisig.asMulti(
threshold,
signatoryList,
maybeTimepoint,
encodedCallData,
maxWeight
);
const proxyExtrinsic = await api.tx.futurepass.proxyExtrinsic(fpassAccount, multiSigCall);
const { result, extrinsicId } = await sendExtrinsic(proxyExtrinsic, caller, { log: logger });
console.log("Result:", result);
console.log("Extrinsic id::", extrinsicId);
// const [proxyEvent, futurepassEvent] = filterExtrinsicEvents(result.events, [
// "FeeProxy.CallWithFeePreferences",
// "Futurepass.ProxyExecuted",
// ]);
});src/fundMultiSigWallet.ts2.42.2.3
ts
import { sendExtrinsic } from "@trne/utils/sendExtrinsic";
import { withChainApi } from "@trne/utils/withChainApi";
export const command = "fundMultiSigWallet";
export const desc = "Fund a multisig wallet with xrp";
// Multi sig wallet used is 0xe944FAd69B79125706D2481f58b66fcDbED358d7
// Update the env CALLER_PRIVATE_KEY with funder account
withChainApi("porcini", async (api, caller, logger) => {
const multiSigWallet = "0xe944FAd69B79125706D2481f58b66fcDbED358d7";
const xrp = 2;
const amount = 100000;
const extrinsic = await api.tx.assets.transfer(xrp, multiSigWallet, amount);
const { result, extrinsicId } = await sendExtrinsic(extrinsic, caller, { log: logger });
// const response = await submitExtrinsic(extrinsic, caller);
console.log("Result:", result);
console.log("Extrinsic id::", extrinsicId);
});src/signBySecondHolder.ts2.42.2.4
ts
import { u8aToHex } from "@polkadot/util";
import { filterExtrinsicEvents } from "@trne/utils/filterExtrinsicEvents";
import { sendExtrinsic } from "@trne/utils/sendExtrinsic";
import { withChainApi } from "@trne/utils/withChainApi";
// Sign multisig proxy extrinsic by second holder, update the env CALLER_PRIVATE_KEY to second holders
// MultiSig wallet used is 0xe944FAd69B79125706D2481f58b66fcDbED358d7
withChainApi("porcini", async (api, caller, logger) => {
console.log("signer.address:", caller.address);
const xrp = 2;
const amount = 100;
const randomAccount = "0xd094275a3eb8D3300faD74370a71633CfedDCA99";
// Extrinsic to be used for multi signature call
const extrinsic = api.tx.assets.transfer(xrp, randomAccount, amount);
const u8a = extrinsic.method.toU8a();
const encodedCallData = u8aToHex(u8a);
// Update the signatoryList with the other two multisig wallet addresses
const signatoryList = [
"0x6D1eFDE1BbF146EF88c360AF255D9d54A5D39408",
"0xFFfFffFF000000000000000000000000000003CD",
];
const multiSigWallet = "0xe944FAd69B79125706D2481f58b66fcDbED358d7";
console.log("signatoryList::", signatoryList);
const threshold = signatoryList.length;
let timepoint = {};
const allEntries = await api.query.multisig.multisigs.entries(multiSigWallet);
allEntries.forEach(
([
{
args: [accountId],
},
value,
]) => {
const time = JSON.parse(value);
timepoint = time.when;
}
);
const maybeTimepoint = api.registry.createType("Option<Timepoint>", timepoint);
const proofSize = 882400098;
console.log("maybeTimepointData::", maybeTimepoint.toHuman());
const maxWeight = api.registry.createType("SpWeightsWeightV2Weight", {
refTime: 646755879000,
proofSize,
});
const multiSigCall = await api.tx.multisig.asMulti(
threshold,
signatoryList,
maybeTimepoint,
encodedCallData,
maxWeight
);
const { result, extrinsicId } = await sendExtrinsic(multiSigCall, caller);
console.log("Result:", result);
console.log("Extrinsic id::", extrinsicId);
const [assetTransferredEvent] = filterExtrinsicEvents(result.events, ["Assets.Transferred"]);
if (assetTransferredEvent) {
const data = assetTransferredEvent.event.data;
console.log("Transfer xrp successfully executed:::", data.toHuman());
} else {
console.log("Something wrong with multi sig signing...");
}
});package.json2.42.2.5
json
{
"scripts": {
"call": "tsx --tsconfig ../../../tsconfig.json",
"call:createMultiSigWallet": "pnpm call src/createMultiSigWallet.ts",
"call:createProxyExtrinsic": "pnpm call src/createProxyExtrinsic.ts",
"call:fundMultiSigWallet": "pnpm call src/fundMultiSigWallet.ts",
"call:signBySecondHolder": "pnpm call src/signBySecondHolder.ts"
}
}Source:
futureversecom/trn-examples, examples/substrate/use-fp-multisig-wallet (deleted) · commit 44c9e16, 2024-10-31 · Apache-2.0, license · part of Code examplesAssociated docs
Where else you might go from here. Following one is optional — this chapter stands without them.
- Back matter — read in order
See also— The whole of Back matter as one continuous page, reprinted from its chapters without change.- Code examples
See also— Futureverse’s code examples for The Root Network: scripts for the native API and the EVM, and two contract projects, each with its README and source.- Contents
See also— Every chapter in this book, each with the one line that tells you what you get by reading it.- Front matter — read in order
See also— The whole of Front matter as one continuous page, reprinted from its chapters without change.- FuturePass
See also— FuturePass, the on-chain identity + proxy account model Futureverse built on The Root Network: what it is, where it lives on chain, and how a holder moves what's in it to their own wallet.- Guides for the code examples
See also— How Futureverse’s TRN code examples get a test account: create one in MetaMask and fund it from the Porcini faucet.- Index · C
See also— 550 index terms that start with C, each linked to the pages that define or use it.- Index · E
See also— 695 index terms that start with E, each linked to the pages that define or use it.