Skip to content

Commit

Permalink
feat(sdk-core): add staking SDK functionality
Browse files Browse the repository at this point in the history
Ticket: BOS-360
  • Loading branch information
perkinstyler committed Jun 16, 2022
1 parent bdf4452 commit 20371c9
Show file tree
Hide file tree
Showing 15 changed files with 794 additions and 0 deletions.
1 change: 1 addition & 0 deletions commitlint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ module.exports = {
'BG-',
'CR-',
'STLX-',
'BOS-',
],
},
},
Expand Down
4 changes: 4 additions & 0 deletions modules/bitgo/src/v2/coins/eth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1694,6 +1694,10 @@ export class Eth extends BaseCoin {
return true;
}

supportsStaking(): boolean {
return true;
}

private isETHAddress(address: string): boolean {
return !!address.match(/0x[a-fA-F0-9]{40}/);
}
Expand Down
4 changes: 4 additions & 0 deletions modules/bitgo/src/v2/coins/near.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ export class Near extends BaseCoin {
return true;
}

supportsStaking(): boolean {
return true;
}

getChain(): string {
return this._staticsCoin.name;
}
Expand Down
60 changes: 60 additions & 0 deletions modules/bitgo/test/v2/fixtures/staking/stakingWallet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import {
PrebuildTransactionOptions,
StakingRequest,
StakingTransaction,
} from '@bitgo/sdk-core';

export default {
txRequestId: '55ba0198-0b1f-44f6-94fe-8d782d633dde',
stakingRequest: function(transactions: StakingTransaction[]) : StakingRequest {
return {
id: '8638284a-dab2-46b9-b07f-21109a6e7220',
amount: '1234',
withdrawalAddress: 'DM24xSVH88kSSKWa6ujYomLVsPwZajhZHzYiQDo7Ntn8',
clientId: '13f3f4fe-bff2-46df-97e8-53d914dcbbdd',
requestingUserId: '691823db-c57b-4c7c-a3e8-7e8d7c997e6c',
type: 'STAKE',
enterpriseId: '4517abfb-f567-4b7a-9f91-407509d29403',
walletId: '03564b8e-e8e7-476e-b33c-2b0ffce0b49a',
walletType: 'hot',
coin: 'near',
status: 'NEW',
statusModifiedDate: '2022-01-03T22:04:29.264Z',
createdDate: '2019-01-03T22:04:29.264Z',
transactions: transactions,
};
},
transaction: function(status: string, buildParams?: PrebuildTransactionOptions): StakingTransaction {
const transaction: StakingTransaction = {
id: '00566722-daef-40eb-b0ac-fa5402bbfe72',
stakingRequestId: '8638284a-dab2-46b9-b07f-21109a6e7220',
delegationId: '505bda16-a000-461a-8421-1cf3f8617883',
transactionType: 'DELEGATE',
createdDate: '2022-01-03T22:04:29.264Z',
status: status,
statusModifiedDate: '2022-01-03T22:04:29.264Z',
amount: '1234',
pendingApprovalId: 'd99e3ae1-d2a6-4f57-87b6-d04c24854739',
transferId: 'e4b482b0-54d5-474b-bb2b-c56ce8516b5e',
txRequestId: this.txRequestId,
};
if (buildParams) {
transaction.buildParams = buildParams;
}
return transaction;
},
buildParams: {
recipients: [{
amount: '1234',
address: 'address',
data: 'data',
}],
stakingParams: {
requestId: '8638284a-dab2-46b9-b07f-21109a6e7220',
amount: '1234',
validator: 'validator',
actionType: 'DELEGATE',
},
},
};

206 changes: 206 additions & 0 deletions modules/bitgo/test/v2/unit/staking/stakingWalletCommon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
import * as should from 'should';
import * as nock from 'nock';
import fixtures from '../../fixtures/staking/stakingWallet';

import {
Enterprise,
Environments,
StakingRequest,
StakingWallet,
Wallet,
} from '@bitgo/sdk-core';
import { TestBitGo } from '@bitgo/sdk-test';
import { BitGo } from '../../../../src';

