1
+ import { Interface , Schema , Transaction , TransactionBuilder } from '@bitgo/abstract-substrate' ;
2
+ import { InvalidTransactionError , TransactionType } from '@bitgo/sdk-core' ;
3
+ import { BaseCoin as CoinConfig } from '@bitgo/statics' ;
4
+ import { DecodedSignedTx , DecodedSigningPayload , defineMethod , UnsignedTransaction } from '@substrate/txwrapper-core' ;
5
+ import BigNumber from 'bignumber.js' ;
6
+ import { MoveStakeTransaction } from './moveStakeTransaction' ;
7
+
8
+ export class MoveStakeBuilder extends TransactionBuilder {
9
+ protected _originHotkey : string ;
10
+ protected _destinationHotkey : string ;
11
+ protected _originNetuid : string ;
12
+ protected _destinationNetuid : string ;
13
+ protected _alphaAmount : string ;
14
+
15
+ constructor ( _coinConfig : Readonly < CoinConfig > ) {
16
+ super ( _coinConfig ) ;
17
+ this . _transaction = new MoveStakeTransaction ( _coinConfig ) ;
18
+ }
19
+
20
+ /**
21
+ * Construct a transaction to move stake
22
+ * @returns {UnsignedTransaction } an unsigned move stake transaction
23
+ */
24
+ protected buildTransaction ( ) : UnsignedTransaction {
25
+ const baseTxInfo = this . createBaseTxInfo ( ) ;
26
+ return this . moveStake (
27
+ {
28
+ originHotkey : this . _originHotkey ,
29
+ destinationHotkey : this . _destinationHotkey ,
30
+ originNetuid : this . _originNetuid ,
31
+ destinationNetuid : this . _destinationNetuid ,
32
+ alphaAmount : this . _alphaAmount ,
33
+ } ,
34
+ baseTxInfo
35
+ ) ;
36
+ }
37
+
38
+ /** @inheritdoc */
39
+ protected get transactionType ( ) : TransactionType {
40
+ return TransactionType . StakingRedelegate ;
41
+ }
42
+
43
+ /**
44
+ * Set the amount to move
45
+ * @param {string } amount to move
46
+ * @returns {MoveStakeBuilder } This builder.
47
+ */
48
+ amount ( amount : string ) : this {
49
+ const value = new BigNumber ( amount ) ;
50
+ this . validateValue ( value ) ;
51
+ if ( value . isEqualTo ( 0 ) ) {
52
+ throw new InvalidTransactionError ( 'Value cannot be less than zero' ) ;
53
+ }
54
+ this . _alphaAmount = amount ;
55
+ return this ;
56
+ }
57
+
58
+ /**
59
+ * Set the origin hot key address
60
+ * @param {string } address of origin hotkey
61
+ * @returns {MoveStakeBuilder } This builder.
62
+ */
63
+ originHotkey ( address : string ) : this {
64
+ this . validateAddress ( { address } ) ;
65
+ this . _originHotkey = address ;
66
+ return this ;
67
+ }
68
+
69
+ /**
70
+ * Set the destination hot key address
71
+ * @param {string } address of destination hotkey
72
+ * @returns {MoveStakeBuilder } This builder.
73
+ */
74
+ destinationHotkey ( address : string ) : this {
75
+ this . validateAddress ( { address } ) ;
76
+ this . _destinationHotkey = address ;
77
+ return this ;
78
+ }
79
+
80
+ /**
81
+ * Set the origin netuid of the subnet (root network is 0)
82
+ * @param {string } netuid of subnet
83
+ * @returns {MoveStakeBuilder } This builder.
84
+ */
85
+ originNetuid ( netuid : string ) : this {
86
+ this . validateNetuid ( netuid ) ;
87
+ this . _originNetuid = netuid ;
88
+ return this ;
89
+ }
90
+
91
+ /**
92
+ * Set the destination netuid of the subnet (root network is 0)
93
+ * @param {string } netuid of subnet
94
+ * @returns {MoveStakeBuilder } This builder.
95
+ */
96
+ destinationNetuid ( netuid : string ) : this {
97
+ this . validateNetuid ( netuid ) ;
98
+ this . _destinationNetuid = netuid ;
99
+ return this ;
100
+ }
101
+
102
+ /** @inheritdoc */
103
+ protected fromImplementation ( rawTransaction : string ) : Transaction {
104
+ const tx = super . fromImplementation ( rawTransaction ) ;
105
+ if ( this . _method ?. name === Interface . MethodNames . MoveStake ) {
106
+ const txMethod = this . _method . args as Interface . MoveStakeArgs ;
107
+ this . amount ( txMethod . alphaAmount ) ;
108
+ this . originHotkey ( txMethod . originHotkey ) ;
109
+ this . destinationHotkey ( txMethod . destinationHotkey ) ;
110
+ this . originNetuid ( txMethod . originNetuid ) ;
111
+ this . destinationNetuid ( txMethod . destinationNetuid ) ;
112
+ } else {
113
+ throw new InvalidTransactionError (
114
+ `Invalid Transaction Type: ${ this . _method ?. name } . Expected ${ Interface . MethodNames . MoveStake } `
115
+ ) ;
116
+ }
117
+ return tx ;
118
+ }
119
+
120
+ /** @inheritdoc */
121
+ validateTransaction ( _ : Transaction ) : void {
122
+ super . validateTransaction ( _ ) ;
123
+ this . validateFields (
124
+ this . _originHotkey ,
125
+ this . _destinationHotkey ,
126
+ this . _originNetuid ,
127
+ this . _destinationNetuid ,
128
+ this . _alphaAmount
129
+ ) ;
130
+ }
131
+
132
+ /**
133
+ * Helper method to validate netuid range (0-128)
134
+ * @param {string } netuid netuid to validate
135
+ * @throws {InvalidTransactionError } if netuid is out of range
136
+ */
137
+ private validateNetuid ( netuid : string ) : void {
138
+ const netuidNum = parseInt ( netuid , 10 ) ;
139
+ if ( isNaN ( netuidNum ) || netuidNum < 0 || netuidNum > 128 ) {
140
+ throw new InvalidTransactionError ( `Invalid netuid: ${ netuid } . Netuid must be between 0 and 128.` ) ;
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Helper method to validate whether tx params have the correct type and format
146
+ * @param {string } originHotkey origin hotkey address
147
+ * @param {string } destinationHotkey destination hotkey address
148
+ * @param {string } originNetuid netuid of the origin subnet
149
+ * @param {string } destinationNetuid netuid of the destination subnet
150
+ * @param {string } alphaAmount amount to move
151
+ * @throws {InvalidTransactionError } if validation fails
152
+ */
153
+ private validateFields (
154
+ originHotkey : string ,
155
+ destinationHotkey : string ,
156
+ originNetuid : string ,
157
+ destinationNetuid : string ,
158
+ alphaAmount : string
159
+ ) : void {
160
+ // Validate netuid ranges
161
+ this . validateNetuid ( originNetuid ) ;
162
+ this . validateNetuid ( destinationNetuid ) ;
163
+
164
+ const validationResult = Schema . MoveStakeTransactionSchema . validate ( {
165
+ originHotkey,
166
+ destinationHotkey,
167
+ originNetuid,
168
+ destinationNetuid,
169
+ alphaAmount,
170
+ } ) ;
171
+
172
+ if ( validationResult . error ) {
173
+ throw new InvalidTransactionError ( `Transaction validation failed: ${ validationResult . error . message } ` ) ;
174
+ }
175
+ }
176
+
177
+ /** @inheritdoc */
178
+ validateDecodedTransaction ( decodedTxn : DecodedSigningPayload | DecodedSignedTx , rawTransaction : string ) : void {
179
+ if ( decodedTxn . method ?. name === Interface . MethodNames . MoveStake ) {
180
+ const txMethod = decodedTxn . method . args as unknown as Interface . MoveStakeArgs ;
181
+
182
+ const validationResult = Schema . MoveStakeTransactionSchema . validate ( txMethod ) ;
183
+ if ( validationResult . error ) {
184
+ throw new InvalidTransactionError ( `Move Stake Transaction validation failed: ${ validationResult . error . message } ` ) ;
185
+ }
186
+ }
187
+ }
188
+
189
+ /**
190
+ * Construct a transaction to move stake
191
+ *
192
+ * @param {Interface.MoveStakeArgs } args arguments to be passed to the moveStake method
193
+ * @param {Interface.CreateBaseTxInfo } info txn info required to construct the moveStake txn
194
+ * @returns {UnsignedTransaction } an unsigned move stake transaction
195
+ */
196
+ private moveStake ( args : Interface . MoveStakeArgs , info : Interface . CreateBaseTxInfo ) : UnsignedTransaction {
197
+ return defineMethod (
198
+ {
199
+ method : {
200
+ args,
201
+ name : 'moveStake' ,
202
+ pallet : 'subtensorModule' ,
203
+ } ,
204
+ ...info . baseTxInfo ,
205
+ } ,
206
+ info . options
207
+ ) ;
208
+ }
209
+ }
0 commit comments