Skip to content

Commit

Permalink
feat(sdk-coin-baby): add sdk support for babylon native staking
Browse files Browse the repository at this point in the history
TICKET: SC-1276
  • Loading branch information
sbplat committed Mar 6, 2025
1 parent 59399d5 commit 1b09c77
Show file tree
Hide file tree
Showing 15 changed files with 755 additions and 31 deletions.
11 changes: 10 additions & 1 deletion modules/abstract-cosmos/src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,15 @@ export class CosmosUtils implements BaseUtils {
return encodePubkey(encodeSecp256k1Pubkey(fromHex(pubkey)));
}

/**
* Gets the send messages used in the final step of encoding a transaction. This allows for any final processing needed.
* @param {CosmosLikeTransaction} cosmosLikeTransaction transaction to get send messages from
* @returns {Any[]} processed send messages
*/
getSendMessagesForEncodingTx(cosmosLikeTransaction: CosmosLikeTransaction): Any[] {
return cosmosLikeTransaction.sendMessages as unknown as Any[];
}

/**
* Creates a txRaw from an cosmos like transaction @see CosmosLikeTransaction
* @Precondition cosmosLikeTransaction.publicKey must be defined
Expand All @@ -380,7 +389,7 @@ export class CosmosUtils implements BaseUtils {
throw new Error('publicKey is required to create a txRaw');
}
const encodedPublicKey: Any = this.getEncodedPubkey(cosmosLikeTransaction.publicKey);
const messages = cosmosLikeTransaction.sendMessages as unknown as Any[];
const messages = this.getSendMessagesForEncodingTx(cosmosLikeTransaction);
let txBodyValue;
if (cosmosLikeTransaction.memo) {
txBodyValue = {
Expand Down
5 changes: 4 additions & 1 deletion modules/sdk-coin-baby/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,16 @@
]
},
"dependencies": {
"@babylonlabs-io/babylon-proto-ts": "0.0.3-canary.6",
"@bitgo/abstract-cosmos": "^11.5.0",
"@bitgo/sdk-core": "^31.0.0",
"@bitgo/statics": "^51.2.0",
"@cosmjs/amino": "^0.29.5",
"@cosmjs/encoding": "^0.29.5",
"@cosmjs/proto-signing": "^0.29.5",
"@cosmjs/stargate": "^0.29.5",
"bignumber.js": "^9.1.1"
"bignumber.js": "^9.1.1",
"cosmjs-types": "^0.6.1"
},
"devDependencies": {
"@bitgo/sdk-api": "^1.60.0",
Expand Down
11 changes: 11 additions & 0 deletions modules/sdk-coin-baby/src/lib/EpochedStakingActivateBuilder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import * as constants from './constants';
import { StakingActivateBuilder } from '@bitgo/abstract-cosmos';

export class EpochedStakingActivateBuilder extends StakingActivateBuilder {
/** @inheritdoc */
messages(delegateMessages: Parameters<StakingActivateBuilder['messages']>[0]): this {
super.messages(delegateMessages);
this._messages.forEach((message) => (message.typeUrl = constants.wrappedDelegateMsgTypeUrl));
return this;
}
}
11 changes: 11 additions & 0 deletions modules/sdk-coin-baby/src/lib/EpochedStakingDeactivateBuilder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import * as constants from './constants';
import { StakingDeactivateBuilder } from '@bitgo/abstract-cosmos';

export class EpochedStakingDeactivateBuilder extends StakingDeactivateBuilder {
/** @inheritdoc */
messages(undelegateMessages: Parameters<StakingDeactivateBuilder['messages']>[0]): this {
super.messages(undelegateMessages);
this._messages.forEach((message) => (message.typeUrl = constants.wrappedUndelegateMsgTypeUrl));
return this;
}
}
11 changes: 11 additions & 0 deletions modules/sdk-coin-baby/src/lib/EpochedStakingRedelegateBuilder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import * as constants from './constants';
import { StakingRedelegateBuilder } from '@bitgo/abstract-cosmos';

