Skip to content

Commit

Permalink
feat: add max-amount and auth-id params
Browse files Browse the repository at this point in the history
  • Loading branch information
hstove authored and janniks committed Mar 5, 2024
1 parent 115847b commit 36558cf
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 10 deletions.
105 changes: 101 additions & 4 deletions packages/stacking/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,10 @@ export interface LockStxOptions {
signerKey?: string;
/** hex-encoded signature `(buff 65)`, required for >= PoX-4 */
signerSignature?: string;
/** Maximum amount of STX that can be locked in this transaction */
maxAmount?: IntegerType;
/** Random integer to prevent re-use of signer signature */
authId?: number;
}

/**
Expand All @@ -247,6 +251,10 @@ export interface StackExtendOptions {
signerKey?: string;
/** hex-encoded signature `(buff 65)`, required for >= PoX-4 */
signerSignature?: string;
/** Maximum amount of STX that can be locked in this transaction */
maxAmount?: IntegerType;
/** Random integer to prevent re-use of signer signature */
authId?: number;
}

/**
Expand All @@ -257,6 +265,14 @@ export interface StackIncreaseOptions {
privateKey: string;
/** number of ustx to increase by */
increaseBy: IntegerType;
/** hex-encoded signer key `(buff 33)`, required for >= PoX-4 */
signerKey?: string;
/** hex-encoded signature `(buff 65)`, required for >= PoX-4 */
signerSignature?: string;
/** Maximum amount of STX that can be locked in this transaction */
maxAmount?: IntegerType;
/** Random integer to prevent re-use of signer signature */
authId?: number;
}

/**
Expand Down Expand Up @@ -331,6 +347,10 @@ export interface StackAggregationCommitOptions {
signerKey?: string;
/** hex-encoded signature `(buff 65)`, required for >= PoX-4 */
signerSignature?: string;
/** Maximum amount of STX that can be locked in this transaction */
maxAmount?: IntegerType;
/** Random integer to prevent re-use of signer signature */
authId?: number;
}

