feat: add multisig-stx-transfer

This commit is contained in:
friedger
2024-03-16 16:12:48 +01:00
parent 3f8db427ab
commit 2c8aceb3b3
2 changed files with 99 additions and 1 deletions

View File

@@ -7,6 +7,7 @@
"type": "module",
"scripts": {
"multisig-plan": "node --no-warnings=ExperimentalWarning --loader ts-node/esm ./scripts/create-multisig-deployment-plan.ts",
"multisig-stx-transfer": "node --no-warnings=ExperimentalWarning --loader ts-node/esm ./scripts/create-multisig-stx-transfer-plan.ts",
"multisig-sign": "node --no-warnings=ExperimentalWarning --loader ts-node/esm ./scripts/sign-multisig-deployment-plan.ts",
"multisig-broadcast": "node --no-warnings=ExperimentalWarning --loader ts-node/esm ./scripts/broadcast-multisig-deployment-plan.ts",
"multisig-analyse": "node --no-warnings=ExperimentalWarning --loader ts-node/esm ./scripts/analyse-multisig-deployment-plan.ts",
@@ -22,7 +23,7 @@
"license": "BSL",
"prettier": "@stacks/prettier-config",
"dependencies": {
"@hirosystems/clarinet-sdk": "2.4.0-beta2",
"@hirosystems/clarinet-sdk": "2.4.0-beta3",
"@stacks/stacking": "6.11.4-pr.36558cf.0",
"@stacks/prettier-config": "^0.0.10",
"@stacks/transactions": "^6.9.0",

View File

@@ -0,0 +1,97 @@
// SPDX-License-Identifier: BUSL-1.1
import { bytesToHex } from '@stacks/common';
import {
Address,
AddressHashMode,
AnchorMode,
StacksPublicKey,
StacksTransaction,
addressToString,
createMultiSigSpendingCondition,
makeUnsignedSTXTokenTransfer,
} from '@stacks/transactions';
import fs from 'fs';
import { getNetwork, getStacksAddress, getStacksPubkeys } from './config.ts';
import { assertSigner, planFile, verboseLog } from './utils.ts';
const lisaDaoContractName = 'lisa-dao';
const network = getNetwork();
const address = getStacksAddress();
const pubKeys = getStacksPubkeys();
let nonce = 27;
const feeMultiplier = 100; // transaction bytes * feeMultiplier
const feeAddition = 1; // add a flat amount on top
const feeCap = 0; //15 * 1000000; // 15 STX
const multisigSpendConditionByteLength = 66; // don't change
let tempTotalFee = 0n;
verboseLog(`Using address ${addressToString(address)}`);
async function createMultisigStxTransaction(
amount: bigint,
recipient: string,
feeMultiplier: number,
nonce: number,
numSignatures: number,
pubkeys: StacksPublicKey[],
signer: Address
) {
const publicKeys = pubkeys.map(pk => bytesToHex(pk.data));
const tx = await makeUnsignedSTXTokenTransfer({
numSignatures,
publicKeys,
recipient,
fee: 1,
nonce,
network,
amount,
anchorMode: AnchorMode.OnChainOnly,
});
// makeUnsignedContractCall() forces a AddressHashMode.SerializeP2SH spending condition, so we construct it manually
// and replace it.
tx.auth.spendingCondition = createMultiSigSpendingCondition(
AddressHashMode.SerializeP2WSH,
numSignatures,
publicKeys,
nonce,
1
);
assertSigner(tx.auth.spendingCondition, signer);
let calculatedFee =
(tx.serialize().byteLength + multisigSpendConditionByteLength * pubKeys.length) *
feeMultiplier +
feeAddition;
if (feeCap > 0 && calculatedFee > feeCap) calculatedFee = feeCap;
tx.setFee(calculatedFee);
tempTotalFee += BigInt(calculatedFee);
return tx;
}
// adding fields on the unsigned tx makes it easier to manage
function addPubkeyFields(tx: StacksTransaction, pubKeys: StacksPublicKey[]) {
for (const pk of pubKeys) tx.appendPubkey(pk);
return tx;
}
const addressString = addressToString(address);
(async () => {
const fundingTx = await createMultisigStxTransaction(
394399976n - 52501n,
`${addressString}.${lisaDaoContractName}`,
feeMultiplier,
nonce++,
pubKeys.length,
pubKeys,
address
);
return [bytesToHex(addPubkeyFields(fundingTx, pubKeys).serialize())];
})().then(plan => {
fs.writeFileSync(planFile, JSON.stringify(plan), 'utf-8');
verboseLog(`Last nonce is ${nonce}, total fee: ${Number(tempTotalFee) / 1000000} STX`);
console.log(`Deploy plan written to ${planFile}, total of ${plan.length} transactions`);
});