export class EpochedStakingRedelegateBuilder extends StakingRedelegateBuilder {
/** @inheritdoc */
messages(redelegateMessages: Parameters<StakingRedelegateBuilder['messages']>[0]): this {
super.messages(redelegateMessages);
this._messages.forEach((message) => (message.typeUrl = constants.wrappedBeginRedelegateTypeUrl));
return this;
}
}
3 changes: 3 additions & 0 deletions modules/sdk-coin-baby/src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@ export const contractAddressRegex = /^(bbn)1(['qpzry9x8gf2tvdw0s3jn54khce6mua7l]
export const ADDRESS_PREFIX = 'bbn';
export const GAS_AMOUNT = '7000';
export const GAS_LIMIT = 200000;
export const wrappedDelegateMsgTypeUrl = '/babylon.epoching.v1.MsgWrappedDelegate';
export const wrappedUndelegateMsgTypeUrl = '/babylon.epoching.v1.MsgWrappedUndelegate';
export const wrappedBeginRedelegateTypeUrl = '/babylon.epoching.v1.MsgWrappedBeginRedelegate';
9 changes: 6 additions & 3 deletions modules/sdk-coin-baby/src/lib/transactionBuilderFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import {
} from '@bitgo/abstract-cosmos';
import { BaseTransactionBuilderFactory, InvalidTransactionError, TransactionType } from '@bitgo/sdk-core';
import { BaseCoin as CoinConfig } from '@bitgo/statics';
import { EpochedStakingActivateBuilder } from './EpochedStakingActivateBuilder';
import { EpochedStakingDeactivateBuilder } from './EpochedStakingDeactivateBuilder';
import { EpochedStakingRedelegateBuilder } from './EpochedStakingRedelegateBuilder';
import utils from './utils';

export class TransactionBuilderFactory extends BaseTransactionBuilderFactory {
Expand Down Expand Up @@ -49,11 +52,11 @@ export class TransactionBuilderFactory extends BaseTransactionBuilderFactory {
}
/** @inheritdoc */
getStakingActivateBuilder(tx?: CosmosTransaction): StakingActivateBuilder {
return this.initializeBuilder(tx, new StakingActivateBuilder(this._coinConfig, utils));
return this.initializeBuilder(tx, new EpochedStakingActivateBuilder(this._coinConfig, utils));
}
/** @inheritdoc */
getStakingDeactivateBuilder(tx?: CosmosTransaction): StakingDeactivateBuilder {
return this.initializeBuilder(tx, new StakingDeactivateBuilder(this._coinConfig, utils));
return this.initializeBuilder(tx, new EpochedStakingDeactivateBuilder(this._coinConfig, utils));
}
/** @inheritdoc */
getStakingWithdrawRewardsBuilder(tx?: CosmosTransaction): StakingWithdrawRewardsBuilder {
Expand All @@ -65,7 +68,7 @@ export class TransactionBuilderFactory extends BaseTransactionBuilderFactory {
}

getStakingRedelegateBuilder(tx?: CosmosTransaction): StakingRedelegateBuilder {
return this.initializeBuilder(tx, new StakingRedelegateBuilder(this._coinConfig, utils));
return this.initializeBuilder(tx, new EpochedStakingRedelegateBuilder(this._coinConfig, utils));
}

/** @inheritdoc */
Expand Down
75 changes: 73 additions & 2 deletions modules/sdk-coin-baby/src/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,81 @@
import { CosmosUtils } from '@bitgo/abstract-cosmos';
import { InvalidTransactionError } from '@bitgo/sdk-core';
import { CosmosUtils, MessageData } from '@bitgo/abstract-cosmos';
import { InvalidTransactionError, TransactionType } from '@bitgo/sdk-core';
import { Coin } from '@cosmjs/stargate';
import BigNumber from 'bignumber.js';
import * as constants from './constants';
import { epochingtx } from '@babylonlabs-io/babylon-proto-ts';
import { DecodedTxRaw } from '@cosmjs/proto-signing';
import { Any } from 'cosmjs-types/google/protobuf/any';

export class Utils extends CosmosUtils {
protected wrappedMsgTypeUrls = new Set<string>([
constants.wrappedDelegateMsgTypeUrl,
constants.wrappedUndelegateMsgTypeUrl,
constants.wrappedBeginRedelegateTypeUrl,
]);

constructor() {
super();
this.registry.register(constants.wrappedDelegateMsgTypeUrl, epochingtx.MsgWrappedDelegate);
this.registry.register(constants.wrappedUndelegateMsgTypeUrl, epochingtx.MsgWrappedUndelegate);
this.registry.register(constants.wrappedBeginRedelegateTypeUrl, epochingtx.MsgWrappedBeginRedelegate);
}

/** @inheritdoc */
getDelegateOrUndelegateMessageDataFromDecodedTx(decodedTx: DecodedTxRaw): MessageData[] {
return decodedTx.body.messages.map((message) => {
const value = this.registry.decode(message).msg;
return {
typeUrl: message.typeUrl,
value: {
delegatorAddress: value.delegatorAddress,
validatorAddress: value.validatorAddress,
amount: value.amount,
},
};
});
}

/** @inheritdoc */
getRedelegateMessageDataFromDecodedTx(decodedTx: DecodedTxRaw): MessageData[] {
return decodedTx.body.messages.map((message) => {
const value = this.registry.decode(message).msg;
return {
typeUrl: message.typeUrl,
value: {
delegatorAddress: value.delegatorAddress,
validatorSrcAddress: value.validatorSrcAddress,
validatorDstAddress: value.validatorDstAddress,
amount: value.amount,
},
};
});
}

/** @inheritdoc */
getTransactionTypeFromTypeUrl(typeUrl: string): TransactionType | undefined {
switch (typeUrl) {
case constants.wrappedDelegateMsgTypeUrl:
return TransactionType.StakingActivate;
case constants.wrappedUndelegateMsgTypeUrl:
return TransactionType.StakingDeactivate;
case constants.wrappedBeginRedelegateTypeUrl:
return TransactionType.StakingRedelegate;
default:
return super.getTransactionTypeFromTypeUrl(typeUrl);
}
}

/** @inheritdoc */
getSendMessagesForEncodingTx(
cosmosLikeTransaction: Parameters<CosmosUtils['getSendMessagesForEncodingTx']>[0]
): Any[] {
return cosmosLikeTransaction.sendMessages.map(({ typeUrl, value }) => ({
typeUrl,
value: this.wrappedMsgTypeUrls.has(typeUrl) ? { msg: value } : value,
})) as unknown as Any[];
}

/** @inheritdoc */
isValidAddress(address: string): boolean {
return this.isValidCosmosLikeAddressWithMemoId(address, constants.accountAddressRegex);
Expand Down
141 changes: 141 additions & 0 deletions modules/sdk-coin-baby/test/resources/baby.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,147 @@ export const TEST_TX_WITH_MEMO = {
},
};

export const TEST_DELEGATE_TX = {
hash: 'EC11EACC2692B1B741D0CEC74FD4E5DA2D9E8875623713ED0EAE63ED43FB5A91',
signature: 'BkwhIYhaVq5UPGEpCUPBCdq59j/JrHCQHn86UiREjL5BqvvPaQZM+hexBpSNoXDokiwoQDQK53VSO4iUumCUGQ==',
pubKey: 'AwT4xoruxA+DkKynr1LH7CM60RwR7Lp5InwQ5ISaN1Hw',
privateKey: 'QAzeAkPWRGyRT8/TvJcRC7VSzQHV9QhH6YTmGZbnvmk=',
signedTxBase64:
'Cp4BCpsBCicvYmFieWxvbi5lcG9jaGluZy52MS5Nc2dXcmFwcGVkRGVsZWdhdGUScApuCipiYm4xMjc0ZXA4cG5ybGVqNXZnbXR3cHB5c3luemNkNGZoeGMza3UwdDMSMWJibnZhbG9wZXIxemw3OGV6aGd3NmZxeHlkZWNubWNteWRzbTBndHJqZHRhbm03bnIaDQoEdWJibhIFMTAwMDASZQpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAwT4xoruxA+DkKynr1LH7CM60RwR7Lp5InwQ5ISaN1HwEgQKAggBGB0SEQoLCgR1YmJuEgM1MDAQwJoMGkAGTCEhiFpWrlQ8YSkJQ8EJ2rn2P8mscJAefzpSJESMvkGq+89pBkz6F7EGlI2hcOiSLChANArndVI7iJS6YJQZ',
from: 'bbn1274ep8pnrlej5vgmtwppysynzcd4fhxc3ku0t3',
to: 'bbnvaloper1zl78ezhgw6fqxydecnmcmydsm0gtrjdtanm7nr',
chainId: 'bbn-test-5',
accountNumber: 59235,
sequence: 29,
sendAmount: '10000',
feeAmount: '500',
sendMessage: {
typeUrl: '/babylon.epoching.v1.MsgWrappedDelegate',
value: {
delegatorAddress: 'bbn1274ep8pnrlej5vgmtwppysynzcd4fhxc3ku0t3',
validatorAddress: 'bbnvaloper1zl78ezhgw6fqxydecnmcmydsm0gtrjdtanm7nr',
amount: {
denom: 'ubbn',
amount: '10000',
},
},
},
gasBudget: {
amount: [
{
denom: 'ubbn',
amount: '500',
},
],
gasLimit: 200000,
},
};

export const TEST_UNDELEGATE_TX = {
hash: '009D9A8F17B4DB99B86F1EBAFC801AA2A887E89D4D20EAE2FE30203484A3273E',
signature: 'm8XIY/88A66uBWShNLGFxLqgftFF18dpqni4ChvS6Wsn+xZCqZhcrpoJbmLzaFTRp+YnPvLawLy0QEAkff5NyA==',
pubKey: 'AwT4xoruxA+DkKynr1LH7CM60RwR7Lp5InwQ5ISaN1Hw',
privateKey: 'QAzeAkPWRGyRT8/TvJcRC7VSzQHV9QhH6YTmGZbnvmk=',
signedTxBase64:
'CqABCp0BCikvYmFieWxvbi5lcG9jaGluZy52MS5Nc2dXcmFwcGVkVW5kZWxlZ2F0ZRJwCm4KKmJibjEyNzRlcDhwbnJsZWo1dmdtdHdwcHlzeW56Y2Q0Zmh4YzNrdTB0MxIxYmJudmFsb3BlcjF6bDc4ZXpoZ3c2ZnF4eWRlY25tY215ZHNtMGd0cmpkdGFubTduchoNCgR1YmJuEgUxMDAwMBJlClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiEDBPjGiu7ED4OQrKevUsfsIzrRHBHsunkifBDkhJo3UfASBAoCCAEYHhIRCgsKBHViYm4SAzUwMBDAmgwaQJvFyGP/PAOurgVkoTSxhcS6oH7RRdfHaap4uAob0ulrJ/sWQqmYXK6aCW5i82hU0afmJz7y2sC8tEBAJH3+Tcg=',
from: 'bbn1274ep8pnrlej5vgmtwppysynzcd4fhxc3ku0t3',
to: 'bbnvaloper1zl78ezhgw6fqxydecnmcmydsm0gtrjdtanm7nr',
chainId: 'bbn-test-5',
accountNumber: 59235,
sequence: 30,
sendAmount: '10000',
feeAmount: '500',
sendMessage: {
typeUrl: '/babylon.epoching.v1.MsgWrappedUndelegate',
value: {
delegatorAddress: 'bbn1274ep8pnrlej5vgmtwppysynzcd4fhxc3ku0t3',
validatorAddress: 'bbnvaloper1zl78ezhgw6fqxydecnmcmydsm0gtrjdtanm7nr',
amount: {
denom: 'ubbn',
amount: '10000',
},
},
},
gasBudget: {
amount: [
{
denom: 'ubbn',
amount: '500',
},
],
gasLimit: 200000,
},
};

export const TEST_REDELEGATE_TX = {
hash: 'CC45369A75E76D3287A3831853369EEE6C5D1B5FB4277CC4FA0D4D06A0DFA661',
signature: 'bJlHHrfB08ypEZkSzy6wbqvNJhGB6OgPd2quqW5GqNYPtBniVHiS0jjVwgoldkM0cLdj8EilWP663Donqci8Aw==',
pubKey: 'AwT4xoruxA+DkKynr1LH7CM60RwR7Lp5InwQ5ISaN1Hw',
privateKey: 'QAzeAkPWRGyRT8/TvJcRC7VSzQHV9QhH6YTmGZbnvmk=',
signedTxBase64:
'CtoBCtcBCi4vYmFieWxvbi5lcG9jaGluZy52MS5Nc2dXcmFwcGVkQmVnaW5SZWRlbGVnYXRlEqQBCqEBCipiYm4xMjc0ZXA4cG5ybGVqNXZnbXR3cHB5c3luemNkNGZoeGMza3UwdDMSMWJibnZhbG9wZXIxemw3OGV6aGd3NmZxeHlkZWNubWNteWRzbTBndHJqZHRhbm03bnIaMWJibnZhbG9wZXIxMmptOXo2aHdtNGV4N2MwejhodGV5M3loeWZoeDQ0em43MGE1bDYiDQoEdWJibhIFMTAwMDASZQpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAwT4xoruxA+DkKynr1LH7CM60RwR7Lp5InwQ5ISaN1HwEgQKAggBGB8SEQoLCgR1YmJuEgM1MDAQwJoMGkBsmUcet8HTzKkRmRLPLrBuq80mEYHo6A93aq6pbkao1g+0GeJUeJLSONXCCiV2QzRwt2PwSKVY/rrcOiepyLwD',
delegator: 'bbn1274ep8pnrlej5vgmtwppysynzcd4fhxc3ku0t3',
validator: 'bbnvaloper12jm9z6hwm4ex7c0z8htey3yhyfhx44zn70a5l6',
chainId: 'bbn-test-5',
accountNumber: 59235,
sequence: 31,
sendAmount: '10000',
feeAmount: '500',
sendMessage: {
typeUrl: '/babylon.epoching.v1.MsgWrappedBeginRedelegate',
value: {
delegatorAddress: 'bbn1274ep8pnrlej5vgmtwppysynzcd4fhxc3ku0t3',
validatorSrcAddress: 'bbnvaloper1zl78ezhgw6fqxydecnmcmydsm0gtrjdtanm7nr',
validatorDstAddress: 'bbnvaloper12jm9z6hwm4ex7c0z8htey3yhyfhx44zn70a5l6',
amount: {
denom: 'ubbn',
amount: '10000',
},
},
},
gasBudget: {
amount: [
{
denom: 'ubbn',
amount: '500',
},
],
gasLimit: 200000,
},
};

export const TEST_WITHDRAW_REWARDS_TX = {
hash: 'EA4ECA4DA17B158238242572AD9A7E7734D7085BF679555EE90FA4F0EF3CFA79',
signature: 'vyVU/1zVhp6m0uMF5mLaKr3o/Oe36O+EF3E7Dgw7smlUjOW5Ttwgnl44zO1vro4MU2frc/KV4Ji2IqWKi8QlLA==',
pubKey: 'AwT4xoruxA+DkKynr1LH7CM60RwR7Lp5InwQ5ISaN1Hw',
privateKey: 'QAzeAkPWRGyRT8/TvJcRC7VSzQHV9QhH6YTmGZbnvmk=',
signedTxBase64:
'Cp0BCpoBCjcvY29zbW9zLmRpc3RyaWJ1dGlvbi52MWJldGExLk1zZ1dpdGhkcmF3RGVsZWdhdG9yUmV3YXJkEl8KKmJibjEyNzRlcDhwbnJsZWo1dmdtdHdwcHlzeW56Y2Q0Zmh4YzNrdTB0MxIxYmJudmFsb3BlcjF6bDc4ZXpoZ3c2ZnF4eWRlY25tY215ZHNtMGd0cmpkdGFubTduchJlClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiEDBPjGiu7ED4OQrKevUsfsIzrRHBHsunkifBDkhJo3UfASBAoCCAEYIBIRCgsKBHViYm4SAzUwMBDAmgwaQL8lVP9c1YaeptLjBeZi2iq96Pznt+jvhBdxOw4MO7JpVIzluU7cIJ5eOMztb66ODFNn63PyleCYtiKliovEJSw=',
from: 'bbn1274ep8pnrlej5vgmtwppysynzcd4fhxc3ku0t3',
to: 'bbnvaloper1zl78ezhgw6fqxydecnmcmydsm0gtrjdtanm7nr',
chainId: 'bbn-test-5',
accountNumber: 59235,
sequence: 32,
sendAmount: '10000',
feeAmount: '500',
sendMessage: {
typeUrl: '/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward',
value: {
delegatorAddress: 'bbn1274ep8pnrlej5vgmtwppysynzcd4fhxc3ku0t3',
validatorAddress: 'bbnvaloper1zl78ezhgw6fqxydecnmcmydsm0gtrjdtanm7nr',
},
},
gasBudget: {
amount: [
{
denom: 'ubbn',
amount: '500',
},
],
gasLimit: 200000,
},
};

export const address = {
address1: 'bbn1897xa4swxx9dr7z0zut0mfs7efplx80q86kd8q',
address2: 'bbn1274ep8pnrlej5vgmtwppysynzcd4fhxc3ku0t3',
Expand Down
Loading

0 comments on commit 1b09c77

Please sign in to comment.