describe('Staking Wallet Common', function () {
const microservicesUri = Environments['mock'].uri;
let bitgo;
let baseCoin;
let enterprise;
let stakingWallet: StakingWallet;

before(function () {
bitgo = TestBitGo.decorate(BitGo, { env: 'mock', microservicesUri } as any);
bitgo.initializeTestVars();
baseCoin = bitgo.coin('eth');
baseCoin.keychains();
enterprise = new Enterprise(bitgo, baseCoin, { id: '5cf940949449412d00f53b3d92dbcaa3', name: 'Test Enterprise' });
const walletData = {
id: 'walletId',
coin: 'eth',
enterprise: enterprise.id,
keys: ['5b3424f91bf349930e340175'],
};
const wallet = new Wallet(bitgo, baseCoin, walletData);
stakingWallet = wallet.toStakingWallet();
});

describe('stake', function () {
it('should call staking-service to stake', async function () {
const expected = fixtures.stakingRequest(
[
fixtures.transaction('NEW'),
]
);
const msScope = nock(microservicesUri)
.post(`/api/staking/v1/${stakingWallet.coin}/wallets/${stakingWallet.walletId}/requests`, {
amount: '1',
clientId: 'clientId',
type: 'STAKE',
})
.reply(201, expected);

const stakingRequest = await stakingWallet.stake({
amount: '1',
clientId: 'clientId',
});

should.exist(stakingRequest);

stakingRequest.should.deepEqual(expected);
msScope.isDone().should.be.True();
});
});

describe('unstake', function () {
it('should call staking-service to unstake', async function () {
const expected = fixtures.stakingRequest(
[
fixtures.transaction('NEW'),
]);
const msScope = nock(microservicesUri)
.post(`/api/staking/v1/${stakingWallet.coin}/wallets/${stakingWallet.walletId}/requests`, {
amount: '1',
clientId: 'clientId',
type: 'UNSTAKE',
})
.reply(201, expected);

const stakingRequest = await stakingWallet.unstake({
amount: '1',
clientId: 'clientId',
});

should.exist(stakingRequest);

stakingRequest.should.deepEqual(expected);
msScope.isDone().should.be.True();
});
});

describe('getStakingRequest', function () {
it('should call staking-service to get staking request', async function () {
const stakingRequestId = '8638284a-dab2-46b9-b07f-21109a6e7220';
const expected = fixtures.stakingRequest(
[
fixtures.transaction('NEW'),
]);
const msScope = nock(microservicesUri)
.get(`/api/staking/v1/${stakingWallet.coin}/wallets/${stakingWallet.walletId}/requests/${stakingRequestId}`)
.reply(200, expected);

const stakingRequest = await stakingWallet.getStakingRequest(stakingRequestId);

should.exist(stakingRequest);

stakingRequest.should.deepEqual(expected);
msScope.isDone().should.be.True();
});
});

describe('getTransactionsReadyToSign', function () {
function mockGetStakingRequest(stakingRequestId: string, expected: StakingRequest) {
return nock(microservicesUri)
.get(`/api/staking/v1/${stakingWallet.coin}/wallets/${stakingWallet.walletId}/requests/${stakingRequestId}`)
.reply(200, expected);
}

it('should return allSigningComplete false when no transactions exist', async function () {
const stakingRequestId = '8638284a-dab2-46b9-b07f-21109a6e7220';
const expected = fixtures.stakingRequest(
[]);
const msScope = mockGetStakingRequest(stakingRequestId, expected);

const transactionsReadyToSign = await stakingWallet.getTransactionsReadyToSign(stakingRequestId);

should.exist(transactionsReadyToSign);
transactionsReadyToSign.allSigningComplete.should.be.False();
transactionsReadyToSign.transactions.should.be.empty();

msScope.isDone().should.be.True();
});

it('should return allSigningComplete true and 0 transactions when only a CONFIRMED transaction exists', async function () {
const stakingRequestId = '8638284a-dab2-46b9-b07f-21109a6e7220';
const expected = fixtures.stakingRequest(
[
fixtures.transaction('CONFIRMED'),
]);
const msScope = mockGetStakingRequest(stakingRequestId, expected);

const transactionsReadyToSign = await stakingWallet.getTransactionsReadyToSign(stakingRequestId);

should.exist(transactionsReadyToSign);
transactionsReadyToSign.allSigningComplete.should.be.True();
transactionsReadyToSign.transactions.should.be.empty();

msScope.isDone().should.be.True();
});

it('should return allSigningComplete false and 0 transactions when only a NEW transaction exists', async function () {
const stakingRequestId = '8638284a-dab2-46b9-b07f-21109a6e7220';
const expectedStakingRequest = fixtures.stakingRequest(
[
fixtures.transaction('NEW'),
]);
const msScope = mockGetStakingRequest(stakingRequestId, expectedStakingRequest);

const transactionsReadyToSign = await stakingWallet.getTransactionsReadyToSign(stakingRequestId);

should.exist(transactionsReadyToSign);
transactionsReadyToSign.allSigningComplete.should.be.False();
transactionsReadyToSign.transactions.should.be.empty();

msScope.isDone().should.be.True();
});

it('should return allSigningComplete false and 1 transactions when only a READY transaction exists', async function () {
const stakingRequestId = '8638284a-dab2-46b9-b07f-21109a6e7220';
const expectedTransaction = fixtures.transaction('READY');
const expectedStakingRequest = fixtures.stakingRequest(
[
expectedTransaction,
]);
const msScope = mockGetStakingRequest(stakingRequestId, expectedStakingRequest);

const transactionsReadyToSign = await stakingWallet.getTransactionsReadyToSign(stakingRequestId);

should.exist(transactionsReadyToSign);
transactionsReadyToSign.allSigningComplete.should.be.False();
transactionsReadyToSign.transactions.should.containEql(expectedTransaction);

msScope.isDone().should.be.True();
});

it('should return allSigningComplete false and 1 transaction when NEW and READY transaction exists', async function () {
const stakingRequestId = '8638284a-dab2-46b9-b07f-21109a6e7220';
const expectedTransaction = fixtures.transaction('READY');
const expectedStakingRequest = fixtures.stakingRequest(
[
expectedTransaction,
fixtures.transaction('NEW'),
]);
const msScope = mockGetStakingRequest(stakingRequestId, expectedStakingRequest);

const transactionsReadyToSign = await stakingWallet.getTransactionsReadyToSign(stakingRequestId);

should.exist(transactionsReadyToSign);
transactionsReadyToSign.allSigningComplete.should.be.False();
transactionsReadyToSign.transactions.should.containEql(expectedTransaction);

msScope.isDone().should.be.True();
});
});


});
Loading

0 comments on commit 20371c9

Please sign in to comment.