Skip to content

Commit

Permalink
Merge branch 'master' into zec-sig
Browse files Browse the repository at this point in the history
  • Loading branch information
rushilbg authored Jun 30, 2023
2 parents 3f43d34 + c488f0e commit 9536c29
Show file tree
Hide file tree
Showing 150 changed files with 8,629 additions and 5,625 deletions.
59 changes: 59 additions & 0 deletions examples/js/ada/send-pledge-transaction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
const BitGoJS = require('bitgo');
const bitgo = new BitGoJS.BitGo({ env: 'test' });
const Promise = require('bluebird');
const coin = 'tada';
const basecoin = bitgo.coin(coin);

// TODO: set your access token here
const accessToken = '';
const walletId = '649b57342f95fd00075b0ab393822a33';

// TODO: set your passphrase here
const walletPassphrase = null;

// TODO: set pledging inputs
const pledgingRawTxHex = '';
const pledgingNodePubKey = '';
const pledgingNodeKeySignature = '';

Promise.coroutine(function *() {
// generating address w/ same staking key and same payment key
bitgo.authenticateWithAccessToken({ accessToken: accessToken });
yield bitgo.unlock({ otp: '000000', duration: 3600 });
const walletInstance = yield basecoin.wallets().get({ id: walletId });

const whitelistedParams = {
intent: {
intentType: 'pledge',
rawTx: pledgingRawTxHex,
nodePublicKey: pledgingNodePubKey,
nodeKeySignature: pledgingNodeKeySignature,
},
apiVersion: 'lite',
preview: undefined,
};

const unsignedTx = (yield bitgo
.post(bitgo.url('/wallet/' + walletId + '/txrequests', 2))
.send(whitelistedParams)
.result());

// sign tx
const keychains = yield basecoin.keychains().getKeysForSigning({ wallet: walletInstance });
const signedStakingTransaction = yield walletInstance.signTransaction({
txPrebuild: unsignedTx,
keychain: keychains[0],
walletPassphrase: walletPassphrase,
pubs: keychains.map((k) => k.pub),
reqId: unsignedTx.txRequestId,
});

// submit tx
const submittedTx = yield bitgo
.post(basecoin.url('/wallet/' + walletId + '/tx/send'))
.send({ txRequestId: signedStakingTransaction.txRequestId })
.result();

console.log('New Transaction:', JSON.stringify(submittedTx, null, 4));

})();
4 changes: 2 additions & 2 deletions modules/abstract-cosmos/src/cosmosCoin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,8 @@ export class CosmosCoin extends BaseCoin {
if (!_.isEqual(filteredOutputs, filteredRecipients)) {
throw new Error('Tx outputs does not match with expected txParams recipients');
}
// WithdrawDelegatorRewards transaction doesn't have amount
if (transaction.type !== TransactionType.StakingWithdraw) {
// WithdrawDelegatorRewards and ContractCall transaction don't have amount
if (transaction.type !== TransactionType.StakingWithdraw && transaction.type !== TransactionType.ContractCall) {
for (const recipients of txParams.recipients) {
totalAmount = totalAmount.plus(recipients.amount);
}
Expand Down
32 changes: 32 additions & 0 deletions modules/abstract-cosmos/src/lib/ContractCallBuilder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { TransactionType } from '@bitgo/sdk-core';
import { BaseCoin as CoinConfig } from '@bitgo/statics';

import * as constants from './constants';
import { ExecuteContractMessage } from './iface';
import { CosmosTransactionBuilder } from './transactionBuilder';
import { CosmosUtils } from './utils';

export class ContractCallBuilder extends CosmosTransactionBuilder {
protected _utils: CosmosUtils;

constructor(_coinConfig: Readonly<CoinConfig>, utils: CosmosUtils) {
super(_coinConfig, utils);
this._utils = utils;
}

protected get transactionType(): TransactionType {
return TransactionType.ContractCall;
}

/** @inheritdoc */
messages(messages: ExecuteContractMessage[]): this {
this._messages = messages.map((executeContractMessage) => {
this._utils.validateExecuteContractMessage(executeContractMessage);
return {
typeUrl: constants.executeContractMsgTypeUrl,
value: executeContractMessage,
};
});
return this;
}
}
1 change: 1 addition & 0 deletions modules/abstract-cosmos/src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ export const sendMsgTypeUrl = '/cosmos.bank.v1beta1.MsgSend';
export const delegateMsgTypeUrl = '/cosmos.staking.v1beta1.MsgDelegate';
export const undelegateMsgTypeUrl = '/cosmos.staking.v1beta1.MsgUndelegate';
export const withdrawDelegatorRewardMsgTypeUrl = '/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward';
export const executeContractMsgTypeUrl = '/cosmwasm.wasm.v1.MsgExecuteContract';
export const UNAVAILABLE_TEXT = 'UNAVAILABLE';
23 changes: 18 additions & 5 deletions modules/abstract-cosmos/src/lib/iface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@ export interface TransactionExplanation extends BaseTransactionExplanation {
type: TransactionType;
}

export interface MessageData {
typeUrl: string;
value: SendMessage | DelegateOrUndelegeteMessage | WithdrawDelegatorRewardsMessage;
}

export interface SendMessage {
fromAddress: string;
toAddress: string;
Expand All @@ -27,6 +22,24 @@ export interface WithdrawDelegatorRewardsMessage {
validatorAddress: string;
}

export interface ExecuteContractMessage {
sender: string;
contract: string;
msg: Uint8Array;
funds?: Coin[];
}

export type CosmosTransactionMessage =
| SendMessage
| DelegateOrUndelegeteMessage
| WithdrawDelegatorRewardsMessage
| ExecuteContractMessage;

export interface MessageData {
typeUrl: string;
value: CosmosTransactionMessage;
}

export interface FeeData {
amount: Coin[];
gasLimit: number;
Expand Down
1 change: 1 addition & 0 deletions modules/abstract-cosmos/src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export * from './iface';
export { StakingActivateBuilder } from './StakingActivateBuilder';
export { StakingDeactivateBuilder } from './StakingDeactivateBuilder';
export { StakingWithdrawRewardsBuilder } from './StakingWithdrawRewardsBuilder';
export { ContractCallBuilder } from './ContractCallBuilder';
export { CosmosKeyPair } from './keyPair';
export { CosmosTransaction } from './transaction';
export { CosmosTransactionBuilder } from './transactionBuilder';
Expand Down
26 changes: 26 additions & 0 deletions modules/abstract-cosmos/src/lib/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { UNAVAILABLE_TEXT } from './constants';
import {
CosmosLikeTransaction,
DelegateOrUndelegeteMessage,
ExecuteContractMessage,
SendMessage,
TransactionExplanation,
TxData,
Expand Down Expand Up @@ -218,6 +219,18 @@ export class CosmosTransaction extends BaseTransaction {
amount: UNAVAILABLE_TEXT,
},
];
outputAmount = UNAVAILABLE_TEXT;
break;
case TransactionType.ContractCall:
explanationResult.type = TransactionType.ContractCall;
message = json.sendMessages[0].value as ExecuteContractMessage;
outputs = [
{
address: message.contract,
amount: UNAVAILABLE_TEXT,
},
];
outputAmount = UNAVAILABLE_TEXT;
break;
default:
throw new InvalidTransactionError('Transaction type not supported');
Expand Down Expand Up @@ -287,6 +300,19 @@ export class CosmosTransaction extends BaseTransaction {
coin: this._coinConfig.name,
});
break;
case TransactionType.ContractCall:
const executeContractMessage = this.cosmosLikeTransaction.sendMessages[0].value as ExecuteContractMessage;
inputs.push({
address: executeContractMessage.sender,
value: UNAVAILABLE_TEXT,
coin: this._coinConfig.name,
});
outputs.push({
address: executeContractMessage.contract,
value: UNAVAILABLE_TEXT,
coin: this._coinConfig.name,
});
break;
default:
throw new InvalidTransactionError('Transaction type not supported');
}
Expand Down
16 changes: 5 additions & 11 deletions modules/abstract-cosmos/src/lib/transactionBuilder.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,18 @@
import {
BaseAddress,
BaseKey,
PublicKey as BasePublicKey,
BaseTransactionBuilder,
BuildTransactionError,
InvalidTransactionError,
PublicKey as BasePublicKey,
SigningError,
TransactionType,
} from '@bitgo/sdk-core';
import { BaseCoin as CoinConfig } from '@bitgo/statics';
import { Secp256k1, sha256 } from '@cosmjs/crypto';
import { makeSignBytes } from '@cosmjs/proto-signing';
import BigNumber from 'bignumber.js';

import {
DelegateOrUndelegeteMessage,
FeeData,
MessageData,
SendMessage,
WithdrawDelegatorRewardsMessage,
} from './iface';
import { CosmosTransactionMessage, FeeData, MessageData } from './iface';
import { CosmosKeyPair as KeyPair } from './keyPair';
import { CosmosTransaction } from './transaction';
import { CosmosUtils } from './utils';
Expand Down Expand Up @@ -81,10 +74,11 @@ export abstract class CosmosTransactionBuilder extends BaseTransactionBuilder {
* - For @see TransactionType.StakingDeactivate required type is @see DelegateOrUndelegeteMessage
* - For @see TransactionType.Send required type is @see SendMessage
* - For @see TransactionType.StakingWithdraw required type is @see WithdrawDelegatorRewardsMessage
* @param {(SendMessage | DelegateOrUndelegeteMessage | WithdrawDelegatorRewardsMessage)[]} messages
* - For @see TransactionType.ContractCall required type is @see ExecuteContractMessage
* @param {CosmosTransactionMessage[]} messages
* @returns {TransactionBuilder} This transaction builder
*/
abstract messages(messages: (SendMessage | DelegateOrUndelegeteMessage | WithdrawDelegatorRewardsMessage)[]): this;
abstract messages(messages: CosmosTransactionMessage[]): this;

publicKey(publicKey: string | undefined): this {
this._publicKey = publicKey;
Expand Down
74 changes: 69 additions & 5 deletions modules/abstract-cosmos/src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ import { Coin, defaultRegistryTypes } from '@cosmjs/stargate';
import BigNumber from 'bignumber.js';
import { SignDoc, TxRaw } from 'cosmjs-types/cosmos/tx/v1beta1/tx';
import { Any } from 'cosmjs-types/google/protobuf/any';
import { MsgExecuteContract } from 'cosmjs-types/cosmwasm/wasm/v1/tx';

import * as crypto from 'crypto';
import * as constants from './constants';
import {
CosmosLikeTransaction,
DelegateOrUndelegeteMessage,
ExecuteContractMessage,
FeeData,
MessageData,
SendMessage,
Expand All @@ -35,7 +37,12 @@ import {
import { CosmosKeyPair as KeyPair } from './keyPair';

export class CosmosUtils implements BaseUtils {
private registry = new Registry([...defaultRegistryTypes]);
private registry;

constructor() {
this.registry = new Registry([...defaultRegistryTypes]);
this.registry.register(constants.executeContractMsgTypeUrl, MsgExecuteContract);
}

/** @inheritdoc */
isValidBlockId(hash: string): boolean {
Expand Down Expand Up @@ -260,6 +267,26 @@ export class CosmosUtils implements BaseUtils {
});
}

/**
* Returns the array of MessageData[] from the decoded transaction
* @param {DecodedTxRaw} decodedTx
* @returns {MessageData[]} Execute contract transaction message data
*/
getExecuteContractMessageDataFromDecodedTx(decodedTx: DecodedTxRaw): MessageData[] {
return decodedTx.body.messages.map((message) => {
const value = this.registry.decode(message);
return {
value: {
sender: value.sender,
contract: value.contract,
msg: value.msg,
funds: value.funds,
},
typeUrl: message.typeUrl,
};
});
}

/**
* Determines bitgo transaction type based on cosmos proto type url
* @param {string} typeUrl
Expand All @@ -275,6 +302,8 @@ export class CosmosUtils implements BaseUtils {
return TransactionType.StakingDeactivate;
case constants.withdrawDelegatorRewardMsgTypeUrl:
return TransactionType.StakingWithdraw;
case constants.executeContractMsgTypeUrl:
return TransactionType.ContractCall;
default:
return undefined;
}
Expand Down Expand Up @@ -473,19 +502,23 @@ export class CosmosUtils implements BaseUtils {
switch (type) {
case TransactionType.Send: {
const value = messageData.value as SendMessage;
this.isObjPropertyNull(value, ['toAddress', 'fromAddress']);
this.validateSendMessage(value);
break;
}
case TransactionType.StakingActivate:
case TransactionType.StakingDeactivate: {
const value = messageData.value as DelegateOrUndelegeteMessage;
this.isObjPropertyNull(value, ['validatorAddress', 'delegatorAddress']);
this.validateAmount(value.amount);
this.validateDelegateOrUndelegateMessage(value);
break;
}
case TransactionType.StakingWithdraw: {
const value = messageData.value as WithdrawDelegatorRewardsMessage;
this.isObjPropertyNull(value, ['validatorAddress', 'delegatorAddress']);
this.validateWithdrawRewardsMessage(value);
break;
}
case TransactionType.ContractCall: {
const value = messageData.value as ExecuteContractMessage;
this.validateExecuteContractMessage(value);
break;
}
default:
Expand Down Expand Up @@ -591,6 +624,8 @@ export class CosmosUtils implements BaseUtils {
sendMessageData = this.getDelegateOrUndelegateMessageDataFromDecodedTx(decodedTx);
} else if (type === TransactionType.StakingWithdraw) {
sendMessageData = this.getWithdrawRewardsMessageDataFromDecodedTx(decodedTx);
} else if (type === TransactionType.ContractCall) {
sendMessageData = this.getExecuteContractMessageDataFromDecodedTx(decodedTx);
} else {
throw new Error('Transaction type not supported: ' + typeUrl);
}
Expand Down Expand Up @@ -671,6 +706,35 @@ export class CosmosUtils implements BaseUtils {
isValidAddress(address: string): boolean {
throw new NotImplementedError('isValidAddress not implemented');
}

/**
* Validates if the address matches with regex @see contractAddressRegex
* @param {string} address
* @returns {boolean} - the validation result
*/
isValidContractAddress(address: string): boolean {
throw new NotImplementedError('isValidContractAddress not implemented');
}

/**
* Validates a execute contract message
* @param {ExecuteContractMessage} message - The execute contract message to validate
* @throws {InvalidTransactionError} Throws an error if the message is invalid
*/
validateExecuteContractMessage(message: ExecuteContractMessage) {
if (!message.contract || !this.isValidContractAddress(message.contract)) {
throw new InvalidTransactionError(`Invalid ExecuteContractMessage contract address: ` + message.contract);
}
if (!message.sender || !this.isValidAddress(message.sender)) {
throw new InvalidTransactionError(`Invalid ExecuteContractMessage sender address: ` + message.sender);
}
if (!message.msg) {
throw new InvalidTransactionError(`Invalid ExecuteContractMessage msg: ` + message.msg);
}
if (message.funds) {
this.validateAmountData(message.funds);
}
}
}

const utils = new CosmosUtils();
Expand Down
3 changes: 2 additions & 1 deletion modules/bitgo/.prettierignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
*.json
*.yml
CHANGELOG.md
CHANGELOG.md
dist/
3 changes: 3 additions & 0 deletions modules/bitgo/.prettierrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
printWidth: 120
singleQuote: true
trailingComma: 'es5'
Loading

0 comments on commit 9536c29

Please sign in to comment.