Skip to content

Commit f9cab3a

Browse files
feat(sdk-coin-stx): add support to nakamoto updgrade stack-stx tx
EA-3369 TICKET: EA-3369
1 parent c8d69d0 commit f9cab3a

File tree

6 files changed

+157
-18
lines changed

6 files changed

+157
-18
lines changed

modules/sdk-coin-stx/src/lib/constants.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
export const FUNCTION_NAME_SENDMANY = 'send-many';
22
export const CONTRACT_NAME_SENDMANY = 'send-many-memo';
3-
export const CONTRACT_NAME_STAKING = 'pox-3';
3+
export const CONTRACT_NAME_STAKING = 'pox-4';
4+
5+
// TODO: remove support to this contract version after STX fork
6+
// https://bitgoinc.atlassian.net/browse/EA-3482
7+
export const BACKWARD_COMPATIBILITY_CONTRACT_NAME_STAKING = 'pox-3';
48

59
export const VALID_CONTRACT_FUNCTION_NAMES = [
610
'stack-stx',

modules/sdk-coin-stx/src/lib/contractBuilder.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@ import { Transaction } from './transaction';
1515
import { isValidAddress } from './utils';
1616
import { ClarityValueJson } from './iface';
1717
import { Utils } from '.';
18-
import { CONTRACT_NAME_SENDMANY, CONTRACT_NAME_STAKING } from './constants';
18+
import {
19+
BACKWARD_COMPATIBILITY_CONTRACT_NAME_STAKING,
20+
CONTRACT_NAME_SENDMANY,
21+
CONTRACT_NAME_STAKING,
22+
} from './constants';
1923
import { AbstractContractBuilder } from './abstractContractBuilder';
2024

2125
export class ContractBuilder extends AbstractContractBuilder {
@@ -60,8 +64,14 @@ export class ContractBuilder extends AbstractContractBuilder {
6064
if (name.length === 0) {
6165
throw new InvalidParameterValueError('Invalid name');
6266
}
63-
if (name !== CONTRACT_NAME_STAKING && name !== CONTRACT_NAME_SENDMANY) {
64-
throw new InvalidParameterValueError('Only pox-3 and send-many-memo contracts supported');
67+
if (
68+
// TODO: remove support to this contract version after STX fork
69+
// https://bitgoinc.atlassian.net/browse/EA-3482
70+
name !== BACKWARD_COMPATIBILITY_CONTRACT_NAME_STAKING &&
71+
name !== CONTRACT_NAME_STAKING &&
72+
name !== CONTRACT_NAME_SENDMANY
73+
) {
74+
throw new InvalidParameterValueError('Only pox-3, pox-4 and send-many-memo contracts supported');
6575
}
6676
this._contractName = name;
6777
return this;

modules/sdk-coin-stx/test/fixtures.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ export const txExplainedTransfer = {
1010
};
1111

1212
export const txForExplainContract =
13-
'80800000000400164247d6f2b425ac5771423ae6c80c754f7172b0000000000000000000000000000000b4000037980c4fe5607724d9d6cbd320d38cea5931cc940ac23b6e36316cf5f077a0ec260e7c5d3916d0e375d4c68e94e4779ad8b7226c34c71fd453b818066a5a30ac030200000000021a000000000000000000000000000000000000000005706f782d3309737461636b2d737478000000040100000000000000000000000017d7840005163248e7aa6879968d241f3e5152d9f2796994d96c090a0c00000002096861736862797465730200000009736f6d652d686173680776657273696f6e020000000101';
13+
'80800000000400164247d6f2b425ac5771423ae6c80c754f7172b0000000000000000000000000000000b40000dba4d775d6894746f0b8ff3cbc1b0f2696890d0669ffbb8eb8df51098741115e28af448a59583e3d34e344c34879c5c7a82d223833c1214d13cd9813165d8e1e030200000000021a000000000000000000000000000000000000000005706f782d3409737461636b2d737478000000040100000000000000000000000017d7840005163248e7aa6879968d241f3e5152d9f2796994d96c090a0c00000002096861736862797465730200000009736f6d652d686173680776657273696f6e020000000101';
1414

1515
export const txExplainedContract = {
16-
id: '686864ede927cc05a16a842951d96e6e4a201432f33ab01d58e952ef0e958832',
16+
id: '14e44db3f77b527d7a9a0fb2d09c079a7a50c03e06e6a4508a4be417346d810d',
1717
fee: '180',
1818
contractAddress: 'ST000000000000000000002AMW42H',
19-
contractName: 'pox-3',
19+
contractName: 'pox-4',
2020
functionName: 'stack-stx',
2121
functionArgs: [{ type: 1, value: '400000000' }],
2222
};

modules/sdk-coin-stx/test/unit/resources.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -125,20 +125,25 @@ export const MULTI_SIG_SIGNED_TRANSACTION =
125125

126126
// contract call
127127
export const CONTRACT_ADDRESS = 'ST000000000000000000002AMW42H';
128-
export const CONTRACT_NAME = 'pox-3';
128+
export const CONTRACT_NAME = 'pox-4';
129129
export const CONTRACT_FUNCTION_NAME = 'stack-stx';
130130
export const UNSIGNED_SELF_STACK_CONTRACT_CALL =
131-
'80800000000400164247d6f2b425ac5771423ae6c80c754f7172b0000000000000000000000000000000b4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030200000000021a000000000000000000000000000000000000000005706f782d3309737461636b2d737478000000040100000000000000000000000017d784000c00000002096861736862797465730200000009736f6d652d686173680776657273696f6e020000000101010000000000000000000000000000ce400100000000000000000000000000000002';
131+
'80800000000400164247d6f2b425ac5771423ae6c80c754f7172b0000000000000000000000000000000b4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030200000000021a000000000000000000000000000000000000000005706f782d3409737461636b2d737478000000080100000000000000000000000017d784000c00000002096861736862797465730200000009736f6d652d686173680776657273696f6e020000000101010000000000000000000000000000ce4001000000000000000000000000000000020a0200000009736f6d652d686173680200000009736f6d652d6861736801ffffffffffffffffffffffffffffffff010000000000000000000000000001e240';
132132
export const SIGNED_SELF_STACK_CONTRACT_CALL =
133-
'80800000000400164247d6f2b425ac5771423ae6c80c754f7172b0000000000000000000000000000000b40001710f7ede3c8a199c5303fe42f3b2e984db28b60fd288257773287f595c8142ba43a2dc3c60cf332c9f123ceffd24a26e0fe8d08a1fd36bc3cba118a4c436f397030200000000021a000000000000000000000000000000000000000005706f782d3309737461636b2d737478000000040100000000000000000000000017d784000c00000002096861736862797465730200000009736f6d652d686173680776657273696f6e020000000101010000000000000000000000000000ce400100000000000000000000000000000002';
133+
'80800000000400164247d6f2b425ac5771423ae6c80c754f7172b0000000000000000000000000000000b40001833db79cd9c39c7262b808b6ea80a001a9bd290648cf3a19da95f10d1814d69437a00075e8325ef564ed9d6690c575583bcbb29c4e33c8ce80c34e7efd1f5457030200000000021a000000000000000000000000000000000000000005706f782d3409737461636b2d737478000000080100000000000000000000000017d784000c00000002096861736862797465730200000009736f6d652d686173680776657273696f6e020000000101010000000000000000000000000000ce4001000000000000000000000000000000020a0200000009736f6d652d686173680200000009736f6d652d6861736801ffffffffffffffffffffffffffffffff010000000000000000000000000001e240';
134+
135+
// TODO: remove support to this contract version after STX fork
136+
// https://bitgoinc.atlassian.net/browse/EA-3482
137+
export const POX_3_SIGNED_SELF_STACK_CONTRACT_CALL =
138+
'80800000000400164247d6f2b425ac5771423ae6c80c754f7172b0000000000000000000000000000000b400002a0f4802bba8d6c1e6311b55a47500046e59be18dc9dc0166fb358d05a20f0cd20f970859601f9f7a558483cc2cf22abd7dab2f5bc05df60093e7ad04a04c6d8030200000000021a000000000000000000000000000000000000000005706f782d3409737461636b2d737478000000040100000000000000000000000017d784000c00000002096861736862797465730200000009736f6d652d686173680776657273696f6e020000000101010000000000000000000000000000ce400100000000000000000000000000000002';
134139
export const UNSIGNED_CONTRACT_CALL =
135-
'80800000000400164247d6f2b425ac5771423ae6c80c754f7172b0000000000000000000000000000000b4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030200000000021a000000000000000000000000000000000000000005706f782d3309737461636b2d737478000000040100000000000000000000000017d7840005163248e7aa6879968d241f3e5152d9f2796994d96c0a01000000000000000000000000000000c80a0c00000002096861736862797465730200000009736f6d652d686173680776657273696f6e020000000101';
140+
'80800000000400164247d6f2b425ac5771423ae6c80c754f7172b0000000000000000000000000000000b4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030200000000021a000000000000000000000000000000000000000005706f782d3409737461636b2d737478000000040100000000000000000000000017d7840005163248e7aa6879968d241f3e5152d9f2796994d96c0a01000000000000000000000000000000c80a0c00000002096861736862797465730200000009736f6d652d686173680776657273696f6e020000000101';
136141
export const SIGNED_CONTRACT_WITH_ARGS =
137-
'80800000000400164247d6f2b425ac5771423ae6c80c754f7172b0000000000000000000000000000000b4000037980c4fe5607724d9d6cbd320d38cea5931cc940ac23b6e36316cf5f077a0ec260e7c5d3916d0e375d4c68e94e4779ad8b7226c34c71fd453b818066a5a30ac030200000000021a000000000000000000000000000000000000000005706f782d3309737461636b2d737478000000040100000000000000000000000017d7840005163248e7aa6879968d241f3e5152d9f2796994d96c090a0c00000002096861736862797465730200000009736f6d652d686173680776657273696f6e020000000101';
142+
'80800000000400164247d6f2b425ac5771423ae6c80c754f7172b0000000000000000000000000000000b40000dba4d775d6894746f0b8ff3cbc1b0f2696890d0669ffbb8eb8df51098741115e28af448a59583e3d34e344c34879c5c7a82d223833c1214d13cd9813165d8e1e030200000000021a000000000000000000000000000000000000000005706f782d3409737461636b2d737478000000040100000000000000000000000017d7840005163248e7aa6879968d241f3e5152d9f2796994d96c090a0c00000002096861736862797465730200000009736f6d652d686173680776657273696f6e020000000101';
138143
export const SIGNED_CONTRACT_CALL =
139-
'80800000000400164247d6f2b425ac5771423ae6c80c754f7172b0000000000000000000000000000000b40001d140984651d7b3339c85df78cc59c176237579421112da4eb45ce75803d87176155d48c63afd595a3469b0b2b2a9f81e4806d7ce9b9acdc59d4af23356d37b84030200000000021a000000000000000000000000000000000000000005706f782d3309737461636b2d737478000000010a000000000000000000000000000000007b';
144+
'80800000000400164247d6f2b425ac5771423ae6c80c754f7172b0000000000000000000000000000000b40001b693bdf014c1ac78e307fed49bc1df8fed2a5e42d4b79993f5df626d7b1b2516741495198a3dbf079deebc46ecdd535fe66f518c0a695c533f1667455a05036a030200000000021a000000000000000000000000000000000000000005706f782d3409737461636b2d737478000000010a000000000000000000000000000000007b';
140145
export const MULTI_SIG_CONTRACT_CALL =
141-
'808000000004012fe507c09dbb23c3b7e5d166c81fc4b87692510b000000000000000000000000000000b400000003020037aed42ad2597983a6803c8122d9bca4c6f3566fc078fe2bbfdcf0d5f7eae68121e575eb94704c0c6cb40a5b1e7a8458bba45ffd1a54408c8efa4bcf0c0a818f02011478d5f323ea1da203fc2661aeb363f66e5b19e4e9d1ed80de99054372d573cd0942fad4e9f5cfba3e8086bb75f5def6c2220afb1feae11f249f334304831f7b00038e3c4529395611be9abf6fa3b6987e81d402385e3d605a073f42f407565a4a3d0002030200000000021a000000000000000000000000000000000000000005706f782d3309737461636b2d737478000000010a000000000000000000000000000000007b';
146+
'808000000004012fe507c09dbb23c3b7e5d166c81fc4b87692510b000000000000000000000000000000b400000003020169a032112373b481c48ecb12b8847519f89720d4297a7bf5cac43b9daa8d4ad23dd53657089dad55c94e8be5762e97052683f49e1f457b2e9252f27e975603230200128d0ba2b9584339359ff42c1d6f35ce79a7073152b3c90879bc5162eb8df73b02eada3ee322873a5fa736479e6c536109681482d9f22f1e1a89608090b8666800038e3c4529395611be9abf6fa3b6987e81d402385e3d605a073f42f407565a4a3d0002030200000000021a000000000000000000000000000000000000000005706f782d3409737461636b2d737478000000010a000000000000000000000000000000007b';
142147

143148
// contract call with memo nonce 45n,
144149
export const SEND_MANY_CONTRACT_ADDRESS_WITH_MEMO = 'ST3F1X4QGV2SM8XD96X45M6RTQXKA1PZJZZCQAB4B';

modules/sdk-coin-stx/test/unit/transactionBuilder/contractBuilder.ts

Lines changed: 98 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,16 @@ describe('Stacks: Contract Builder', function () {
9595

9696
it('an unsigned self stacking contract call transaction', async () => {
9797
const builder = initTxBuilder();
98+
/* Contract call in clarity POX-4
99+
(define-public (stack-stx (amount-ustx uint)
100+
(pox-addr (tuple (version (buff 1)) (hashbytes (buff 32))))
101+
(start-burn-ht uint)
102+
(lock-period uint)
103+
(signer-sig (optional (buff 65)))
104+
(signer-key (buff 33))
105+
(max-amount uint)
106+
(auth-id uint))
107+
*/
98108
builder.functionArgs([
99109
{ type: 'uint128', val: '400000000' },
100110
{
@@ -106,6 +116,12 @@ describe('Stacks: Contract Builder', function () {
106116
},
107117
{ type: 'uint128', val: '52800' },
108118
{ type: 'uint128', val: '2' },
119+
// Nakamoto upgrade new 4 parameters
120+
// https://docs.stacks.co/nakamoto-upgrade/signing-and-stacking/stacking-flow#solo-stacker-flow
121+
{ type: 'optional', val: { type: 'buffer', val: Buffer.from('some-hash') } },
122+
{ type: 'buffer', val: Buffer.from('some-hash') },
123+
{ type: 'uint128', val: '340282366920938463463374607431768211455' },
124+
{ type: 'uint128', val: '123456' },
109125
]);
110126
builder.fromPubKey(testData.TX_SENDER.pub);
111127
builder.numberSignatures(1);
@@ -167,6 +183,16 @@ describe('Stacks: Contract Builder', function () {
167183

168184
it('a signed self stacking contract call', async () => {
169185
const builder = initTxBuilder();
186+
/* Contract call in clarity POX-4
187+
(define-public (stack-stx (amount-ustx uint)
188+
(pox-addr (tuple (version (buff 1)) (hashbytes (buff 32))))
189+
(start-burn-ht uint)
190+
(lock-period uint)
191+
(signer-sig (optional (buff 65)))
192+
(signer-key (buff 33))
193+
(max-amount uint)
194+
(auth-id uint))
195+
*/
170196
builder.functionArgs([
171197
{ type: 'uint128', val: '400000000' },
172198
{
@@ -178,6 +204,12 @@ describe('Stacks: Contract Builder', function () {
178204
},
179205
{ type: 'uint128', val: '52800' },
180206
{ type: 'uint128', val: '2' },
207+
// Nakamoto upgrade new 4 parameters
208+
// https://docs.stacks.co/nakamoto-upgrade/signing-and-stacking/stacking-flow#solo-stacker-flow
209+
{ type: 'optional', val: { type: 'buffer', val: Buffer.from('some-hash') } },
210+
{ type: 'buffer', val: Buffer.from('some-hash') },
211+
{ type: 'uint128', val: '340282366920938463463374607431768211455' },
212+
{ type: 'uint128', val: '123456' },
181213
]);
182214
builder.sign({ key: testData.TX_SENDER.prv });
183215
const tx = await builder.build();
@@ -199,6 +231,42 @@ describe('Stacks: Contract Builder', function () {
199231
tx.inputs[0].value.should.equal('0');
200232
});
201233

234+
// TODO: remove support to this contract version after STX fork
235+
// https://bitgoinc.atlassian.net/browse/EA-3482
236+
it('a signed pox-3 backward support pre-fork self stacking contract call', async () => {
237+
const builder = initTxBuilder();
238+
builder.functionArgs([
239+
{ type: 'uint128', val: '400000000' },
240+
{
241+
type: 'tuple',
242+
val: [
243+
{ key: 'hashbytes', type: 'buffer', val: Buffer.from('some-hash') },
244+
{ key: 'version', type: 'buffer', val: new BigNum(1).toBuffer() },
245+
],
246+
},
247+
{ type: 'uint128', val: '52800' },
248+
{ type: 'uint128', val: '2' },
249+
]);
250+
builder.sign({ key: testData.TX_SENDER.prv });
251+
const tx = await builder.build();
252+
253+
const txJson = tx.toJson();
254+
should.deepEqual(txJson.payload.contractAddress, testData.CONTRACT_ADDRESS);
255+
should.deepEqual(txJson.payload.contractName, testData.CONTRACT_NAME);
256+
should.deepEqual(txJson.payload.functionName, testData.CONTRACT_FUNCTION_NAME);
257+
should.deepEqual(txJson.nonce, 0);
258+
should.deepEqual(txJson.fee.toString(), '180');
259+
should.deepEqual(tx.toBroadcastFormat(), testData.POX_3_SIGNED_SELF_STACK_CONTRACT_CALL);
260+
261+
tx.type.should.equal(TransactionType.ContractCall);
262+
tx.outputs.length.should.equal(1);
263+
tx.outputs[0].address.should.equal(testData.CONTRACT_ADDRESS);
264+
tx.outputs[0].value.should.equal('0');
265+
tx.inputs.length.should.equal(1);
266+
tx.inputs[0].address.should.equal(testData.TX_SENDER.address);
267+
tx.inputs[0].value.should.equal('0');
268+
});
269+
202270
it('a signed contract call transaction', async () => {
203271
const amount = 123;
204272
const builder = initTxBuilder();
@@ -246,7 +314,9 @@ describe('Stacks: Contract Builder', function () {
246314
should.deepEqual(txJson.payload.functionName, testData.CONTRACT_FUNCTION_NAME);
247315
should.deepEqual(txJson.nonce, 0);
248316
should.deepEqual(txJson.fee.toString(), '180');
249-
should.deepEqual(txJson.payload.functionArgs.length, 4);
317+
// Now stacks-stx self-stacking supports 8 parameters
318+
// https://docs.stacks.co/nakamoto-upgrade/signing-and-stacking/stacking-flow#solo-stacker-flow
319+
should.deepEqual(txJson.payload.functionArgs.length, 8);
250320
should.deepEqual(tx.toBroadcastFormat(), testData.SIGNED_SELF_STACK_CONTRACT_CALL);
251321
tx.type.should.equal(TransactionType.ContractCall);
252322
tx.outputs.length.should.equal(1);
@@ -257,6 +327,29 @@ describe('Stacks: Contract Builder', function () {
257327
tx.inputs[0].value.should.equal('0');
258328
});
259329

330+
// TODO: remove support to this contract version after STX fork
331+
// https://bitgoinc.atlassian.net/browse/EA-3482
332+
it('a signed serialized pox-3 backward support pre-fork self stacking contract call transaction', async () => {
333+
const builder = factory.from(testData.POX_3_SIGNED_SELF_STACK_CONTRACT_CALL);
334+
const tx = await builder.build();
335+
const txJson = tx.toJson();
336+
should.deepEqual(txJson.payload.contractAddress, testData.CONTRACT_ADDRESS);
337+
should.deepEqual(txJson.payload.contractName, testData.CONTRACT_NAME);
338+
should.deepEqual(txJson.payload.functionName, testData.CONTRACT_FUNCTION_NAME);
339+
should.deepEqual(txJson.nonce, 0);
340+
should.deepEqual(txJson.fee.toString(), '180');
341+
// POX-3 stacks-stx self-stacking supports 4 parameters
342+
should.deepEqual(txJson.payload.functionArgs.length, 4);
343+
should.deepEqual(tx.toBroadcastFormat(), testData.POX_3_SIGNED_SELF_STACK_CONTRACT_CALL);
344+
tx.type.should.equal(TransactionType.ContractCall);
345+
tx.outputs.length.should.equal(1);
346+
tx.outputs[0].address.should.equal(testData.CONTRACT_ADDRESS);
347+
tx.outputs[0].value.should.equal('0');
348+
tx.inputs.length.should.equal(1);
349+
tx.inputs[0].address.should.equal(testData.TX_SENDER.address);
350+
tx.inputs[0].value.should.equal('0');
351+
});
352+
260353
it('a multisig transfer transaction', async () => {
261354
const builder = initTxBuilder();
262355
builder.functionArgs([{ type: 'optional', val: { type: 'int128', val: '123' } }]);
@@ -374,7 +467,10 @@ describe('Stacks: Contract Builder', function () {
374467
});
375468
it('a contract call with an invalid contract name', () => {
376469
const builder = initTxBuilder();
377-
assert.throws(() => builder.contractName('pox-2'), /Only pox-3 and send-many-memo contracts supported/);
470+
assert.throws(
471+
() => builder.contractName('pox-2'),
472+
/Only pox-3, pox-4 and send-many-memo contracts supported/
473+
);
378474
});
379475
it('a contract call with an invalid contract function name', () => {
380476
const builder = initTxBuilder();

modules/sdk-core/src/bitgo/staking/iStakingWallet.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,13 @@ export interface DelegationRequest {
3232
* @property {string} [duration] - delegation duration: a numeric string, in days or cycles
3333
* @property {string} [subType] - coin sepcific staking subtype
3434
* @property {string} [btcRewardAddress] - btc reward address
35+
* @property {string} [signerPub] - stx signer public key
36+
* @property {string} [signerSignature] - stx signer signature
3537
* @property {DelegationRequest[]} [delegationRequests] - The delegation requests
38+
* TODO: remove support to this contract version after STX fork
39+
* https://bitgoinc.atlassian.net/browse/EA-3482
40+
* @property {string} [contractName] - stx contract name: valid names are pox-3 and pox-4 only, used only for backward compatibility during nakamoto fork
41+
3642
*/
3743
export interface StakeOptions {
3844
amount?: string;
@@ -51,14 +57,32 @@ export interface StakeOptions {
5157
*/
5258
blsSignature?: string;
5359
/**
54-
* coin sepcific staking subtype
60+
* coin specific staking subtype
5561
*/
5662
subType?: string;
5763
/**
58-
* btc reward address
64+
* stx btc reward address
5965
*/
6066
btcRewardAddress?: string;
67+
68+
/**
69+
* stx signer pub
70+
*/
71+
signerPub?: string;
72+
73+
/**
74+
* stx signer signature
75+
*/
76+
signerSignature?: string;
77+
6178
delegationRequests?: DelegationRequest[];
79+
80+
// TODO: remove support to this contract version after STX fork
81+
// https://bitgoinc.atlassian.net/browse/EA-3482
82+
/**
83+
* pox-contract name (valid values are pox-3 and pox-4)
84+
*/
85+
contractName?: 'pox-3' | 'pox-4';
6286
}
6387

6488
export interface UnstakeOptions {

0 commit comments

Comments
 (0)