export interface StackAggregationIncreaseOptions {
Expand Down Expand Up @@ -623,6 +643,8 @@ export class StackingClient {
burnBlockHeight,
signerKey,
signerSignature,
maxAmount,
authId,
...txOptions
}: LockStxOptions & BaseTxOptions): Promise<TxBroadcastResult> {
const poxInfo = await this.getPoxInfo();
Expand All @@ -631,7 +653,7 @@ export class StackingClient {
const contract = await this.getStackingContract(poxOperationInfo);

ensureLegacyBtcAddressForPox1({ contract, poxAddress });
ensureSignerArgsReadiness({ contract, signerKey, signerSignature });
ensureSignerArgsReadiness({ contract, signerKey, signerSignature, maxAmount, authId });

const callOptions = this.getStackOptions({
contract,
Expand All @@ -641,6 +663,8 @@ export class StackingClient {
burnBlockHeight,
signerKey,
signerSignature,
maxAmount,
authId,
});
const tx = await makeContractCall({
...callOptions,
Expand All @@ -663,20 +687,30 @@ export class StackingClient {
poxAddress,
signerKey,
signerSignature,
maxAmount,
authId,
...txOptions
}: StackExtendOptions & BaseTxOptions): Promise<TxBroadcastResult> {
const poxInfo = await this.getPoxInfo();
const poxOperationInfo = await this.getPoxOperationInfo(poxInfo);

ensurePox2Activated(poxOperationInfo);
ensureSignerArgsReadiness({ contract: poxInfo.contract_id, signerKey, signerSignature });
ensureSignerArgsReadiness({
contract: poxInfo.contract_id,
signerKey,
signerSignature,
maxAmount,
authId,
});

const callOptions = this.getStackExtendOptions({
contract: poxInfo.contract_id,
extendCycles,
poxAddress,
signerKey,
signerSignature,
maxAmount,
authId,
});
const tx = await makeContractCall({
...callOptions,
Expand All @@ -694,15 +728,30 @@ export class StackingClient {
*/
async stackIncrease({
increaseBy,
signerKey,
signerSignature,
maxAmount,
authId,
...txOptions
}: StackIncreaseOptions & BaseTxOptions): Promise<TxBroadcastResult> {
const poxInfo = await this.getPoxInfo();
const poxOperationInfo = await this.getPoxOperationInfo(poxInfo);
ensurePox2Activated(poxOperationInfo);
ensureSignerArgsReadiness({
contract: poxInfo.contract_id,
signerKey,
signerSignature,
maxAmount,
authId,
});

const callOptions = this.getStackIncreaseOptions({
contract: poxInfo.contract_id,
increaseBy,
signerKey,
signerSignature,
maxAmount,
authId,
});
const tx = await makeContractCall({
...callOptions,
Expand Down Expand Up @@ -857,6 +906,8 @@ export class StackingClient {
rewardCycle,
signerKey,
signerSignature,
maxAmount,
authId,
...txOptions
}: StackAggregationCommitOptions & BaseTxOptions): Promise<TxBroadcastResult> {
const contract = await this.getStackingContract();
Expand Down Expand Up @@ -988,6 +1039,8 @@ export class StackingClient {
burnBlockHeight,
signerKey,
signerSignature,
maxAmount,
authId,
}: {
cycles: number;
poxAddress: string;
Expand All @@ -996,6 +1049,8 @@ export class StackingClient {
burnBlockHeight: number;
signerKey?: string;
signerSignature?: string;
maxAmount?: IntegerType;
authId?: number;
}) {
const address = poxAddressToTuple(poxAddress);
const [contractAddress, contractName] = this.parseContractId(contract);
Expand All @@ -1009,6 +1064,8 @@ export class StackingClient {

if (signerSignature) functionArgs.push(someCV(bufferCV(hexToBytes(signerSignature))));
if (signerKey) functionArgs.push(bufferCV(hexToBytes(signerKey)));
if (maxAmount) functionArgs.push(uintCV(maxAmount));
if (authId) functionArgs.push(uintCV(authId));

const callOptions: ContractCallOptions = {
contractAddress,
Expand All @@ -1028,12 +1085,16 @@ export class StackingClient {
contract,
signerKey,
signerSignature,
maxAmount,
authId,
}: {
extendCycles: number;
poxAddress: string;
contract: string;
signerKey?: string;
signerSignature?: string;
maxAmount?: IntegerType;
authId?: number;
}) {
const address = poxAddressToTuple(poxAddress);
const [contractAddress, contractName] = this.parseContractId(contract);
Expand All @@ -1042,6 +1103,8 @@ export class StackingClient {

if (signerSignature) functionArgs.push(someCV(bufferCV(hexToBytes(signerSignature))));
if (signerKey) functionArgs.push(bufferCV(hexToBytes(signerKey)));
if (maxAmount) functionArgs.push(uintCV(maxAmount));
if (authId) functionArgs.push(uintCV(authId));

const callOptions: ContractCallOptions = {
contractAddress,
Expand All @@ -1055,13 +1118,35 @@ export class StackingClient {
return callOptions;
}

getStackIncreaseOptions({ increaseBy, contract }: { increaseBy: IntegerType; contract: string }) {
getStackIncreaseOptions({
increaseBy,
contract,
signerKey,
signerSignature,
maxAmount,
authId,
}: {
increaseBy: IntegerType;
contract: string;
signerKey?: string;
signerSignature?: string;
maxAmount?: IntegerType;
authId?: number;
}) {
const [contractAddress, contractName] = this.parseContractId(contract);

const functionArgs = [uintCV(increaseBy)] as ClarityValue[];

if (signerSignature) functionArgs.push(someCV(bufferCV(hexToBytes(signerSignature))));
if (signerKey) functionArgs.push(bufferCV(hexToBytes(signerKey)));
if (maxAmount) functionArgs.push(uintCV(maxAmount));
if (authId) functionArgs.push(uintCV(authId));

const callOptions: ContractCallOptions = {
contractAddress,
contractName,
functionName: 'stack-increase',
functionArgs: [uintCV(increaseBy)],
functionArgs,
validateWithAbi: true,
network: this.network,
anchorMode: AnchorMode.Any,
Expand Down Expand Up @@ -1197,12 +1282,16 @@ export class StackingClient {
rewardCycle,
signerKey,
signerSignature,
maxAmount,
authId,
}: {
contract: string;
poxAddress: string;
rewardCycle: number;
signerKey?: string;
signerSignature?: string;
maxAmount?: IntegerType;
authId?: number;
}) {
const address = poxAddressToTuple(poxAddress);
const [contractAddress, contractName] = this.parseContractId(contract);
Expand All @@ -1211,6 +1300,8 @@ export class StackingClient {

if (signerSignature) functionArgs.push(someCV(bufferCV(hexToBytes(signerSignature))));
if (signerKey) functionArgs.push(bufferCV(hexToBytes(signerKey)));
if (maxAmount) functionArgs.push(uintCV(maxAmount));
if (authId) functionArgs.push(uintCV(authId));

const callOptions: ContractCallOptions = {
contractAddress,
Expand Down Expand Up @@ -1452,12 +1543,16 @@ export class StackingClient {
rewardCycle,
period,
signerPrivateKey,
authId,
maxAmount,
}: {
topic: `${Pox4SignatureTopic}`;
poxAddress: string;
rewardCycle: number;
period: number;
signerPrivateKey: StacksPrivateKey;
maxAmount: IntegerType;
authId: number;
}) {
// todo: in the future add logic to determine if a later version of pox
// needs a different domain and thus use a different `signPox4SignatureHash`
Expand All @@ -1468,6 +1563,8 @@ export class StackingClient {
period,
network: this.network,
privateKey: signerPrivateKey,
maxAmount,
authId,
});
}
}
Expand Down
29 changes: 24 additions & 5 deletions packages/stacking/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { sha256 } from '@noble/hashes/sha256';
import { bech32, bech32m } from '@scure/base';
import { bigIntToBytes } from '@stacks/common';
import { IntegerType, bigIntToBytes } from '@stacks/common';
import {
base58CheckDecode,
base58CheckEncode,
Expand Down Expand Up @@ -368,19 +368,25 @@ export function ensureSignerArgsReadiness({
contract,
signerKey,
signerSignature,
maxAmount,
authId,
}: {
contract: string;
signerKey?: string;
signerSignature?: string;
maxAmount?: IntegerType;
authId?: number;
}) {
const hasMaxAmount = typeof maxAmount !== 'undefined';
const hasAuthId = typeof authId !== 'undefined';
if (/\.pox(-[2-3])?$/.test(contract)) {
// .pox, .pox-2 or .pox-3
if (signerKey || signerSignature) {
if (signerKey || signerSignature || hasMaxAmount || hasAuthId) {
throw new Error('PoX-1, PoX-2 and PoX-3 do not accept a signer-key or signer-sig');
}
} else {
// .pox-4 or later
if (!signerKey || !signerSignature) {
if (!signerKey || !signerSignature || !hasMaxAmount || !hasAuthId) {
throw new Error(
'PoX-4 or later requires a signer-key (buff 33) and signer-sig (buff 65) to stack'
);
Expand All @@ -392,6 +398,7 @@ export enum Pox4SignatureTopic {
StackStx = 'stack-stx',
AggregateCommit = 'agg-commit',
StackExtend = 'stack-extend',
StackIncrease = 'stack-increase',
}

export interface Pox4SignatureOptions {
Expand All @@ -403,6 +410,10 @@ export interface Pox4SignatureOptions {
/** lock period (in cycles) */
period: number;
network: StacksNetwork;
/** Maximum amount of uSTX that can be locked during this function call */
maxAmount: IntegerType;
/** Random integer to prevent signature re-use */
authId: number;
}

/**
Expand All @@ -415,9 +426,11 @@ export function signPox4SignatureHash({
period,
network,
privateKey,
maxAmount,
authId,
}: Pox4SignatureOptions & { privateKey: StacksPrivateKey }) {
return signStructuredData({
...pox4SignatureMessage({ topic, poxAddress, rewardCycle, period, network }),
...pox4SignatureMessage({ topic, poxAddress, rewardCycle, period, network, maxAmount, authId }),
privateKey,
}).data;
}
Expand All @@ -434,11 +447,13 @@ export function verifyPox4SignatureHash({
network,
publicKey,
signature,
maxAmount,
authId,
}: Pox4SignatureOptions & { publicKey: string; signature: string }) {
return verifyMessageSignatureRsv({
message: sha256(
encodeStructuredData(
pox4SignatureMessage({ topic, poxAddress, rewardCycle, period, network })
pox4SignatureMessage({ topic, poxAddress, rewardCycle, period, network, maxAmount, authId })
)
),
publicKey,
Expand All @@ -456,12 +471,16 @@ export function pox4SignatureMessage({
rewardCycle,
period: lockPeriod,
network,
maxAmount,
authId,
}: Pox4SignatureOptions) {
const message = tupleCV({
'pox-addr': poxAddressToTuple(poxAddress),
'reward-cycle': uintCV(rewardCycle),
topic: stringAsciiCV(topic),
period: uintCV(lockPeriod),
'max-amount': uintCV(maxAmount),
'auth-id': uintCV(authId),
});
const domain = tupleCV({
name: stringAsciiCV('pox-4-signer'),
Expand Down
Loading

0 comments on commit 36558cf

Please sign in to comment.