npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@anastasia-labs/aiken-multisig-offchain

v1.0.0

Published

Off-Chain SDK for Aiken Upgradable Multisig

Downloads

59

Readme

Table of Contents

Aiken Upgradable Multisig Offchain

Introduction

The Aiken Multisig Offchain is a typescript based SDK built to conveniently interact with an Aiken based secure and flexible multi-signature smart contract designed specifically for the Cardano Blockchain. It provides developers with an easy-to-use interface to manage multisig wallets, enabling secure and flexible transactions that require multiple authorized signatures.

Key features:

  • Secure Spending of Assets: Ensure that asset transactions can only be executed by authorized members.
  • Seamless Adjustment of Signer Thresholds: Adjust the required number of signatures needed to approve transactions without compromising security.
  • Addition or Removal of Signers: Update the list of signatories and threshold as needed.
  • Spending Limits Enforcement: Define and enforce spending limits for transactions to enhance security.
  • Asset Support: Manage both ADA and other Cardano native tokens.

This project is funded by the Cardano Treasury in Catalyst Fund 11

Documentation

What is a Multisignature (Multisig) Contract?

A multisignature (multisig) contract is a smart contract that requires multiple parties to authorize a transaction before it can be executed. This adds an extra layer of security by distributing the approval authority among multiple trusted signatories. Multisig contracts are commonly used in scenarios where assets need to be managed collectively, such as joint accounts, corporate treasury management, or any situation where shared control over funds is desired.

How Does This Project Facilitate Multisig Transactions?

This project provides an off-chain SDK to interact along with our Aiken Upgradable Multisig. The contract allows authorized members to execute asset transactions within predefined thresholds. It fullfills the requirements of an upgradable multisig by enabling:

  • Transaction Approval: Ensure transactions execute only with the required number of signatures.
  • Signer Management: Add or remove signers to reflect organizational changes.
  • Threshold Adjustment: Seamlessly adjust the signature threshold as needed.
  • Spending Limits: Define and enforce maximum withdrawal amounts per transaction.

Design Documentation

For a comprehensive understanding of the contract's architecture, design decisions, and implementation details, please refer to the Design Documentation. This documentation provides in-depth insights into the contract's design, including its components, and detailed explanations of its functionality.

Usage Example

Install package

npm install @anastasia-labs/aiken-multisig-offchain

or

pnpm install @anastasia-labs/aiken-multisig-offchain

Setup Lucid & Multisig Scripts

// You can get the compiled scripts here: https://github.com/Anastasia-Labs/direct-offer/tree/master/compiled
import Script from "../src/validator/multisig_validator.json" assert { type: "json" };

const lucid = await Lucid(
  new Maestro({
    network: "Preprod", // For MAINNET: "Mainnet"
    apiKey: "<Your-API-Key>", // Get yours by visiting https://docs.gomaestro.org/docs/Getting-started/Sign-up-login
    turboSubmit: false, // Read about paid turbo transaction submission feature at https://docs.gomaestro.org/docs/Dapp%20Platform/Turbo%20Transaction
  }),
  "Preprod" // For MAINNET: "Mainnet"
);

lucid.selectWallet.fromPrivateKey("your secret key here e.g. ed25519_...");

const multiSigVal: SpendingValidator = {
  type: "PlutusV2",
  script: Script.validators[0].compiledCode,
};

const multisigScripts = {
  multisig: multisigScript.script,
};

Initiate Multisig Contract

import { initiateMultisig, InitiateMultisigConfig } from "@anastasia-labs/aiken-multisig-offchain";

// Define signatories' public key hashes
const initiatorPkh = getAddressDetails(initiatorAddress).paymentCredential?.hash!;
const signer1Pkh = getAddressDetails(signer1Address).paymentCredential?.hash!;
const signer2Pkh = getAddressDetails(signer2Address).paymentCredential?.hash!;

// Configure the multisig parameters
const initConfig: InitiateMultisigConfig = {
  signers: [initiatorPkh, signer1Pkh, signer2Pkh],
  threshold: 2n, // Require two out of three signatures
  funds: {
    policyId: "", // For ADA, leave empty
    assetName: "", // For ADA, leave empty
  },
  spendingLimit: 10_000_000n, // 10 ADA in lovelace
  minimumAda: 2_000_000n, // Minimum ADA required in lovelace
  scripts: multisigScripts,
};

// Initiate the multisig contract
const initTxUnsigned = await initiateMultisig(lucid, initConfig);

