Skip to content

Commit

Permalink
Merge pull request #4361 from BitGo/CR-1073
Browse files Browse the repository at this point in the history
feat(sdk-coin-avaxp): add avaxp new SDK
  • Loading branch information
noel-bitgo authored Mar 26, 2024
2 parents b3d135f + abe6595 commit 4b6738b
Show file tree
Hide file tree
Showing 20 changed files with 357 additions and 31 deletions.
1 change: 1 addition & 0 deletions modules/sdk-coin-avaxp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"@bitgo/sdk-core": "^26.3.0",
"@bitgo/statics": "^48.1.0",
"@bitgo/utxo-lib": "^9.34.0",
"@bitgo/avalanchejs": "1.0.0-alpha.3",
"avalanche": "3.15.3",
"bignumber.js": "^9.0.0",
"create-hash": "^1.2.0",
Expand Down
5 changes: 5 additions & 0 deletions modules/sdk-coin-avaxp/src/avaxp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,10 +151,15 @@ export class AvaxP extends BaseCoin {
}

switch (explainedTx.type) {
// @deprecated
case TransactionType.AddDelegator:
case TransactionType.AddValidator:
this.validateStakingTx(stakingOptions, explainedTx);
break;
case TransactionType.AddPermissionlessDelegator:
case TransactionType.AddPermissionlessValidator:
this.validateStakingTx(stakingOptions, explainedTx);
break;
case TransactionType.Export:
if (!params.txParams.recipients || params.txParams.recipients?.length !== 1) {
throw new Error('Export Tx requires a recipient');
Expand Down
4 changes: 2 additions & 2 deletions modules/sdk-coin-avaxp/src/lib/delegatorTxBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
UnsignedTx,
} from 'avalanche/dist/apis/platformvm';
import { BinTools, BN } from 'avalanche';
import { SECP256K1_Transfer_Output, Tx, BaseTx } from './iface';
import { SECP256K1_Transfer_Output, DeprecatedTx, BaseTx } from './iface';
import utils from './utils';
import { Credential } from 'avalanche/dist/common';
import { recoverUtxos } from './utxoEngine';
Expand Down Expand Up @@ -149,7 +149,7 @@ export class DelegatorTxBuilder extends TransactionBuilder {
// endregion

/** @inheritdoc */
initBuilder(tx: Tx): this {
initBuilder(tx: DeprecatedTx): this {
super.initBuilder(tx);
const baseTx: BaseTx = tx.getUnsignedTx().getTransaction();
if (!this.verifyTxType(baseTx)) {
Expand Down
4 changes: 2 additions & 2 deletions modules/sdk-coin-avaxp/src/lib/exportInCTxBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
import utils from './utils';
import { BN, Buffer as BufferAvax } from 'avalanche';
import { Transaction } from './transaction';
import { Tx, BaseTx, DecodedUtxoObj } from './iface';
import { DeprecatedTx, BaseTx, DecodedUtxoObj } from './iface';
import { AtomicInCTransactionBuilder } from './atomicInCTransactionBuilder';

export class ExportInCTxBuilder extends AtomicInCTransactionBuilder {
Expand Down Expand Up @@ -75,7 +75,7 @@ export class ExportInCTxBuilder extends AtomicInCTransactionBuilder {
return TransactionType.Export;
}

initBuilder(tx: Tx): this {
initBuilder(tx: DeprecatedTx): this {
const baseTx: BaseTx = tx.getUnsignedTx().getTransaction();
if (
baseTx.getNetworkID() !== this.transaction._networkID ||
Expand Down
4 changes: 2 additions & 2 deletions modules/sdk-coin-avaxp/src/lib/exportTxBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { BN } from 'avalanche';
import { AmountOutput } from 'avalanche/dist/apis/evm/outputs';
import utils from './utils';
import { recoverUtxos } from './utxoEngine';
import { Tx, BaseTx } from './iface';
import { DeprecatedTx, BaseTx } from './iface';

export class ExportTxBuilder extends AtomicTransactionBuilder {
private _amount: BN;
Expand All @@ -40,7 +40,7 @@ export class ExportTxBuilder extends AtomicTransactionBuilder {
}

/** @inheritdoc */
initBuilder(tx: Tx): this {
initBuilder(tx: DeprecatedTx): this {
super.initBuilder(tx);
const baseTx: BaseTx = tx.getUnsignedTx().getTransaction();
if (!this.verifyTxType(baseTx)) {
Expand Down
4 changes: 3 additions & 1 deletion modules/sdk-coin-avaxp/src/lib/iface.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Entry, TransactionExplanation as BaseTransactionExplanation, TransactionType } from '@bitgo/sdk-core';
import { BaseTx as PMVBaseTx, TransferableOutput, Tx as PMVTx } from 'avalanche/dist/apis/platformvm';
import { EVMBaseTx, EVMOutput, Tx as EMVTx } from 'avalanche/dist/apis/evm';
import { pvmSerial as avaxSdkPvmSerial } from '@bitgo/avalanchejs';

export interface AvaxpEntry extends Entry {
id: string;
Expand Down Expand Up @@ -63,6 +64,7 @@ export const SECP256K1_Transfer_Output = 7;

export const ADDRESS_SEPARATOR = '~';
export const INPUT_SEPARATOR = ':';
export type Tx = PMVTx | EMVTx;
export type DeprecatedTx = PMVTx | EMVTx;
export type Tx = avaxSdkPvmSerial.AddPermissionlessValidatorTx;
export type BaseTx = PMVBaseTx | EVMBaseTx;
export type Output = TransferableOutput | EVMOutput;
4 changes: 2 additions & 2 deletions modules/sdk-coin-avaxp/src/lib/importInCTxBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { costImportTx } from 'avalanche/dist/utils';
import { BN } from 'avalanche';
import { Credential } from 'avalanche/dist/common';
import { recoverUtxos, utxoToInput } from './utxoEngine';
import { BaseTx, Tx } from './iface';
import { BaseTx, DeprecatedTx } from './iface';
import { AtomicInCTransactionBuilder } from './atomicInCTransactionBuilder';
import utils from './utils';

Expand All @@ -39,7 +39,7 @@ export class ImportInCTxBuilder extends AtomicInCTransactionBuilder {
}

/** @inheritdoc */
initBuilder(tx: Tx): this {
initBuilder(tx: DeprecatedTx): this {
const baseTx: BaseTx = tx.getUnsignedTx().getTransaction();
if (
baseTx.getNetworkID() !== this.transaction._networkID ||
Expand Down
4 changes: 2 additions & 2 deletions modules/sdk-coin-avaxp/src/lib/importTxBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { ImportTx, PlatformVMConstants, Tx as PVMTx, UnsignedTx } from 'avalanch
import utils from './utils';
import { BN } from 'avalanche';
import { recoverUtxos } from './utxoEngine';
import { Tx, BaseTx } from './iface';
import { DeprecatedTx, BaseTx } from './iface';

export class ImportTxBuilder extends AtomicTransactionBuilder {
constructor(_coinConfig: Readonly<CoinConfig>) {
Expand All @@ -17,7 +17,7 @@ export class ImportTxBuilder extends AtomicTransactionBuilder {
return TransactionType.Import;
}

initBuilder(tx: Tx): this {
initBuilder(tx: DeprecatedTx): this {
super.initBuilder(tx);
const baseTx: BaseTx = tx.getUnsignedTx().getTransaction();
if (!this.verifyTxType(baseTx)) {
Expand Down
248 changes: 248 additions & 0 deletions modules/sdk-coin-avaxp/src/lib/permissionlessValidatorTxBuilder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
import {
BaseAddress,
BaseKey,
BaseTransaction,
BaseTransactionBuilder,
BuildTransactionError,
TransactionType,
} from '@bitgo/sdk-core';
import { AvalancheNetwork, BaseCoin as CoinConfig } from '@bitgo/statics';
import { BinTools, BN } from 'avalanche';
import { pvmSerial as avaxSdkPvmSerial } from '@bitgo/avalanchejs';
import { Tx } from './iface';
import { Transaction } from './transaction';
import { KeyPair } from './keyPair';
import utils from './utils';
import BigNumber from 'bignumber.js';

export class PermissionlessValidatorTxBuilder extends BaseTransactionBuilder {
private _transaction: BaseTransaction;
protected _nodeID: string;
protected _blsPublicKey: string;
protected _blsSignature: string;
protected _startTime: BN;
protected _endTime: BN;
protected _stakeAmount: BN;

/**
*
* @param coinConfig
*/
constructor(coinConfig: Readonly<CoinConfig>) {
super(coinConfig);
const network = coinConfig.network as AvalancheNetwork;
this._stakeAmount = new BN(network.minStake);
}

/**
* get transaction type
* @protected
*/
protected get transactionType(): TransactionType {
return TransactionType.AddPermissionlessValidator;
}

/**
* Addresses where reward should be deposit
* @param {string | string[]} address - single address or array of addresses to receive rewards
*/
rewardAddresses(address: string | string[]): this {
// TODO Implement
return this;
}

/**
*
* @param nodeID
*/
nodeID(nodeID: string): this {
this.validateNodeID(nodeID);
this._nodeID = nodeID;
return this;
}

/**
*
* @param blsPublicKey
*/
blsPublicKey(blsPublicKey: string): this {
// TODO add
// this.validateBlsKey(blsPublicKey);
this._blsPublicKey = blsPublicKey;
return this;
}

/**
*
* @param blsSignature
*/
blsSignature(blsSignature: string): this {
// TODO add
// this.validateBlsSignature(blsSignature);
this._blsSignature = blsSignature;
return this;
}

/**
* start time of staking period
* @param value
*/
startTime(value: string | number): this {
this._startTime = new BN(value);
return this;
}

/**
* end time of staking period
* @param value
*/
endTime(value: string | number): this {
this._endTime = new BN(value);
return this;
}

/**
*
* @param value
*/
stakeAmount(value: BN | string): this {
const valueBN = BN.isBN(value) ? value : new BN(value);
this.validateStakeAmount(valueBN);
this._stakeAmount = valueBN;
return this;
}

// region Validators
/**
* validates a correct NodeID is used
* @param nodeID
*/
validateNodeID(nodeID: string): void {
if (!nodeID) {
throw new BuildTransactionError('Invalid transaction: missing nodeID');
}
if (nodeID.slice(0, 6) !== 'NodeID') {
throw new BuildTransactionError('Invalid transaction: invalid NodeID tag');
}
const bintools = BinTools.getInstance();
if (!(bintools.b58ToBuffer(nodeID.slice(7)).length === 24)) {
throw new BuildTransactionError('Invalid transaction: NodeID is not in cb58 format');
}
}

/**
*
* protected _startTime: Date;
* protected _endTime: Date;
* 2 weeks = 1209600
* 1 year = 31556926
* unix time stamp based off seconds
*/
validateStakeDuration(startTime: BN, endTime: BN): void {
const oneDayLater = new BN(Date.now()).add(new BN(86400));
if (!startTime.gt(oneDayLater)) {
throw new BuildTransactionError('Start time needs to be one day greater than current time');
}
if (endTime < startTime) {
throw new BuildTransactionError('End date cannot be less than start date');
}
// TODO implement checks for start/end time
}

/**
*
* @param amount
*/
validateStakeAmount(amount: BN): void {
// TODO implement
return;
}

// endregion

/** @inheritdoc */
initBuilder(tx: Tx): this {
// super.initBuilder(tx);
return this;
}

// TODO Implement
static verifyTxType(tx: Tx): tx is avaxSdkPvmSerial.AddPermissionlessValidatorTx {
return true;
}

verifyTxType(tx: Tx): tx is avaxSdkPvmSerial.AddPermissionlessValidatorTx {
return PermissionlessValidatorTxBuilder.verifyTxType(tx);
}

/**
*
* @protected
*/
protected buildAvaxTransaction(): void {
// TODO Implement
}

/** @inheritdoc */
// protected async buildImplementation(): Promise<Transaction> {
protected async buildImplementation(): Promise<BaseTransaction> {
// TODO Implement
return this.transaction;
}

/** @inheritdoc */
protected fromImplementation(rawTransaction: string): BaseTransaction {
// TODO Implement
return this.transaction;
}

/** @inheritdoc */
protected signImplementation({ key }: BaseKey): BaseTransaction {
// TODO Implement
return this.transaction;
}

/** @inheritdoc */
validateAddress(address: BaseAddress, addressFormat?: string): void {
if (!utils.isValidAddress(address.address)) {
throw new BuildTransactionError('Invalid address');
}
}

/** @inheritdoc */
protected get transaction(): BaseTransaction {
return this._transaction;
}

protected set transaction(transaction: BaseTransaction) {
this._transaction = transaction;
}

/** @inheritdoc */
validateKey({ key }: BaseKey): void {
if (!new KeyPair({ prv: key })) {
throw new BuildTransactionError('Invalid key');
}
}

/**
* Check the raw transaction has a valid format in the blockchain context, throw otherwise.
*
* @param rawTransaction Transaction in any format
*/
validateRawTransaction(rawTransaction: string): void {
utils.validateRawTransaction(rawTransaction);
}

/** @inheritdoc */
validateTransaction(transaction?: Transaction): void {
// throw new NotImplementedError('validateTransaction not implemented');
}

/** @inheritdoc */
validateValue(value: BigNumber): void {
if (value.isLessThan(0)) {
throw new BuildTransactionError('Value cannot be less than zero');
}
}
}
Loading

0 comments on commit 4b6738b

Please sign in to comment.