Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(sdk-coin-avaxp): add avaxp new SDK #4361

Merged
merged 5 commits into from
Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading