UNPKG

@mysten/sui

Version:
193 lines (150 loc) 7.06 kB
# Multi-Signature Transactions > Create multi-signature transactions with multiple signers The Sui TypeScript SDK provides a `MultiSigPublicKey` class to support [Multi-Signature](https://docs.sui.io/concepts/cryptography/transaction-auth/multisig) (MultiSig) transaction and personal message signing. This class implements the same interface as the `PublicKey` classes that [Keypairs](./keypairs) uses and you call the same methods to verify signatures for `PersonalMessages` and `Transactions`. ## Creating a MultiSigPublicKey To create a `MultiSigPublicKey`, you provide a `threshold`(u16) value and an array of objects that contain `publicKey` and `weight`(u8) values. If the combined weight of valid signatures for a transaction is equal to or greater than the threshold value, then the Sui network considers the transdaction valid. ```typescript const kp1 = new Ed25519Keypair(); const kp2 = new Ed25519Keypair(); const kp3 = new Ed25519Keypair(); const multiSigPublicKey = MultiSigPublicKey.fromPublicKeys({ threshold: 2, publicKeys: [ { publicKey: kp1.getPublicKey(), weight: 1, }, { publicKey: kp2.getPublicKey(), weight: 1, }, { publicKey: kp3.getPublicKey(), weight: 2, }, ], }); const multisigAddress = multiSigPublicKey.toSuiAddress(); ``` The `multiSigPublicKey` in the preceding code enables you to verify that signatures have a combined weight of at least `2`. A signature signed with only `kp1` or `kp2` is not valid, but a signature signed with both `kp1` and `kp2`, or just `kp3` is valid. ## Combining signatures with a MultiSigPublicKey To sign a message or transaction for a MultiSig address, you must collect signatures from the individual key pairs, and then combine them into a signature using the `MultiSigPublicKey` class for the address. ```typescript // This example uses the same imports, key pairs, and multiSigPublicKey from the previous example const message = new TextEncoder().encode('hello world'); const signature1 = (await kp1.signPersonalMessage(message)).signature; const signature2 = (await kp2.signPersonalMessage(message)).signature; const combinedSignature = multiSigPublicKey.combinePartialSignatures([signature1, signature2]); const isValid = await multiSigPublicKey.verifyPersonalMessage(message, combinedSignature); ``` ## Creating a MultiSigSigner The `MultiSigSigner` class allows you to create a Signer that can be used to sign personal messages and Transactions like any other keypair or signer class. This is often easier than manually combining signatures, since many methods accept Signers and handle signing directly. A `MultiSigSigner` is created by providing the underlying Signers to the `getSigner` method on the `MultiSigPublicKey`. You can provide a subset of the Signers that make up the public key, so long as their combined weight is equal to or greater than the threshold. ```typescript const kp1 = new Ed25519Keypair(); const kp2 = new Ed25519Keypair(); const multiSigPublicKey = MultiSigPublicKey.fromPublicKeys({ threshold: 1, publicKeys: [ { publicKey: kp1.getPublicKey(), weight: 1, }, { publicKey: kp2.getPublicKey(), weight: 1, }, ], }); const signer = multiSigPublicKey.getSigner(kp1); const message = new TextEncoder().encode('hello world'); const { signature } = await signer.signPersonalMessage(message); const isValid = await multiSigPublicKey.verifyPersonalMessage(message, signature); ``` ## Multisig with zkLogin You can use zkLogin to participate in multisig just like keys for other signature schemes. Unlike other keys that come with a public key, you define a public identifier for zkLogin. For example, the following example creates a 1-out-of-2 multisig with a single key and a zkLogin public identifier: ```typescript // a single Ed25519 keypair and its public key. const kp1 = new Ed25519Keypair(); const pkSingle = kp1.getPublicKey(); // compute the address seed based on user salt and jwt token values. const decodedJWT = decodeJwt('a valid jwt token here'); const userSalt = BigInt('123'); // a valid user salt const addressSeed = genAddressSeed(userSalt, 'sub', decodedJwt.sub, decodedJwt.aud).toString(); // a zkLogin public identifier derived from an address seed and an iss string. let pkZklogin = toZkLoginPublicIdentifier(addressSeed, decodedJwt.iss); // derive multisig address from multisig public key defined by the single key and zkLogin public // identifier with weight and threshold. const multiSigPublicKey = MultiSigPublicKey.fromPublicKeys({ threshold: 1, publicKeys: [ { publicKey: pkSingle, weight: 1 }, { publicKey: pkZklogin, weight: 1 }, ], }); // this is the sender of any transactions from this multisig account. const multisigAddress = multiSigPublicKey.toSuiAddress(); // create a regular zklogin signature from the zkproof and ephemeral signature for zkLogin. // see zklogin-integration.mdx for more details. const zkLoginSig = getZkLoginSignature({ inputs: zkLoginInputs, maxEpoch: '2', userSignature: fromBase64(ephemeralSig), }); // a valid multisig with just the zklogin signature. const multisig = multiSigPublicKey.combinePartialSignatures([zkLoginSig]); ``` ### Benefits and Design for zkLogin in Multisig Because zkLogin assumes the application client ID and its issuer (such as Google) liveliness, using zkLogin with multisig provides improved recoverability to a zkLogin account. In the previous example of 1-out-of-2 multisig, users can use zkLogin in their regular wallet flow, but if the application or the issuer is deprecated, the user can still use the regular private key account to access funds in the multisig wallet. This also opens the door to design multisig across any number of zkLogin accounts and of different providers (max number is capped at 10 accounts) with customizable weights and thresholds. For example, you can set up a multisig address with threshold of 2, where the public keys or identifiers are defined as: 1. Charlie's own Google account with weight 2 2. Charlie's friend Alice's Apple account with weight 1 3. Charlie's friend Bob's Facebook account with weight 1 In this case, Charlie can always use their Google account for transactions out of the multisig address for the threshold. At the same time, Charlie still has access to his account by combining partial signatures from Alice and Bob. ## Multisig with Passkey You can use a `PasskeyKeypair` (a `Keypair`) and `PasskeyPublicKey` (a `PublicKey`) as components in a Multisig setup. Once initialized, they support both Multisig address derivation and transaction signing. ```typescript const passkeyKeypair = await PasskeyKeypair.getPasskeyInstance( new BrowserPasskeyProvider('Sui Passkey Example', { rpName: 'Sui Passkey Example', rpId: window.location.hostname, } as BrowserPasswordProviderOptions), ); const passkeyPublicKey = passkeyKeypair.getPublicKey(); const multiSigPublicKey = MultiSigPublicKey.fromPublicKeys({ threshold: 1, publicKeys: [ { publicKey: passkeyPublicKey, weight: 1 }, // other keys ], }); ```