Skip to content

Commit 2f0c31c

Browse files
authored
Merge pull request #6304 from BitGo/SC-2156
feat: staking support for fetch
2 parents 3940123 + 57d2964 commit 2f0c31c

File tree

5 files changed

+441
-0
lines changed

5 files changed

+441
-0
lines changed

modules/sdk-coin-asi/test/resources/asi.ts

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,3 +192,135 @@ export const coinAmounts = {
192192
amount4: { amount: '-1', denom: 'atestfet' },
193193
amount5: { amount: '1000000000', denom: 'matestfet' },
194194
};
195+
196+
export const TEST_DELEGATE_TX = {
197+
hash: 'A8BD3DE82A4B2453EC5EC86577EB60638741C8A10EEE0038941D36F5D5EC4F1A',
198+
signature: '+p86as/2XFjVHVLjQERp5aeMa7lHuxdoXiZGYp2onbs2Yt3QsLuElqD6Eg3wNq5BbCyzoj89d7l1tmoJHWML4Q==',
199+
pubKey: 'A/iPk4rOMnePj2eLaJrJuP+3wfQl5RuNWnatW8Ppwj3L',
200+
privateKey: '06tW+IizHiD8gQGxJ6f6xSty27MlzdUs9TwMc9iWcVM=',
201+
signedTxBase64:
202+
'CqIBCp8BCiMvY29zbW9zLnN0YWtpbmcudjFiZXRhMS5Nc2dEZWxlZ2F0ZRJ4CixmZXRjaDE3NTNmNzhmMmNwNjdzM2RkODkzZDBnbjBmYTNyeTBybmM2eTJ6dRIzZmV0Y2h2YWxvcGVyMXJzYW5lOTg4dmtzcmdwMm1scXpjbG10OHd1Y3h2MGVqNGhybjJrGhMKCGF0ZXN0ZmV0EgcxMDAwMDAwEnUKUApGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQP4j5OKzjJ3j49ni2iaybj/t8H0JeUbjVp2rVvD6cI9yxIECgIIARgIEiEKGwoIYXRlc3RmZXQSDzI1MDAwMDAwMDAwMDAwMBCQoQ8aQPqfOmrP9lxY1R1S40BEaeWnjGu5R7sXaF4mRmKdqJ27NmLd0LC7hJag+hIN8DauQWwss6I/PXe5dbZqCR1jC+E=',
203+
validator: 'fetchvaloper1rsane988vksrgp2mlqzclmt8wucxv0ej4hrn2k',
204+
delegator: 'fetch1753f78f2cp67s3dd893d0gn0fa3ry0rnc6y2zu',
205+
chainId: 'dorado-1',
206+
accountNumber: 8,
207+
sequence: 8,
208+
sendAmount: '1000000',
209+
feeAmount: '250000000000000',
210+
sendMessage: {
211+
typeUrl: '/cosmos.staking.v1beta1.MsgDelegate',
212+
value: {
213+
validatorAddress: 'fetchvaloper1rsane988vksrgp2mlqzclmt8wucxv0ej4hrn2k',
214+
delegatorAddress: 'fetch1753f78f2cp67s3dd893d0gn0fa3ry0rnc6y2zu',
215+
amount: {
216+
denom: 'atestfet',
217+
amount: '1000000',
218+
},
219+
},
220+
},
221+
gasBudget: {
222+
amount: [
223+
{
224+
denom: 'atestfet',
225+
amount: '250000000000000',
226+
},
227+
],
228+
gasLimit: 250000,
229+
},
230+
};
231+
232+
export const TEST_UNDELEGATE_TX = {
233+
hash: 'DC6F8F0115C069B9C4D2013F7D1515432C0CEDC821B50E045FE4CEB3E442EC3A',
234+
signature: 'oXtW0tQjyKJB5Du66WTSrFyqMbKEQ2vnXHKwmSqtsY8x7uqNCenJSbN0QL6GZ48Fr7iNaTZBE7+TJOEL5RfiTg==',
235+
pubKey: 'A/iPk4rOMnePj2eLaJrJuP+3wfQl5RuNWnatW8Ppwj3L',
236+
privateKey: '06tW+IizHiD8gQGxJ6f6xSty27MlzdUs9TwMc9iWcVM=',
237+
signedTxBase64:
238+
'CqABCp0BCiUvY29zbW9zLnN0YWtpbmcudjFiZXRhMS5Nc2dVbmRlbGVnYXRlEnQKLGZldGNoMTc1M2Y3OGYyY3A2N3MzZGQ4OTNkMGduMGZhM3J5MHJuYzZ5Mnp1EjNmZXRjaHZhbG9wZXIxcnNhbmU5ODh2a3NyZ3AybWxxemNsbXQ4d3VjeHYwZWo0aHJuMmsaDwoIYXRlc3RmZXQSAzEwMBJ1ClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiED+I+Tis4yd4+PZ4tomsm4/7fB9CXlG41adq1bw+nCPcsSBAoCCAEYCBIhChsKCGF0ZXN0ZmV0Eg8yNTAwMDAwMDAwMDAwMDAQkKEPGkChe1bS1CPIokHkO7rpZNKsXKoxsoRDa+dccrCZKq2xjzHu6o0J6clJs3RAvoZnjwWvuI1pNkETv5Mk4QvlF+JO',
239+
validator: 'fetchvaloper1rsane988vksrgp2mlqzclmt8wucxv0ej4hrn2k',
240+
delegator: 'fetch1753f78f2cp67s3dd893d0gn0fa3ry0rnc6y2zu',
241+
chainId: 'dorado-1',
242+
accountNumber: 8,
243+
sequence: 8,
244+
sendAmount: '100',
245+
feeAmount: '250000000000000',
246+
sendMessage: {
247+
typeUrl: '/cosmos.staking.v1beta1.MsgUndelegate',
248+
value: {
249+
delegatorAddress: 'fetch1753f78f2cp67s3dd893d0gn0fa3ry0rnc6y2zu',
250+
validatorAddress: 'fetchvaloper1rsane988vksrgp2mlqzclmt8wucxv0ej4hrn2k',
251+
amount: {
252+
denom: 'atestfet',
253+
amount: '100',
254+
},
255+
},
256+
},
257+
gasBudget: {
258+
amount: [
259+
{
260+
denom: 'atestfet',
261+
amount: '250000000000000',
262+
},
263+
],
264+
gasLimit: 250000,
265+
},
266+
};
267+
268+
export const TEST_REDELEGATE_TX = {
269+
hash: 'A36F88D8A810DFCD256746E55D29644D491812B2D13FB8E98C1708749885042D',
270+
signature: 'cPbIm75ZXDUnNOyfNWoRXIPfI2QchcghQ7O86PgOGppHAWWsDa5e9MfOKOnO42CnPNz79hWbFJHSos9blQEMEA==',
271+
pubKey: 'A/iPk4rOMnePj2eLaJrJuP+3wfQl5RuNWnatW8Ppwj3L',
272+
privateKey: '06tW+IizHiD8gQGxJ6f6xSty27MlzdUs9TwMc9iWcVM=',
273+
signedTxBase64:
274+
'Ct4BCtsBCiovY29zbW9zLnN0YWtpbmcudjFiZXRhMS5Nc2dCZWdpblJlZGVsZWdhdGUSrAEKLGZldGNoMTc1M2Y3OGYyY3A2N3MzZGQ4OTNkMGduMGZhM3J5MHJuYzZ5Mnp1EjNmZXRjaHZhbG9wZXIxcnNhbmU5ODh2a3NyZ3AybWxxemNsbXQ4d3VjeHYwZWo0aHJuMmsaM2ZldGNodmFsb3BlcjFqZTdyOHl1cWdhZjVmMnR4NHoyZjkwMDh3cDRqeDBjdDZtc256ZyISCghhdGVzdGZldBIGMTAwMDAwEnUKUApGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQP4j5OKzjJ3j49ni2iaybj/t8H0JeUbjVp2rVvD6cI9yxIECgIIARgIEiEKGwoIYXRlc3RmZXQSDzI1MDAwMDAwMDAwMDAwMBCQoQ8aQHD2yJu+WVw1JzTsnzVqEVyD3yNkHIXIIUOzvOj4DhqaRwFlrA2uXvTHzijpzuNgpzzc+/YVmxSR0qLPW5UBDBA=',
275+
validator: 'fetchvaloper1rsane988vksrgp2mlqzclmt8wucxv0ej4hrn2k',
276+
validatorDst: 'fetchvaloper1je7r8yuqgaf5f2tx4z2f9008wp4jx0ct6msnzg',
277+
delegator: 'fetch1753f78f2cp67s3dd893d0gn0fa3ry0rnc6y2zu',
278+
chainId: 'dorado-1',
279+
accountNumber: 8,
280+
sequence: 8,
281+
sendAmount: '100',
282+
feeAmount: '250000000000000',
283+
sendMessage: {
284+
typeUrl: '/cosmos.staking.v1beta1.MsgBeginRedelegate',
285+
value: {
286+
delegatorAddress: 'fetch1753f78f2cp67s3dd893d0gn0fa3ry0rnc6y2zu',
287+
validatorSrcAddress: 'fetchvaloper1rsane988vksrgp2mlqzclmt8wucxv0ej4hrn2k',
288+
validatorDstAddress: 'fetchvaloper1je7r8yuqgaf5f2tx4z2f9008wp4jx0ct6msnzg',
289+
amount: {
290+
denom: 'atestfet',
291+
amount: '100000',
292+
},
293+
},
294+
},
295+
gasBudget: {
296+
amount: [{ denom: 'atestfet', amount: '250000000000000' }],
297+
gasLimit: 250000,
298+
},
299+
};
300+
301+
export const TEST_WITHDRAW_REWARDS_TX = {
302+
hash: '3B9236E5EEB2D657E4A7F1E32583C621B7EB1EAF5E51F841010F39F63FD3DBC4',
303+
signature: 'WjzqdlfBdzokA8AbalFgT6oKeDbBaKbcTP4z+Auo/AdvZytEu77j1CSGmVXOprx12npT33FI+hZvMmPp5YZshg==',
304+
pubKey: 'A/iPk4rOMnePj2eLaJrJuP+3wfQl5RuNWnatW8Ppwj3L',
305+
privateKey: '06tW+IizHiD8gQGxJ6f6xSty27MlzdUs9TwMc9iWcVM=',
306+
signedTxBase64:
307+
'CqEBCp4BCjcvY29zbW9zLmRpc3RyaWJ1dGlvbi52MWJldGExLk1zZ1dpdGhkcmF3RGVsZWdhdG9yUmV3YXJkEmMKLGZldGNoMTc1M2Y3OGYyY3A2N3MzZGQ4OTNkMGduMGZhM3J5MHJuYzZ5Mnp1EjNmZXRjaHZhbG9wZXIxcnNhbmU5ODh2a3NyZ3AybWxxemNsbXQ4d3VjeHYwZWo0aHJuMmsSdQpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohA/iPk4rOMnePj2eLaJrJuP+3wfQl5RuNWnatW8Ppwj3LEgQKAggBGAgSIQobCghhdGVzdGZldBIPMjUwMDAwMDAwMDAwMDAwEJChDxpAWjzqdlfBdzokA8AbalFgT6oKeDbBaKbcTP4z+Auo/AdvZytEu77j1CSGmVXOprx12npT33FI+hZvMmPp5YZshg==',
308+
validator: 'fetchvaloper1rsane988vksrgp2mlqzclmt8wucxv0ej4hrn2k',
309+
delegator: 'fetch1753f78f2cp67s3dd893d0gn0fa3ry0rnc6y2zu',
310+
chainId: 'dorado-1',
311+
accountNumber: 8,
312+
sequence: 8,
313+
sendAmount: '100',
314+
feeAmount: '250000000000000',
315+
sendMessage: {
316+
typeUrl: '/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward',
317+
value: {
318+
delegatorAddress: 'fetch1753f78f2cp67s3dd893d0gn0fa3ry0rnc6y2zu',
319+
validatorAddress: 'fetchvaloper1rsane988vksrgp2mlqzclmt8wucxv0ej4hrn2k',
320+
},
321+
},
322+
gasBudget: {
323+
amount: [{ denom: 'atestfet', amount: '250000000000000' }],
324+
gasLimit: 250000,
325+
},
326+
};
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import { toHex, TransactionType } from '@bitgo/sdk-core';
2+
import { fromBase64 } from '@cosmjs/encoding';
3+
import axios from 'axios';
4+
import should from 'should';
5+
import * as testData from '../../resources/asi';
6+
import { KeyPair } from '../../../src';
7+
import { getBuilderFactory } from '../getBuilderFactory';
8+
9+
describe('Fetch delegate transaction builder', () => {
10+
const factory = getBuilderFactory('tasi');
11+
const testTx = testData.TEST_DELEGATE_TX;
12+
it('should build a Delegate transaction with signature', async function () {
13+
const txBuilder = factory.getStakingActivateBuilder();
14+
txBuilder.sequence(testTx.sequence);
15+
txBuilder.gasBudget(testTx.gasBudget);
16+
txBuilder.messages([testTx.sendMessage.value]);
17+
txBuilder.publicKey(toHex(fromBase64(testTx.pubKey)));
18+
txBuilder.addSignature({ pub: toHex(fromBase64(testTx.pubKey)) }, Buffer.from(testTx.signature, 'base64'));
19+
20+
const tx = await txBuilder.build();
21+
const json = await (await txBuilder.build()).toJson();
22+
should.equal(tx.type, TransactionType.StakingActivate);
23+
should.deepEqual(json.gasBudget, testTx.gasBudget);
24+
should.deepEqual(json.sendMessages, [testTx.sendMessage]);
25+
should.deepEqual(json.publicKey, toHex(fromBase64(testTx.pubKey)));
26+
should.deepEqual(json.sequence, testTx.sequence);
27+
const rawTx = tx.toBroadcastFormat();
28+
should.equal(rawTx, testTx.signedTxBase64);
29+
should.deepEqual(tx.inputs, [
30+
{
31+
address: testTx.delegator,
32+
value: testTx.sendMessage.value.amount.amount,
33+
coin: 'tasi',
34+
},
35+
]);
36+
should.deepEqual(tx.outputs, [
37+
{
38+
address: testTx.validator,
39+
value: testTx.sendMessage.value.amount.amount,
40+
coin: 'tasi',
41+
},
42+
]);
43+
});
44+
45+
it('should build a Delegate tx without signature', async function () {
46+
const txBuilder = factory.getStakingActivateBuilder();
47+
txBuilder.sequence(testTx.sequence);
48+
txBuilder.gasBudget(testTx.gasBudget);
49+
txBuilder.messages([testTx.sendMessage.value]);
50+
txBuilder.publicKey(toHex(fromBase64(testTx.pubKey)));
51+
const tx = await txBuilder.build();
52+
const json = await (await txBuilder.build()).toJson();
53+
should.equal(tx.type, TransactionType.StakingActivate);
54+
should.deepEqual(json.gasBudget, testTx.gasBudget);
55+
should.deepEqual(json.sendMessages, [testTx.sendMessage]);
56+
should.deepEqual(json.publicKey, toHex(fromBase64(testTx.pubKey)));
57+
should.deepEqual(json.sequence, testTx.sequence);
58+
tx.toBroadcastFormat();
59+
should.deepEqual(tx.inputs, [
60+
{
61+
address: testTx.delegator,
62+
value: testTx.sendMessage.value.amount.amount,
63+
coin: 'tasi',
64+
},
65+
]);
66+
should.deepEqual(tx.outputs, [
67+
{
68+
address: testTx.validator,
69+
value: testTx.sendMessage.value.amount.amount,
70+
coin: 'tasi',
71+
},
72+
]);
73+
});
74+
75+
xit('should submit a send transaction', async () => {
76+
const keyPair = new KeyPair({ prv: toHex(fromBase64(testTx.privateKey)) });
77+
const axiosConfig = {
78+
headers: {
79+
'Content-Type': 'application/cbor',
80+
},
81+
timeout: 10000,
82+
};
83+
84+
const txBuilder = factory.getStakingActivateBuilder();
85+
txBuilder.sequence(10);
86+
txBuilder.gasBudget(testTx.gasBudget);
87+
txBuilder.messages([testTx.sendMessage.value]);
88+
txBuilder.accountNumber(testTx.accountNumber);
89+
txBuilder.chainId(testTx.chainId);
90+
txBuilder.sign({ key: keyPair.getKeys().prv });
91+
const tx = await txBuilder.build();
92+
const serializedTx = tx.toBroadcastFormat();
93+
94+
try {
95+
const res = await axios.post(
96+
'https://rest.sentry-01.theta-testnet.polypore.xyz/cosmos/tx/v1beta1/txs',
97+
{
98+
mode: 'BROADCAST_MODE_BLOCK',
99+
tx_bytes: serializedTx,
100+
},
101+
axiosConfig
102+
);
103+
console.log(res);
104+
} catch (err) {
105+
console.log(err);
106+
}
107+
});
108+
});
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { toHex, TransactionType } from '@bitgo/sdk-core';
2+
import { fromBase64 } from '@cosmjs/encoding';
3+
import should from 'should';
4+
5+
import * as testData from '../../resources/asi';
6+
import { getBuilderFactory } from '../getBuilderFactory';
7+
8+
describe('Fetch Undelegate txn Builder', () => {
9+
const factory = getBuilderFactory('tasi');
10+
const testTx = testData.TEST_UNDELEGATE_TX;
11+
it('should build undelegate tx with signature', async function () {
12+
const txBuilder = factory.getStakingDeactivateBuilder();
13+
txBuilder.sequence(testTx.sequence);
14+
txBuilder.gasBudget(testTx.gasBudget);
15+
txBuilder.messages([testTx.sendMessage.value]);
16+
txBuilder.addSignature({ pub: toHex(fromBase64(testTx.pubKey)) }, Buffer.from(testTx.signature, 'base64'));
17+
const tx = await txBuilder.build();
18+
const json = await (await txBuilder.build()).toJson();
19+
should.equal(tx.type, TransactionType.StakingDeactivate);
20+
should.deepEqual(json.gasBudget, testTx.gasBudget);
21+
should.deepEqual(json.sendMessages, [testTx.sendMessage]);
22+
should.deepEqual(json.publicKey, toHex(fromBase64(testTx.pubKey)));
23+
should.deepEqual(json.sequence, testTx.sequence);
24+
const rawTx = tx.toBroadcastFormat();
25+
should.equal(rawTx, testTx.signedTxBase64);
26+
should.deepEqual(tx.inputs, [
27+
{
28+
address: testTx.delegator,
29+
value: testTx.sendMessage.value.amount.amount,
30+
coin: 'tasi',
31+
},
32+
]);
33+
should.deepEqual(tx.outputs, [
34+
{
35+
address: testTx.validator,
36+
value: testTx.sendMessage.value.amount.amount,
37+
coin: 'tasi',
38+
},
39+
]);
40+
});
41+
42+
it('should build undelegate tx without signature', async function () {
43+
const txBuilder = factory.getStakingDeactivateBuilder();
44+
txBuilder.sequence(testTx.sequence);
45+
txBuilder.gasBudget(testTx.gasBudget);
46+
txBuilder.messages([testTx.sendMessage.value]);
47+
txBuilder.publicKey(toHex(fromBase64(testTx.pubKey)));
48+
const tx = await txBuilder.build();
49+
const json = await (await txBuilder.build()).toJson();
50+
should.equal(tx.type, TransactionType.StakingDeactivate);
51+
should.deepEqual(json.gasBudget, testTx.gasBudget);
52+
should.deepEqual(json.sendMessages, [testTx.sendMessage]);
53+
should.deepEqual(json.publicKey, toHex(fromBase64(testTx.pubKey)));
54+
should.deepEqual(json.sequence, testTx.sequence);
55+
tx.toBroadcastFormat();
56+
should.deepEqual(tx.inputs, [
57+
{
58+
address: testTx.delegator,
59+
value: testTx.sendMessage.value.amount.amount,
60+
coin: 'tasi',
61+
},
62+
]);
63+
should.deepEqual(tx.outputs, [
64+
{
65+
address: testTx.validator,
66+
value: testTx.sendMessage.value.amount.amount,
67+
coin: 'tasi',
68+
},
69+
]);
70+
});
71+
});
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { BitGoAPI } from '@bitgo/sdk-api';
2+
import { toHex, TransactionType } from '@bitgo/sdk-core';
3+
import { TestBitGo, TestBitGoAPI } from '@bitgo/sdk-test';
4+
import { fromBase64 } from '@cosmjs/encoding';
5+
import should from 'should';
6+
import { Asi, Tasi } from '../../../src';
7+
import * as testData from '../../resources/asi';
8+
9+
describe('Fetch Redelegate txn Builder', () => {
10+
let bitgo: TestBitGoAPI;
11+
let basecoin;
12+
let factory;
13+
let testTx;
14+
before(function () {
15+
bitgo = TestBitGo.decorate(BitGoAPI, { env: 'mock' });
16+
bitgo.safeRegister('asi', Asi.createInstance);
17+
bitgo.safeRegister('tasi', Tasi.createInstance);
18+
bitgo.initializeTestVars();
19+
basecoin = bitgo.coin('tasi');
20+
factory = basecoin.getBuilder();
21+
testTx = testData.TEST_REDELEGATE_TX;
22+
});
23+
24+
it('should build a Redelegate tx with signature', async function () {
25+
const txBuilder = factory.getStakingRedelegateBuilder();
26+
txBuilder.sequence(testTx.sequence);
27+
txBuilder.gasBudget(testTx.gasBudget);
28+
txBuilder.messages([testTx.sendMessage.value]);
29+
txBuilder.memo('');
30+
txBuilder.publicKey(toHex(fromBase64(testTx.pubKey)));
31+
txBuilder.addSignature({ pub: toHex(fromBase64(testTx.pubKey)) }, Buffer.from(testTx.signature, 'base64'));
32+
33+
const tx = await txBuilder.build();
34+
const json = await (await txBuilder.build()).toJson();
35+
should.equal(tx.type, TransactionType.StakingRedelegate);
36+
should.deepEqual(json.gasBudget, testTx.gasBudget);
37+
should.deepEqual(json.sendMessages, [testTx.sendMessage]);
38+
should.deepEqual(json.publicKey, toHex(fromBase64(testTx.pubKey)));
39+
should.deepEqual(json.sequence, testTx.sequence);
40+
const rawTx = tx.toBroadcastFormat();
41+
should.equal(rawTx, testTx.signedTxBase64);
42+
should.deepEqual(tx.inputs, [
43+
{
44+
address: testTx.delegator,
45+
value: testTx.sendMessage.value.amount.amount,
46+
coin: basecoin.getChain(),
47+
},
48+
]);
49+
should.deepEqual(tx.outputs, [
50+
{
51+
address: testTx.validatorDst,
52+
value: testTx.sendMessage.value.amount.amount,
53+
coin: basecoin.getChain(),
54+
},
55+
]);
56+
});
57+
});

0 commit comments

Comments
 (0)