Skip to content

feat: add MultiSig examples #58

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
## Description

Example project that demonstrate how to use Polymesh SDK along with some of its use cases
Example project that demonstrate how to use [Polymesh SDK](https://github.com/PolymeshAssociation/polymesh-sdk) along with some of its use cases

## Setup

### Requirements

- node.js version 14.x
- yarn version 1.x
- node.js [maintained version] (https://github.com/nodejs/release#release-schedule)
- yarn version 1.x (your preferred one should still work with minor translations)

### Installing Dependencies

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
},
"dependencies": {
"@polymeshassociation/local-signing-manager": "^3.1.0",
"@polymeshassociation/polymesh-sdk": "26.2.0-alpha.4",
"@polymeshassociation/polymesh-sdk": "27.0.1",
"bluebird": "^3.7.2",
"dotenv": "^16.0.3"
},
Expand Down
19 changes: 15 additions & 4 deletions src/common/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,21 @@ let api: Polymesh;
/**
* @hidden
*/
export async function getClient(mnemonic?: string): Promise<Polymesh> {
const localSigningManager = await LocalSigningManager.create({
accounts: [mnemonic ? { mnemonic } : { uri: '//Alice' }],
});
export async function getClient(mnemonics?: string | string[]): Promise<Polymesh> {
let localSigningManager: LocalSigningManager;
if (!mnemonics) {
localSigningManager = await LocalSigningManager.create({
accounts: [{ uri: '//Alice' }],
});
} else if (typeof mnemonics === 'string') {
localSigningManager = await LocalSigningManager.create({
accounts: [{ mnemonic: mnemonics }],
});
} else {
localSigningManager = await LocalSigningManager.create({
accounts: mnemonics.map(mnemonic => ({ mnemonic })),
});
}

if (!api) {
api = await Polymesh.connect({
Expand Down
2 changes: 1 addition & 1 deletion src/examples/compliance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { toHumanObject } from '~/common/utils';

/*
This script showcases Compliance related functionality. Covered functionality:
- Setting Compliance rules
- Setting Compliance rules
- Getting existing Compliance rules
- Pausing Compliance rules
- Unpausing Compliance rules
Expand Down
129 changes: 129 additions & 0 deletions src/examples/multiSig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { BigNumber } from '@polymeshassociation/polymesh-sdk';

import { getClient } from '~/common/client';

/*
This script showcases MultiSig related functionality. It:
- Demonstrates signing with different accounts using derivation paths
- Creates a MultiSig
- Accept MultiSig authorizations
- Joins the MultiSig to an Identity
- Signing transactions with different accounts
*/
(async (): Promise<void> => {
console.log('\n----------------------------------------------------------------------\n');
console.log('💡 Creating and using a MultiSig account:\n');
const accountSeed = '//Alice';

// Note: addresses assume `//Alice` as base
const creatorAddress = '5Ge4F7x6oVYQtNzyK2Y9tVWeeRoNMeWU8Rh5LbQ8A7KzpzGt';
const signerOne = '5EeaL92sHzBXti4oDQJLKbHjAAJzzHLVBgYK7pG9KpRKtXHd';
const signerTwo = '5DRygDgq77PN6z9sBkjhDEAoXtWNpSzn2F8yWY3CzUuWAFV6';

console.log('🔌 Connecting to the node...\n');
const api = await getClient([
accountSeed,
`${accountSeed}/creator`, // 5Ge4F7x6oVYQtNzyK2Y9tVWeeRoNMeWU8Rh5LbQ8A7KzpzGt (if seed is //Alice)
`${accountSeed}/signerOne`, // 5EeaL92sHzBXti4oDQJLKbHjAAJzzHLVBgYK7pG9KpRKtXHd (if seed is //Alice)
`${accountSeed}/signerTwo`, // 5DRygDgq77PN6z9sBkjhDEAoXtWNpSzn2F8yWY3CzUuWAFV6 (if seed is //Alice)
]);

// The creator of the MultiSig has to go through a KYC process
const registerCreator = await api.identities.registerIdentity({
targetAccount: creatorAddress,
createCdd: true,
});
const creatorId = await registerCreator.run();

// Transfer POLYX to the creator. Note: The creator's primary key is responsible for all of the MultiSig's fees
const transfer = await api.network.transferPolyx({
to: creatorAddress,
amount: new BigNumber(5000),
});
await transfer.run();
console.log(
`🚀 Creator account DID ("${creatorId.did}") created and account was seeded with POLYX\n`
);

const [signerOneAccount, signerTwoAccount] = await Promise.all([
api.accountManagement.getAccount({ address: signerOne }),
api.accountManagement.getAccount({ address: signerTwo }),
]);

const createMultiSig = await api.accountManagement.createMultiSigAccount(
{
signers: [signerOneAccount, signerTwoAccount],
requiredSignatures: new BigNumber(2),
},
{
signingAccount: creatorAddress,
}
);
const multiSig = await createMultiSig.run();

console.log(`ℹ️ multiSig created. Address: "${multiSig.address}"\n`);

// Each signer has to accept joining the MultiSig, here we perform both actions in parallel
const [signerOneAuths, signerTwoAuths] = await Promise.all([
signerOneAccount.authorizations.getReceived(),
signerTwoAccount.authorizations.getReceived(),
]);

const [signerOneAccept, signerTwoAccept] = await Promise.all([
signerOneAuths[0].accept({ signingAccount: signerOne }),
signerTwoAuths[0].accept({ signingAccount: signerTwo }),
]);

await Promise.all([await signerOneAccept.run(), await signerTwoAccept.run()]);
console.log('👥 Signers accepted the MultiSig authorization\n');

/**
* Submitting transactions for a MultiSig is a bit different compared to regular accounts.
*
* Because the signing account is a multiSig signer, the transaction needs to be wrapped as
* a proposal, which will then need to be accepted. `runAsProposal` must be called instead of
* the usual `.run`.
*
* Generically the procedure's `.multiSig` param can be checked, if set the `runAsProposal`
* should be called
*/
const createPortfolio = await api.identities.createPortfolio(
{
name: 'MultiSig Portfolio',
},
{ signingAccount: signerOne }
);

console.log('📝 submitting proposal for MultiSig', createPortfolio.multiSig?.address); // Since `.multiSig` is set, `runAsProposal` should be used
const portfolioProposal = await createPortfolio.runAsProposal();
console.log(`ℹ️ proposal created id: ${portfolioProposal.id.toString()}\n`);

/**
* Accepting and rejecting proposal transactions will not be wrapped even though the
* signer is a MultiSig signer the transaction will not be wrapped as a proposal.
*/
const acceptProposal = await portfolioProposal.approve({ signingAccount: signerTwo });
// acceptProposal.multiSig - will be null, since approve is not wrapped as a proposal
await acceptProposal.run();
console.log(`✅ Account: ${signerTwo} approved proposal ${portfolioProposal.id.toString()}\n`);

const reserveTicker = await api.assets.reserveTicker(
{
ticker: 'MULTI',
},
{ signingAccount: signerOne }
);

const reserveProposal = await reserveTicker.runAsProposal();
console.log(`ℹ️ proposal created id: ${reserveProposal.id.toString()}\n`);

/**
* Rejecting a proposal is similar to accepting one
*/
const rejectProposal = await reserveProposal.reject({ signingAccount: signerTwo });
await rejectProposal.run();
console.log(`❌ Account: ${signerTwo} rejected proposal: ${reserveProposal.id.toString()}`);

console.log('🔌 Disconnecting from the node...\n');
await api.disconnect();
})();
Loading
Loading