if (initTxUnsigned.type === "ok") {
  // Sign the transaction with the initiator's wallet
  const initTxSigned = await initTxUnsigned.data.sign().complete();
  const initTxHash = await initTxSigned.submit();
  console.log(`Multisig Contract Initiated: ${initTxHash}`);
} else {
  console.error("Failed to initiate multisig contract:", initTxUnsigned.error);
}

Sign

import { validateSign, ValidateSignConfig } from "@anastasia-labs/aiken-multisig-offchain";

// Configure the sign transaction
const validateSignConfig: ValidateSignConfig = {
  withdrawalAmount: 5_000_000n, // Amount to withdraw in lovelace
  recipientAddress: recipientAddress, // Address to receive the funds
  signersList: [initiatorPkh, signer1Pkh], // Signatories participating
  scripts: multisigScripts,
};

// Validate and prepare the transaction
const signTxUnsigned = await validateSign(lucid, validateSignConfig);

if (signTxUnsigned.type === "ok") {
  const partialSignatures: string[] = [];

  // Collect partial signatures from each signatory
  for (const signerSeed of [users.initiator.seedPhrase, users.signer1.seedPhrase]) {
    lucid.selectWalletFromSeed(signerSeed);
    const partialSign = await signTxUnsigned.data.partialSign.withWallet();
    partialSignatures.push(partialSign);
  }

  // Assemble and complete the transaction
  const assembledTx = signTxUnsigned.data.assemble(partialSignatures);
  const completeTx = await assembledTx.complete();
  const signTxHash = await completeTx.submit();
  console.log(`Transaction Signed and Submitted: ${signTxHash}`);
} else {
  console.error("Failed to sign transaction:", signTxUnsigned.error);
}

Update Multisig Contract

Adjust Signer Threshold

import { validateUpdate, UpdateValidateConfig } from "@anastasia-labs/aiken-multisig-offchain";

// Adjust the threshold to require all three signatures
const updateConfig: UpdateValidateConfig = {
  newSigners: [initiatorPkh, signer1Pkh, signer2Pkh],
  newThreshold: 3n,
  funds: {
    policyId: "",
    assetName: "",
  },
  newSpendingLimit: 15_000_000n,
  minimumAda: 2_000_000n,
  scripts: multisigScripts,
};

// Validate and prepare the update transaction
const updateTxUnsigned = await validateUpdate(lucid, updateConfig);

if (updateTxUnsigned.type === "ok") {
  const partialSignatures: string[] = [];

  // Collect partial signatures from each signatory
  for (const signerSeed of [users.initiator.seedPhrase, users.signer1.seedPhrase, users.signer2.seedPhrase]) {
    lucid.selectWalletFromSeed(signerSeed);
    const partialSign = await updateTxUnsigned.data.partialSign.withWallet();
    partialSignatures.push(partialSign);
  }

  // Assemble and complete the transaction
  const assembledTx = updateTxUnsigned.data.assemble(partialSignatures);
  const completeTx = await assembledTx.complete();
  const updateTxHash = await completeTx.submit();
  console.log(`Multisig Contract Updated: ${updateTxHash}`);
} else {
  console.error("Failed to update multisig contract:", updateTxUnsigned.error);
}

Add or Remove Signers

Adding a New Signer:

// Add a new signer
const signer3Pkh = getAddressDetails(signer3Address).paymentCredential?.hash!;

// Update the signers list and threshold
const addSignerConfig: UpdateValidateConfig = {
  newSigners: [initiatorPkh, signer1Pkh, signer2Pkh, signer3Pkh],
  newThreshold: 3n,
  funds: {
    policyId: "",
    assetName: "",
  },
  newSpendingLimit: 20_000_000n,
  minimumAda: 2_000_000n,
  scripts: multisigScripts,
};

// Proceed with validation and signing as shown in the update example

Removing a Signer:

// Remove a signer (e.g., signer2)
const updatedSigners = [initiatorPkh, signer1Pkh];

// Update the signers list and threshold
const removeSignerConfig: UpdateValidateConfig = {
  newSigners: updatedSigners,
  newThreshold: 2n,
  funds: {
    policyId: "",
    assetName: "",
  },
  newSpendingLimit: 10_000_000n,
  minimumAda: 2_000_000n,
  scripts: multisigScripts,
};

// Proceed with validation and signing as shown in the update example

Local Build

In the main directory

pnpm run build

Test framework

https://github.com/vitest-dev/vitest

Running Tests

pnpm test

aiken-multisig-offchain

Test Results:

aiken-multisig-tests